3 # Copyright 2013 Google Inc.
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 require 'google/api_client'
20 require 'google/api_client/service'
22 fixtures_path = File.expand_path('../../../fixtures', __FILE__)
24 describe Google::APIClient::Service do
25 include ConnectionHelpers
27 APPLICATION_NAME = 'API Client Tests'
29 it 'should error out when called without an API name or version' do
31 Google::APIClient::Service.new
32 end).should raise_error(ArgumentError)
35 it 'should error out when called without an API version' do
37 Google::APIClient::Service.new('foo')
38 end).should raise_error(ArgumentError)
41 it 'should error out when the options hash is not a hash' do
43 Google::APIClient::Service.new('foo', 'v1', 42)
44 end).should raise_error(ArgumentError)
47 describe 'with the AdSense Management API' do
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|
54 adsense = Google::APIClient::Service.new(
58 :application_name => APPLICATION_NAME,
59 :authenticated => false,
65 req = adsense.adclients.list.execute()
69 it 'should make a valid call for a method with parameters' do
70 conn = stub_connection do |stub|
71 stub.get('/adsense/v1.3/adclients/1/adunits') do |env|
74 adsense = Google::APIClient::Service.new(
78 :application_name => APPLICATION_NAME,
79 :authenticated => false,
84 req = adsense.adunits.list(:adClientId => '1').execute()
87 it 'should make a valid call for a deep method' do
88 conn = stub_connection do |stub|
89 stub.get('/adsense/v1.3/accounts/1/adclients') do |env|
92 adsense = Google::APIClient::Service.new(
96 :application_name => APPLICATION_NAME,
97 :authenticated => false,
102 req = adsense.accounts.adclients.list(:accountId => '1').execute()
105 describe 'with no connection' do
107 @adsense = Google::APIClient::Service.new('adsense', 'v1.3',
108 {:application_name => APPLICATION_NAME, :cache_store => nil})
111 it 'should return a resource when using a valid resource name' do
112 @adsense.accounts.should be_a(Google::APIClient::Service::Resource)
115 it 'should throw an error when using an invalid resource name' do
117 @adsense.invalid_resource
118 end).should raise_error
121 it 'should return a request when using a valid method name' do
122 req = @adsense.adclients.list
123 req.should be_a(Google::APIClient::Service::Request)
124 req.method.id.should == 'adsense.adclients.list'
125 req.parameters.should be_nil
128 it 'should throw an error when using an invalid method name' do
130 @adsense.adclients.invalid_method
131 end).should raise_error
134 it 'should return a valid request with parameters' do
135 req = @adsense.adunits.list(:adClientId => '1')
136 req.should be_a(Google::APIClient::Service::Request)
137 req.method.id.should == 'adsense.adunits.list'
138 req.parameters.should_not be_nil
139 req.parameters[:adClientId].should == '1'
144 describe 'with the Prediction API' do
146 it 'should make a valid call with an object body' do
147 conn = stub_connection do |stub|
148 stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
149 env.body.should == '{"id":"1"}'
152 prediction = Google::APIClient::Service.new(
156 :application_name => APPLICATION_NAME,
157 :authenticated => false,
162 req = prediction.trainedmodels.insert(:project => '1').body({'id' => '1'}).execute()
166 it 'should make a valid call with a text body' do
167 conn = stub_connection do |stub|
168 stub.post('/prediction/v1.5/trainedmodels?project=1') do |env|
169 env.body.should == '{"id":"1"}'
172 prediction = Google::APIClient::Service.new(
176 :application_name => APPLICATION_NAME,
177 :authenticated => false,
182 req = prediction.trainedmodels.insert(:project => '1').body('{"id":"1"}').execute()
186 describe 'with no connection' do
188 @prediction = Google::APIClient::Service.new('prediction', 'v1.5',
189 {:application_name => APPLICATION_NAME, :cache_store => nil})
192 it 'should return a valid request with a body' do
193 req = @prediction.trainedmodels.insert(:project => '1').body({'id' => '1'})
194 req.should be_a(Google::APIClient::Service::Request)
195 req.method.id.should == 'prediction.trainedmodels.insert'
196 req.body.should == {'id' => '1'}
197 req.parameters.should_not be_nil
198 req.parameters[:project].should == '1'
201 it 'should return a valid request with a body when using resource name' do
202 req = @prediction.trainedmodels.insert(:project => '1').training({'id' => '1'})
203 req.should be_a(Google::APIClient::Service::Request)
204 req.method.id.should == 'prediction.trainedmodels.insert'
205 req.training.should == {'id' => '1'}
206 req.parameters.should_not be_nil
207 req.parameters[:project].should == '1'
212 describe 'with the Drive API' do
216 'title' => 'My movie',
217 'description' => 'The best home movie ever made'
219 @file = File.expand_path('files/sample.txt', fixtures_path)
220 @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
223 it 'should make a valid call with an object body and media upload' do
224 conn = stub_connection do |stub|
225 stub.post('/upload/drive/v1/files?uploadType=multipart') do |env|
226 env.body.should be_a Faraday::CompositeReadIO
229 drive = Google::APIClient::Service.new(
233 :application_name => APPLICATION_NAME,
234 :authenticated => false,
239 req = drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media).execute()
243 describe 'with no connection' do
245 @drive = Google::APIClient::Service.new('drive', 'v1',
246 {:application_name => APPLICATION_NAME, :cache_store => nil})
249 it 'should return a valid request with a body and media upload' do
250 req = @drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media)
251 req.should be_a(Google::APIClient::Service::Request)
252 req.method.id.should == 'drive.files.insert'
253 req.body.should == @metadata
254 req.media.should == @media
255 req.parameters.should_not be_nil
256 req.parameters[:uploadType].should == 'multipart'
259 it 'should return a valid request with a body and media upload when using resource name' do
260 req = @drive.files.insert(:uploadType => 'multipart').file(@metadata).media(@media)
261 req.should be_a(Google::APIClient::Service::Request)
262 req.method.id.should == 'drive.files.insert'
263 req.file.should == @metadata
264 req.media.should == @media
265 req.parameters.should_not be_nil
266 req.parameters[:uploadType].should == 'multipart'
271 describe 'with the Discovery API' do
272 it 'should make a valid end-to-end request' do
273 discovery = Google::APIClient::Service.new('discovery', 'v1',
274 {:application_name => APPLICATION_NAME, :authenticated => false,
275 :cache_store => nil})
276 result = discovery.apis.get_rest(:api => 'discovery', :version => 'v1').execute
277 result.should_not be_nil
278 result.data.name.should == 'discovery'
279 result.data.version.should == 'v1'
285 describe Google::APIClient::Service::Result do
287 describe 'with the plus API' do
289 @plus = Google::APIClient::Service.new('plus', 'v1',
290 {:application_name => APPLICATION_NAME, :cache_store => nil})
291 @reference = Google::APIClient::Reference.new({
292 :api_method => @plus.activities.list.method,
295 'collection' => 'public',
299 @request = @plus.activities.list(:userId => 'me', :collection => 'public',
303 @response = double("response")
304 @response.stub(:status).and_return(200)
305 @response.stub(:headers).and_return({
307 'x-google-apiary-auth-scopes' =>
308 'https://www.googleapis.com/auth/plus.me',
309 'content-type' => 'application/json; charset=UTF-8',
310 'date' => 'Mon, 23 Apr 2012 00:00:00 GMT',
311 'cache-control' => 'private, max-age=0, must-revalidate, no-transform',
313 'connection' => 'close'
317 describe 'with a next page token' do
319 @body = <<-END_OF_STRING
321 "kind": "plus#activityFeed",
323 "nextPageToken": "NEXT+PAGE+TOKEN",
324 "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
325 "nextLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN",
326 "title": "Plus Public Activity Feed for ",
327 "updated": "2012-04-23T00:00:00.000Z",
332 @response.stub(:body).and_return(@body)
333 base_result = Google::APIClient::Result.new(@reference, @response)
334 @result = Google::APIClient::Service::Result.new(@request, base_result)
337 it 'should indicate a successful response' do
338 @result.error?.should be_false
341 it 'should return the correct next page token' do
342 @result.next_page_token.should == 'NEXT+PAGE+TOKEN'
345 it 'generate a correct request when calling next_page' do
346 next_page_request = @result.next_page
347 next_page_request.parameters.should include('pageToken')
348 next_page_request.parameters['pageToken'].should == 'NEXT+PAGE+TOKEN'
349 @request.parameters.each_pair do |param, value|
350 next_page_request.parameters[param].should == value
354 it 'should return content type correctly' do
355 @result.media_type.should == 'application/json'
358 it 'should return the body correctly' do
359 @result.body.should == @body
362 it 'should return the result data correctly' do
363 @result.data?.should be_true
364 @result.data.class.to_s.should ==
365 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
366 @result.data.kind.should == 'plus#activityFeed'
367 @result.data.etag.should == 'FOO'
368 @result.data.nextPageToken.should == 'NEXT+PAGE+TOKEN'
369 @result.data.selfLink.should ==
370 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
371 @result.data.nextLink.should ==
372 'https://www.googleapis.com/plus/v1/people/foo/activities/public?' +
373 'maxResults=20&pageToken=NEXT%2BPAGE%2BTOKEN'
374 @result.data.title.should == 'Plus Public Activity Feed for '
375 @result.data.id.should == "123456790"
376 @result.data.items.should be_empty
380 describe 'without a next page token' do
382 @body = <<-END_OF_STRING
384 "kind": "plus#activityFeed",
386 "selfLink": "https://www.googleapis.com/plus/v1/people/foo/activities/public?",
387 "title": "Plus Public Activity Feed for ",
388 "updated": "2012-04-23T00:00:00.000Z",
393 @response.stub(:body).and_return(@body)
394 base_result = Google::APIClient::Result.new(@reference, @response)
395 @result = Google::APIClient::Service::Result.new(@request, base_result)
398 it 'should not return a next page token' do
399 @result.next_page_token.should == nil
402 it 'should return content type correctly' do
403 @result.media_type.should == 'application/json'
406 it 'should return the body correctly' do
407 @result.body.should == @body
410 it 'should return the result data correctly' do
411 @result.data?.should be_true
412 @result.data.class.to_s.should ==
413 'Google::APIClient::Schema::Plus::V1::ActivityFeed'
414 @result.data.kind.should == 'plus#activityFeed'
415 @result.data.etag.should == 'FOO'
416 @result.data.selfLink.should ==
417 'https://www.googleapis.com/plus/v1/people/foo/activities/public?'
418 @result.data.title.should == 'Plus Public Activity Feed for '
419 @result.data.id.should == "123456790"
420 @result.data.items.should be_empty
424 describe 'with JSON error response' do
426 @body = <<-END_OF_STRING
432 "reason": "parseError",
433 "message": "Parse Error"
437 "message": "Parse Error"
441 @response.stub(:body).and_return(@body)
442 @response.stub(:status).and_return(400)
443 base_result = Google::APIClient::Result.new(@reference, @response)
444 @result = Google::APIClient::Service::Result.new(@request, base_result)
447 it 'should return error status correctly' do
448 @result.error?.should be_true
451 it 'should return the correct error message' do
452 @result.error_message.should == 'Parse Error'
455 it 'should return the body correctly' do
456 @result.body.should == @body
460 describe 'with 204 No Content response' do
462 @response.stub(:body).and_return('')
463 @response.stub(:status).and_return(204)
464 @response.stub(:headers).and_return({})
465 base_result = Google::APIClient::Result.new(@reference, @response)
466 @result = Google::APIClient::Service::Result.new(@request, base_result)
469 it 'should indicate no data is available' do
470 @result.data?.should be_false
473 it 'should return nil for data' do
474 @result.data.should == nil
477 it 'should return nil for media_type' do
478 @result.media_type.should == nil
484 describe Google::APIClient::Service::BatchRequest do
485 describe 'with the discovery API' do
487 @discovery = Google::APIClient::Service.new('discovery', 'v1',
488 {:application_name => APPLICATION_NAME, :authorization => nil,
489 :cache_store => nil})
492 describe 'with two valid requests' do
495 @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
496 @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
500 it 'should execute both when using a global callback' do
502 batch = @discovery.batch(@calls) do |result|
504 result.status.should == 200
508 block_called.should == 2
511 it 'should execute both when using individual callbacks' do
512 call1_returned, call2_returned = false, false
513 batch = @discovery.batch
515 batch.add(@calls[0]) do |result|
516 call1_returned = true
517 result.status.should == 200
518 result.call_index.should == 0
521 batch.add(@calls[1]) do |result|
522 call2_returned = true
523 result.status.should == 200
524 result.call_index.should == 1
528 call1_returned.should == true
529 call2_returned.should == true
533 describe 'with a valid request and an invalid one' do
536 @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
537 @discovery.apis.get_rest(:api => 'invalid', :version => 'invalid')
541 it 'should execute both when using a global callback' do
543 batch = @discovery.batch(@calls) do |result|
545 if result.call_index == 0
546 result.status.should == 200
548 result.status.should >= 400
549 result.status.should < 500
554 block_called.should == 2
557 it 'should execute both when using individual callbacks' do
558 call1_returned, call2_returned = false, false
559 batch = @discovery.batch
561 batch.add(@calls[0]) do |result|
562 call1_returned = true
563 result.status.should == 200
564 result.call_index.should == 0
567 batch.add(@calls[1]) do |result|
568 call2_returned = true
569 result.status.should >= 400
570 result.status.should < 500
571 result.call_index.should == 1
575 call1_returned.should == true
576 call2_returned.should == true