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