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 RSpec.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).to raise_error(ArgumentError)
35 it 'should error out when called without an API version' do
37 Google::APIClient::Service.new('foo')
38 end).to 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).to 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|
55 adsense = Google::APIClient::Service.new(
59 :application_name => APPLICATION_NAME,
60 :authenticated => false,
66 req = adsense.adclients.list.execute()
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|
76 adsense = Google::APIClient::Service.new(
80 :application_name => APPLICATION_NAME,
81 :authenticated => false,
86 req = adsense.adunits.list(:adClientId => '1').execute()
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|
95 adsense = Google::APIClient::Service.new(
99 :application_name => APPLICATION_NAME,
100 :authenticated => false,
105 req = adsense.accounts.adclients.list(:accountId => '1').execute()
108 describe 'with no connection' do
110 @adsense = Google::APIClient::Service.new('adsense', 'v1.3',
111 {:application_name => APPLICATION_NAME, :cache_store => nil})
114 it 'should return a resource when using a valid resource name' do
115 expect(@adsense.accounts).to be_a(Google::APIClient::Service::Resource)
118 it 'should throw an error when using an invalid resource name' do
120 @adsense.invalid_resource
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
131 it 'should throw an error when using an invalid method name' do
133 @adsense.adclients.invalid_method
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')
147 describe 'with the Prediction API' do
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"}')
156 prediction = Google::APIClient::Service.new(
160 :application_name => APPLICATION_NAME,
161 :authenticated => false,
166 req = prediction.trainedmodels.insert(:project => '1').body({'id' => '1'}).execute()
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"}')
177 prediction = Google::APIClient::Service.new(
181 :application_name => APPLICATION_NAME,
182 :authenticated => false,
187 req = prediction.trainedmodels.insert(:project => '1').body('{"id":"1"}').execute()
191 describe 'with no connection' do
193 @prediction = Google::APIClient::Service.new('prediction', 'v1.5',
194 {:application_name => APPLICATION_NAME, :cache_store => nil})
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')
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')
217 describe 'with the Drive API' do
221 'title' => 'My movie',
222 'description' => 'The best home movie ever made'
224 @file = File.expand_path('files/sample.txt', fixtures_path)
225 @media = Google::APIClient::UploadIO.new(@file, 'text/plain')
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
235 drive = Google::APIClient::Service.new(
239 :application_name => APPLICATION_NAME,
240 :authenticated => false,
245 req = drive.files.insert(:uploadType => 'multipart').body(@metadata).media(@media).execute()
249 describe 'with no connection' do
251 @drive = Google::APIClient::Service.new('drive', 'v2',
252 {:application_name => APPLICATION_NAME, :cache_store => nil})
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')
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')
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')
291 RSpec.describe Google::APIClient::Service::Result do
293 describe 'with the plus API' 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,
301 'collection' => 'public',
305 @request = @plus.activities.list(:userId => 'me', :collection => 'public',
309 @response = double("response")
310 allow(@response).to receive(:status).and_return(200)
311 allow(@response).to receive(:headers).and_return({
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',
319 'connection' => 'close'
323 describe 'with a next page token' do
325 @body = <<-END_OF_STRING
327 "kind": "plus#activityFeed",
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",
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)
343 it 'should indicate a successful response' do
344 expect(@result.error?).to be_falsey
347 it 'should return the correct next page token' do
348 expect(@result.next_page_token).to eq('NEXT+PAGE+TOKEN')
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)
360 it 'should return content type correctly' do
361 expect(@result.media_type).to eq('application/json')
364 it 'should return the body correctly' do
365 expect(@result.body).to eq(@body)
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'
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?'
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'
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
389 describe 'without a next page token' do
391 @body = <<-END_OF_STRING
393 "kind": "plus#activityFeed",
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",
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)
407 it 'should not return a next page token' do
408 expect(@result.next_page_token).to eq(nil)
411 it 'should return content type correctly' do
412 expect(@result.media_type).to eq('application/json')
415 it 'should return the body correctly' do
416 expect(@result.body).to eq(@body)
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'
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?'
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
435 describe 'with JSON error response' do
437 @body = <<-END_OF_STRING
443 "reason": "parseError",
444 "message": "Parse Error"
448 "message": "Parse Error"
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)
458 it 'should return error status correctly' do
459 expect(@result.error?).to be_truthy
462 it 'should return the correct error message' do
463 expect(@result.error_message).to eq('Parse Error')
466 it 'should return the body correctly' do
467 expect(@result.body).to eq(@body)
471 describe 'with 204 No Content response' 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)
480 it 'should indicate no data is available' do
481 expect(@result.data?).to be_falsey
484 it 'should return nil for data' do
485 expect(@result.data).to eq(nil)
488 it 'should return nil for media_type' do
489 expect(@result.media_type).to eq(nil)
495 RSpec.describe Google::APIClient::Service::BatchRequest do
497 include ConnectionHelpers
499 context 'with a service connection' do
501 @conn = stub_connection do |stub|
502 stub.post('/batch') do |env|
503 [500, {'Content-Type' => 'application/json'}, '{}']
506 @discovery = Google::APIClient::Service.new('discovery', 'v1',
507 {:application_name => APPLICATION_NAME, :authorization => nil,
508 :cache_store => nil, :connection => @conn})
510 @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
511 @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
515 it 'should use the service connection' do
516 batch = @discovery.batch(@calls) do
523 describe 'with the discovery API' do
525 @discovery = Google::APIClient::Service.new('discovery', 'v1',
526 {:application_name => APPLICATION_NAME, :authorization => nil,
527 :cache_store => nil})
530 describe 'with two valid requests' do
533 @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
534 @discovery.apis.get_rest(:api => 'discovery', :version => 'v1')
538 it 'should execute both when using a global callback' do
540 batch = @discovery.batch(@calls) do |result|
542 expect(result.status).to eq(200)
546 expect(block_called).to eq(2)
549 it 'should execute both when using individual callbacks' do
550 call1_returned, call2_returned = false, false
551 batch = @discovery.batch
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)
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)
566 expect(call1_returned).to eq(true)
567 expect(call2_returned).to eq(true)
571 describe 'with a valid request and an invalid one' do
574 @discovery.apis.get_rest(:api => 'plus', :version => 'v1'),
575 @discovery.apis.get_rest(:api => 'invalid', :version => 'invalid')
579 it 'should execute both when using a global callback' do
581 batch = @discovery.batch(@calls) do |result|
583 if result.call_index == 0
584 expect(result.status).to eq(200)
586 expect(result.status).to be >= 400
587 expect(result.status).to be < 500
592 expect(block_called).to eq(2)
595 it 'should execute both when using individual callbacks' do
596 call1_returned, call2_returned = false, false
597 batch = @discovery.batch
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)
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)
613 expect(call1_returned).to eq(true)
614 expect(call2_returned).to eq(true)