Unit test updates + ensure auth retry only done once per execute
[arvados.git] / spec / google / api_client_spec.rb
1 # Copyright 2010 Google Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 require 'spec_helper'
16
17 require 'faraday'
18 require 'signet/oauth_1/client'
19 require 'google/api_client'
20 require 'google/api_client/version'
21
22 shared_examples_for 'configurable user agent' do
23   include ConnectionHelpers
24   
25   it 'should allow the user agent to be modified' do
26     client.user_agent = 'Custom User Agent/1.2.3'
27     client.user_agent.should == 'Custom User Agent/1.2.3'
28   end
29
30   it 'should allow the user agent to be set to nil' do
31     client.user_agent = nil
32     client.user_agent.should == nil
33   end
34
35   it 'should not allow the user agent to be used with bogus values' do
36     (lambda do
37       client.user_agent = 42
38       client.execute(:uri=>'https://www.google.com/')
39     end).should raise_error(TypeError)
40   end
41
42   it 'should transmit a User-Agent header when sending requests' do
43     client.user_agent = 'Custom User Agent/1.2.3'
44
45     conn = stub_connection do |stub|
46       stub.get('/') do |env|
47         headers = env[:request_headers]
48         headers.should have_key('User-Agent')
49         headers['User-Agent'].should == client.user_agent
50         [200, {}, ['']]
51       end
52     end
53     client.execute(:uri=>'https://www.google.com/', :connection => conn)
54     conn.verify
55   end
56 end
57
58 describe Google::APIClient do
59   include ConnectionHelpers
60
61   let(:client) { Google::APIClient.new(:application_name => 'API Client Tests') }
62
63   it 'should make its version number available' do
64     Google::APIClient::VERSION::STRING.should be_instance_of(String)
65   end
66
67   it 'should default to OAuth 2' do
68     Signet::OAuth2::Client.should === client.authorization
69   end
70
71   describe 'configure for no authentication' do
72     before do
73       client.authorization = nil
74     end
75     it_should_behave_like 'configurable user agent'
76   end
77     
78   describe 'configured for OAuth 1' do
79     before do
80       client.authorization = :oauth_1
81       client.authorization.token_credential_key = 'abc'
82       client.authorization.token_credential_secret = '123'
83     end
84
85     it 'should use the default OAuth1 client configuration' do
86       client.authorization.temporary_credential_uri.to_s.should ==
87         'https://www.google.com/accounts/OAuthGetRequestToken'
88       client.authorization.authorization_uri.to_s.should include(
89         'https://www.google.com/accounts/OAuthAuthorizeToken'
90       )
91       client.authorization.token_credential_uri.to_s.should ==
92         'https://www.google.com/accounts/OAuthGetAccessToken'
93       client.authorization.client_credential_key.should == 'anonymous'
94       client.authorization.client_credential_secret.should == 'anonymous'
95     end
96
97     it_should_behave_like 'configurable user agent'
98   end
99
100   describe 'configured for OAuth 2' do
101     before do
102       client.authorization = :oauth_2
103       client.authorization.access_token = '12345'
104     end
105
106     # TODO
107     it_should_behave_like 'configurable user agent'
108   end
109   
110   describe 'when executing requests' do
111     before do
112       @prediction = client.discovered_api('prediction', 'v1.2')
113       client.authorization = :oauth_2
114       @connection = stub_connection do |stub|
115         stub.post('/prediction/v1.2/training?data=12345') do |env|
116           env[:request_headers]['Authorization'].should == 'Bearer 12345'
117           [200, {}, '{}']
118         end
119       end
120     end
121
122     after do
123       @connection.verify
124     end
125     
126     it 'should use default authorization' do
127       client.authorization.access_token = "12345"
128       client.execute(  
129         :api_method => @prediction.training.insert,
130         :parameters => {'data' => '12345'},
131         :connection => @connection
132       )
133     end
134
135     it 'should use request scoped authorization when provided' do
136       client.authorization.access_token = "abcdef"
137       new_auth = Signet::OAuth2::Client.new(:access_token => '12345')
138       client.execute(  
139         :api_method => @prediction.training.insert,
140         :parameters => {'data' => '12345'},
141         :authorization => new_auth,
142         :connection => @connection
143       )
144     end
145     
146     it 'should accept options with batch/request style execute' do
147       client.authorization.access_token = "abcdef"
148       new_auth = Signet::OAuth2::Client.new(:access_token => '12345')
149       request = client.generate_request(
150         :api_method => @prediction.training.insert,
151         :parameters => {'data' => '12345'}
152       )
153       client.execute(
154         request,
155         :authorization => new_auth,
156         :connection => @connection
157       )
158     end
159     
160     
161     it 'should accept options in array style execute' do
162        client.authorization.access_token = "abcdef"
163        new_auth = Signet::OAuth2::Client.new(:access_token => '12345')
164        client.execute(  
165          @prediction.training.insert, {'data' => '12345'}, '', {},
166          { :authorization => new_auth, :connection => @connection }         
167        )
168      end
169   end  
170
171   describe 'when retries enabled' do
172     before do
173       client.retries = 2
174     end
175
176     after do
177       @connection.verify
178     end
179     
180     it 'should follow redirects' do
181       client.authorization = nil
182       @connection = stub_connection do |stub|
183         stub.get('/foo') do |env|
184           [302, {'location' => 'https://www.google.com/bar'}, '{}']
185         end
186         stub.get('/bar') do |env|
187           [200, {}, '{}']
188         end
189       end
190
191       client.execute(  
192         :uri => 'https://www.gogole.com/foo',
193         :connection => @connection
194       )
195     end
196
197     it 'should refresh tokens on 401 errors' do
198       client.authorization.access_token = '12345'
199       expect(client.authorization).to receive(:fetch_access_token!)
200
201       @connection = stub_connection do |stub|
202         stub.get('/foo') do |env|
203           [401, {}, '{}']
204         end
205         stub.get('/foo') do |env|
206           [200, {}, '{}']
207         end
208       end
209
210       client.execute(  
211         :uri => 'https://www.gogole.com/foo',
212         :connection => @connection
213       )
214     end
215
216
217     it 'should not attempt multiple token refreshes' do
218       client.authorization.access_token = '12345'
219       expect(client.authorization).to receive(:fetch_access_token!).once
220
221       @connection = stub_connection do |stub|
222         stub.get('/foo') do |env|
223           [401, {}, '{}']
224         end
225       end
226
227       client.execute(  
228         :uri => 'https://www.gogole.com/foo',
229         :connection => @connection
230       )
231     end
232
233     it 'should not retry on client errors' do
234       count = 0
235       @connection = stub_connection do |stub|
236         stub.get('/foo') do |env|
237           count.should == 0
238           count += 1
239           [403, {}, '{}']
240         end
241       end
242
243       client.execute(  
244         :uri => 'https://www.gogole.com/foo',
245         :connection => @connection,
246         :authenticated => false
247       )
248     end
249
250     it 'should retry on 500 errors' do
251       client.authorization = nil
252
253       @connection = stub_connection do |stub|
254         stub.get('/foo') do |env|
255           [500, {}, '{}']
256         end
257         stub.get('/foo') do |env|
258           [200, {}, '{}']
259         end
260       end
261
262       client.execute(  
263         :uri => 'https://www.gogole.com/foo',
264         :connection => @connection
265       ).status.should == 200
266
267     end
268
269     it 'should fail after max retries' do
270       client.authorization = nil
271       count = 0
272       @connection = stub_connection do |stub|
273         stub.get('/foo') do |env|
274           count += 1
275           [500, {}, '{}']
276         end
277       end
278
279       client.execute(  
280         :uri => 'https://www.gogole.com/foo',
281         :connection => @connection
282       ).status.should == 500
283       count.should == 3
284     end
285
286   end
287
288   describe 'when retries disabled and expired_auth_retry on (default)' do
289     before do
290       client.retries = 0
291     end
292
293     after do
294       @connection.verify
295     end
296
297     it 'should refresh tokens on 401 errors' do
298       client.authorization.access_token = '12345'
299       expect(client.authorization).to receive(:fetch_access_token!)
300
301       @connection = stub_connection do |stub|
302         stub.get('/foo') do |env|
303           [401, {}, '{}']
304         end
305         stub.get('/foo') do |env|
306           [200, {}, '{}']
307         end
308       end
309
310       client.execute(
311         :uri => 'https://www.gogole.com/foo',
312         :connection => @connection
313       )
314     end
315
316   end
317
318   describe 'when retries disabled and expired_auth_retry off' do
319     before do
320       client.retries = 0
321       client.expired_auth_retry = false
322     end
323
324     it 'should not refresh tokens on 401 errors' do
325       client.authorization.access_token = '12345'
326       expect(client.authorization).not_to receive(:fetch_access_token!)
327
328       @connection = stub_connection do |stub|
329         stub.get('/foo') do |env|
330           [401, {}, '{}']
331         end
332         stub.get('/foo') do |env|
333           [200, {}, '{}']
334         end
335       end
336
337       resp = client.execute(
338         :uri => 'https://www.gogole.com/foo',
339         :connection => @connection
340       )
341
342       resp.response.status.should == 401
343     end
344
345   end
346 end