- Improving the Service interface with access to more properties
[arvados.git] / 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 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     (lambda do
31       Google::APIClient::Service.new
32     end).should raise_error(ArgumentError)
33   end
34
35   it 'should error out when called without an API version' do
36     (lambda do
37       Google::APIClient::Service.new('foo')
38     end).should raise_error(ArgumentError)
39   end
40
41   it 'should error out when the options hash is not a hash' do
42     (lambda do
43       Google::APIClient::Service.new('foo', 'v1', 42)
44     end).should 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         end
53       end
54       adsense = Google::APIClient::Service.new(
55         'adsense',
56         'v1.3',
57         {
58           :application_name => APPLICATION_NAME,
59           :authenticated => false,
60           :connection => conn
61         }
62       )
63
64       req = adsense.adclients.list.execute()
65       conn.verify
66     end
67
68     it 'should make a valid call for a method with parameters' do
69       conn = stub_connection do |stub|
70         stub.get('/adsense/v1.3/adclients/1/adunits') do |env|
71         end
72       end
73       adsense = Google::APIClient::Service.new(
74         'adsense',
75         'v1.3',
76         {
77           :application_name => APPLICATION_NAME,
78           :authenticated => false,
79           :connection => conn
80         }
81       )
82       req = adsense.adunits.list(:adClientId => '1').execute()
83     end
84
85     it 'should make a valid call for a deep method' do
86       conn = stub_connection do |stub|
87         stub.get('/adsense/v1.3/accounts/1/adclients') do |env|
88         end
89       end
90       adsense = Google::APIClient::Service.new(
91         'adsense',
92         'v1.3',
93         {
94           :application_name => APPLICATION_NAME,
95           :authenticated => false,
96           :connection => conn
97         }
98       )
99       req = adsense.accounts.adclients.list(:accountId => '1').execute()
100     end
101
102     describe 'with no connection' do
103       before do
104         @adsense = Google::APIClient::Service.new('adsense', 'v1.3',
105           {:application_name => APPLICATION_NAME})
106       end
107
108       it 'should return a resource when using a valid resource name' do
109         @adsense.accounts.should be_a(Google::APIClient::Service::Resource)
110       end
111
112       it 'should throw an error when using an invalid resource name' do
113         (lambda do
114            @adsense.invalid_resource
115         end).should raise_error
116       end
117
118       it 'should return a request when using a valid method name' do
119         req = @adsense.adclients.list
120         req.should be_a(Google::APIClient::Service::Request)
121         req.method.id.should == 'adsense.adclients.list'
122         req.parameters.should be_nil
123       end
124
125       it 'should throw an error when using an invalid method name' do
126         (lambda do
127            @adsense.adclients.invalid_method
128         end).should raise_error
129       end
130
131       it 'should return a valid request with parameters' do
132         req = @adsense.adunits.list(:adClientId => '1')
133         req.should be_a(Google::APIClient::Service::Request)
134         req.method.id.should == 'adsense.adunits.list'
135         req.parameters.should_not be_nil
136         req.parameters[:adClientId].should == '1'
137       end
138     end
139   end
140
141   describe 'with the Prediction API' do
142
143     it 'should make a valid call with an object body' do
144       conn = stub_connection do |stub|
145         stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
146           env.body.should == '{"id":"1"}'
147         end
148       end
149       prediction = Google::APIClient::Service.new(
150         'prediction',
151         'v1.5',
152         {
153           :application_name => APPLICATION_NAME,
154           :authenticated => false,
155           :connection => conn
156         }
157       )
158       req = prediction.trainedmodels.insert(:project => '1').body({'id' => '1'}).execute()
159       conn.verify
160     end
161
162     it 'should make a valid call with a text body' do
163       conn = stub_connection do |stub|
164         stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
165           env.body.should == '{"id":"1"}'
166         end
167       end
168       prediction = Google::APIClient::Service.new(
169         'prediction',
170         'v1.5',
171         {
172           :application_name => APPLICATION_NAME,
173           :authenticated => false,
174           :connection => conn
175         }
176       )
177       req = prediction.trainedmodels.insert(:project => '1').body('{"id":"1"}').execute()
178       conn.verify
179     end
180
181     describe 'with no connection' do
182       before do
183         @prediction = Google::APIClient::Service.new('prediction', 'v1.5',
184           {:application_name => APPLICATION_NAME})
185       end
186
187       it 'should return a valid request with a body' do
188         req = @prediction.trainedmodels.insert(:project => '1').body({'id' => '1'})
189         req.should be_a(Google::APIClient::Service::Request)
190         req.method.id.should == 'prediction.trainedmodels.insert'
191         req.body.should == {'id' => '1'}
192         req.parameters.should_not be_nil
193         req.parameters[:project].should == '1'
194       end
195
196       it 'should return a valid request with a body when using resource name' do
197         req = @prediction.trainedmodels.insert(:project => '1').training({'id' => '1'})
198         req.should be_a(Google::APIClient::Service::Request)
199         req.method.id.should == 'prediction.trainedmodels.insert'
200         req.training.should == {'id' => '1'}
201         req.parameters.should_not be_nil
202         req.parameters[:project].should == '1'
203       end
204     end
205   end
206
207   describe 'with the Drive API' do
208
209     before do
210       @metadata = {
211         'title' => 'My movie',
212         'description' => 'The best home movie ever made'
213       }
214       @file = File.expand_path('files/sample.txt', fixtures_path)
215       @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
216     end
217
218     it 'should make a valid call with an object body and media upload' do
219       conn = stub_connection do |stub|
220         stub.post('/upload/drive/v1/files?uploadType=multipart') do |env|
221           env.body.should be_a Faraday::CompositeReadIO
222         end
223       end
224       drive = Google::APIClient::Service.new(
225         'drive',
226         'v1',
227         {
228           :application_name => APPLICATION_NAME,
229           :authenticated => false,
230           :connection => conn
231         }
232       )
233       req = drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media).execute()
234       conn.verify
235     end
236
237     describe 'with no connection' do
238       before do
239         @drive = Google::APIClient::Service.new('drive', 'v1',
240           {:application_name => APPLICATION_NAME})
241       end
242
243       it 'should return a valid request with a body and media upload' do
244         req = @drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media)
245         req.should be_a(Google::APIClient::Service::Request)
246         req.method.id.should == 'drive.files.insert'
247         req.body.should == @metadata
248         req.media.should == @media
249         req.parameters.should_not be_nil
250         req.parameters[:uploadType].should == 'multipart'
251       end
252
253       it 'should return a valid request with a body and media upload when using resource name' do
254         req = @drive.files.insert(:uploadType => 'multipart').file(@metadata).media(@media)
255         req.should be_a(Google::APIClient::Service::Request)
256         req.method.id.should == 'drive.files.insert'
257         req.file.should == @metadata
258         req.media.should == @media
259         req.parameters.should_not be_nil
260         req.parameters[:uploadType].should == 'multipart'
261       end
262     end
263   end
264
265   describe 'with the Discovery API' do
266     it 'should make a valid end-to-end request' do
267       discovery = Google::APIClient::Service.new('discovery', 'v1',
268           {:application_name => APPLICATION_NAME, :authenticated => false})
269       result = discovery.apis.get_rest(:api => 'discovery', :version => 'v1').execute
270       result.should_not be_nil
271       result.data.name.should == 'discovery'
272       result.data.version.should == 'v1'
273     end
274   end
275 end
276
277
278 describe Google::APIClient::Service::Result do
279
280   describe 'with the plus API' do
281     before do
282       @plus = Google::APIClient::Service.new('plus', 'v1',
283           {:application_name => APPLICATION_NAME})
284       @reference = Google::APIClient::Reference.new({
285         :api_method => @plus.activities.list.method,
286         :parameters => {
287           'userId' => 'me',
288           'collection' => 'public',
289           'maxResults' => 20
290         }
291       })
292       @request = @plus.activities.list(:userId => 'me', :collection => 'public',
293         :maxResults => 20)
294
295       # Response double
296       @response = double("response")
297       @response.stub(:status).and_return(200)
298       @response.stub(:headers).and_return({
299         'etag' => '12345',
300         'x-google-apiary-auth-scopes' =>
301           'https://www.googleapis.com/auth/plus.me',
302         'content-type' => 'application/json; charset=UTF-8',
303         'date' => 'Mon, 23 Apr 2012 00:00:00 GMT',
304         'cache-control' => 'private, max-age=0, must-revalidate, no-transform',
305         'server' => 'GSE',
306         'connection' => 'close'
307       })
308     end
309
310     describe 'with a next page token' do
311       before do
312         @body = <<-END_OF_STRING
313           {
314             "kind": "plus#activityFeed",
315             "etag": "FOO",
316             "nextPageToken": "NEXT+PAGE+TOKEN",
317             "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
318             "nextLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN",
319             "title": "Plus Public Activity Feed for ",
320             "updated": "2012-04-23T00:00:00.000Z",
321             "id": "123456790",
322             "items": []
323           }
324           END_OF_STRING
325         @response.stub(:body).and_return(@body)
326         base_result = Google::APIClient::Result.new(@reference, @response)
327         @result = Google::APIClient::Service::Result.new(@request, base_result)
328       end
329
330       it 'should indicate a successful response' do
331         @result.error?.should be_false
332       end
333
334       it 'should return the correct next page token' do
335         @result.next_page_token.should == 'NEXT+PAGE+TOKEN'
336       end
337
338       it 'generate a correct request when calling next_page' do
339         next_page_request = @result.next_page
340         next_page_request.parameters.should include('pageToken')
341         next_page_request.parameters['pageToken'].should == 'NEXT+PAGE+TOKEN'
342         @request.parameters.each_pair do |param, value|
343           next_page_request.parameters[param].should == value
344         end
345       end
346
347       it 'should return content type correctly' do
348         @result.media_type.should == 'application/json'
349       end
350
351       it 'should return the body correctly' do
352         @result.body.should == @body
353       end
354
355       it 'should return the result data correctly' do
356         @result.data?.should be_true
357         @result.data.class.to_s.should ==
358             'Google::APIClient::Schema::Plus::V1::ActivityFeed'
359         @result.data.kind.should == 'plus#activityFeed'
360         @result.data.etag.should == 'FOO'
361         @result.data.nextPageToken.should == 'NEXT+PAGE+TOKEN'
362         @result.data.selfLink.should ==
363             'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
364         @result.data.nextLink.should ==
365             'https://www.googleapis.com/plus/v1/people/foo/activities/public?' +
366             'maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN'
367         @result.data.title.should == 'Plus Public Activity Feed for '
368         @result.data.id.should == "123456790"
369         @result.data.items.should be_empty
370       end
371     end
372
373     describe 'without a next page token' do
374       before do
375         @body = <<-END_OF_STRING
376           {
377             "kind": "plus#activityFeed",
378             "etag": "FOO",
379             "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
380             "title": "Plus Public Activity Feed for ",
381             "updated": "2012-04-23T00:00:00.000Z",
382             "id": "123456790",
383             "items": []
384           }
385           END_OF_STRING
386         @response.stub(:body).and_return(@body)
387         base_result = Google::APIClient::Result.new(@reference, @response)
388         @result = Google::APIClient::Service::Result.new(@request, base_result)
389       end
390
391       it 'should not return a next page token' do
392         @result.next_page_token.should == nil
393       end
394
395       it 'should return content type correctly' do
396         @result.media_type.should == 'application/json'
397       end
398
399       it 'should return the body correctly' do
400         @result.body.should == @body
401       end
402
403       it 'should return the result data correctly' do
404         @result.data?.should be_true
405         @result.data.class.to_s.should ==
406             'Google::APIClient::Schema::Plus::V1::ActivityFeed'
407         @result.data.kind.should == 'plus#activityFeed'
408         @result.data.etag.should == 'FOO'
409         @result.data.selfLink.should ==
410             'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
411         @result.data.title.should == 'Plus Public Activity Feed for '
412         @result.data.id.should == "123456790"
413         @result.data.items.should be_empty
414       end
415     end
416
417     describe 'with JSON error response' do
418       before do
419         @body = <<-END_OF_STRING
420          {
421           "error": {
422            "errors": [
423             {
424              "domain": "global",
425              "reason": "parseError",
426              "message": "Parse Error"
427             }
428            ],
429            "code": 400,
430            "message": "Parse Error"
431           }
432          }
433          END_OF_STRING
434         @response.stub(:body).and_return(@body)
435         @response.stub(:status).and_return(400)
436         base_result = Google::APIClient::Result.new(@reference, @response)
437         @result = Google::APIClient::Service::Result.new(@request, base_result)
438       end
439
440       it 'should return error status correctly' do
441         @result.error?.should be_true
442       end
443
444       it 'should return the correct error message' do
445         @result.error_message.should == 'Parse Error'
446       end
447
448       it 'should return the body correctly' do
449         @result.body.should == @body
450       end
451     end
452
453     describe 'with 204 No Content response' do
454       before do
455         @response.stub(:body).and_return('')
456         @response.stub(:status).and_return(204)
457         @response.stub(:headers).and_return({})
458         base_result = Google::APIClient::Result.new(@reference, @response)
459         @result = Google::APIClient::Service::Result.new(@request, base_result)
460       end
461
462       it 'should indicate no data is available' do
463         @result.data?.should be_false
464       end
465
466       it 'should return nil for data' do
467         @result.data.should == nil
468       end
469
470       it 'should return nil for media_type' do
471         @result.media_type.should == nil
472       end
473     end
474   end
475 end