baidu-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +15 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +17 -0
  7. data/Guardfile +6 -0
  8. data/HISTORY.md +2 -0
  9. data/LICENSE.txt +22 -0
  10. data/README.md +161 -0
  11. data/Rakefile +4 -0
  12. data/baidu-pcs.gemspec +24 -0
  13. data/lib/baidu/configure.rb +27 -0
  14. data/lib/baidu/core.rb +19 -0
  15. data/lib/baidu/errors/error.rb +22 -0
  16. data/lib/baidu/oauth.rb +11 -0
  17. data/lib/baidu/oauth/client.rb +66 -0
  18. data/lib/baidu/oauth/flow/base.rb +44 -0
  19. data/lib/baidu/oauth/flow/code.rb +69 -0
  20. data/lib/baidu/oauth/flow/device.rb +75 -0
  21. data/lib/baidu/pcs.rb +19 -0
  22. data/lib/baidu/pcs/client.rb +1090 -0
  23. data/lib/baidu/session.rb +36 -0
  24. data/lib/baidu/support/cacert.pem +37 -0
  25. data/lib/baidu/support/request.rb +127 -0
  26. data/lib/baidu/support/util.rb +67 -0
  27. data/lib/baidu/version.rb +5 -0
  28. data/spec/baidu/core_spec.rb +20 -0
  29. data/spec/baidu/oauth/client_spec.rb +199 -0
  30. data/spec/baidu/pcs/client_spec.rb +878 -0
  31. data/spec/baidu/session_spec.rb +27 -0
  32. data/spec/baidu/support/request_spec.rb +58 -0
  33. data/spec/baidu/support/util_spec.rb +48 -0
  34. data/spec/fixtures/add_task.json +1 -0
  35. data/spec/fixtures/cancel_task.json +3 -0
  36. data/spec/fixtures/copy.json +7 -0
  37. data/spec/fixtures/delete.json +3 -0
  38. data/spec/fixtures/diff.json +17 -0
  39. data/spec/fixtures/empty.json +1 -0
  40. data/spec/fixtures/get_token_code.json +8 -0
  41. data/spec/fixtures/get_token_device.json +8 -0
  42. data/spec/fixtures/list.json +11 -0
  43. data/spec/fixtures/list_task_0.json +1 -0
  44. data/spec/fixtures/list_task_1.json +1 -0
  45. data/spec/fixtures/listrecycle.json +23 -0
  46. data/spec/fixtures/logo.png +0 -0
  47. data/spec/fixtures/meta.json +11 -0
  48. data/spec/fixtures/mkdir.json +7 -0
  49. data/spec/fixtures/move.json +8 -0
  50. data/spec/fixtures/query_task_0.json +24 -0
  51. data/spec/fixtures/query_task_1.json +22 -0
  52. data/spec/fixtures/quota.json +5 -0
  53. data/spec/fixtures/rapidupload.json +10 -0
  54. data/spec/fixtures/refresh_token.json +8 -0
  55. data/spec/fixtures/restore.json +1 -0
  56. data/spec/fixtures/search.json +11 -0
  57. data/spec/fixtures/stream_list.json +16 -0
  58. data/spec/fixtures/streaming.m3u8 +5 -0
  59. data/spec/fixtures/upload.json +9 -0
  60. data/spec/fixtures/upload_block.json +4 -0
  61. data/spec/fixtures/user_and_device_code.json +8 -0
  62. data/spec/spec_helper.rb +66 -0
  63. metadata +169 -0
@@ -0,0 +1,878 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'baidu/pcs'
5
+
6
+ describe Baidu::PCS do
7
+ let(:target_file) { ft('logo.png') }
8
+
9
+ before :all do
10
+ Baidu.config do |c|
11
+ c.pcs_dir_name = 'Backups'
12
+ end
13
+ @client = Baidu::PCS::Client.new('ATOKEN')
14
+ end
15
+
16
+ describe '.initialize' do
17
+ it 'assgins access token and default site' do
18
+ expect(Baidu::PCS::SITE).to eq('https://pcs.baidu.com')
19
+ expect(Baidu::PCS::UPLOAD_SITE).to eq('https://c.pcs.baidu.com')
20
+ expect(Baidu::PCS::DOWNLOAD_SITE).to eq('https://d.pcs.baidu.com')
21
+ expect(Baidu::PCS::BASE_PATH).to eq('/rest/2.0/pcs')
22
+ expect(@client.instance_variable_get('@site')).to eq('https://pcs.baidu.com')
23
+ expect(@client.instance_variable_get('@access_token')).to eq('ATOKEN')
24
+ end
25
+
26
+ it 'assgins access token with session' do
27
+ session = Baidu::Session.new
28
+ session.access_token = 'ATOKEN_SESSION'
29
+ client = Baidu::PCS::Client.new(session)
30
+ expect(client.instance_variable_get(:@access_token)).to eq('ATOKEN_SESSION')
31
+ end
32
+
33
+ it 'raises argument error' do
34
+ expect { Baidu::PCS::Client.new(nil) }.to raise_error(ArgumentError, 'access_token must not be blank')
35
+ expect {
36
+ Baidu::PCS::Client.new('ATOKEN', nil)
37
+ }.to raise_error(ArgumentError, 'dir_name must not be blank')
38
+ end
39
+
40
+ it 'uses app name from method param' do
41
+ Baidu.pcs_dir_name = 'Backups'
42
+ c = Baidu::PCS::Client.new('ATOKEN', 'Backups2')
43
+ expect(c.instance_variable_get(:@dir_name)).to eq('Backups2')
44
+ end
45
+ end
46
+
47
+ describe '#quota' do
48
+ before do
49
+ stub_get(:pcs, '/quota', method: 'info', access_token: 'ATOKEN').to_return(status: 200, body: ft('quota.json'))
50
+ end
51
+
52
+ it 'requests quota api' do
53
+ @client.quota
54
+ a_get(:pcs, '/quota', method: 'info', access_token: 'ATOKEN').should have_been_made
55
+ end
56
+
57
+ it 'responses quota info' do
58
+ result = @client.quota
59
+ expect(result).to be_instance_of(Hash)
60
+ expect(result).to have_key(:quota)
61
+ expect(result).to have_key(:used)
62
+ end
63
+ end
64
+
65
+ describe '#upload' do
66
+ describe 'single file' do
67
+ let(:file) { ft('logo.png') }
68
+ let(:url) { /pcs.baidu.com\/rest\/2.0\/pcs\// }
69
+ let(:uploadquery) { { method: 'upload', access_token: 'ATOKEN', ondup: 'newcopy' } }
70
+
71
+ before do
72
+ IO.stub(:binread).and_return('')
73
+ stub_request(:post, url)
74
+ end
75
+
76
+ it 'validates method parmas' do
77
+ expect { @client.upload('/tmp/file.txt') }.to raise_error(ArgumentError, 'file must be an instance of File')
78
+ end
79
+
80
+ context 'without block_upload' do
81
+ it 'uploads 256MB file' do
82
+ file.stub(:size).and_return 256*1024*1024
83
+ @client.upload file, block_upload: false
84
+ a_request(:post, url).should have_been_made.times(1)
85
+ a_request(:post, url).with(query: hash_including({ method: 'upload' })).should have_been_made.times(1)
86
+ a_request(:post, url).with(query: hash_including({ type: 'tmpfile' })).should have_been_made.times(0)
87
+ a_request(:post, url).with(query: hash_including({ method: 'createsuperfile' })).should have_been_made.times(0)
88
+ end
89
+
90
+ it 'uploads 2GB file' do
91
+ file.stub(:size).and_return 2*1024*1024*1024
92
+ expect { @client.upload(file, block_upload: false) }.not_to raise_error
93
+ end
94
+
95
+ it 'uploads 2.1GB file' do
96
+ file.stub(:size).and_return 2.1*1024*1024*1024
97
+ expect { @client.upload(file, block_upload: false) }.to raise_error(IOError, 'file is too large (larger than 2G)')
98
+ end
99
+ end
100
+
101
+ context 'with block_upload' do
102
+ it 'uploads 7MB file' do
103
+ file.stub(:size).and_return 7*1024*1024
104
+ @client.upload file, block_upload: true
105
+ a_request(:post, url).should have_been_made.times(1)
106
+ a_request(:post, url).with(query: hash_including({ method: 'upload' })).should have_been_made.times(1)
107
+ a_request(:post, url).with(query: hash_including({ type: 'tmpfile' })).should_not have_been_made
108
+ a_request(:post, url).with(query: hash_including({ method: 'createsuperfile' })).should_not have_been_made
109
+ end
110
+
111
+ it 'uploads 8MB file' do
112
+ file.stub(:size).and_return 8*1024*1024
113
+ @client.upload file, block_upload: true
114
+ a_request(:post, url).should have_been_made.times(3)
115
+ a_request(:post, url).with(query: hash_including({ method: 'upload' })).should have_been_made.times(2)
116
+ a_request(:post, url).with(query: hash_including({ type: 'tmpfile' })).should have_been_made.times(2)
117
+ a_request(:post, url).with(query: hash_including({ method: 'createsuperfile' })).should have_been_made.times(1)
118
+ end
119
+
120
+ it 'uploads 22MB file' do
121
+ file.stub(:size).and_return 22*1024*1024
122
+ @client.upload file, block_upload: true
123
+ a_request(:post, url).should have_been_made.times(7)
124
+ a_request(:post, url).with(query: hash_including({ method: 'upload' })).should have_been_made.times(6)
125
+ a_request(:post, url).with(query: hash_including({ type: 'tmpfile' })).should have_been_made.times(6)
126
+ a_request(:post, url).with(query: hash_including({ method: 'createsuperfile' })).should have_been_made.times(1)
127
+ end
128
+
129
+ it 'is waiting for retry'
130
+ # it 'is waiting for retry' do
131
+ # allow(Kernel).to receive(:sleep)
132
+ # allow(file).to receive(:size).and_return(256*1024*1024)
133
+ # allow(@client).to receive(:upload_block).and_raise(ArgumentError)
134
+ # begin
135
+ # @client.upload file, retry_waitsec: 1, retry_times: 2
136
+ # rescue
137
+ # end
138
+ # expect(@client).to receive(:upload_block).exactly(2).times
139
+ # # expect(Kernel).to receive(:sleep).exactly(2).times
140
+ # end
141
+ end
142
+
143
+ it 'sets right request header' do
144
+ query = URI.encode_www_form( { path: '/apps/Backups/api测试/图片/标识.png' }.merge uploadquery )
145
+ stub_post(:pcs_upload, '/file?' + query).to_return(status: 200, body: ft('upload.json'))
146
+
147
+ @client.upload(target_file, path: 'api测试/图片/标识.png')
148
+ a_post(:pcs_upload, '/file?' + query).should have_been_made
149
+ end
150
+
151
+ it 'overwrite existing file' do
152
+ uploadquery[:ondup] = 'overwrite'
153
+ query = URI.encode_www_form( { path: '/apps/Backups/a/apitest.png' }.merge uploadquery )
154
+ stub_post(:pcs_upload, '/file?' + query).to_return(status: 200, body: ft('upload.json'))
155
+
156
+ @client.upload(target_file, path: 'a/apitest.png', overwrite: true)
157
+ a_post(:pcs_upload, '/file?' + query).should have_been_made
158
+ end
159
+
160
+ it 'raise exception when path length is too long' do
161
+ expect {
162
+ @client.upload(target_file, path: 'a'*1001) # name bytesize > 1000
163
+ }.to raise_error(ArgumentError, 'path length must not be greater than 1000')
164
+
165
+ query = URI.encode_www_form( { path: ('/apps/Backups/' + 'a'*1000) }.merge uploadquery )
166
+ stub_post(:pcs_upload, '/file?' + query)
167
+ expect { @client.upload(target_file, path: 'a'*1000) }.not_to raise_error
168
+ end
169
+ end
170
+ end
171
+
172
+ describe '#upload_block' do
173
+ let(:query) {
174
+ URI.encode_www_form({
175
+ method: 'upload',
176
+ access_token: 'ATOKEN',
177
+ type: 'tmpfile'
178
+ })
179
+ }
180
+
181
+ it 'sets blocked upload params type tmpfile' do
182
+ stub_post(:pcs_upload, '/file?' + query).to_return(status: 200, body: ft('upload_block.json'))
183
+ @client.upload_block(IO.read target_file, 10)
184
+ a_post(:pcs_upload, '/file?' + query).should have_been_made
185
+ end
186
+
187
+ end
188
+
189
+ describe '#create_super_file' do
190
+ let(:uploadquery) { {
191
+ method: 'createsuperfile',
192
+ access_token: 'ATOKEN',
193
+ ondup: 'newcopy'
194
+ } }
195
+
196
+ let(:block_list) { %w[abc def ghi] }
197
+
198
+ it 'overwrite existing file' do
199
+ uploadquery[:ondup] = 'overwrite'
200
+ query = URI.encode_www_form( { path: '/apps/Backups/a/apitest.png' }.merge uploadquery )
201
+ param = JSON.dump({ block_list: block_list })
202
+ stub_post(:pcs, '/file?' + query, param: param).to_return(status: 200, body: ft('upload.json'))
203
+
204
+ @client.create_super_file(block_list, 'a/apitest.png', true)
205
+ a_post(:pcs, '/file?' + query, param: param).should have_been_made
206
+ end
207
+
208
+ it 'finish blocked file upload' do
209
+ query = URI.encode_www_form({ path: ('/apps/Backups/upload_block/标识.png') }.merge uploadquery)
210
+ param = JSON.dump({ block_list: block_list })
211
+ stub_post(:pcs, '/file?' + query, param: param).to_return(status: 200, body: ft('upload.json'))
212
+ @client.create_super_file(block_list, 'upload_block/标识.png')
213
+ a_post(:pcs, '/file?' + query, param: param).should have_been_made
214
+ end
215
+
216
+ it 'raise exception when path length is too long' do
217
+ expect {
218
+ @client.create_super_file(block_list, 'a'*1001) # name bytesize > 1000
219
+ }.to raise_error(ArgumentError, 'path length must not be greater than 1000')
220
+
221
+ query = URI.encode_www_form({ path: ('/apps/Backups/' + 'a'*1000) }.merge uploadquery)
222
+ stub_post(:pcs, '/file?' + query)
223
+ expect { @client.create_super_file(block_list, 'a'*1000) }.not_to raise_error
224
+ end
225
+
226
+ it 'raise exception when path is blank' do
227
+ expect { @client.create_super_file(block_list, ' ') }.to raise_error(ArgumentError, 'path must not be blank')
228
+ expect { @client.create_super_file(block_list, nil) }.to raise_error(ArgumentError, 'path must not be blank')
229
+ end
230
+ end
231
+
232
+ describe '#download' do
233
+ let(:qparams) { { method: 'download', access_token: 'ATOKEN', path: '/apps/Backups/api测试/图片/标识.png'} }
234
+ let(:query) { URI.encode_www_form(qparams) }
235
+
236
+ it 'download file with raw body' do
237
+ stub_get(:pcs_download, '/file?' + query).to_return(status: 200, body: ft('logo.png'))
238
+ @client.download('api测试/图片/标识.png')
239
+ a_get(:pcs_download, '/file?' + query).should have_been_made
240
+ end
241
+
242
+ it 'download specified range(0-4) of file' do
243
+ stub_get(:pcs_download, '/file?' + query)
244
+ @client.download('api测试/图片/标识.png', begin: 0, end: 4)
245
+ a_request(:get, /d.pcs.baidu.com.+\/file\?/).
246
+ with(query: hash_including(qparams), headers: { Range: 'bytes=0-4' }).should have_been_made
247
+ end
248
+
249
+ it 'download specified range(0-100) of file' do
250
+ stub_get(:pcs_download, '/file?' + query)
251
+ @client.download('api测试/图片/标识.png', end: 100)
252
+ a_request(:get, /d.pcs.baidu.com.+\/file\?/).
253
+ with(query: hash_including(qparams), headers: { Range: 'bytes=0-100' }).should have_been_made
254
+ end
255
+
256
+ it 'download specified range(100-) of file' do
257
+ stub_get(:pcs_download, '/file?' + query)
258
+ @client.download('api测试/图片/标识.png', begin: 100)
259
+ a_request(:get, /d.pcs.baidu.com.+\/file\?/).
260
+ with(query: hash_including(qparams), headers: { Range: 'bytes=100-' }).should have_been_made
261
+ end
262
+
263
+ it 'download specified range(100-200) of file' do
264
+ stub_get(:pcs_download, '/file?' + query)
265
+ @client.download('api测试/图片/标识.png', begin: 100, end: 200)
266
+ a_request(:get, /d.pcs.baidu.com.+\/file\?/).
267
+ with(query: hash_including(qparams), headers: { Range: 'bytes=100-200' }).should have_been_made
268
+ end
269
+
270
+ it 'download file chunked' do
271
+ stub_get(:pcs_download, '/file?' + query).to_return(status: 200, body: ft('logo.png'))
272
+ content = ''
273
+ @client.download('api测试/图片/标识.png') do |chunk|
274
+ content << chunk
275
+ end
276
+ a_get(:pcs_download, '/file?' + query).should have_been_made
277
+ expect(content).not_to be_empty
278
+ end
279
+
280
+ it 'download specified range(100-200) of file chunked' do
281
+ stub_get(:pcs_download, '/file?' + query).to_return(status: 200, body: ft('logo.png'))
282
+ content = ''
283
+ @client.download('api测试/图片/标识.png', begin: 100, end: 200) do |chunk|
284
+ content << chunk
285
+ end
286
+ a_request(:get, /d.pcs.baidu.com.+\/file\?/).
287
+ with(query: hash_including(qparams), headers: { Range: 'bytes=100-200' }).should have_been_made
288
+ expect(content).not_to be_empty
289
+ end
290
+ end
291
+
292
+ describe '#mkdir' do
293
+ let(:query) {
294
+ URI.encode_www_form({
295
+ method: 'mkdir',
296
+ access_token: 'ATOKEN',
297
+ path: '/apps/Backups/apitest'
298
+ })
299
+ }
300
+
301
+ it 'makes dir' do
302
+ stub_post(:pcs, '/file?' + query).to_return(status: 200, body: ft('mkdir.json'))
303
+ @client.mkdir('apitest')
304
+ a_post(:pcs, '/file?' + query).should have_been_made
305
+ end
306
+ end
307
+
308
+ describe '#meta' do
309
+ describe 'when single file or dir' do
310
+ let(:query) {
311
+ URI.encode_www_form({
312
+ method: 'meta',
313
+ access_token: 'ATOKEN',
314
+ path: '/apps/Backups/apitest'
315
+ })
316
+ }
317
+
318
+ it 'raise exception when path is blank' do
319
+ expect { @client.meta('') }.to raise_error(ArgumentError, 'path must not be blank')
320
+ expect { @client.meta(' ') }.to raise_error(ArgumentError, 'path must not be blank')
321
+ expect { @client.meta(nil) }.to raise_error(ArgumentError, 'path must be kind of String or Array')
322
+ end
323
+
324
+ it 'gets meta infomation' do
325
+ stub_get(:pcs, '/file?' + query).to_return(status: 200, body: ft('meta.json'))
326
+ @client.meta('apitest')
327
+ a_get(:pcs, '/file?' + query).should have_been_made
328
+ end
329
+ end
330
+
331
+ describe 'when multiple files or dirs' do
332
+ let(:query) {
333
+ URI.encode_www_form({
334
+ method: 'meta',
335
+ access_token: 'ATOKEN',
336
+ param: JSON.dump({list: [ { path: '/apps/Backups/a/b' },
337
+ { path: '/apps/Backups/c' },
338
+ { path: '/apps/Backups/d/e/f' } ]
339
+ })
340
+ })
341
+ }
342
+ let(:paths) { %w[a/b c d/e/f] }
343
+
344
+ it 'raise exception when path is blank' do
345
+ expect { @client.meta([]) }.to raise_error(ArgumentError, 'path(s) must not be empty')
346
+ expect { @client.meta(nil) }.to raise_error(ArgumentError, 'path must be kind of String or Array')
347
+ expect { @client.meta(['']) }.to raise_error(ArgumentError, 'path must not be blank')
348
+ expect { @client.meta(['a'*1001]) }.to raise_error(ArgumentError, 'path length must not be greater than 1000')
349
+ end
350
+
351
+ it 'gets meta infomation' do
352
+ stub_get(:pcs, '/file?' + query).to_return(status: 200, body: ft('meta.json'))
353
+ @client.meta(paths)
354
+ a_get(:pcs, '/file?' + query).should have_been_made
355
+ end
356
+ end
357
+ end
358
+
359
+ describe '#list' do
360
+ let(:query) {
361
+ URI.encode_www_form({
362
+ method: 'list',
363
+ access_token: 'ATOKEN',
364
+ path: '/apps/Backups/apitest'
365
+ })
366
+ }
367
+
368
+ it 'requests with default params' do
369
+ stub = stub_get(:pcs, '/file?' + query + '&by=name&order=desc').to_return(status: 200, body: ft('list.json'))
370
+ @client.list('apitest')
371
+ stub.should have_been_requested
372
+ end
373
+
374
+ it 'requests with options' do
375
+ stub = stub_get(:pcs, '/file?' + query + '&order=asc&by=time&limit=2-10').to_return(status: 200, body: ft('list.json'))
376
+ @client.list('apitest', order: 'asc', by: 'time', limit: '2-10')
377
+ stub.should have_been_requested
378
+ end
379
+ end
380
+
381
+ describe '#move' do
382
+ describe 'when from and to are both single paths' do
383
+ let(:query) {
384
+ URI.encode_www_form({
385
+ method: 'move',
386
+ access_token: 'ATOKEN'
387
+ })
388
+ }
389
+
390
+ it 'moves apitest to apitestnew' do
391
+ stub_post(:pcs, '/file?' + query, { from: '/apps/Backups/apitest', to: '/apps/Backups/apitestnew' })
392
+ .to_return(status: 200, body: ft('move.json'))
393
+ @client.move('apitest', 'apitestnew')
394
+ a_post(:pcs, '/file?' + query, { from: '/apps/Backups/apitest', to: '/apps/Backups/apitestnew' })
395
+ .should have_been_made
396
+ end
397
+
398
+ it 'raise exception when from or to is invalid' do
399
+ expect{ @client.move('a', %w[b]) }.to raise_error(ArgumentError, 'from and to must have the same type')
400
+ expect{ @client.move(nil, '') }.to raise_error(ArgumentError, 'from and to must be kind of String or Array')
401
+ expect{ @client.move('', 'b') }.to raise_error(ArgumentError, 'path must not be blank')
402
+ expect{ @client.move('a', '') }.to raise_error(ArgumentError, 'path must not be blank')
403
+ end
404
+ end
405
+
406
+ describe 'when from and to are both multiple paths' do
407
+ let(:query) {
408
+ URI.encode_www_form({
409
+ method: 'move',
410
+ access_token: 'ATOKEN',
411
+ })
412
+ }
413
+ let(:param) {
414
+ JSON.dump({ list: [{from: '/apps/Backups/a', to: '/apps/Backups/b'},
415
+ {from: '/apps/Backups/a/b/c', to: '/apps/Backups/abc'}]
416
+ })
417
+ }
418
+
419
+ it 'moves multiple paths to other paths' do
420
+ stub_post(:pcs, '/file?' + query, { param: param }).to_return(status: 200, body: ft('move.json'))
421
+ @client.move(%w[a a/b/c], %w[b abc])
422
+ a_post(:pcs, '/file?' + query, { param: param })
423
+ end
424
+
425
+ it 'raise exception when from or to is invalid' do
426
+ expect{ @client.move([], %w[a]) }.to raise_error(ArgumentError, 'from or to must not be empty')
427
+ expect{ @client.move(%w[a], []) }.to raise_error(ArgumentError, 'from or to must not be empty')
428
+ expect{ @client.move(%w[a], ['']) }.to raise_error(ArgumentError, 'path must not be blank')
429
+ expect{ @client.move(nil, ['']) }.to raise_error(ArgumentError, 'from and to must be kind of String or Array')
430
+ expect{ @client.move([''], nil) }.to raise_error(ArgumentError, 'from and to must have the same type')
431
+ expect{ @client.move(%w[a], %w[ab cd]) }.to raise_error(ArgumentError, 'from and to must have the same size')
432
+ end
433
+ end
434
+ end
435
+
436
+ describe '#copy' do
437
+ describe 'when from and to are both single paths' do
438
+ let(:query) {
439
+ URI.encode_www_form({
440
+ method: 'copy',
441
+ access_token: 'ATOKEN'
442
+ })
443
+ }
444
+
445
+ it 'copys apitest to apitestnew' do
446
+ stub_post(:pcs, '/file?' + query, { from: '/apps/Backups/apitest', to: '/apps/Backups/apitestcopy' })
447
+ .to_return(status: 200, body: ft('copy.json'))
448
+ @client.copy('apitest', 'apitestcopy')
449
+ a_post(:pcs, '/file?' + query, { from: '/apps/Backups/apitest', to: '/apps/Backups/apitestcopy' })
450
+ .should have_been_made
451
+ end
452
+
453
+ it 'raise exception when from or to is invalid' do
454
+ expect{ @client.copy('a', %w[b]) }.to raise_error(ArgumentError, 'from and to must have the same type')
455
+ expect{ @client.copy(nil, '') }.to raise_error(ArgumentError, 'from and to must be kind of String or Array')
456
+ expect{ @client.copy('', 'b') }.to raise_error(ArgumentError, 'path must not be blank')
457
+ expect{ @client.copy('a', '') }.to raise_error(ArgumentError, 'path must not be blank')
458
+ end
459
+ end
460
+
461
+ describe 'when from and to are both multiple paths' do
462
+ let(:query) {
463
+ URI.encode_www_form({
464
+ method: 'copy',
465
+ access_token: 'ATOKEN',
466
+ })
467
+ }
468
+ let(:param) {
469
+ JSON.dump({ list: [{from: '/apps/Backups/a', to: '/apps/Backups/b'},
470
+ {from: '/apps/Backups/a/b/c', to: '/apps/Backups/abc'}]
471
+ })
472
+ }
473
+
474
+ it 'copys multiple paths to other paths' do
475
+ stub_post(:pcs, '/file?' + query, { param: param }).to_return(status: 200, body: ft('copy.json'))
476
+ @client.copy(%w[a a/b/c], %w[b abc])
477
+ a_post(:pcs, '/file?' + query, { param: param })
478
+ end
479
+
480
+ it 'raise exception when from or to is invalid' do
481
+ expect{ @client.copy([], %w[a]) }.to raise_error(ArgumentError, 'from or to must not be empty')
482
+ expect{ @client.copy(%w[a], []) }.to raise_error(ArgumentError, 'from or to must not be empty')
483
+ expect{ @client.copy(%w[a], ['']) }.to raise_error(ArgumentError, 'path must not be blank')
484
+ expect{ @client.copy(nil, ['']) }.to raise_error(ArgumentError, 'from and to must be kind of String or Array')
485
+ expect{ @client.copy([''], nil) }.to raise_error(ArgumentError, 'from and to must have the same type')
486
+ expect{ @client.copy(%w[a], %w[ab cd]) }.to raise_error(ArgumentError, 'from and to must have the same size')
487
+ end
488
+ end
489
+ end
490
+
491
+ describe '#delete' do
492
+ describe 'when single file or dir' do
493
+ let(:query) {
494
+ URI.encode_www_form({
495
+ method: 'delete',
496
+ access_token: 'ATOKEN',
497
+ path: '/apps/Backups/apitest'
498
+ })
499
+ }
500
+
501
+ it 'raise exception when path is blank' do
502
+ expect { @client.meta('') }.to raise_error(ArgumentError, 'path must not be blank')
503
+ expect { @client.meta(' ') }.to raise_error(ArgumentError, 'path must not be blank')
504
+ expect { @client.meta(nil) }.to raise_error(ArgumentError, 'path must be kind of String or Array')
505
+ end
506
+
507
+ it 'delete file or dir' do
508
+ stub_get(:pcs, '/file?' + query).to_return(status: 200, body: ft('delete.json'))
509
+ @client.delete('apitest')
510
+ a_get(:pcs, '/file?' + query).should have_been_made
511
+ end
512
+ end
513
+
514
+ describe 'when multiple files or dirs' do
515
+ let(:query) {
516
+ URI.encode_www_form({
517
+ method: 'delete',
518
+ access_token: 'ATOKEN',
519
+ param: JSON.dump({list: [ { path: '/apps/Backups/a/b' },
520
+ { path: '/apps/Backups/c' },
521
+ { path: '/apps/Backups/d/e/f' } ]
522
+ })
523
+ })
524
+ }
525
+ let(:paths) { %w[a/b c d/e/f] }
526
+
527
+ it 'raise exception when path is blank' do
528
+ expect { @client.delete([]) }.to raise_error(ArgumentError, 'path(s) must not be empty')
529
+ expect { @client.delete(nil) }.to raise_error(ArgumentError, 'path must be kind of String or Array')
530
+ expect { @client.delete(['']) }.to raise_error(ArgumentError, 'path must not be blank')
531
+ expect { @client.delete(['a'*1001]) }.to raise_error(ArgumentError, 'path length must not be greater than 1000')
532
+ end
533
+
534
+ it 'delete files or dirs' do
535
+ stub_get(:pcs, '/file?' + query).to_return(status: 200, body: ft('delete.json'))
536
+ @client.delete(paths)
537
+ a_get(:pcs, '/file?' + query).should have_been_made
538
+ end
539
+ end
540
+ end
541
+
542
+ describe '#search' do
543
+ let(:query) {
544
+ URI.encode_www_form({
545
+ method: 'search',
546
+ access_token: 'ATOKEN',
547
+ path: '/apps/Backups/apitest',
548
+ wd: 'keyword'
549
+ })
550
+ }
551
+ it 'search with default params' do
552
+ stub_get(:pcs, '/file?' + query + '&re=0').to_return(status:200, body: ft('search.json'))
553
+ @client.search('apitest', 'keyword')
554
+ a_get(:pcs, '/file?' + query + '&re=0').should have_been_made
555
+ end
556
+
557
+ it 'search recursively' do
558
+ stub_get(:pcs, '/file?' + query + '&re=1').to_return(status:200, body: ft('search.json'))
559
+ @client.search('apitest', 'keyword', true)
560
+ a_get(:pcs, '/file?' + query + '&re=1').should have_been_made
561
+ end
562
+
563
+ it 'raise exception when path is blank' do
564
+ expect { @client.search('', 'keyword') }.to raise_error(ArgumentError, 'path must not be blank')
565
+ end
566
+ end
567
+
568
+ describe '#thumbnail' do
569
+ let(:query) {
570
+ URI.encode_www_form({
571
+ method: 'generate',
572
+ access_token: 'ATOKEN',
573
+ path: '/apps/Backups/apitest/logo.png',
574
+ width: 120,
575
+ height: 40
576
+ })
577
+ }
578
+
579
+ it 'raise error when path is blank' do
580
+ expect { @client.thumbnail('', 100, 200) }.to raise_error(ArgumentError, 'path must not be blank')
581
+ end
582
+
583
+ it 'requests with default params' do
584
+ stub = stub_get(:pcs, '/thumbnail?' + query + '&quality=100').to_return(status: 200, body: ft('logo.png'))
585
+ @client.thumbnail('apitest/logo.png', 120, 40)
586
+ stub.should have_been_requested
587
+ end
588
+
589
+ it 'processes by block' do
590
+ stub = stub_get(:pcs, '/thumbnail?' + query + '&quality=100').to_return(status: 200, body: ft('logo.png'))
591
+ content = ''
592
+ @client.thumbnail('apitest/logo.png', 120, 40) do |c|
593
+ content << c
594
+ end
595
+ stub.should have_been_requested
596
+ expect(content).not_to be_empty
597
+ end
598
+ end
599
+
600
+ describe '#diff' do
601
+ let(:query) {
602
+ URI.encode_www_form({
603
+ method: 'diff',
604
+ access_token: 'ATOKEN'
605
+ })
606
+ }
607
+
608
+ it 'requests with default params' do
609
+ stub = stub_get(:pcs, '/file?' + query + '&cursor=null').to_return(status: 200, body: ft('diff.json'))
610
+ @client.diff
611
+ stub.should have_been_requested
612
+ end
613
+
614
+ it 'requests with cursor' do
615
+ stub = stub_get(:pcs, '/file?' + query + '&cursor=mynewcursor').to_return(status: 200, body: ft('diff.json'))
616
+ @client.diff('mynewcursor')
617
+ stub.should have_been_requested
618
+ end
619
+ end
620
+
621
+ describe '#streaming' do
622
+ let(:query) {
623
+ URI.encode_www_form({
624
+ method: 'streaming',
625
+ access_token: 'ATOKEN',
626
+ path: '/apps/Backups/hi.mp4',
627
+ type: 'M3U8_480_360'
628
+ })
629
+ }
630
+
631
+ it 'raise error when path is blank' do
632
+ expect { @client.streaming('', 'M3U8_480_360') }.to raise_error(ArgumentError, 'path must not be blank')
633
+ end
634
+
635
+ it 'requests streaming content' do
636
+ stub = stub_get(:pcs, '/file?' + query).to_return(status: 200, body: ft('streaming.m3u8'))
637
+ @client.streaming('hi.mp4', 'M3U8_480_360') do |c|
638
+ end
639
+ stub.should have_been_requested
640
+ end
641
+ end
642
+
643
+ describe '#stream_list' do
644
+ let(:query) {
645
+ URI.encode_www_form({
646
+ method: 'list',
647
+ access_token: 'ATOKEN',
648
+ type: 'video'
649
+ })
650
+ }
651
+
652
+ it 'requests with default parmas' do
653
+ stub = stub_get(:pcs, '/stream?' + query).to_return(status: 200, body: ft('stream_list.json'))
654
+ @client.stream_list('video')
655
+ stub.should have_been_requested
656
+ end
657
+
658
+ it 'requests stream list' do
659
+ stub = stub_get(:pcs, '/stream?' + query + '&start=10&limit=34&filter_path=/apps/Backups').to_return(status: 200, body: ft('stream_list.json'))
660
+ @client.stream_list('video', start: 10, limit: 34, filter_path: '/apps/Backups')
661
+ stub.should have_been_requested
662
+ end
663
+ end
664
+
665
+ describe '#rapid_upload' do
666
+ let(:query) {
667
+ URI.encode_www_form({
668
+ method: 'rapidupload',
669
+ access_token: 'ATOKEN',
670
+ path: '/apps/Backups/my_goo_gl.mkv',
671
+ :'content-length' => 74818037,
672
+ :'content-md5' => 'xxx',
673
+ :'slice-md5' => 'yyy',
674
+ :'content-crc32' => 'zzz'
675
+ })
676
+ }
677
+
678
+ it 'checks and edit path' do
679
+ stub = stub_post(:pcs, '/file?' + query + '&ondup=newcopy').to_return(status: 200, body: ft('rapidupload.json'))
680
+ expect { @client.rapid_upload('', 1, '2', '3', '4') }.to raise_error(ArgumentError, 'path must not be blank')
681
+ @client.rapid_upload('my|goo>gl.mkv', 74818037, 'xxx', 'yyy', 'zzz')
682
+ stub.should have_been_requested
683
+ end
684
+
685
+ it 'requests with default params' do
686
+ stub = stub_post(:pcs, '/file?' + query + '&ondup=newcopy').to_return(status: 200, body: ft('rapidupload.json'))
687
+ @client.rapid_upload('my?goo>gl.mkv', 74818037, 'xxx', 'yyy', 'zzz')
688
+ stub.should have_been_requested
689
+ end
690
+ end
691
+
692
+ describe '#add_task' do
693
+ let(:query) {
694
+ URI.encode_www_form({
695
+ method: 'add_task',
696
+ access_token: 'ATOKEN',
697
+ source_url: 'http://test.com/1.png',
698
+ timeout: 3600
699
+ })
700
+ }
701
+
702
+ it 'requests with default params' do
703
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&save_path=/apps/Backups/1.png').to_return(status: 200, body: ft('add_task.json'))
704
+ @client.add_task('http://test.com/1.png', save_path: '1.png')
705
+ stub.should have_been_requested
706
+ end
707
+
708
+ it 'requests with params' do
709
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&save_path=/apps/Backups/2.png&timeout=100&expires=200&rate_limit=300&callback=cb')
710
+ .to_return(status: 200, body: ft('add_task.json'))
711
+ @client.add_task('http://test.com/1.png', save_path: '2.png', timeout: 100, expires: 200, rate_limit: 300, callback: 'cb')
712
+ stub.should have_been_requested
713
+ end
714
+
715
+ it 'set save_path automatically' do
716
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&save_path=/apps/Backups/1.png').to_return(status: 200, body: ft('add_task.json'))
717
+ @client.add_task('http://test.com/1.png')
718
+ stub.should have_been_requested
719
+ end
720
+
721
+ it 'set save_path automatically' do
722
+ stub_request(:post, /https:\/\/pcs.baidu.com\/rest\/2.0\/pcs\/services\/cloud_dl/)
723
+ @client.add_task('http://test.com/')
724
+ q = 'save_path=/apps/Backups/' + Time.now.localtime.to_s[0..7]
725
+ WebMock.should have_requested(:post, /https:\/\/pcs.baidu.com\/rest\/2.0\/pcs\/services\/cloud_dl/).with { |req| req.uri.query.include? q }
726
+ end
727
+ end
728
+
729
+ describe '#query_task' do
730
+ let(:query) {
731
+ URI.encode_www_form({
732
+ method: 'query_task',
733
+ access_token: 'ATOKEN'
734
+ })
735
+ }
736
+
737
+ it 'requests with default params' do
738
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&task_ids=123')
739
+ .to_return(status: 200, body: ft('query_task_1.json'))
740
+ @client.query_task('123')
741
+ stub.should have_been_requested
742
+ end
743
+
744
+ it 'requests with params' do
745
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&task_ids=456,123&op_type=0&expires=10')
746
+ .to_return(status: 200, body: ft('query_task_0.json'))
747
+ @client.query_task([456, '123'], op_type: 0, expires: 10)
748
+ stub.should have_been_requested
749
+ end
750
+ end
751
+
752
+ describe '#list_task' do
753
+ let(:query) {
754
+ URI.encode_www_form({
755
+ method: 'list_task',
756
+ access_token: 'ATOKEN'
757
+ })
758
+ }
759
+
760
+ it 'requests with default params' do
761
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query)
762
+ .to_return(status: 200, body: ft('list_task_1.json'))
763
+ @client.list_task
764
+ stub.should have_been_requested
765
+ end
766
+
767
+ it 'requests with params' do
768
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&start=10&limit=20&asc=1&need_task_info=0' \
769
+ '&status=2&create_time=NULL,1111&source_url=su&save_path=2.png&expires=30')
770
+ .to_return(status: 200, body: ft('list_task_0.json'))
771
+ @client.list_task(start: 10, limit: 20, asc: 1, need_task_info: 0, status: 2,
772
+ create_time: 'NULL,1111', source_url: 'su', save_path: '2.png', expires: 30)
773
+ stub.should have_been_requested
774
+ end
775
+ end
776
+
777
+ describe '#cancel_task' do
778
+ let(:query) {
779
+ URI.encode_www_form({
780
+ method: 'cancel_task',
781
+ access_token: 'ATOKEN',
782
+ task_id: '48393833'
783
+ })
784
+ }
785
+
786
+ it 'requests with default params' do
787
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query).to_return(status: 200, body: ft('cancel_task.json'))
788
+ @client.cancel_task('48393833')
789
+ stub.should have_been_requested
790
+ end
791
+
792
+ it 'requests with params' do
793
+ stub = stub_post(:pcs, '/services/cloud_dl?' + query + '&expires=10').to_return(status: 200, body: ft('cancel_task.json'))
794
+ @client.cancel_task('48393833', 10)
795
+ stub.should have_been_requested
796
+ end
797
+ end
798
+
799
+ describe '#listrecycle' do
800
+ let(:query) {
801
+ URI.encode_www_form({
802
+ method: 'listrecycle',
803
+ access_token: 'ATOKEN'
804
+ })
805
+ }
806
+
807
+ it 'requests with default params' do
808
+ stub = stub_get(:pcs, '/file?' + query + '&start=0&limit=1000').to_return(status: 200, body: ft('listrecycle.json'))
809
+ @client.listrecycle
810
+ stub.should have_been_requested
811
+ end
812
+
813
+ it 'requests with params' do
814
+ stub = stub_get(:pcs, '/file?' + query + '&start=8&limit=30').to_return(status: 200, body: ft('listrecycle.json'))
815
+ @client.listrecycle(8, 30)
816
+ stub.should have_been_requested
817
+ end
818
+ end
819
+
820
+ describe '#restore' do
821
+ describe 'when single file or dir' do
822
+ let(:query) {
823
+ URI.encode_www_form({
824
+ method: 'restore',
825
+ access_token: 'ATOKEN',
826
+ fs_id: '8383838383'
827
+ })
828
+ }
829
+
830
+ it 'restore with fs_id' do
831
+ stub = stub_post(:pcs, '/file?' + query).to_return(status: 200, body: ft('restore.json'))
832
+ @client.restore('8383838383')
833
+ stub.should have_been_requested
834
+ end
835
+ end
836
+
837
+ describe 'when multiple files or dirs' do
838
+ let(:query) {
839
+ URI.encode_www_form({
840
+ method: 'restore',
841
+ access_token: 'ATOKEN',
842
+ param: JSON.dump({list: [ { fs_id: '333333' },
843
+ { fs_id: '444444' },
844
+ { fs_id: '555555' } ]
845
+ })
846
+ })
847
+ }
848
+ let(:fs_ids) { %w[333333 444444 555555] }
849
+
850
+ it 'restore with fs_id list' do
851
+ stub = stub_post(:pcs, '/file?' + query).to_return(status: 200, body: ft('restore.json'))
852
+ @client.restore(fs_ids)
853
+ stub.should have_been_requested
854
+ end
855
+ end
856
+
857
+ it 'restore with invalid fs_id(s)' do
858
+ expect { @client.restore({}) }.to raise_error(ArgumentError, 'fs_id(s) must be kind of String or Array')
859
+ expect { @client.restore(:invalid) }.to raise_error(ArgumentError, 'fs_id(s) must be kind of String or Array')
860
+ end
861
+ end
862
+
863
+ describe '#empty' do
864
+ let(:query) {
865
+ URI.encode_www_form({
866
+ method: 'delete',
867
+ access_token: 'ATOKEN',
868
+ type: 'recycle'
869
+ })
870
+ }
871
+
872
+ it 'empty recycle' do
873
+ stub = stub_post(:pcs, '/file?' + query).to_return(status: 200, body: ft('empty.json'))
874
+ @client.empty
875
+ stub.should have_been_requested
876
+ end
877
+ end
878
+ end