Minor cleanup on tests
[arvados.git] / spec / google / api_client / discovery_spec.rb
1 # encoding:utf-8
2
3 # Copyright 2010 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
18 require 'spec_helper'
19
20 require 'faraday'
21 require 'faraday/utils'
22 require 'multi_json'
23 require 'compat/multi_json'
24 require 'signet/oauth_1/client'
25 require 'google/api_client'
26 require 'google/api_client/version'
27
28 describe Google::APIClient do
29   include ConnectionHelpers
30   CLIENT = Google::APIClient.new(:application_name => 'API Client Tests') unless defined?(CLIENT)
31
32   after do
33     # Reset client to not-quite-pristine state
34     CLIENT.key = nil
35     CLIENT.user_ip = nil
36   end
37
38   it 'should raise a type error for bogus authorization' do
39     (lambda do
40       Google::APIClient.new(:application_name => 'API Client Tests', :authorization => 42)
41     end).should raise_error(TypeError)
42   end
43
44   it 'should not be able to retrieve the discovery document for a bogus API' do
45     (lambda do
46       CLIENT.discovery_document('bogus')
47     end).should raise_error(Google::APIClient::TransmissionError)
48     (lambda do
49       CLIENT.discovered_api('bogus')
50     end).should raise_error(Google::APIClient::TransmissionError)
51   end
52
53   it 'should raise an error for bogus services' do
54     (lambda do
55       CLIENT.discovered_api(42)
56     end).should raise_error(TypeError)
57   end
58
59   it 'should raise an error for bogus services' do
60     (lambda do
61       CLIENT.preferred_version(42)
62     end).should raise_error(TypeError)
63   end
64
65   it 'should raise an error for bogus methods' do
66     (lambda do
67       CLIENT.execute(42)
68     end).should raise_error(TypeError)
69   end
70
71   it 'should not return a preferred version for bogus service names' do
72     CLIENT.preferred_version('bogus').should == nil
73   end
74
75   describe 'with the prediction API' do
76     before do
77       CLIENT.authorization = nil
78       # The prediction API no longer exposes a v1, so we have to be
79       # careful about looking up the wrong API version.
80       @prediction = CLIENT.discovered_api('prediction', 'v1.2')
81     end
82
83     it 'should correctly determine the discovery URI' do
84       CLIENT.discovery_uri('prediction').should ===
85         'https://www.googleapis.com/discovery/v1/apis/prediction/v1/rest'
86     end
87
88     it 'should correctly determine the discovery URI if :user_ip is set' do
89       CLIENT.user_ip = '127.0.0.1'
90       
91       conn = stub_connection do |stub|
92         stub.get('/discovery/v1/apis/prediction/v1.2/rest?userIp=127.0.0.1') do |env|
93         end
94       end
95       CLIENT.execute(
96         :http_method => 'GET',
97         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
98         :authenticated => false,
99         :connection => conn
100       )
101       conn.verify
102     end
103
104     it 'should correctly determine the discovery URI if :key is set' do
105       CLIENT.key = 'qwerty'
106       conn = stub_connection do |stub|
107         stub.get('/discovery/v1/apis/prediction/v1.2/rest?key=qwerty') do |env|
108         end
109       end
110       request = CLIENT.execute(
111         :http_method => 'GET',
112         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
113         :authenticated => false,
114         :connection => conn
115         )
116         conn.verify
117     end
118
119     it 'should correctly determine the discovery URI if both are set' do
120       CLIENT.key = 'qwerty'
121       CLIENT.user_ip = '127.0.0.1'
122       conn = stub_connection do |stub|
123         stub.get('/discovery/v1/apis/prediction/v1.2/rest?key=qwerty&userIp=127.0.0.1') do |env|
124         end
125       end
126       request = CLIENT.execute(
127         :http_method => 'GET',
128         :uri => CLIENT.discovery_uri('prediction', 'v1.2'),
129         :authenticated => false,
130         :connection => conn
131         )
132         conn.verify
133     end
134
135     it 'should correctly generate API objects' do
136       CLIENT.discovered_api('prediction', 'v1.2').name.should == 'prediction'
137       CLIENT.discovered_api('prediction', 'v1.2').version.should == 'v1.2'
138       CLIENT.discovered_api(:prediction, 'v1.2').name.should == 'prediction'
139       CLIENT.discovered_api(:prediction, 'v1.2').version.should == 'v1.2'
140     end
141
142     it 'should discover methods' do
143       CLIENT.discovered_method(
144         'prediction.training.insert', 'prediction', 'v1.2'
145       ).name.should == 'insert'
146       CLIENT.discovered_method(
147         :'prediction.training.insert', :prediction, 'v1.2'
148       ).name.should == 'insert'
149       CLIENT.discovered_method(
150         'prediction.training.delete', 'prediction', 'v1.2'
151       ).name.should == 'delete'
152     end
153
154     it 'should define the origin API in discovered methods' do
155       CLIENT.discovered_method(
156         'prediction.training.insert', 'prediction', 'v1.2'
157       ).api.name.should == 'prediction'
158     end
159
160     it 'should not find methods that are not in the discovery document' do
161       CLIENT.discovered_method(
162         'prediction.bogus', 'prediction', 'v1.2'
163       ).should == nil
164     end
165
166     it 'should raise an error for bogus methods' do
167       (lambda do
168         CLIENT.discovered_method(42, 'prediction', 'v1.2')
169       end).should raise_error(TypeError)
170     end
171
172     it 'should raise an error for bogus methods' do
173       (lambda do
174         CLIENT.execute(:api_method => CLIENT.discovered_api('prediction', 'v1.2'))
175       end).should raise_error(TypeError)
176     end
177
178     it 'should correctly determine the preferred version' do
179       CLIENT.preferred_version('prediction').version.should_not == 'v1'
180       CLIENT.preferred_version(:prediction).version.should_not == 'v1'
181     end
182
183     it 'should return a batch path' do
184       CLIENT.discovered_api('prediction', 'v1.2').batch_path.should_not be_nil
185     end
186
187     it 'should generate valid requests' do
188       conn = stub_connection do |stub|
189         stub.post('/prediction/v1.2/training?data=12345') do |env|
190           env[:body].should == ''
191         end
192       end
193       request = CLIENT.execute(
194         :api_method => @prediction.training.insert,
195         :parameters => {'data' => '12345'},
196         :connection => conn
197       )
198       conn.verify
199     end
200
201     it 'should generate valid requests when parameter value includes semicolon' do
202       conn = stub_connection do |stub|
203         # semicolon (;) in parameter value was being converted to 
204         # bare ampersand (&) in 0.4.7. ensure that it gets converted 
205         # to a CGI-escaped semicolon (%3B) instead.
206         stub.post('/prediction/v1.2/training?data=12345%3B67890') do |env|
207           env[:body].should == ''
208         end
209       end
210       request = CLIENT.execute(
211         :api_method => @prediction.training.insert,
212         :parameters => {'data' => '12345;67890'},
213         :connection => conn
214       )
215       conn.verify
216     end
217
218     it 'should generate valid requests when multivalued parameters are passed' do
219       conn = stub_connection do |stub|
220          stub.post('/prediction/v1.2/training?data=1&data=2') do |env|
221            env.params['data'].should include('1', '2')
222          end
223        end
224       request = CLIENT.execute(
225         :api_method => @prediction.training.insert,
226         :parameters => {'data' => ['1', '2']},
227         :connection => conn
228       )
229       conn.verify
230     end
231
232     it 'should generate requests against the correct URIs' do
233       conn = stub_connection do |stub|
234          stub.post('/prediction/v1.2/training?data=12345') do |env|
235          end
236        end
237       request = CLIENT.execute(
238         :api_method => @prediction.training.insert,
239         :parameters => {'data' => '12345'},
240         :connection => conn
241       )
242       conn.verify
243     end
244
245     it 'should generate requests against the correct URIs' do
246       conn = stub_connection do |stub|
247         stub.post('/prediction/v1.2/training?data=12345') do |env|
248         end
249       end
250       request = CLIENT.execute(
251         :api_method => @prediction.training.insert,
252         :parameters => {'data' => '12345'},
253         :connection => conn
254       )
255       conn.verify
256     end
257
258     it 'should allow modification to the base URIs for testing purposes' do
259       # Using a new client instance here to avoid caching rebased discovery doc
260       prediction_rebase =
261         Google::APIClient.new(:application_name => 'API Client Tests').discovered_api('prediction', 'v1.2')
262       prediction_rebase.method_base =
263         'https://testing-domain.example.com/prediction/v1.2/'
264
265       conn = stub_connection do |stub|
266         stub.post('/prediction/v1.2/training') do |env|
267           env[:url].host.should == 'testing-domain.example.com'
268         end
269       end
270         
271       request = CLIENT.execute(
272         :api_method => prediction_rebase.training.insert,
273         :parameters => {'data' => '123'},
274         :connection => conn
275       )
276       conn.verify
277     end
278
279     it 'should generate OAuth 1 requests' do
280       CLIENT.authorization = :oauth_1
281       CLIENT.authorization.token_credential_key = '12345'
282       CLIENT.authorization.token_credential_secret = '12345'
283
284       conn = stub_connection do |stub|
285         stub.post('/prediction/v1.2/training?data=12345') do |env|
286           env[:request_headers].should have_key('Authorization')
287           env[:request_headers]['Authorization'].should =~ /^OAuth/
288         end
289       end
290
291       request = CLIENT.execute(
292         :api_method => @prediction.training.insert,
293         :parameters => {'data' => '12345'},
294         :connection => conn
295       )
296       conn.verify
297     end
298
299     it 'should generate OAuth 2 requests' do
300       CLIENT.authorization = :oauth_2
301       CLIENT.authorization.access_token = '12345'
302
303       conn = stub_connection do |stub|
304         stub.post('/prediction/v1.2/training?data=12345') do |env|
305           env[:request_headers].should have_key('Authorization')
306           env[:request_headers]['Authorization'].should =~ /^Bearer/
307         end
308       end
309
310       request = CLIENT.execute(
311         :api_method => @prediction.training.insert,
312         :parameters => {'data' => '12345'},
313         :connection => conn
314       )
315       conn.verify
316     end
317
318     it 'should not be able to execute improperly authorized requests' do
319       CLIENT.authorization = :oauth_1
320       CLIENT.authorization.token_credential_key = '12345'
321       CLIENT.authorization.token_credential_secret = '12345'
322       result = CLIENT.execute(
323         @prediction.training.insert,
324         {'data' => '12345'}
325       )
326       result.response.status.should == 401
327     end
328
329     it 'should not be able to execute improperly authorized requests' do
330       CLIENT.authorization = :oauth_2
331       CLIENT.authorization.access_token = '12345'
332       result = CLIENT.execute(
333         @prediction.training.insert,
334         {'data' => '12345'}
335       )
336       result.response.status.should == 401
337     end
338
339     it 'should not be able to execute improperly authorized requests' do
340       (lambda do
341         CLIENT.authorization = :oauth_1
342         CLIENT.authorization.token_credential_key = '12345'
343         CLIENT.authorization.token_credential_secret = '12345'
344         result = CLIENT.execute!(
345           @prediction.training.insert,
346           {'data' => '12345'}
347         )
348       end).should raise_error(Google::APIClient::ClientError)
349     end
350
351     it 'should not be able to execute improperly authorized requests' do
352       (lambda do
353         CLIENT.authorization = :oauth_2
354         CLIENT.authorization.access_token = '12345'
355         result = CLIENT.execute!(
356           @prediction.training.insert,
357           {'data' => '12345'}
358         )
359       end).should raise_error(Google::APIClient::ClientError)
360     end
361
362     it 'should correctly handle unnamed parameters' do
363       conn = stub_connection do |stub|
364         stub.post('/prediction/v1.2/training') do |env|
365           env[:request_headers].should have_key('Content-Type')
366           env[:request_headers]['Content-Type'].should == 'application/json'
367         end
368       end
369       CLIENT.authorization = :oauth_2
370       CLIENT.authorization.access_token = '12345'
371       CLIENT.execute(
372         :api_method => @prediction.training.insert,
373         :body => MultiJson.dump({"id" => "bucket/object"}),
374         :headers => {'Content-Type' => 'application/json'},
375         :connection => conn
376       )
377       conn.verify
378     end
379   end
380
381   describe 'with the plus API' do
382     before do
383       CLIENT.authorization = nil
384       @plus = CLIENT.discovered_api('plus')
385     end
386
387     it 'should correctly determine the discovery URI' do
388       CLIENT.discovery_uri('plus').should ===
389         'https://www.googleapis.com/discovery/v1/apis/plus/v1/rest'
390     end
391
392     it 'should find APIs that are in the discovery document' do
393       CLIENT.discovered_api('plus').name.should == 'plus'
394       CLIENT.discovered_api('plus').version.should == 'v1'
395       CLIENT.discovered_api(:plus).name.should == 'plus'
396       CLIENT.discovered_api(:plus).version.should == 'v1'
397     end
398
399     it 'should find methods that are in the discovery document' do
400       # TODO(bobaman) Fix this when the RPC names are correct
401       CLIENT.discovered_method(
402         'plus.activities.list', 'plus'
403       ).name.should == 'list'
404     end
405
406     it 'should define the origin API in discovered methods' do
407       CLIENT.discovered_method(
408         'plus.activities.list', 'plus'
409       ).api.name.should == 'plus'
410     end
411
412     it 'should not find methods that are not in the discovery document' do
413       CLIENT.discovered_method('plus.bogus', 'plus').should == nil
414     end
415
416     it 'should generate requests against the correct URIs' do
417       conn = stub_connection do |stub|
418         stub.get('/plus/v1/people/107807692475771887386/activities/public') do |env|
419         end
420       end
421       
422       request = CLIENT.execute(
423         :api_method => @plus.activities.list,
424         :parameters => {
425           'userId' => '107807692475771887386', 'collection' => 'public'
426         },
427         :authenticated => false,
428         :connection => conn
429       )
430       conn.verify
431     end
432
433     it 'should correctly validate parameters' do
434       (lambda do
435         CLIENT.execute(
436           :api_method => @plus.activities.list,
437           :parameters => {'alt' => 'json'},
438           :authenticated => false
439         )
440       end).should raise_error(ArgumentError)
441     end
442
443     it 'should correctly validate parameters' do
444       (lambda do
445         CLIENT.execute(
446           :api_method => @plus.activities.list,
447           :parameters => {
448             'userId' => '107807692475771887386', 'collection' => 'bogus'
449           },
450           :authenticated => false
451         ).to_env(CLIENT.connection)
452       end).should raise_error(ArgumentError)
453     end
454   end
455   
456   describe 'with the latitude API' do
457     before do
458       CLIENT.authorization = nil
459       @latitude = CLIENT.discovered_api('latitude')
460     end
461
462     it 'should correctly determine the discovery URI' do
463       CLIENT.discovery_uri('latitude').should ===
464         'https://www.googleapis.com/discovery/v1/apis/latitude/v1/rest'
465     end
466
467     it 'should find APIs that are in the discovery document' do
468       CLIENT.discovered_api('latitude').name.should == 'latitude'
469       CLIENT.discovered_api('latitude').version.should == 'v1'
470     end
471
472     it 'should return a batch path' do
473       CLIENT.discovered_api('latitude').batch_path.should_not be_nil
474     end
475
476     it 'should find methods that are in the discovery document' do
477       CLIENT.discovered_method(
478         'latitude.currentLocation.get', 'latitude'
479       ).name.should == 'get'
480     end
481
482     it 'should define the origin API in discovered methods' do
483       CLIENT.discovered_method(
484         'latitude.currentLocation.get', 'latitude'
485       ).api.name.should == 'latitude'
486     end
487
488     it 'should not find methods that are not in the discovery document' do
489       CLIENT.discovered_method('latitude.bogus', 'latitude').should == nil
490     end
491
492     it 'should generate requests against the correct URIs' do
493       request = CLIENT.generate_request(
494         :api_method => @latitude.current_location.get,
495         :authenticated => false
496       )
497       request.to_env(CLIENT.connection)[:url].to_s.should ===
498         'https://www.googleapis.com/latitude/v1/currentLocation'
499     end
500
501     it 'should generate requests against the correct URIs' do
502       request = CLIENT.generate_request(
503         :api_method => @latitude.current_location.get,
504         :authenticated => false
505       )
506       request.to_env(CLIENT.connection)[:url].to_s.should ===
507         'https://www.googleapis.com/latitude/v1/currentLocation'
508     end
509
510     it 'should not be able to execute requests without authorization' do
511       result = CLIENT.execute(
512         :api_method => @latitude.current_location.get,
513         :authenticated => false
514       )
515       result.response.status.should == 401
516     end
517   end
518
519   describe 'with the adsense API' do
520     before do
521       CLIENT.authorization = nil
522       @adsense = CLIENT.discovered_api('adsense', 'v1')
523     end
524
525     it 'should correctly determine the discovery URI' do
526       CLIENT.discovery_uri('adsense').should ===
527         'https://www.googleapis.com/discovery/v1/apis/adsense/v1/rest'
528     end
529
530     it 'should find APIs that are in the discovery document' do
531       CLIENT.discovered_api('adsense').name.should == 'adsense'
532       CLIENT.discovered_api('adsense').version.should == 'v1'
533     end
534
535     it 'should return a batch path' do
536       CLIENT.discovered_api('adsense').batch_path.should_not be_nil
537     end
538
539     it 'should find methods that are in the discovery document' do
540       CLIENT.discovered_method(
541         'adsense.reports.generate', 'adsense'
542       ).name.should == 'generate'
543     end
544
545     it 'should not find methods that are not in the discovery document' do
546       CLIENT.discovered_method('adsense.bogus', 'adsense').should == nil
547     end
548
549     it 'should generate requests against the correct URIs' do
550       conn = stub_connection do |stub|
551         stub.get('/adsense/v1/adclients') do |env|
552         end
553       end
554       request = CLIENT.execute(
555         :api_method => @adsense.adclients.list,
556         :authenticated => false,
557         :connection => conn
558       )
559       conn.verify
560     end
561
562     it 'should not be able to execute requests without authorization' do
563       result = CLIENT.execute(
564         :api_method => @adsense.adclients.list,
565         :authenticated => false
566       )
567       result.response.status.should == 401
568     end
569
570     it 'should fail when validating missing required parameters' do
571       (lambda do
572         CLIENT.execute(
573           :api_method => @adsense.reports.generate,
574           :authenticated => false
575         )
576       end).should raise_error(ArgumentError)
577     end
578
579     it 'should succeed when validating parameters in a correct call' do
580       conn = stub_connection do |stub|
581         stub.get('/adsense/v1/reports?dimension=DATE&endDate=2010-01-01&metric=PAGE_VIEWS&startDate=2000-01-01') do |env|
582         end
583       end
584       (lambda do
585         CLIENT.execute(
586           :api_method => @adsense.reports.generate,
587           :parameters => {
588             'startDate' => '2000-01-01',
589             'endDate' => '2010-01-01',
590             'dimension' => 'DATE',
591             'metric' => 'PAGE_VIEWS'
592           },
593           :authenticated => false,
594           :connection => conn
595         )
596       end).should_not raise_error
597       conn.verify
598     end
599
600     it 'should fail when validating parameters with invalid values' do
601       (lambda do
602         CLIENT.execute(
603           :api_method => @adsense.reports.generate,
604           :parameters => {
605             'startDate' => '2000-01-01',
606             'endDate' => '2010-01-01',
607             'dimension' => 'BAD_CHARACTERS=-&*(£&',
608             'metric' => 'PAGE_VIEWS'
609           },
610           :authenticated => false
611         )
612       end).should raise_error(ArgumentError)
613     end
614
615     it 'should succeed when validating repeated parameters in a correct call' do
616 #      pending("This is caused by Faraday's encoding of query parameters.")
617       conn = stub_connection do |stub|
618         stub.get('/adsense/v1/reports?dimension=DATE&dimension=PRODUCT_CODE'+
619                  '&endDate=2010-01-01&metric=CLICKS&metric=PAGE_VIEWS&'+
620                  'startDate=2000-01-01') do |env|
621         end
622       end
623       (lambda do
624         CLIENT.execute(
625           :api_method => @adsense.reports.generate,
626           :parameters => {
627             'startDate' => '2000-01-01',
628             'endDate' => '2010-01-01',
629             'dimension' => ['DATE', 'PRODUCT_CODE'],
630             'metric' => ['PAGE_VIEWS', 'CLICKS']
631           },
632           :authenticated => false,
633           :connection => conn
634         )
635       end).should_not raise_error
636       conn.verify
637     end
638
639     it 'should fail when validating incorrect repeated parameters' do
640       (lambda do
641         CLIENT.execute(
642           :api_method => @adsense.reports.generate,
643           :parameters => {
644             'startDate' => '2000-01-01',
645             'endDate' => '2010-01-01',
646             'dimension' => ['DATE', 'BAD_CHARACTERS=-&*(£&'],
647             'metric' => ['PAGE_VIEWS', 'CLICKS']
648           },
649           :authenticated => false
650         )
651       end).should raise_error(ArgumentError)
652     end
653   end
654
655   describe 'with the Drive API' do
656     before do
657       CLIENT.authorization = nil
658       @drive = CLIENT.discovered_api('drive', 'v1')
659     end
660
661     it 'should include media upload info methods' do
662       @drive.files.insert.media_upload.should_not == nil
663     end
664
665     it 'should include accepted media types' do
666       @drive.files.insert.media_upload.accepted_types.should_not be_empty
667     end
668
669     it 'should have an upload path' do
670       @drive.files.insert.media_upload.uri_template.should_not == nil
671     end
672
673     it 'should have a max file size' do
674       @drive.files.insert.media_upload.max_size.should_not == nil
675     end
676   end
677 end