chore: Split google-apis-core into a separate gem (#2134)

This commit is contained in:
Daniel Azuma authored and GitHub committed 2020-12-27 17:31:47 -08:00
1 parent 1bc1737dc1
commit a6a5b6071e
66 files changed
+763 -151

No files matched your search

@@ -1,41 +0,0 @@
require 'spec_helper'
require 'google/api_client/auth/installed_app'
describe Google::APIClient::InstalledAppFlow do
let(:secrets) do
file = File.join(FIXTURES_DIR, 'files', 'client_secrets.json')
Google::APIClient::ClientSecrets.load(file)
end
subject(:flow) { Google::APIClient::InstalledAppFlow.new(
:client_id => secrets.client_id,
:client_secret => secrets.client_secret,
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly',
:port => 5000)}
context 'initialization' do
it 'should be valid instance' do
expect(flow).to be_a Google::APIClient::InstalledAppFlow
end
it 'should have valid port' do
expect(flow.instance_variable_get("@port")).to eq 5000
end
it 'should have default port' do
another_flow = Google::APIClient::InstalledAppFlow.new(
:client_id => secrets.client_id,
:client_secret => secrets.client_secret,
:scope => 'https://www.googleapis.com/auth/drive.metadata.readonly')
expect(another_flow.instance_variable_get("@port")).to eq 9292
end
it 'should have valid client_id' do
auth = flow.instance_variable_get "@authorization"
expect(auth.client_id).to eq "898243283568.apps.googleusercontent.com"
end
end
end
@@ -1,128 +0,0 @@
require 'spec_helper'
require 'google/api_client/auth/storage'
describe Google::APIClient::Storage do
let(:root_path) { ROOT_DIR }
let(:json_file) { File.expand_path(File.join(FIXTURES_DIR, 'files', 'auth_stored_credentials.json')) }
let(:store) { double }
let(:client_stub) { double }
subject { Google::APIClient::Storage.new(store) }
describe 'constants' do
it "should have valid authorization and token_credential_uri" do
expect(Google::APIClient::Storage::AUTHORIZATION_URI).to eq "https://accounts.google.com/o/oauth2/auth"
expect(Google::APIClient::Storage::TOKEN_CREDENTIAL_URI).to eq "https://accounts.google.com/o/oauth2/token"
end
end
describe 'authorize' do
it 'should authorize' do
expect(subject).to respond_to(:authorization)
expect(subject.authorization).to be_nil
expect(subject.store).to be == store
end
end
describe 'authorize' do
describe 'with credentials' do
it 'should initialize a new OAuth Client' do
expect(subject).to receive(:load_credentials).and_return({:first => 'a dummy'})
expect(client_stub).to receive(:issued_at=)
expect(client_stub).to receive(:expired?).and_return(false)
expect(Signet::OAuth2::Client).to receive(:new).and_return(client_stub)
expect(subject).not_to receive(:refresh_authorization)
subject.authorize
end
it 'should refresh authorization' do
expect(subject).to receive(:load_credentials).and_return({:first => 'a dummy'})
expect(client_stub).to receive(:issued_at=)
expect(client_stub).to receive(:expired?).and_return(true)
expect(Signet::OAuth2::Client).to receive(:new).and_return(client_stub)
expect(subject).to receive(:refresh_authorization)
auth = subject.authorize
expect(auth).to be == subject.authorization
expect(auth).not_to be_nil
end
end
describe 'without credentials' do
it 'should return nil' do
expect(subject.authorization).to be_nil
expect(subject).to receive(:load_credentials).and_return({})
expect(subject.authorize).to be_nil
expect(subject.authorization).to be_nil
end
end
end
describe 'write_credentials' do
it 'should call store to write credentials' do
authorization_stub = double
expect(authorization_stub).to receive(:refresh_token).and_return(true)
expect(subject).to receive(:credentials_hash)
expect(subject.store).to receive(:write_credentials)
subject.write_credentials(authorization_stub)
expect(subject.authorization).to be == authorization_stub
end
it 'should not call store to write credentials' do
expect(subject).not_to receive(:credentials_hash)
expect(subject.store).not_to receive(:write_credentials)
expect {
subject.write_credentials()
}.not_to raise_error
end
it 'should not call store to write credentials' do
expect(subject).not_to receive(:credentials_hash)
expect(subject.store).not_to receive(:write_credentials)
expect {
subject.write_credentials('something')
}.not_to raise_error
end
end
describe 'refresh_authorization' do
it 'should call refresh and write credentials' do
expect(subject).to receive(:write_credentials)
authorization_stub = double
expect(subject).to receive(:authorization).and_return(authorization_stub)
expect(authorization_stub).to receive(:refresh!).and_return(true)
subject.refresh_authorization
end
end
describe 'load_credentials' do
it 'should call store to load credentials' do
expect(subject.store).to receive(:load_credentials)
subject.send(:load_credentials)
end
end
describe 'credentials_hash' do
it 'should return an hash' do
authorization_stub = double
expect(authorization_stub).to receive(:access_token)
expect(authorization_stub).to receive(:client_id)
expect(authorization_stub).to receive(:client_secret)
expect(authorization_stub).to receive(:expires_in)
expect(authorization_stub).to receive(:refresh_token)
expect(authorization_stub).to receive(:issued_at).and_return('100')
allow(subject).to receive(:authorization).and_return(authorization_stub)
credentials = subject.send(:credentials_hash)
expect(credentials).to include(:access_token)
expect(credentials).to include(:authorization_uri)
expect(credentials).to include(:client_id)
expect(credentials).to include(:client_secret)
expect(credentials).to include(:expires_in)
expect(credentials).to include(:refresh_token)
expect(credentials).to include(:token_credential_uri)
expect(credentials).to include(:issued_at)
end
end
end
@@ -1,48 +0,0 @@
require 'spec_helper'
require 'google/api_client/auth/storages/file_store'
describe Google::APIClient::FileStore do
let(:json_file) { File.join(FIXTURES_DIR, 'files', 'auth_stored_credentials.json') }
let(:credentials_hash) {{
"access_token"=>"my_access_token",
"authorization_uri"=>"https://accounts.google.com/o/oauth2/auth",
"client_id"=>"123456_test_client_id@.apps.googleusercontent.com",
"client_secret"=>"123456_client_secret",
"expires_in"=>3600,
"refresh_token"=>"my_refresh_token",
"token_credential_uri"=>"https://accounts.google.com/o/oauth2/token",
"issued_at"=>1384440275
}}
subject{Google::APIClient::FileStore.new('a file path')}
it 'should have a path' do
expect(subject.path).to be == 'a file path'
subject.path = 'an other file path'
expect(subject.path).to be == 'an other file path'
end
it 'should load credentials' do
subject.path = json_file
credentials = subject.load_credentials
expect(credentials).to include(
'access_token', 'authorization_uri', 'refresh_token', 'client_id',
'client_secret', 'expires_in', 'token_credential_uri', 'issued_at')
end
it 'should write credentials' do
io_stub = StringIO.new
expect(subject).to receive(:open).and_return(io_stub)
subject.write_credentials(credentials_hash)
end
it 'should not load credentials' do
file = StringIO.new
file.write "{invalid_hash}"
invalid_subject = Google::APIClient::FileStore.new(file)
expect(invalid_subject.load_credentials).to be_nil
end
end
@@ -1,68 +0,0 @@
require 'spec_helper'
require 'google/api_client/auth/storages/redis_store'
describe Google::APIClient::RedisStore do
let(:root_path) { File.expand_path(File.join(__FILE__, '..', '..', '..', '..', '..')) }
let(:json_file) { File.expand_path(File.join(root_path, 'fixtures', 'files', 'auth_stored_credentials.json')) }
let(:redis) {double}
let(:credentials_hash) { {
"access_token" => "my_access_token",
"authorization_uri" => "https://accounts.google.com/o/oauth2/auth",
"client_id" => "123456_test_client_id@.apps.googleusercontent.com",
"client_secret" => "123456_client_secret",
"expires_in" => 3600,
"refresh_token" => "my_refresh_token",
"token_credential_uri" => "https://accounts.google.com/o/oauth2/token",
"issued_at" => 1384440275
} }
subject { Google::APIClient::RedisStore.new('a redis instance') }
it 'should have a redis instance' do
expect(subject.redis).to be == 'a redis instance'
subject.redis = 'an other redis instance'
expect(subject.redis).to be == 'an other redis instance'
end
describe 'load_credentials' do
it 'should load credentials' do
subject.redis= redis
expect(redis).to receive(:get).and_return(credentials_hash.to_json)
expect(subject.load_credentials).to be == credentials_hash
end
it 'should return nil' do
subject.redis= redis
expect(redis).to receive(:get).and_return(nil)
expect(subject.load_credentials).to be_nil
end
end
describe 'redis_credentials_key' do
context 'without given key' do
it 'should return default key' do
expect(subject.redis_credentials_key).to be == "google_api_credentials"
end
end
context 'with given key' do
let(:redis_store) { Google::APIClient::RedisStore.new('a redis instance', 'another_google_api_credentials') }
it 'should use given key' do
expect(redis_store.redis_credentials_key).to be == "another_google_api_credentials"
end
end
end
describe 'write credentials' do
it 'should write credentials' do
subject.redis= redis
expect(redis).to receive(:set).and_return('ok')
expect(subject.write_credentials(credentials_hash)).to be_truthy
end
end
end
@@ -1,387 +0,0 @@
# Copyright 2013 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/api_client/client_secrets'
FIXTURES_PATH = File.expand_path('../../../fixtures', __FILE__)
RSpec.describe Google::APIClient::ClientSecrets do
describe '::new' do
let(:filename) { File.join(FIXTURES_PATH, 'files', 'client_secrets.json') }
let(:data) { File.open(filename, 'r') { |file| JSON.load(file.read) } }
context 'without options' do
subject { Google::APIClient::ClientSecrets.new(data) }
describe 'existence of instance variables' do
it '@access_token' do
expect(subject).to respond_to(:access_token)
end
it '@authorization_uri' do
expect(subject).to respond_to(:authorization_uri)
end
it '@client_id' do
expect(subject).to respond_to(:client_id)
end
it '@client_secret' do
expect(subject).to respond_to(:client_secret)
end
it '@expires_at' do
expect(subject).to respond_to(:expires_at)
end
it '@expires_in' do
expect(subject).to respond_to(:expires_in)
end
it '@id_token' do
expect(subject).to respond_to(:id_token)
end
it '@issued_at' do
expect(subject).to respond_to(:issued_at)
end
it '@javascript_origins' do
expect(subject).to respond_to(:javascript_origins)
end
it '@redirect_uris' do
expect(subject).to respond_to(:redirect_uris)
end
it '@refresh_token' do
expect(subject).to respond_to(:refresh_token)
end
it '@token_credential_uri' do
expect(subject).to respond_to(:token_credential_uri)
end
end
describe 'value of instance variables' do
it 'should set @access_token' do
expect(subject.access_token).to be_nil
end
it 'should set @authorization_uri' do
expect(subject.authorization_uri).to eq 'https://accounts.google.com/o/oauth2/auth'
end
it 'should set @client_id' do
expect(subject.client_id).to eq '898243283568.apps.googleusercontent.com'
end
it 'should set @client_secret' do
expect(subject.client_secret).to eq 'i8YaXdGgiQ4_KrTVNGsB7QP1'
end
it 'should set @expires_at' do
expect(subject.expires_at).to be_nil
end
it 'should set @expires_in' do
expect(subject.expires_in).to be_nil
end
it 'should set @flow' do
expect(subject.flow).to eq 'installed'
end
it 'should set @id_token' do
expect(subject.id_token).to be_nil
end
it 'should set @issued_at' do
expect(subject.issued_at).to be_nil
end
it 'should set @javascript_origins' do
expect(subject.javascript_origins).to eq []
end
it 'should set @redirect_uris' do
expect(subject.redirect_uris).to eq []
end
it 'should set @refresh_token' do
expect(subject.refresh_token).to be_nil
end
it 'should set @token_credential_uri' do
expect(subject.token_credential_uri).to eq 'https://accounts.google.com/o/oauth2/token'
end
end
end
context 'with options' do
context 'option keys are string' do
let(:string_options) do
{ 'samples' =>
{
'access_token' => 'sample_access_token',
'auth_uri' => 'sample_auth_uri',
'authorization_uri' => 'sample_authorization_uri',
'client_id' => 'sample_client_id',
'client_secret' => 'sample_client_secret',
'expires_at' => 'sample_expires_at',
'expires_in' => 'sample_expires_in',
'id_token' => 'sample_id_token',
'issued_at' => 'sample_issued_at',
'javascript_origin' => 'sample_javascript_origin',
'javascript_origins' => 'sample_javascript_origins',
'redirect_uris' => 'sample_redirect_uris',
'refresh_token' => 'sample_refresh_token',
'token_credential_uri' => 'sample_token_credential_uri',
'token_uri' => 'sample_token_uri' } }
end
subject { Google::APIClient::ClientSecrets.new(string_options) }
describe 'existence of instance variables' do
it '@access_token' do
expect(subject).to respond_to(:access_token)
end
it '@authorization_uri' do
expect(subject).to respond_to(:authorization_uri)
end
it '@client_id' do
expect(subject).to respond_to(:client_id)
end
it '@client_secret' do
expect(subject).to respond_to(:client_secret)
end
it '@expires_at' do
expect(subject).to respond_to(:expires_at)
end
it '@expires_in' do
expect(subject).to respond_to(:expires_in)
end
it '@id_token' do
expect(subject).to respond_to(:id_token)
end
it '@issued_at' do
expect(subject).to respond_to(:issued_at)
end
it '@javascript_origins' do
expect(subject).to respond_to(:javascript_origins)
end
it '@redirect_uris' do
expect(subject).to respond_to(:redirect_uris)
end
it '@refresh_token' do
expect(subject).to respond_to(:refresh_token)
end
it '@token_credential_uri' do
expect(subject).to respond_to(:token_credential_uri)
end
end
describe 'value of instance variables' do
it 'should set @access_token' do
expect(subject.access_token).to eq 'sample_access_token'
end
it 'should set @authorization_uri' do
expect(subject.authorization_uri).to eq 'sample_auth_uri'
end
it 'should set @client_id' do
expect(subject.client_id).to eq 'sample_client_id'
end
it 'should set @client_secret' do
expect(subject.client_secret).to eq 'sample_client_secret'
end
it 'should set @expires_at' do
expect(subject.expires_at).to eq 'sample_expires_at'
end
it 'should set @expires_in' do
expect(subject.expires_in).to eq 'sample_expires_in'
end
it 'should set @flow' do
expect(subject.flow).to eq 'samples'
end
it 'should set @id_token' do
expect(subject.id_token).to eq 'sample_id_token'
end
it 'should set @issued_at' do
expect(subject.issued_at).to eq 'sample_issued_at'
end
it 'should set @javascript_origins' do
expect(subject.javascript_origins).to eq 'sample_javascript_origins'
end
it 'should set @redirect_uris' do
expect(subject.redirect_uris).to eq 'sample_redirect_uris'
end
it 'should set @refresh_token' do
expect(subject.refresh_token).to eq 'sample_refresh_token'
end
it 'should set @token_credential_uri' do
expect(subject.token_credential_uri).to eq 'sample_token_uri'
end
end
end
context 'option keys are symbol' do
let(:symbol_options) do
{ samples:
{
access_token: 'sample_access_token',
auth_uri: 'sample_auth_uri',
authorization_uri: 'sample_authorization_uri',
client_id: 'sample_client_id',
client_secret: 'sample_client_secret',
expires_at: 'sample_expires_at',
expires_in: 'sample_expires_in',
id_token: 'sample_id_token',
issued_at: 'sample_issued_at',
javascript_origin: 'sample_javascript_origin',
javascript_origins: 'sample_javascript_origins',
redirect_uris: 'sample_redirect_uris',
refresh_token: 'sample_refresh_token',
token_credential_uri: 'sample_token_credential_uri',
token_uri: 'sample_token_uri' } }
end
subject { Google::APIClient::ClientSecrets.new(symbol_options) }
describe 'existence of instance variables' do
it '@access_token' do
expect(subject).to respond_to(:access_token)
end
it '@authorization_uri' do
expect(subject).to respond_to(:authorization_uri)
end
it '@client_id' do
expect(subject).to respond_to(:client_id)
end
it '@client_secret' do
expect(subject).to respond_to(:client_secret)
end
it '@expires_at' do
expect(subject).to respond_to(:expires_at)
end
it '@expires_in' do
expect(subject).to respond_to(:expires_in)
end
it '@id_token' do
expect(subject).to respond_to(:id_token)
end
it '@issued_at' do
expect(subject).to respond_to(:issued_at)
end
it '@javascript_origins' do
expect(subject).to respond_to(:javascript_origins)
end
it '@redirect_uris' do
expect(subject).to respond_to(:redirect_uris)
end
it '@refresh_token' do
expect(subject).to respond_to(:refresh_token)
end
it '@token_credential_uri' do
expect(subject).to respond_to(:token_credential_uri)
end
end
describe 'value of instance variables' do
it 'should set @access_token' do
expect(subject.access_token).to eq 'sample_access_token'
end
it 'should set @authorization_uri' do
expect(subject.authorization_uri).to eq 'sample_auth_uri'
end
it 'should set @client_id' do
expect(subject.client_id).to eq 'sample_client_id'
end
it 'should set @client_secret' do
expect(subject.client_secret).to eq 'sample_client_secret'
end
it 'should set @expires_at' do
expect(subject.expires_at).to eq 'sample_expires_at'
end
it 'should set @expires_in' do
expect(subject.expires_in).to eq 'sample_expires_in'
end
it 'should set @flow' do
expect(subject.flow).to eq 'samples'
end
it 'should set @id_token' do
expect(subject.id_token).to eq 'sample_id_token'
end
it 'should set @issued_at' do
expect(subject.issued_at).to eq 'sample_issued_at'
end
it 'should set @javascript_origins' do
expect(subject.javascript_origins).to eq 'sample_javascript_origins'
end
it 'should set @redirect_uris' do
expect(subject.redirect_uris).to eq 'sample_redirect_uris'
end
it 'should set @refresh_token' do
expect(subject.refresh_token).to eq 'sample_refresh_token'
end
it 'should set @token_credential_uri' do
expect(subject.token_credential_uri).to eq 'sample_token_uri'
end
end
end
end
end
context 'with JSON file' do
let(:file) { File.join(FIXTURES_PATH, 'files', 'client_secrets.json') }
subject(:secrets) { Google::APIClient::ClientSecrets.load(file)}
it 'should load the correct client ID' do
expect(secrets.client_id).to be == '898243283568.apps.googleusercontent.com'
end
it 'should load the correct client secret' do
expect(secrets.client_secret).to be == 'i8YaXdGgiQ4_KrTVNGsB7QP1'
end
context 'serialzed to hash' do
subject(:hash) { secrets.to_hash }
it 'should contain the flow as the first key' do
expect(hash).to have_key "installed"
end
it 'should contain the client ID' do
expect(hash["installed"]["client_id"]).to be == '898243283568.apps.googleusercontent.com'
end
it 'should contain the client secret' do
expect(hash["installed"]["client_secret"]).to be == 'i8YaXdGgiQ4_KrTVNGsB7QP1'
end
it 'should remove empty value' do
expect(hash["installed"]["redirect_uris"]).to be nil
end
it 'should remove nil values' do
expect(hash["installed"]["issued_at"]).to be nil
end
end
end
context 'with no existing JSON file' do
it 'should raise exception' do
file = File.join(FIXTURES_PATH, 'files', 'no_file.json')
expect { Google::APIClient::ClientSecrets.load(file) }.to raise_exception(Errno::ENOENT)
end
end
context 'with invalid JSON file' do
it 'should raise exception' do
file = File.join(FIXTURES_PATH, 'files', 'invalid.json')
expect { Google::APIClient::ClientSecrets.load(file) }.to raise_exception(JSON::ParserError)
end
end
context 'with folder name, which have json file in parents' do
it 'should load the correct client id' do
folder = File.join(FIXTURES_PATH, 'files', 'child')
secrets = Google::APIClient::ClientSecrets.load(folder)
expect(secrets.client_id).to be == '898243283568.apps.googleusercontent.com'
end
end
context 'with folder wihout client_secrets.json' do
it "should raise exception", fakefs: true do
FileUtils.mkdir("/tmp")
expect { Google::APIClient::ClientSecrets.load('/tmp') }.to raise_exception(ArgumentError)
end
end
end
@@ -1,384 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/api_command'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::ApiCommand do
include TestHelpers
include_context 'HTTP client'
let(:model_class) do
Class.new do
attr_accessor :value
end
end
let(:representer_class) do
Class.new(Google::Apis::Core::JsonRepresentation) do
property :value
end
end
let(:x_goog_api_client_value) { "gl-ruby/#{RUBY_VERSION} gdcl/#{Google::Apis::VERSION}" }
context('with preparation') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
it 'should set X-Goog-Api-Client header if none is set' do
command.prepare!
expect(command.header['X-Goog-Api-Client']).to eql x_goog_api_client_value
end
it 'should append to x-goog-api-client header with case difference' do
command.header['x-goog-api-client'] = "foo/1.2.3"
command.prepare!
expect(command.header['X-Goog-Api-Client']).to eql "foo/1.2.3 #{x_goog_api_client_value}"
end
it 'should append to multiple x-goog-api-client headers' do
command.header['x-goog-api-client'] = "foo/1.2.3"
command.header['X-Goog-Api-Client'] = "bar/4.5.6"
command.prepare!
expect(command.header['X-Goog-Api-Client']).to eql "foo/1.2.3 bar/4.5.6 #{x_goog_api_client_value}"
expect(command.header['x-goog-api-client']).to be nil
end
it 'should override existing gl-ruby/ and gdcl/ clauses' do
command.header['x-goog-api-client'] = "gl-ruby/0.0 foo/1.2.3"
command.header['X-Goog-Api-Client'] = "bar/4.5.6 gdcl/0.0"
command.prepare!
expect(command.header['X-Goog-Api-Client']).to eql "foo/1.2.3 bar/4.5.6 #{x_goog_api_client_value}"
expect(command.header['x-goog-api-client']).to be nil
end
it 'should not set the X-Goog-User-Project header if there is no quota_project' do
command.prepare!
expect(command.header['X-Goog-User-Project']).to be_nil
end
it 'should set the X-Goog-User-Project to the credentials quota_project' do
command.options.authorization = OpenStruct.new quota_project_id: "b_project_id"
command.prepare!
expect(command.header['X-Goog-User-Project']).to eql "b_project_id"
end
it 'should set the X-Goog-User-Project to a custom quota_project in preference to credentials' do
command.options.authorization = OpenStruct.new quota_project_id: "b_project_id"
command.options.quota_project = "c_project_id"
command.prepare!
expect(command.header['X-Goog-User-Project']).to eql "c_project_id"
end
end
context('with a request body') do
let(:command) do
request = model_class.new
request.value = 'hello'
command = Google::Apis::Core::ApiCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.request_representation = representer_class
command.request_object = request
command.query['a'] = 'b'
command
end
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals?a=b')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should serialize the request object' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals?a=b').with do |req|
be_json_eql(%({"value":"hello"})).matches?(req.body)
end).to have_been_made
end
it 'should not form encode query parameters when body expected but nil' do
command.query['a'] = 'b'
command.request_object = nil
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals?a=b').with(body: nil)).to have_been_made
end
end
context('with a raw request body') do
let(:command) do
request = model_class.new
command = Google::Apis::Core::ApiCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.request_representation = representer_class
command.request_object = %({"value": "hello"})
command.options.skip_serialization = true
command
end
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should allow raw JSON if skip_serialization = true' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals').with do |req|
be_json_eql(%({"value":"hello"})).matches?(req.body)
end).to have_been_made
end
end
context('with a JSON response') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.response_representation = representer_class
command.response_class = model_class
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({"value" : "hello"}))
end
it 'should return a model instance' do
result = command.execute(client)
expect(result).to be_kind_of(model_class)
end
it 'should return a populated object' do
result = command.execute(client)
expect(result.value).to eql 'hello'
end
it 'should return a raw JSON if skip_deserialization true' do
command.options.skip_deserialization = true
result = command.execute(client)
expect(result).to eql %({"value" : "hello"})
end
end
context('with an invalid content-type response') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.response_representation = representer_class
command.response_class = model_class
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'text/plain' }, body: %(Ignore me))
end
it 'should return nil' do
result = command.execute(client)
expect(result).to be_nil
end
end
context('with a field parameter') do
let(:command) do
command = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.query['fields'] = ':items(:id, :long_name, :a_really_long_name), shouldBeLeftAlone '
command
end
before(:example) do
stub_request(:get, /.*/)
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should normalize fields params' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with(query: { 'fields' => 'items(id, longName, aReallyLongName), shouldBeLeftAlone ' })) .to have_been_made
end
end
context('with a rate limit response') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = <<EOF
{
"error": {
"errors": [
{
"domain": "global",
"reason": "rateLimitExceeded",
"message": "Rate limit exceeded"
}
],
"code": 403,
"message": "Rate limit exceeded"
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [403, 'Rate Limit Exceeded'], headers: { 'Content-Type' => 'application/json' }, body: json)
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should retry' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')).to have_been_made.times(2)
end
end
context('with a project not linked response') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = <<EOF
{
"error": {
"errors": [
{
"domain": "global",
"reason": "projectNotLinked",
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
}
],
"code": 403,
"message": "The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console."
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [403, 'The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console.'], headers: {
'Content-Type' => 'application/json'
}, body: json)
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %({}))
end
it 'should raise project not linked error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ProjectNotLinkedError)
end
it 'should raise an error with the reason and message' do
expect { command.execute(client) }.to raise_error(
/projectNotLinked: The project id used to call the Google Play Developer API has not been linked in the Google Play Developer Console./)
end
end
context('with a client error response') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = <<EOF
{
"error": {
"errors": [
{
"domain": "global",
"reason": "timeRangeEmpty",
"message": "The specified time range is empty."
}
],
"code": 400,
"message": "The specified time range is empty."
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [400, 'Bad Request'], headers: { 'Content-Type' => 'application/json' }, body: json)
end
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should raise an error with the reason and message' do
expect { command.execute(client) }.to raise_error(
/timeRangeEmpty: The specified time range is empty/)
end
end
context('with an empty error body') do
let(:command) do
Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
json = %({})
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [403, 'Rate Limit Exceeded'], headers: { 'Content-Type' => 'application/json' }, body: json)
end
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should use the default error message' do
expect { command.execute(client) }.to raise_error(/Invalid request/)
end
end
context('with v2 error messages') do
let(:command) do
cmd = Google::Apis::Core::ApiCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
cmd.options.api_format_version = 2
cmd
end
before(:example) do
json = <<EOF
{
"error": {
"code": 400,
"message": "Illegal character ':' in log name",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.logging.v2.WriteLogEntriesPartialErrors",
"logEntryErrors": {
"0": {
"code": 3,
"message": "Illegal character ':' in log name"
},
"1": {
"code": 7,
"message": "User not authorized."
}
}
}
]
}
}
EOF
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.with(headers: {'X-Goog-Api-Format-Version' => '2'})
.to_return(status: [400, 'Invalid Argument'], headers: { 'Content-Type' => 'application/json' }, body: json)
end
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should raise an error with the reason and message' do
expect { command.execute(client) }.to raise_error(
/INVALID_ARGUMENT: Illegal character ':' in log name/)
end
end
end
@@ -1,195 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/batch'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::BatchCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::BatchCommand.new(:post, 'https://www.googleapis.com/batch')
end
let(:get_command) { Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals/1') }
let(:post_with_string_command) do
command = Google::Apis::Core::HttpCommand.new(:post, 'https://www.googleapis.com/zoo/animals/2')
command.body = 'Hello world'
command.header['Content-Type'] = 'text/plain'
command
end
let(:post_with_io_command) do
command = Google::Apis::Core::HttpCommand.new(:post, 'https://www.googleapis.com/zoo/animals/3')
command.body = StringIO.new('Goodbye!')
command.header['Content-Type'] = 'text/plain'
command
end
before(:example) do
allow(SecureRandom).to receive(:uuid).and_return('ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f')
allow(Digest::SHA1).to receive(:hexdigest).and_return('123abc')
response = <<EOF
--batch123
Content-Type: application/http
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+0>
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123
Content-Type: application/http
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+2>
HTTP/1.1 500 Server Error
Content-Type: text/plain; charset=UTF-8
Error!
--batch123
Content-Type: application/http
Content-ID: <response-ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+1>
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
world
--batch123--
EOF
stub_request(:post, 'https://www.googleapis.com/batch')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should send content' do
b = ->(_res, _err) {}
command.add(get_command, &b)
command.add(post_with_string_command, &b)
command.add(post_with_io_command, &b)
command.execute(client)
expected_body = <<EOF.gsub(/\n/, "\r\n")
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+0>
Content-Length: 58
Content-Transfer-Encoding: binary
GET /zoo/animals/1? HTTP/1.1
Host: www.googleapis.com
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+1>
Content-Length: 96
Content-Transfer-Encoding: binary
POST /zoo/animals/2? HTTP/1.1
Content-Type: text/plain
Host: www.googleapis.com
Hello world
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+2>
Content-Length: 93
Content-Transfer-Encoding: binary
POST /zoo/animals/3? HTTP/1.1
Content-Type: text/plain
Host: www.googleapis.com
Goodbye!
--123abc--
EOF
expect(a_request(:post, 'https://www.googleapis.com/batch').with(body: expected_body)).to have_been_made
end
it 'should send content with authorization header when set' do
b = ->(_res, _err) {}
signet_auth = object_double(Signet::OAuth2::Client.new).tap do |a|
allow(a).to receive(:apply!) do |header|
header['Authorization'] = 'Bearer a_token_value'
end
end
command.add(get_command.tap { |c| c.options.authorization = 'oauthtoken' }, &b)
command.add(post_with_string_command.tap { |c| c.options.authorization = signet_auth }, &b)
command.add(post_with_io_command, &b)
command.execute(client)
expected_body = <<EOF.gsub(/\n/, "\r\n")
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+0>
Content-Length: 92
Content-Transfer-Encoding: binary
GET /zoo/animals/1? HTTP/1.1
Authorization: Bearer oauthtoken
Host: www.googleapis.com
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+1>
Content-Length: 133
Content-Transfer-Encoding: binary
POST /zoo/animals/2? HTTP/1.1
Content-Type: text/plain
Authorization: Bearer a_token_value
Host: www.googleapis.com
Hello world
--123abc
Content-Type: application/http
Content-Id: <ffe23d1b-e8f7-47f5-8c01-2a30cf8ecb8f+2>
Content-Length: 93
Content-Transfer-Encoding: binary
POST /zoo/animals/3? HTTP/1.1
Content-Type: text/plain
Host: www.googleapis.com
Goodbye!
--123abc--
EOF
expect(a_request(:post, 'https://www.googleapis.com/batch').with(body: expected_body)).to have_been_made
end
it 'should send decode responses' do
expect do |b|
command.add(get_command) do |res, err|
b.to_proc.call(1, res, err)
end
command.add(post_with_string_command) do |res, err|
b.to_proc.call(2, res, err)
end
command.add(post_with_io_command) do |res, err|
b.to_proc.call(3, res, err)
end
command.execute(client)
end.to yield_successive_args([1, 'Hello', nil], [3, nil, an_instance_of(Google::Apis::ServerError)], [2, 'world', nil],)
end
it 'should raise error if batch is empty' do
expect { command.execute(client) }.to raise_error(Google::Apis::BatchError)
end
end
@@ -1,76 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/composite_io'
RSpec.describe Google::Apis::Core::CompositeIO do
shared_examples 'should act like IO' do
it 'should read from all IOs' do
expect(io.read).to eq 'Hello Cruel World'
end
it 'should respond to size' do
expect(io.size).to eq 17
end
it 'should respond to pos=' do
io.pos = 6
expect(io.read).to eq('Cruel World')
end
it 'should reject negative positions' do
expect { io.pos = -1 }.to raise_error(ArgumentError)
end
it 'should return nil if position beyond size' do
io.pos = 20
expect(io.read).to be_nil
end
it 'should be readable after rewinding' do
expect(io.read).to eq 'Hello Cruel World'
expect(io.read).to be_nil
io.rewind
expect(io.read).to eq 'Hello Cruel World'
end
end
context 'with StringIOs' do
let(:io) do
Google::Apis::Core::CompositeIO.new(
StringIO.new("Hello "),
StringIO.new("Cruel "),
StringIO.new("World"))
end
include_examples 'should act like IO'
end
context 'with Files' do
let(:io) do
files = []
dir = Dir.mktmpdir
['Hello ', 'Cruel ', 'World'].each_with_index do |text, index|
name = File.join(dir, "f#{index}")
File.open(name, 'w') { |f| f.write(text) }
files << name
end
Google::Apis::Core::CompositeIO.new(files.map { |name| File.open(name, 'r') })
end
include_examples 'should act like IO'
end
end
@@ -1,138 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/download'
require 'google/apis/core/json_representation'
require 'tempfile'
require 'tmpdir'
RSpec.describe Google::Apis::Core::DownloadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::DownloadCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.download_dest = dest
command
end
shared_examples 'should download' do
context 'with successful response' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %(Hello world))
end
it 'should not include a range header' do
command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| !req.headers.key?('Range') }
).to have_been_made
end
it 'should receive content' do
expect(received).to eql 'Hello world'
end
end
context 'with error' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [404, 'Not Found'], body: '')
end
it 'should raise error' do
expect {received}.to raise_error(Google::Apis::ClientError)
end
end
context 'with disconnects' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(body: ['Hello ', Timeout::Error])
.to_return(status: [206, 'Partial content'], body: 'world')
end
it 'should receive entire content' do
expect(received).to eql('Hello world')
end
end
context 'with disconnects and no partial response' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(body: ['Hello ', Timeout::Error])
.to_return(body: 'Hello world')
end
it 'should receive entire content' do
expect(received).to eql('Hello world')
end
end
context 'with redirect' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [302, 'Content moved'], headers: { 'Location' => 'https://content.googleapis.com/files/12345' }, body: %(Content moved))
stub_request(:get, 'https://content.googleapis.com/files/12345')
.to_return(headers: { 'Content-Type' => 'application/json' }, body: %(Hello world))
end
it 'should receive content' do
expect(received).to eql 'Hello world'
end
end
end
context 'with filename destination' do
let(:dest) { File.join(Dir.mktmpdir, 'test.txt') }
let(:received) do
command.execute(client)
File.read(dest)
end
include_examples 'should download'
end
context 'with default destination' do
let(:dest) { nil }
let(:received) { command.execute(client).string }
include_examples 'should download'
end
context 'with IO destination' do
let(:dest) { Tempfile.new('test') }
let(:received) do
command.execute(client)
dest.rewind
dest.read
end
include_examples 'should download'
end
context 'with #write destination' do
let(:dest) { WritableIO.new(StringIO.new) }
let(:received) { command.execute(client).io.string }
include_examples 'should download'
WritableIO = Struct.new(:io) do
def write(data)
io.write(data)
end
end
end
end
@@ -1,60 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/hashable'
RSpec.describe Google::Apis::Core::Hashable do
let(:child_class) do
Class.new do
include Google::Apis::Core::Hashable
attr_accessor :value
end
end
let(:model_class) do
Class.new do
include Google::Apis::Core::Hashable
attr_accessor :value
attr_accessor :value2
attr_accessor :children
end
end
let(:model) do
obj = model_class.new
obj.value = 'hello'
obj.value2 = {
a: 'a'
}
child = child_class.new
child.value = 'goodbye'
obj.children = [child]
obj
end
let(:hash) { model.to_h }
it 'should serialize attributes' do
expect(hash).to include(value: 'hello')
end
it 'should serialize collections' do
expect(hash).to include(children: [{ value: 'goodbye' }])
end
it 'should serialize hashes' do
expect(hash[:value2]).to include(a: 'a')
end
end
@@ -1,508 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/http_command'
module Google
module Apis
module CloudkmsV1
class DecryptResponse
end
end
end
end
RSpec.describe Google::Apis::Core::HttpCommand do
include TestHelpers
include_context 'HTTP client'
context('with credentials') do
let(:command) do
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.authorization = Signet::OAuth2::Client.new({
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token'
})
command
end
context('with one token error') do
before(:example) do
stub_request(:post, 'https://accounts.google.com/o/oauth2/token').to_return(
{ status: [500, 'Server error'] },
{ status: [200, ''], headers: { 'Content-Type' => 'application/json' }, body: '{"access_token": "foo"}' })
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
end
it 'should send credentials' do
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer foo' }).to have_been_made
expect(result).to eql 'Hello world'
end
end
context('with two token errors') do
before(:example) do
stub_request(:post, 'https://accounts.google.com/o/oauth2/token').to_return(
{ status: [500, 'Server error'] },
{ status: [401, 'Unauthorized'] })
end
it 'should raise error' do
expect { command.execute(client) }.to raise_error(Signet::AuthorizationError)
end
end
end
context('with credentials') do
let(:command) do
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.authorization = authorization
command
end
context('that are refreshable') do
let(:authorization) do
calls = 0
auth = object_double(Signet::OAuth2::Client.new)
allow(auth).to receive(:apply!) do |header|
header['Authorization'] = sprintf('Bearer a_token_value_%d', calls)
calls += 1
end
auth
end
it 'should send credentials' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_0' }).to have_been_made
end
context('with authorizaton error') do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [401, 'Unauthorized'])
.to_return(body: %(Hello world))
end
it 'should refresh if auth error received' do
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_1' }).to have_been_made
end
it 'should ignore retry count' do
command.options.retries = 0
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['Authorization'] == 'Bearer a_token_value_1' }).to have_been_made
end
end
end
context('that are bare tokens`') do
let(:authorization) { 'a_token_value' }
it 'should send credentials' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| expect(req.headers['Authorization']).to eql 'Bearer a_token_value' }).to have_been_made
end
it 'should send not refresh' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [401, 'Unauthorized'])
expect { command.execute(client) }.to raise_error(Google::Apis::AuthorizationError)
end
end
end
context('with a successful response') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
end
it 'should return the response body if block not present' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
it 'should call block if present' do
expect { |b| command.execute(client, &b) }.to yield_with_args('Hello world', nil)
end
end
context('with server errors') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [500, 'Server error']).times(2)
.to_return(body: %(Hello world))
end
it 'should return the response body' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
it 'should raise error if retries exceeded' do
command.options.retries = 1
expect { command.execute(client) }.to raise_error(Google::Apis::ServerError)
end
context('with retries exceeded') do
before(:example) do
command.options.retries = 1
end
let(:err) do
begin
command.execute(client)
rescue Google::Apis::Error => e
e
end
end
it 'should raise error with HTTP status code' do
expect(err.status_code).to eq 500
end
end
context('with callbacks') do
it 'should return the response body after retries' do
expect { |b| command.execute(client, &b) }.to yield_with_args('Hello world', nil)
end
end
end
context('with unknown errors') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_raise(HTTPClient::BadResponseError) # empty error, no res value
end
it 'should raise transmission error' do
command.options.retries = 1
err = nil
begin
command.execute(client)
rescue Google::Apis::Error => e
err = e
end
expect(err).to be_a(Google::Apis::TransmissionError)
expect(err.cause).to be_a(HTTPClient::BadResponseError)
expect(err.cause.res).to be_nil # no res value
end
end
context('with options') do
let(:command) do
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.header = { 'X-Foo' => 'bar' }
command
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(body: %(Hello world))
end
it 'should send user headers' do
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Foo'] == 'bar' }).to have_been_made
end
end
context('with redirects') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [302, 'Redirect'], headers: { 'Location' => 'https://zoo.googleapis.com/animals' })
stub_request(:get, 'https://zoo.googleapis.com/animals')
.to_return(body: %(Hello world))
end
it 'should return the response body' do
result = command.execute(client)
expect(result).to eql 'Hello world'
end
end
context('with too many redirects') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [302, 'Redirect'], headers: { 'Location' => 'https://www.googleapis.com/zoo/animals' }).times(6)
end
it 'should raise error if retries exceeded' do
expect { command.execute(client) }.to raise_error(Google::Apis::RedirectError)
end
end
context('with no server response') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_timeout
end
it 'should raise transmission error' do
expect { command.execute(client) }.to raise_error(Google::Apis::TransmissionError)
end
end
context('with invalid status code') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [0, 'Wat!?'])
end
it 'should raise transmission error' do
expect { command.execute(client) }.to raise_error(Google::Apis::TransmissionError)
end
end
context('with client errors') do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [400, 'Invalid request'])
end
it 'should raise error without retry' do
command.options.retries = 1
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
it 'should call block if present' do
expect { |b| command.execute(client, &b) }.to yield_with_args(nil, an_instance_of(Google::Apis::ClientError))
end
it 'should not swallow errors raised in block' do
expect { command.execute(client) { raise "Potatoes detected in tailpipe" } }.to raise_error("Potatoes detected in tailpipe")
end
end
context('with opencensus integration') do
it 'should create an opencensus span for a successful call' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [200, ''], body: "Hello world")
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
OpenCensus::Trace.start_request_trace do |span_context|
command.execute(client)
spans = span_context.build_contained_spans
expect(spans.size).to eql 1
span = spans.first
expect(span.name.value).to eql '/zoo/animals'
expect(span.status.code).to eql 0
attrs = span.attributes
expect(attrs['http.host'].value).to eql 'www.googleapis.com'
expect(attrs['http.method'].value).to eql 'GET'
expect(attrs['http.path'].value).to eql '/zoo/animals'
expect(attrs['http.status_code']).to eql 200
events = span.time_events
expect(events.size).to eql 2
expect(events[0].type).to eql :SENT
expect(events[0].uncompressed_size).to eql 0
expect(events[1].type).to eql :RECEIVED
expect(events[1].uncompressed_size).to eql 11
end
end
it 'should create an opencensus span for a call failure' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [403, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
OpenCensus::Trace.start_request_trace do |span_context|
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
spans = span_context.build_contained_spans
expect(spans.size).to eql 1
span = spans.first
expect(span.name.value).to eql '/zoo/animals'
expect(span.status.code).to eql 7
attrs = span.attributes
expect(attrs['http.host'].value).to eql 'www.googleapis.com'
expect(attrs['http.method'].value).to eql 'GET'
expect(attrs['http.path'].value).to eql '/zoo/animals'
expect(attrs['http.status_code']).to eql 403
end
end
it 'should propagate trace context header' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
OpenCensus::Trace.start_request_trace do |span_context|
result = command.execute(client)
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals')
.with { |req| !req.headers['Traceparent'].empty? }).to have_been_made
end
end
it 'should not create an opencensus span if disabled' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.use_opencensus = false
OpenCensus::Trace.start_request_trace do |span_context|
command.execute(client)
spans = span_context.build_contained_spans
expect(spans.size).to eql 0
end
end
end if Google::Apis::Core::HttpCommand::OPENCENSUS_AVAILABLE
it 'should send repeated query parameters' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals?a=1&a=2&a=3')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.query['a'] = [1,2,3]
command.execute(client)
end
it 'should not remove initial query parameters' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals?a=1&a=2&a=3&foo=bar')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals?foo=bar')
command.query['a'] = [1,2,3]
command.execute(client)
end
it 'should send falsey query parameters' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals?a=0&b=false')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.query['a'] = 0
command.query['b'] = false
command.execute(client)
end
it 'should send DateTime query parameters' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals?a=2019-06-22T13:51:37-07:00&b=2019-06-22T13:51:37-07:00&b=2019-07-23T14:54:12-07:00')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
date1 = DateTime.new(2019, 6, 22, 13, 51, 37, "-0700")
date2 = DateTime.new(2019, 7, 23, 14, 54, 12, "-0700")
command.query['a'] = date1
command.query['b'] = [date1, date2]
command.execute(client)
end
it 'should form encode parameters when method is POST and no body present' do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'a=1&a=2&a=3&b=hello&c=&d=0')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.query['a'] = [1,2,3]
command.query['b'] = 'hello'
command.query['c'] = nil
command.query['d'] = 0
command.execute(client)
end
it 'should prepend user query parameters from options and not remove initial query parameters', :focus do
stub_request(:get, 'https://www.googleapis.com/zoo/animals?a=1&a=2&a=3&b=false&foo=bar')
.to_return(status: [200, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals?foo=bar')
command.options.query = {
'a' => [1,2,3],
'b' => false
}
command.execute(client)
end
it 'should raise transmission error instead of socket error' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_raise(SocketError)
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.retries = 0
expect { command.execute(client) }.to raise_error(Google::Apis::TransmissionError)
end
it 'should raise rate limit error for 429 status codes' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(status: [429, ''])
command = Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
command.options.retries = 0
expect { command.execute(client) }.to raise_error(Google::Apis::RateLimitError)
end
it 'should not normalize unicode values by default' do
stub_request(:get, 'https://www.googleapis.com/Cafe%CC%81').to_return(status: [200, ''])
template = Addressable::Template.new('https://www.googleapis.com/{path}')
command = Google::Apis::Core::HttpCommand.new(:get, template)
command.params[:path] = "Cafe\u0301"
command.options.retries = 0
command.execute(client)
end
it 'should normalize unicode values when requested' do
stub_request(:get, 'https://www.googleapis.com/Caf%C3%A9').to_return(status: [200, ''])
template = Addressable::Template.new('https://www.googleapis.com/{path}')
command = Google::Apis::Core::HttpCommand.new(:get, template)
command.params[:path] = "Cafe\u0301"
command.options.retries = 0
command.options.normalize_unicode = true
command.execute(client)
end
describe "#safe_object_representation" do
let(:command) do
Google::Apis::Core::HttpCommand.new(:get, 'https://www.googleapis.com/zoo/animals')
end
it "should show fields in a normal object" do
obj = Object.new
obj.instance_variable_set(:@foobar, "hi")
str = command.send(:safe_object_representation, obj)
expect(str).to match /@foobar/
end
it "should not show fields in a restricted object" do
obj = Google::Apis::CloudkmsV1::DecryptResponse.new
obj.instance_variable_set(:@foobar, "hi")
str = command.send(:safe_object_representation, obj)
expect(str).not_to match /@foobar/
end
end
end
@@ -1,265 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::JsonRepresentation do
let(:child_class) do
Class.new do
attr_accessor :value
end
end
let(:model_class) do
rep_class = representer_class
Class.new do
attr_accessor :unset_value
attr_accessor :nil_value
attr_accessor :numeric_value
attr_accessor :string_value
attr_accessor :boolean_value_true
attr_accessor :boolean_value_false
attr_accessor :datetime_value
attr_accessor :nil_datetime_value
attr_accessor :date_value
attr_accessor :nil_date_value
attr_accessor :bytes_value
attr_accessor :big_value
attr_accessor :items
attr_accessor :child
attr_accessor :children
const_set(:Representation, rep_class)
include Google::Apis::Core::JsonObjectSupport
end
end
let(:representer_class) do
klass = child_class
Class.new(Google::Apis::Core::JsonRepresentation) do
property :unset_value, as: 'unsetValue'
property :nil_value, as: 'nilValue'
property :numeric_value, as: 'numericValue'
property :string_value, as: 'stringValue'
property :boolean_value_true, as: 'booleanValueTrue'
property :boolean_value_false, as: 'booleanValueFalse'
property :datetime_value, as: 'dateTimeValue', type: DateTime
property :nil_datetime_value, as: 'nullDateTimeValue', type: DateTime
property :date_value, as: 'dateValue', type: Date
property :nil_date_value, as: 'nullDateValue', type: Date
property :bytes_value, as: 'bytesValue', base64: true
property :big_value, as: 'bigValue', numeric_string: true
property :items
property :child, class: klass do
property :value
end
collection :children, class: klass do
property :value
end
end
end
shared_examples 'it serializes' do
it 'does not serialize unset values' do
expect(json).not_to have_json_path('unsetValue')
end
it 'serializes explicit nil values' do
expect(json).to be_json_eql(%(null)).at_path('nilValue')
end
it 'serializes numeric values' do
expect(json).to be_json_eql(%(123)).at_path('numericValue')
end
it 'serializes string values' do
expect(json).to be_json_eql(%("test")).at_path('stringValue')
end
it 'serializes boolean values' do
expect(json).to be_json_eql(%(true)).at_path('booleanValueTrue')
expect(json).to be_json_eql(%(false)).at_path('booleanValueFalse')
end
it 'serializes datetime values' do
expect(json).to be_json_eql(%("2015-05-01T12:00:00.000+00:00")).at_path('dateTimeValue')
end
it 'allows nil datetime values' do
expect(json).to be_json_eql(%(null)).at_path('nullDateTimeValue')
end
it 'serializes date values' do
expect(json).to be_json_eql(%("2015-05-01")).at_path('dateValue')
end
it 'allows nil date values' do
expect(json).to be_json_eql(%(null)).at_path('nullDateValue')
end
it 'serializes byte values to base64' do
expect(json).to be_json_eql(%("SGVsbG8gd29ybGQ=")).at_path('bytesValue')
end
it 'serializes basic collections' do
expect(json).to be_json_eql(%([1,2,3])).at_path('items')
end
it 'serializes nested objects' do
expect(json).to be_json_eql(%({"value" : "child"})).at_path('child')
end
it 'serializes object collections' do
expect(json).to be_json_eql(%([{"value" : "child"}])).at_path('children')
end
it 'serializes numeric strings' do
expect(json).to be_json_eql(%("1208925819614629174706176")).at_path('bigValue')
end
end
context 'with model object' do
let(:json) { representer_class.new(model).to_json(user_options: { skip_undefined: true }) }
let(:model) do
model = model_class.new
model.nil_value = nil
model.numeric_value = 123
model.string_value = 'test'
model.datetime_value = DateTime.new(2015, 5, 1, 12)
model.nil_datetime_value = nil
model.date_value = Date.new(2015, 5, 1)
model.nil_date_value = nil
model.boolean_value_true = true
model.boolean_value_false = false
model.bytes_value = 'Hello world'
model.items = [1, 2, 3]
model.child = child_class.new
model.child.value = 'child'
model.children = [model.child]
model.big_value = 1208925819614629174706176
model
end
include_examples 'it serializes'
it 'supports standard serialization from JSON gem' do
str = JSON.generate(model)
expect(str).to start_with("{\"")
end
it 'supports pretty serialization from JSON gem' do
str = JSON.pretty_generate(model)
expect(str).to start_with("{\n ")
end
end
context 'with hash' do
let(:json) { representer_class.new(model).to_json(user_options: { skip_undefined: true }) }
let(:model) do
{
nil_value: nil,
string_value: 'test',
numeric_value: 123,
datetime_value: DateTime.new(2015, 5, 1, 12),
nil_datetime_value: nil,
date_value: Date.new(2015, 5, 1),
nil_date_value: nil,
boolean_value_true: true,
boolean_value_false: false,
bytes_value: 'Hello world',
big_value: 1208925819614629174706176,
items: [1, 2, 3],
child: {
value: 'child'
},
children: [{ value: 'child' }]
}
end
include_examples 'it serializes'
end
context 'when de-serializing' do
let(:model) { representer_class.new(model_class.new).from_json(json) }
let(:json) do
json = <<EOF
{ "stringValue": "test",
"nilValue": null,
"booleanValueTrue": true,
"booleanValueFalse": false,
"numericValue": 123,
"dateTimeValue": "2015-05-01T12:00:00+00:00",
"nullDateTimeValue": null,
"dateValue": "2015-05-01",
"nullDateValue": null,
"bytesValue": "SGVsbG8gd29ybGQ=",
"bigValue": "1208925819614629174706176",
"items": [1,2,3],
"child": {"value" : "hello"},
"children": [{"value" : "hello"}]
}
EOF
end
it 'deserializes string values' do
expect(model.string_value).to eql 'test'
end
it 'deserializes null values' do
expect(model.nil_value).to be_nil
end
it 'deserializes numeric values' do
expect(model.numeric_value).to eql 123
end
it 'deserializes boolean values' do
expect(model.boolean_value_true).to be_truthy
expect(model.boolean_value_false).to be_falsey
end
it 'deserializes datetime values' do
expect(model.datetime_value).to eql DateTime.new(2015, 5, 1, 12)
end
it 'deserializes null datetime values' do
expect(model.nil_datetime_value).to be_nil
end
it 'deserializes date values' do
expect(model.date_value).to eql Date.new(2015, 5, 1)
end
it 'deserializes null date values' do
expect(model.nil_date_value).to be_nil
end
it 'deserializes basic collections' do
expect(model.items).to contain_exactly(1, 2, 3)
end
it 'deserializes nested objects' do
expect(model.child.value).to eql 'hello'
end
it 'serializes object collections' do
expect(model.children[0].value).to eql 'hello'
end
it 'deserializes numeric strings' do
expect(model.big_value).to eql 1208925819614629174706176
end
end
end
@@ -1,428 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/options'
require 'google/apis/core/base_service'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::BaseService do
include TestHelpers
let(:service) { Google::Apis::Core::BaseService.new('https://www.googleapis.com/', '') }
let(:service_with_base_path) { Google::Apis::Core::BaseService.new('https://www.googleapis.com/', 'my_service/v1/') }
let(:x_goog_api_client_value) { "gl-ruby/#{RUBY_VERSION} gdcl/#{Google::Apis::VERSION}" }
before do
Google::Apis::ClientOptions.default.application_name = 'test'
Google::Apis::ClientOptions.default.application_version = '1.0'
end
it 'should inherit default options' do
expect(service.client_options.application_name).to eql 'test'
end
it 'should include application in user agent' do
agent = service.send(:user_agent)
expect(agent).to match /^test\/1.0/
end
it 'should include os version in user agent' do
agent = service.send(:user_agent)
expect(agent).to match Regexp.new(Regexp.escape(Google::Apis::OS_VERSION))
end
it 'should inherit authorization' do
Google::Apis::RequestOptions.default.authorization = 'a token'
expect(service.authorization).to eql 'a token'
end
it 'should allow overiding authorization' do
Google::Apis::RequestOptions.default.authorization = 'a token'
service.authorization = 'another token'
expect(service.authorization).to eql 'another token'
end
shared_examples 'with options' do
it 'should inherit service options' do
service.request_options.retries = 2
expect(command.options.retries).to eql 2
end
it 'should include per-request options' do
expect(command.options.authorization).to eql 'foo'
end
it 'should allow per-request options to override service options' do
service.request_options.authorization = 'bar'
expect(command.options.authorization).to eql 'foo'
end
end
context 'when making raw http requests' do
context 'with :get methods' do
before(:example) do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: 'hello world')
end
it 'should return body for get' do
response = service.http(:get, 'https://www.googleapis.com/zoo/animals')
expect(response).to eq 'hello world'
end
it 'should return allow downloads' do
response = service.http(:get, 'https://www.googleapis.com/zoo/animals', download_dest: StringIO.new)
expect(response.string).to eq 'hello world'
end
end
context 'with :post methods' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: '')
end
it 'should post body' do
service.http(:post, 'https://www.googleapis.com/zoo/animals', body: 'hello')
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'hello')).to have_been_made
end
end
end
context 'with proxy' do
after(:example) do
Google::Apis::ClientOptions.default.proxy_url = nil
end
it 'should allow proxy URLs as strings' do
Google::Apis::ClientOptions.default.proxy_url = 'http://gateway.example.com:1234'
service.client
end
end
context 'when making simple commands' do
let(:command) { service.send(:make_simple_command, :get, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::ApiCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/zoo/animals'
end
it 'should send the command with x-goog-api-client header' do
stub_request(:get, 'https://www.googleapis.com/zoo/animals').to_return(body: '')
service.send(:execute_or_queue_command, command)
expected_headers = {'X-Goog-Api-Client': x_goog_api_client_value}
expect(a_request(:get, 'https://www.googleapis.com/zoo/animals').with(headers: expected_headers)).to have_been_made
end
include_examples 'with options'
end
context 'when making simple commands with a base path' do
it 'should return the correct URL for a relative path' do
command = service_with_base_path.send(:make_simple_command, :get, 'zoo/animals', authorization: 'foo')
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/my_service/v1/zoo/animals'
end
it 'should return the correct URL for an absolute path' do
command = service_with_base_path.send(:make_simple_command, :get, '/zoo/animals', authorization: 'foo')
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/zoo/animals'
end
end
context 'when making download commands' do
let(:command) { service.send(:make_download_command, :get, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::DownloadCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/zoo/animals'
end
it 'should include alt=media in params' do
expect(command.query).to include('alt' => 'media')
end
include_examples 'with options'
end
context 'when making upload commands' do
let(:command) { service.send(:make_upload_command, :post, 'zoo/animals', authorization: 'foo') }
it 'should return the correct command type' do
expect(command).to be_an_instance_of(Google::Apis::Core::ResumableUploadCommand)
end
it 'should build a correct URL' do
url = command.url.expand({}).to_s
expect(url).to eql 'https://www.googleapis.com/upload/zoo/animals'
end
include_examples 'with options'
end
context 'with batch' do
before(:example) do
response = <<EOF.gsub(/\n/, "\r\n")
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123--
EOF
stub_request(:post, 'https://www.googleapis.com/batch')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should add commands to a batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_simple_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should disallow uploads in batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
it 'should disallow downloads in batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_download_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
it 'should prevent mixing services in batch' do
expect do |b|
service.batch do |service|
command = service.send(:make_simple_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
service2 = service.dup
command2 = service.send(:make_simple_command, :get, 'zoo/animals', {})
service2.send(:execute_or_queue_command, command2, &b)
end
end.to raise_error
end
end
context 'with batch uploads' do
before(:example) do
allow(SecureRandom).to receive(:uuid).and_return('b1981e17-f622-49af-b2eb-203308b1b17d')
allow(Digest::SHA1).to receive(:hexdigest).and_return('outer', 'inner')
response = <<EOF.gsub(/\n/, "\r\n")
--batch123
Content-Type: application/http
HTTP/1.1 200 OK
Content-Type: text/plain; charset=UTF-8
Hello
--batch123--
EOF
stub_request(:put, 'https://www.googleapis.com/upload/')
.to_return(headers: { 'Content-Type' => 'multipart/mixed; boundary=batch123' }, body: response)
end
it 'should add upload to a batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
command.upload_source = StringIO.new('test')
command.upload_content_type = 'text/plain'
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should use multipart upload' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
command.upload_source = StringIO.new('test')
command.upload_content_type = 'text/plain'
expect(command).to be_an_instance_of(Google::Apis::Core::MultipartUploadCommand)
service.send(:execute_or_queue_command, command, &b)
end
end.to yield_with_args('Hello', nil)
end
it 'should send nested multipart' do
Google::Apis::RequestOptions.default.authorization = 'a token'
service.batch_upload do |service|
command = service.send(:make_upload_command, :post, 'zoo/animals', {})
command.upload_source = StringIO.new('test')
command.upload_content_type = 'text/plain'
service.send(:execute_or_queue_command, command)
end
expected_body = <<EOF.gsub(/\n/, "\r\n")
--outer
Content-Type: application/http
Content-Id: <b1981e17-f622-49af-b2eb-203308b1b17d\\+0>
Content-Length: \\d+
Content-Transfer-Encoding: binary
POST /upload/zoo/animals\\? HTTP/1\\.1
X-Goog-Api-Client: #{Regexp.escape(x_goog_api_client_value)}
Content-Type: multipart/related; boundary=inner
X-Goog-Upload-Protocol: multipart
Authorization: Bearer a token
Host: www\\.googleapis\\.com
--inner
Content-Type: application/json
--inner
Content-Type: text/plain
Content-Length: 4
Content-Transfer-Encoding: binary
test
--inner--
--outer--
EOF
expect(a_request(:put, 'https://www.googleapis.com/upload/').with(body: Regexp.new(expected_body))).to have_been_made
end
it 'should disallow downloads in batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_download_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
it 'should disallow simple commands in batch' do
expect do |b|
service.batch_upload do |service|
command = service.send(:make_simple_command, :get, 'zoo/animals', {})
service.send(:execute_or_queue_command, command, &b)
end
end.to raise_error(Google::Apis::ClientError)
end
context 'with fetch_all' do
let(:responses) do
data = {}
data[nil] = OpenStruct.new(
next_page_token: 'p1', alt_page_token: 'p2', items: ['a', 'b', 'c'], alt_items: [1, 2, 3], singular: 'foo', hash_: { 'foo' => 1, 'bar' => 2 })
data['p1'] = OpenStruct.new(
next_page_token: 'p2', items: ['d', 'e', 'f'], alt_items: [4, 5, 6], singular: 'bar', hash_: nil)
data['p2'] = OpenStruct.new(
next_page_token: nil, alt_page_token: nil, items: ['g', 'h', 'i'], alt_items: [7, 8, 9], singular: 'baz', hash_: { 'baz' => 3 })
data
end
let(:items) { service.fetch_all { |token| responses[token] } }
it 'should fetch pages until next page token is nil' do
expect(items).to contain_exactly('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
end
it 'should stop on repeated page token' do
responses['p2'].next_page_token = 'p2'
expect(items).to contain_exactly('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i')
end
it 'should allow selecting another field for response page token' do
expect(service.fetch_all(response_page_token: :alt_page_token) { |token| responses[token] }
).to contain_exactly('a', 'b', 'c', 'g', 'h', 'i')
end
it 'should ignore nil collections' do
responses['p1'].items = nil
expect(items).to contain_exactly('a', 'b', 'c', 'g', 'h', 'i')
end
it 'should allow selecting another field for items' do
expect(service.fetch_all(items: :alt_items) { |token| responses[token] } ).to contain_exactly(1, 2, 3, 4, 5, 6, 7, 8, 9)
end
it 'should allow iterating over singular items' do
expect(service.fetch_all(items: :singular) { |token| responses[token] } ).to contain_exactly('foo', 'bar', 'baz')
end
it 'should collate hash entries' do
expect(service.fetch_all(items: :hash_) { |token| responses[token] } ).to contain_exactly(['foo', 1], ['bar', 2], ['baz', 3])
end
it 'should allow limiting the number of items to fetch' do
expect(service.fetch_all(max: 5) { |token| responses[token] } ).to contain_exactly('a', 'b', 'c', 'd', 'e')
end
it 'should yield the next token' do
expect do |b|
service.fetch_all do |token|
b.to_proc.call(token)
responses[token]
end.count
end.to yield_successive_args(nil, 'p1', 'p2')
end
it 'should cache results' do
count = 0
items = service.fetch_all do |token|
count = count + 1
responses[token]
end
items.each{ |i| puts i }
items.each{ |i| puts i }
expect(count).to eq 3
end
it 'should allow disabling caching' do
count = 0
items = service.fetch_all(cache: false) do |token|
count = count + 1
responses[token]
end
items.each{ |i| puts i }
items.each{ |i| puts i }
expect(count).to eq 6
end
end
end
end
@@ -1,247 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/core/upload'
require 'google/apis/core/json_representation'
RSpec.describe Google::Apis::Core::RawUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::RawUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = file
command.upload_content_type = 'text/plain'
command
end
shared_examples 'should upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: '')
end
it 'should send content' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: "Hello world")).to have_been_made
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'raw' }).to have_been_made
end
it 'should send content-type header' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Header-Content-Type'] == 'text/plain' }).to have_been_made
end
end
context('with StringIO input') do
let(:file) { StringIO.new("Hello world") }
include_examples 'should upload'
end
context('with IO input') do
let(:file) { File.open(File.join(FIXTURES_DIR, 'files', 'test.txt'), 'r') }
include_examples 'should upload'
it 'should not close stream' do
expect(file.closed?).to be false
end
end
context('with Tempfile input') do
let(:file) do
temp_file = Tempfile.new("tempfile")
temp_file.write("Hello world")
temp_file.rewind
temp_file
end
include_examples 'should upload'
it 'should not close stream' do
expect(file.closed?).to be false
end
end
context('with file path input') do
let(:file) { File.join(FIXTURES_DIR, 'files', 'test.txt') }
include_examples 'should upload'
end
context('with invalid input') do
let(:file) { -> {} }
it 'should raise client error' do
expect { command.execute(client) }.to raise_error(Google::Apis::ClientError)
end
end
end
RSpec.describe Google::Apis::Core::MultipartUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::MultipartUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = StringIO.new('Hello world')
command.upload_content_type = 'text/plain'
command.body = 'metadata'
command
end
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals').to_return(body: %(Hello world))
allow(Digest::SHA1).to receive(:hexdigest).and_return('123abc')
end
it 'should send content' do
expected_body = <<EOF.gsub(/\n/, "\r\n")
--123abc
Content-Type: application/json
metadata
--123abc
Content-Type: text/plain
Content-Length: 11
Content-Transfer-Encoding: binary
Hello world
--123abc--
EOF
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: expected_body)).to have_been_made
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'multipart' }).to have_been_made
end
end
RSpec.describe Google::Apis::Core::ResumableUploadCommand do
include TestHelpers
include_context 'HTTP client'
let(:command) do
command = Google::Apis::Core::ResumableUploadCommand.new(:post, 'https://www.googleapis.com/zoo/animals')
command.upload_source = StringIO.new('Hello world')
command.upload_content_type = 'text/plain'
command
end
context 'with uninterrupted upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should send upload protocol' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Protocol'] == 'resumable' }).to have_been_made
end
it 'should send start command' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Command'] == 'start' }).to have_been_made
end
it 'should send upload command' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with { |req| req.headers['X-Goog-Upload-Command'].include?('upload') }).to have_been_made
end
it 'should send upload content' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'Hello world')).to have_been_made
end
it 'should generate a proper opencensus span' do
OpenCensus::Trace.start_request_trace do |span_context|
command.execute(client)
spans = span_context.build_contained_spans
expect(spans.size).to eql 1
span = spans.first
expect(span.name.value).to eql '/zoo/animals'
end
end
end
context 'with retriable error on start' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_timeout
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-Size-Received' => '6' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should retry start command and continue' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'Hello world')).to have_been_made
end
end
context 'with non-retriable authorization error on start' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(status: [401, 'unauthorized'], headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(unauthorized))
end
it 'should propagate the original error' do
expect { command.execute(client) }.to raise_error Google::Apis::AuthorizationError
end
end
context 'with interruption' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(status: [500, 'Server error'])
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-Size-Received' => '6' })
.to_return(headers: { 'X-Goog-Upload-Status' => 'final' }, body: %(OK))
end
it 'should send remaining upload content after failure' do
command.execute(client)
expect(a_request(:post, 'https://www.googleapis.com/zoo/animals')
.with(body: 'world')).to have_been_made
end
end
context 'with cancelled upload' do
before(:example) do
stub_request(:post, 'https://www.googleapis.com/zoo/animals')
.to_return(headers: { 'X-Goog-Upload-Status' => 'active', 'X-Goog-Upload-URL' => 'https://www.googleapis.com/zoo/animals' })
.to_return(status: [500, 'Server error'])
.to_return(headers: { 'X-Goog-Upload-Status' => 'cancelled' })
end
it 'should raise error' do
expect { command.execute(client) }.to raise_error Google::Apis::ClientError
end
end
end
@@ -1,93 +0,0 @@
# Copyright 2016 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
RSpec.describe Google::Apis::Error do
describe 'constructor' do
subject { described_class.new(error) }
describe 'message' do
context 'first arg is a string which does not respond to :backtrace' do
let(:error) { 'an error occurred' }
it 'creates a instance of Error with message' do
expect(subject.message).to eq 'an error occurred'
end
end
context 'first arg responds to :backtrace' do
let(:error) { double }
it 'creates a instance of Error with message' do
allow(error).to receive(:respond_to?).with(:backtrace) { true }
allow(error).to receive(:message) { 'error message' }
expect(subject.message).to eq 'error message'
end
end
end
describe 'instance variables @body, @header, and @status_code' do
let(:error) { 'an error occurred' }
context 'params :body, :header, and :status_code are not given' do
it 'instance variables @body, @header, and @status_code will be nil' do
expect(subject.body).to be_nil
expect(subject.header).to be_nil
expect(subject.status_code).to be_nil
end
end
context 'params :body, :header, and :status_code are given' do
subject do
described_class.new(
error,
body: 'this is body',
header: 'this is header',
status_code: 200)
end
it 'instance variables @body, @header, and @status_code will not be nil' do
expect(subject.body).to eq 'this is body'
expect(subject.header).to eq 'this is header'
expect(subject.status_code).to eq 200
end
end
end
end
describe '#backtrace' do
subject { described_class.new('error') }
let(:cause) { instance_double('Error', backtrace: 'this is backtrace') }
context '@cause is truthy' do
it 'calls @cause.backtrace' do
subject.instance_variable_set(:@cause, cause)
expect(subject.backtrace).to eq 'this is backtrace'
end
end
context '@cause is falsy' do
before do
expect_any_instance_of(subject.class.superclass).to receive(:backtrace).and_return(
"super class's #backtrace called")
end
it "calls super class's #backtrace" do
subject.instance_variable_set(:@cause, false)
expect(subject.backtrace).to eq "super class's #backtrace called"
end
end
end
end
@@ -1,100 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis'
require 'google/apis/core/logging'
RSpec.describe Google::Apis do
it 'should have a default logger' do
expect(Google::Apis.logger).to be_an_instance_of(Logger)
end
context 'with service' do
let(:service) do
Class.new do
include Google::Apis::Core::Logging
end.new
end
it 'should have a logger' do
expect(service.logger).to be_an_instance_of(Logger)
end
it 'should use the default logger' do
expect(service.logger).to be Google::Apis.logger
end
it 'should allow custom loggers' do
Google::Apis.logger = Logger.new(STDERR)
expect(service.logger).to be Google::Apis.logger
end
end
context 'with Rails' do
before(:example) do
Google::Apis.logger = nil
Kernel.const_set('Rails', Module.new) unless defined?(::Rails)
end
let(:logger) { Logger.new(STDERR) }
let(:service) do
Class.new do
include Google::Apis::Core::Logging
end.new
end
context 'with logger present' do
before(:example) do
allow(::Rails).to receive(:logger).and_return(logger)
end
it 'should use the Rails logger' do
expect(service.logger).to be Rails.logger
end
end
context 'with ENV bypass' do
before(:example) do
allow(::Rails).to receive(:logger).and_return(logger)
allow(::ENV).to receive(:fetch).and_return('false')
end
it 'should use own logger' do
expect(service.logger).not_to be Rails.logger
end
it 'should have a logger' do
expect(service.logger).to be_an_instance_of(Logger)
end
end
context 'with logger not present' do
before(:example) do
allow(::Rails).to receive(:logger).and_return(nil)
end
it 'should use own logger' do
expect(service.logger).not_to be Rails.logger
end
it 'should have a logger' do
expect(service.logger).to be_an_instance_of(Logger)
end
end
end
end
@@ -1,40 +0,0 @@
# Copyright 2015 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
require 'spec_helper'
require 'google/apis/options'
RSpec.describe Google::Apis::RequestOptions do
let(:options) { Google::Apis::RequestOptions.new }
it 'should not merge nil values' do
options.retries = 1
expect(options.merge(authorization: 'foo').retries).to eql 1
end
it 'should merge non-nil values' do
options.retries = 1
expect(options.merge(authorization: 'foo').authorization).to eql 'foo'
end
it 'should merge from options' do
opts = Google::Apis::RequestOptions.new
opts.authorization = 'foo'
expect(options.merge(opts).authorization).to eql 'foo'
end
it 'should allow nil in merge' do
expect(options.merge(nil)).to be_an_instance_of(Google::Apis::RequestOptions)
end
end
@@ -17,9 +17,4 @@ require 'google/apis/version'
RSpec.describe Google::Apis::VERSION do
it { is_expected.to be_a(String) }
describe Google::Apis::OS_VERSION do
it { is_expected.to be_a(String) }
it { is_expected.not_to match /\n/ }
end
end