20862: Add 'sdk/ruby-google-api-client/' from commit '2f4be67955e48bb65d008ecd9ff6da9...
[arvados.git] / sdk / ruby-google-api-client / spec / google / api_client / service_spec.rb
1 # encoding:utf-8
2
3 # Copyright 2013 Google Inc.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 #      http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 require 'spec_helper'
18
19 require 'google/api_client'
20 require 'google/api_client/service'
21
22 fixtures_path = File.expand_path('../../../fixtures', __FILE__)
23
24 RSpec.describe Google::APIClient::Service do
25   include ConnectionHelpers
26
27   APPLICATION_NAME = 'API Client Tests'
28
29   it 'should error out when called without an API name or version' do
30     expect(lambda do
31       Google::APIClient::Service.new
32     end).to raise_error(ArgumentError)
33   end
34
35   it 'should error out when called without an API version' do
36     expect(lambda do
37       Google::APIClient::Service.new('foo')
38     end).to raise_error(ArgumentError)
39   end
40
41   it 'should error out when the options hash is not a hash' do
42     expect(lambda do
43       Google::APIClient::Service.new('foo', 'v1', 42)
44     end).to raise_error(ArgumentError)
45   end
46
47   describe 'with the AdSense Management API' do
48
49     it 'should make a valid call for a method with no parameters' do
50       conn = stub_connection do |stub|
51         stub.get('/adsense/v1.3/adclients') do |env|
52           [200, {}, '{}']
53         end
54       end
55       adsense = Google::APIClient::Service.new(
56         'adsense',
57         'v1.3',
58         {
59           :application_name => APPLICATION_NAME,
60           :authenticated => false,
61           :connection => conn,
62           :cache_store => nil
63         }
64       )
65
66       req = adsense.adclients.list.execute()
67       conn.verify
68     end
69
70     it 'should make a valid call for a method with parameters' do
71       conn = stub_connection do |stub|
72         stub.get('/adsense/v1.3/adclients/1/adunits') do |env|
73           [200, {}, '{}']
74         end
75       end
76       adsense = Google::APIClient::Service.new(
77         'adsense',
78         'v1.3',
79         {
80           :application_name => APPLICATION_NAME,
81           :authenticated => false,
82           :connection => conn,
83           :cache_store => nil
84         }
85       )
86       req = adsense.adunits.list(:adClientId => '1').execute()
87     end
88
89     it 'should make a valid call for a deep method' do
90       conn = stub_connection do |stub|
91         stub.get('/adsense/v1.3/accounts/1/adclients') do |env|
92           [200, {}, '{}']
93         end
94       end
95       adsense = Google::APIClient::Service.new(
96         'adsense',
97         'v1.3',
98         {
99           :application_name => APPLICATION_NAME,
100           :authenticated => false,
101           :connection => conn,
102           :cache_store => nil
103         }
104       )
105       req = adsense.accounts.adclients.list(:accountId => '1').execute()
106     end
107
108     describe 'with no connection' do
109       before do
110         @adsense = Google::APIClient::Service.new('adsense', 'v1.3',
111           {:application_name => APPLICATION_NAME, :cache_store => nil})
112       end
113
114       it 'should return a resource when using a valid resource name' do
115         expect(@adsense.accounts).to be_a(Google::APIClient::Service::Resource)
116       end
117
118       it 'should throw an error when using an invalid resource name' do
119         expect(lambda do
120            @adsense.invalid_resource
121         end).to raise_error
122       end
123
124       it 'should return a request when using a valid method name' do
125         req = @adsense.adclients.list
126         expect(req).to be_a(Google::APIClient::Service::Request)
127         expect(req.method.id).to eq('adsense.adclients.list')
128         expect(req.parameters).to be_nil
129       end
130
131       it 'should throw an error when using an invalid method name' do
132         expect(lambda do
133            @adsense.adclients.invalid_method
134         end).to raise_error
135       end
136
137       it 'should return a valid request with parameters' do
138         req = @adsense.adunits.list(:adClientId => '1')
139         expect(req).to be_a(Google::APIClient::Service::Request)
140         expect(req.method.id).to eq('adsense.adunits.list')
141         expect(req.parameters).not_to be_nil
142         expect(req.parameters[:adClientId]).to eq('1')
143       end
144     end
145   end
146
147   describe 'with the Prediction API' do
148
149     it 'should make a valid call with an object body' do
150       conn = stub_connection do |stub|
151         stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
152           expect(env.body).to eq('{"id":"1"}')
153           [200, {}, '{}']
154         end
155       end
156       prediction = Google::APIClient::Service.new(
157         'prediction',
158         'v1.5',
159         {
160           :application_name => APPLICATION_NAME,
161           :authenticated => false,
162           :connection => conn,
163           :cache_store => nil
164         }
165       )
166       req = prediction.trainedmodels.insert(:project => '1').body({'id' => '1'}).execute()
167       conn.verify
168     end
169
170     it 'should make a valid call with a text body' do
171       conn = stub_connection do |stub|
172         stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
173           expect(env.body).to eq('{"id":"1"}')
174           [200, {}, '{}']
175         end
176       end
177       prediction = Google::APIClient::Service.new(
178         'prediction',
179         'v1.5',
180         {
181           :application_name => APPLICATION_NAME,
182           :authenticated => false,
183           :connection => conn,
184           :cache_store => nil
185         }
186       )
187       req = prediction.trainedmodels.insert(:project => '1').body('{"id":"1"}').execute()
188       conn.verify
189     end
190
191     describe 'with no connection' do
192       before do
193         @prediction = Google::APIClient::Service.new('prediction', 'v1.5',
194           {:application_name => APPLICATION_NAME, :cache_store => nil})
195       end
196
197       it 'should return a valid request with a body' do
198         req = @prediction.trainedmodels.insert(:project => '1').body({'id' => '1'})
199         expect(req).to be_a(Google::APIClient::Service::Request)
200         expect(req.method.id).to eq('prediction.trainedmodels.insert')
201         expect(req.body).to eq({'id' => '1'})
202         expect(req.parameters).not_to be_nil
203         expect(req.parameters[:project]).to eq('1')
204       end
205
206       it 'should return a valid request with a body when using resource name' do
207         req = @prediction.trainedmodels.insert(:project => '1').training({'id' => '1'})
208         expect(req).to be_a(Google::APIClient::Service::Request)
209         expect(req.method.id).to eq('prediction.trainedmodels.insert')
210         expect(req.training).to eq({'id' => '1'})
211         expect(req.parameters).not_to be_nil
212         expect(req.parameters[:project]).to eq('1')
213       end
214     end
215   end
216
217   describe 'with the Drive API' do
218
219     before do
220       @metadata = {
221         'title' => 'My movie',
222         'description' => 'The best home movie ever made'
223       }
224       @file = File.expand_path('files/sample.txt', fixtures_path)
225       @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
226     end
227
228     it 'should make a valid call with an object body and media upload' do
229       conn = stub_connection do |stub|
230         stub.post('/upload/drive/v2/files?uploadType=multipart') do |env|
231           expect(env.body).to be_a Faraday::CompositeReadIO
232           [200, {}, '{}']
233         end
234       end
235       drive = Google::APIClient::Service.new(
236         'drive',
237         'v2',
238         {
239           :application_name => APPLICATION_NAME,
240           :authenticated => false,
241           :connection => conn,
242           :cache_store => nil
243         }
244       )
245       req = drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media).execute()
246       conn.verify
247     end
248
249     describe 'with no connection' do
250       before do
251         @drive = Google::APIClient::Service.new('drive', 'v2',
252           {:application_name => APPLICATION_NAME, :cache_store => nil})
253       end
254
255       it 'should return a valid request with a body and media upload' do
256         req = @drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media)
257         expect(req).to be_a(Google::APIClient::Service::Request)
258         expect(req.method.id).to eq('drive.files.insert')
259         expect(req.body).to eq(@metadata)
260         expect(req.media).to eq(@media)
261         expect(req.parameters).not_to be_nil
262         expect(req.parameters[:uploadType]).to eq('multipart')
263       end
264
265       it 'should return a valid request with a body and media upload when using resource name' do
266         req = @drive.files.insert(:uploadType => 'multipart').file(@metadata).media(@media)
267         expect(req).to be_a(Google::APIClient::Service::Request)
268         expect(req.method.id).to eq('drive.files.insert')
269         expect(req.file).to eq(@metadata)
270         expect(req.media).to eq(@media)
271         expect(req.parameters).not_to be_nil
272         expect(req.parameters[:uploadType]).to eq('multipart')
273       end
274     end
275   end
276
277   describe 'with the Discovery API' do
278     it 'should make a valid end-to-end request' do
279       discovery = Google::APIClient::Service.new('discovery', 'v1',
280           {:application_name => APPLICATION_NAME, :authenticated => false,
281            :cache_store => nil})
282       result = discovery.apis.get_rest(:api => 'discovery', :version => 'v1').execute
283       expect(result).not_to be_nil
284       expect(result.data.name).to eq('discovery')
285       expect(result.data.version).to eq('v1')
286     end
287   end
288 end
289
290
291 RSpec.describe Google::APIClient::Service::Result do
292
293   describe 'with the plus API' do
294     before do
295       @plus = Google::APIClient::Service.new('plus', 'v1',
296           {:application_name => APPLICATION_NAME, :cache_store => nil})
297       @reference = Google::APIClient::Reference.new({
298         :api_method => @plus.activities.list.method,
299         :parameters => {
300           'userId' => 'me',
301           'collection' => 'public',
302           'maxResults' => 20
303         }
304       })
305       @request = @plus.activities.list(:userId => 'me', :collection => 'public',
306         :maxResults => 20)
307
308       # Response double
309       @response = double("response")
310       allow(@response).to receive(:status).and_return(200)
311       allow(@response).to receive(:headers).and_return({
312         'etag' => '12345',
313         'x-google-apiary-auth-scopes' =>
314           'https://www.googleapis.com/auth/plus.me',
315         'content-type' => 'application/json; charset=UTF-8',
316         'date' => 'Mon, 23 Apr 2012 00:00:00 GMT',
317         'cache-control' => 'private, max-age=0, must-revalidate, no-transform',
318         'server' => 'GSE',
319         'connection' => 'close'
320       })
321     end
322
323     describe 'with a next page token' do
324       before do
325         @body = <<-END_OF_STRING
326           {
327             "kind": "plus#activityFeed",
328             "etag": "FOO",
329             "nextPageToken": "NEXT+PAGE+TOKEN",
330             "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
331             "nextLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN",
332             "title": "Plus Public Activity Feed for ",
333             "updated": "2012-04-23T00:00:00.000Z",
334             "id": "123456790",
335             "items": []
336           }
337           END_OF_STRING
338         allow(@response).to receive(:body).and_return(@body)
339         base_result = Google::APIClient::Result.new(@reference, @response)
340         @result = Google::APIClient::Service::Result.new(@request, base_result)
341       end
342
343       it 'should indicate a successful response' do
344         expect(@result.error?).to be_falsey
345       end
346
347       it 'should return the correct next page token' do
348         expect(@result.next_page_token).to eq('NEXT+PAGE+TOKEN')
349       end
350
351       it 'generate a correct request when calling next_page' do
352         next_page_request = @result.next_page
353         expect(next_page_request.parameters).to include('pageToken')
354         expect(next_page_request.parameters['pageToken']).to eq('NEXT+PAGE+TOKEN')
355         @request.parameters.each_pair do |param, value|
356           expect(next_page_request.parameters[param]).to eq(value)
357         end
358       end
359
360       it 'should return content type correctly' do
361         expect(@result.media_type).to eq('application/json')
362       end
363
364       it 'should return the body correctly' do
365         expect(@result.body).to eq(@body)
366       end
367
368       it 'should return the result data correctly' do
369         expect(@result.data?).to be_truthy
370         expect(@result.data.class.to_s).to eq(
371             'Google::APIClient::Schema::Plus::V1::ActivityFeed'
372         )
373         expect(@result.data.kind).to eq('plus#activityFeed')
374         expect(@result.data.etag).to eq('FOO')
375         expect(@result.data.nextPageToken).to eq('NEXT+PAGE+TOKEN')
376         expect(@result.data.selfLink).to eq(
377             'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
378         )
379         expect(@result.data.nextLink).to eq(
380             'https://www.googleapis.com/plus/v1/people/foo/activities/public?' +
381             'maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN'
382         )
383         expect(@result.data.title).to eq('Plus Public Activity Feed for ')
384         expect(@result.data.id).to eq("123456790")
385         expect(@result.data.items).to be_empty
386       end
387     end
388
389     describe 'without a next page token' do
390       before do
391         @body = <<-END_OF_STRING
392           {
393             "kind": "plus#activityFeed",
394             "etag": "FOO",
395             "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
396             "title": "Plus Public Activity Feed for ",
397             "updated": "2012-04-23T00:00:00.000Z",
398             "id": "123456790",
399             "items": []
400           }
401           END_OF_STRING
402         allow(@response).to receive(:body).and_return(@body)
403         base_result = Google::APIClient::Result.new(@reference, @response)
404         @result = Google::APIClient::Service::Result.new(@request, base_result)
405       end
406
407       it 'should not return a next page token' do
408         expect(@result.next_page_token).to eq(nil)
409       end
410
411       it 'should return content type correctly' do
412         expect(@result.media_type).to eq('application/json')
413       end
414
415       it 'should return the body correctly' do
416         expect(@result.body).to eq(@body)
417       end
418
419       it 'should return the result data correctly' do
420         expect(@result.data?).to be_truthy
421         expect(@result.data.class.to_s).to eq(
422             'Google::APIClient::Schema::Plus::V1::ActivityFeed'
423         )
424         expect(@result.data.kind).to eq('plus#activityFeed')
425         expect(@result.data.etag).to eq('FOO')
426         expect(@result.data.selfLink).to eq(
427             'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
428         )
429         expect(@result.data.title).to eq('Plus Public Activity Feed for ')
430         expect(@result.data.id).to eq("123456790")
431         expect(@result.data.items).to be_empty
432       end
433     end
434
435     describe 'with JSON error response' do
436       before do
437         @body = <<-END_OF_STRING
438          {
439           "error": {
440            "errors": [
441             {
442              "domain": "global",
443              "reason": "parseError",
444              "message": "Parse Error"
445             }
446            ],
447            "code": 400,
448            "message": "Parse Error"
449           }
450          }
451          END_OF_STRING
452         allow(@response).to receive(:body).and_return(@body)
453         allow(@response).to receive(:status).and_return(400)
454         base_result = Google::APIClient::Result.new(@reference, @response)
455         @result = Google::APIClient::Service::Result.new(@request, base_result)
456       end
457
458       it 'should return error status correctly' do
459         expect(@result.error?).to be_truthy
460       end
461
462       it 'should return the correct error message' do
463         expect(@result.error_message).to eq('Parse Error')
464       end
465
466       it 'should return the body correctly' do
467         expect(@result.body).to eq(@body)
468       end
469     end
470
471     describe 'with 204 No Content response' do
472       before do
473         allow(@response).to receive(:body).and_return('')
474         allow(@response).to receive(:status).and_return(204)
475         allow(@response).to receive(:headers).and_return({})
476         base_result = Google::APIClient::Result.new(@reference, @response)
477         @result = Google::APIClient::Service::Result.new(@request, base_result)
478       end
479
480       it 'should indicate no data is available' do
481         expect(@result.data?).to be_falsey
482       end
483
484       it 'should return nil for data' do
485         expect(@result.data).to eq(nil)
486       end
487
488       it 'should return nil for media_type' do
489         expect(@result.media_type).to eq(nil)
490       end
491     end
492   end
493 end
494
495 RSpec.describe Google::APIClient::Service::BatchRequest do
496   
497   include ConnectionHelpers
498   
499   context 'with a service connection' do
500     before do
501       @conn = stub_connection do |stub|
502         stub.post('/batch') do |env|
503           [500, {'Content-Type' => 'application/json'}, '{}']
504         end
505       end
506       @discovery = Google::APIClient::Service.new('discovery', 'v1',
507           {:application_name => APPLICATION_NAME, :authorization => nil,
508            :cache_store => nil, :connection => @conn})
509       @calls = [
510         @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
511         @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
512       ]
513     end
514
515     it 'should use the service connection' do
516       batch = @discovery.batch(@calls) do
517       end
518       batch.execute
519       @conn.verify
520     end  
521   end
522   
523   describe 'with the discovery API' do
524     before do
525       @discovery = Google::APIClient::Service.new('discovery', 'v1',
526           {:application_name => APPLICATION_NAME, :authorization => nil,
527            :cache_store => nil})
528     end
529
530     describe 'with two valid requests' do
531       before do
532         @calls = [
533           @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
534           @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
535         ]
536       end
537
538       it 'should execute both when using a global callback' do
539         block_called = 0
540         batch = @discovery.batch(@calls) do |result|
541           block_called += 1
542           expect(result.status).to eq(200)
543         end
544
545         batch.execute
546         expect(block_called).to eq(2)
547       end
548
549       it 'should execute both when using individual callbacks' do
550         call1_returned, call2_returned = false, false
551         batch = @discovery.batch
552
553         batch.add(@calls[0]) do |result|
554           call1_returned = true
555           expect(result.status).to eq(200)
556           expect(result.call_index).to eq(0)
557         end
558
559         batch.add(@calls[1]) do |result|
560           call2_returned = true
561           expect(result.status).to eq(200)
562           expect(result.call_index).to eq(1)
563         end
564
565         batch.execute
566         expect(call1_returned).to eq(true)
567         expect(call2_returned).to eq(true)
568       end
569     end
570
571     describe 'with a valid request and an invalid one' do
572       before do
573         @calls = [
574           @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
575           @discovery.apis.get_rest(:api => 'invalid', :version => 'invalid')
576         ]
577       end
578
579       it 'should execute both when using a global callback' do
580         block_called = 0
581         batch = @discovery.batch(@calls) do |result|
582           block_called += 1
583           if result.call_index == 0
584             expect(result.status).to eq(200)
585           else
586             expect(result.status).to be >= 400
587             expect(result.status).to be < 500
588           end
589         end
590
591         batch.execute
592         expect(block_called).to eq(2)
593       end
594
595       it 'should execute both when using individual callbacks' do
596         call1_returned, call2_returned = false, false
597         batch = @discovery.batch
598
599         batch.add(@calls[0]) do |result|
600           call1_returned = true
601           expect(result.status).to eq(200)
602           expect(result.call_index).to eq(0)
603         end
604
605         batch.add(@calls[1]) do |result|
606           call2_returned = true
607           expect(result.status).to be >= 400
608           expect(result.status).to be < 500
609           expect(result.call_index).to eq(1)
610         end
611
612         batch.execute
613         expect(call1_returned).to eq(true)
614         expect(call2_returned).to eq(true)
615       end      
616     end
617   end
618 end