blob: 15068ba4d46fadc0dd06235bd679072d981db08d [file] [log] [blame]
Joe Gregorio6bcbcea2011-03-10 15:26:05 -05001#!/usr/bin/python2.4
2#
3# Copyright 2010 Google Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Http tests
18
19Unit tests for the apiclient.http.
20"""
21
22__author__ = 'jcgregorio@google.com (Joe Gregorio)'
23
Joe Gregorio7cbceab2011-06-27 10:46:54 -040024# Do not remove the httplib2 import
25import httplib2
Joe Gregoriod0bd3882011-11-22 09:49:47 -050026import os
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050027import unittest
Joe Gregorio910b9b12012-06-12 09:36:30 -040028import StringIO
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050029
Joe Gregorio708388c2012-06-15 13:43:04 -040030from apiclient.discovery import build
Joe Gregorio66f57522011-11-30 11:00:00 -050031from apiclient.errors import BatchError
Joe Gregorio708388c2012-06-15 13:43:04 -040032from apiclient.errors import HttpError
Joe Gregorio66f57522011-11-30 11:00:00 -050033from apiclient.http import BatchHttpRequest
Joe Gregorio708388c2012-06-15 13:43:04 -040034from apiclient.http import HttpMock
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050035from apiclient.http import HttpMockSequence
Joe Gregoriod0bd3882011-11-22 09:49:47 -050036from apiclient.http import HttpRequest
Joe Gregoriod0bd3882011-11-22 09:49:47 -050037from apiclient.http import MediaFileUpload
Joe Gregorio66f57522011-11-30 11:00:00 -050038from apiclient.http import MediaUpload
Ali Afshar6f11ea12012-02-07 10:32:14 -050039from apiclient.http import MediaInMemoryUpload
Joe Gregorio910b9b12012-06-12 09:36:30 -040040from apiclient.http import MediaIoBaseUpload
Joe Gregorio708388c2012-06-15 13:43:04 -040041from apiclient.http import MediaIoBaseDownload
Joe Gregorio66f57522011-11-30 11:00:00 -050042from apiclient.http import set_user_agent
43from apiclient.model import JsonModel
Joe Gregorio654f4a22012-02-09 14:15:44 -050044from oauth2client.client import Credentials
45
46
47class MockCredentials(Credentials):
48 """Mock class for all Credentials objects."""
49 def __init__(self, bearer_token):
50 super(MockCredentials, self).__init__()
51 self._authorized = 0
52 self._refreshed = 0
53 self._applied = 0
54 self._bearer_token = bearer_token
55
56 def authorize(self, http):
57 self._authorized += 1
58
59 request_orig = http.request
60
61 # The closure that will replace 'httplib2.Http.request'.
62 def new_request(uri, method='GET', body=None, headers=None,
63 redirections=httplib2.DEFAULT_MAX_REDIRECTS,
64 connection_type=None):
65 # Modify the request headers to add the appropriate
66 # Authorization header.
67 if headers is None:
68 headers = {}
69 self.apply(headers)
70
71 resp, content = request_orig(uri, method, body, headers,
72 redirections, connection_type)
73
74 return resp, content
75
76 # Replace the request method with our own closure.
77 http.request = new_request
78
79 # Set credentials as a property of the request method.
80 setattr(http.request, 'credentials', self)
81
82 return http
83
84 def refresh(self, http):
85 self._refreshed += 1
86
87 def apply(self, headers):
88 self._applied += 1
89 headers['authorization'] = self._bearer_token + ' ' + str(self._refreshed)
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050090
91
Joe Gregoriod0bd3882011-11-22 09:49:47 -050092DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
93
94
95def datafile(filename):
96 return os.path.join(DATA_DIR, filename)
97
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050098class TestUserAgent(unittest.TestCase):
99
100 def test_set_user_agent(self):
101 http = HttpMockSequence([
102 ({'status': '200'}, 'echo_request_headers'),
103 ])
104
105 http = set_user_agent(http, "my_app/5.5")
106 resp, content = http.request("http://example.com")
Joe Gregorio654f4a22012-02-09 14:15:44 -0500107 self.assertEqual('my_app/5.5', content['user-agent'])
Joe Gregorio6bcbcea2011-03-10 15:26:05 -0500108
109 def test_set_user_agent_nested(self):
110 http = HttpMockSequence([
111 ({'status': '200'}, 'echo_request_headers'),
112 ])
113
114 http = set_user_agent(http, "my_app/5.5")
115 http = set_user_agent(http, "my_library/0.1")
116 resp, content = http.request("http://example.com")
Joe Gregorio654f4a22012-02-09 14:15:44 -0500117 self.assertEqual('my_app/5.5 my_library/0.1', content['user-agent'])
Joe Gregorio6bcbcea2011-03-10 15:26:05 -0500118
Joe Gregorio910b9b12012-06-12 09:36:30 -0400119
120class TestMediaUpload(unittest.TestCase):
121
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500122 def test_media_file_upload_to_from_json(self):
123 upload = MediaFileUpload(
124 datafile('small.png'), chunksize=500, resumable=True)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500125 self.assertEqual('image/png', upload.mimetype())
126 self.assertEqual(190, upload.size())
127 self.assertEqual(True, upload.resumable())
128 self.assertEqual(500, upload.chunksize())
129 self.assertEqual('PNG', upload.getbytes(1, 3))
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500130
131 json = upload.to_json()
132 new_upload = MediaUpload.new_from_json(json)
133
Joe Gregorio654f4a22012-02-09 14:15:44 -0500134 self.assertEqual('image/png', new_upload.mimetype())
135 self.assertEqual(190, new_upload.size())
136 self.assertEqual(True, new_upload.resumable())
137 self.assertEqual(500, new_upload.chunksize())
138 self.assertEqual('PNG', new_upload.getbytes(1, 3))
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500139
Ali Afshar1cb6b672012-03-12 08:46:14 -0400140 def test_media_inmemory_upload(self):
141 media = MediaInMemoryUpload('abcdef', 'text/plain', chunksize=10,
142 resumable=True)
143 self.assertEqual('text/plain', media.mimetype())
144 self.assertEqual(10, media.chunksize())
145 self.assertTrue(media.resumable())
146 self.assertEqual('bc', media.getbytes(1, 2))
147 self.assertEqual(6, media.size())
148
149 def test_media_inmemory_upload_json_roundtrip(self):
150 media = MediaInMemoryUpload(os.urandom(64), 'text/plain', chunksize=10,
151 resumable=True)
152 data = media.to_json()
153 newmedia = MediaInMemoryUpload.new_from_json(data)
154 self.assertEqual(media._body, newmedia._body)
155 self.assertEqual(media._chunksize, newmedia._chunksize)
156 self.assertEqual(media._resumable, newmedia._resumable)
157 self.assertEqual(media._mimetype, newmedia._mimetype)
158
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500159 def test_http_request_to_from_json(self):
160
161 def _postproc(*kwargs):
162 pass
163
164 http = httplib2.Http()
165 media_upload = MediaFileUpload(
166 datafile('small.png'), chunksize=500, resumable=True)
167 req = HttpRequest(
168 http,
169 _postproc,
170 'http://example.com',
171 method='POST',
172 body='{}',
173 headers={'content-type': 'multipart/related; boundary="---flubber"'},
174 methodId='foo',
175 resumable=media_upload)
176
177 json = req.to_json()
178 new_req = HttpRequest.from_json(json, http, _postproc)
179
Joe Gregorio654f4a22012-02-09 14:15:44 -0500180 self.assertEqual({'content-type':
181 'multipart/related; boundary="---flubber"'},
182 new_req.headers)
183 self.assertEqual('http://example.com', new_req.uri)
184 self.assertEqual('{}', new_req.body)
185 self.assertEqual(http, new_req.http)
186 self.assertEqual(media_upload.to_json(), new_req.resumable.to_json())
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500187
Joe Gregorio910b9b12012-06-12 09:36:30 -0400188
189class TestMediaIoBaseUpload(unittest.TestCase):
190
191 def test_media_io_base_upload_from_file_io(self):
192 try:
193 import io
194
195 fh = io.FileIO(datafile('small.png'), 'r')
196 upload = MediaIoBaseUpload(
197 fh=fh, mimetype='image/png', chunksize=500, resumable=True)
198 self.assertEqual('image/png', upload.mimetype())
199 self.assertEqual(190, upload.size())
200 self.assertEqual(True, upload.resumable())
201 self.assertEqual(500, upload.chunksize())
202 self.assertEqual('PNG', upload.getbytes(1, 3))
203 except ImportError:
204 pass
205
206 def test_media_io_base_upload_from_file_object(self):
207 f = open(datafile('small.png'), 'r')
208 upload = MediaIoBaseUpload(
209 fh=f, mimetype='image/png', chunksize=500, resumable=True)
210 self.assertEqual('image/png', upload.mimetype())
211 self.assertEqual(190, upload.size())
212 self.assertEqual(True, upload.resumable())
213 self.assertEqual(500, upload.chunksize())
214 self.assertEqual('PNG', upload.getbytes(1, 3))
215 f.close()
216
217 def test_media_io_base_upload_serializable(self):
218 f = open(datafile('small.png'), 'r')
219 upload = MediaIoBaseUpload(fh=f, mimetype='image/png')
220
221 try:
222 json = upload.to_json()
223 self.fail('MediaIoBaseUpload should not be serializable.')
224 except NotImplementedError:
225 pass
226
227 def test_media_io_base_upload_from_string_io(self):
228 f = open(datafile('small.png'), 'r')
229 fh = StringIO.StringIO(f.read())
230 f.close()
231
232 upload = MediaIoBaseUpload(
233 fh=fh, mimetype='image/png', chunksize=500, resumable=True)
234 self.assertEqual('image/png', upload.mimetype())
235 self.assertEqual(None, upload.size())
236 self.assertEqual(True, upload.resumable())
237 self.assertEqual(500, upload.chunksize())
238 self.assertEqual('PNG', upload.getbytes(1, 3))
239 f.close()
240
241 def test_media_io_base_upload_from_bytes(self):
242 try:
243 import io
244
245 f = open(datafile('small.png'), 'r')
246 fh = io.BytesIO(f.read())
247 upload = MediaIoBaseUpload(
248 fh=fh, mimetype='image/png', chunksize=500, resumable=True)
249 self.assertEqual('image/png', upload.mimetype())
250 self.assertEqual(None, upload.size())
251 self.assertEqual(True, upload.resumable())
252 self.assertEqual(500, upload.chunksize())
253 self.assertEqual('PNG', upload.getbytes(1, 3))
254 except ImportError:
255 pass
256
257
Joe Gregorio708388c2012-06-15 13:43:04 -0400258class TestMediaIoBaseDownload(unittest.TestCase):
259
260 def setUp(self):
261 http = HttpMock(datafile('zoo.json'), {'status': '200'})
262 zoo = build('zoo', 'v1', http)
263 self.request = zoo.animals().get_media(name='Lion')
264 self.fh = StringIO.StringIO()
265
266 def test_media_io_base_download(self):
267 self.request.http = HttpMockSequence([
268 ({'status': '200',
269 'content-range': '0-2/5'}, '123'),
270 ({'status': '200',
271 'content-range': '3-4/5'}, '45'),
272 ])
273
274 download = MediaIoBaseDownload(
275 fh=self.fh, request=self.request, chunksize=3)
276
277 self.assertEqual(self.fh, download.fh_)
278 self.assertEqual(3, download.chunksize_)
279 self.assertEqual(0, download.progress_)
280 self.assertEqual(None, download.total_size_)
281 self.assertEqual(False, download.done_)
282 self.assertEqual(self.request.uri, download.uri_)
283
284 status, done = download.next_chunk()
285
286 self.assertEqual(self.fh.getvalue(), '123')
287 self.assertEqual(False, done)
288 self.assertEqual(3, download.progress_)
289 self.assertEqual(5, download.total_size_)
290 self.assertEqual(3, status.resumable_progress)
291
292 status, done = download.next_chunk()
293
294 self.assertEqual(self.fh.getvalue(), '12345')
295 self.assertEqual(True, done)
296 self.assertEqual(5, download.progress_)
297 self.assertEqual(5, download.total_size_)
298
299 def test_media_io_base_download_handle_redirects(self):
300 self.request.http = HttpMockSequence([
301 ({'status': '307',
302 'location': 'https://secure.example.net/lion'}, ''),
303 ({'status': '200',
304 'content-range': '0-2/5'}, 'abc'),
305 ])
306
307 download = MediaIoBaseDownload(
308 fh=self.fh, request=self.request, chunksize=3)
309
310 status, done = download.next_chunk()
311
312 self.assertEqual('https://secure.example.net/lion', download.uri_)
313 self.assertEqual(self.fh.getvalue(), 'abc')
314 self.assertEqual(False, done)
315 self.assertEqual(3, download.progress_)
316 self.assertEqual(5, download.total_size_)
317
318 def test_media_io_base_download_handle_4xx(self):
319 self.request.http = HttpMockSequence([
320 ({'status': '400'}, ''),
321 ])
322
323 download = MediaIoBaseDownload(
324 fh=self.fh, request=self.request, chunksize=3)
325
326 try:
327 status, done = download.next_chunk()
328 self.fail('Should raise an exception')
329 except HttpError:
330 pass
331
332 # Even after raising an exception we can pick up where we left off.
333 self.request.http = HttpMockSequence([
334 ({'status': '200',
335 'content-range': '0-2/5'}, '123'),
336 ])
337
338 status, done = download.next_chunk()
339
340 self.assertEqual(self.fh.getvalue(), '123')
341
Joe Gregorio66f57522011-11-30 11:00:00 -0500342EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
343Content-Type: application/json
344MIME-Version: 1.0
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500345Host: www.googleapis.com
346content-length: 2\r\n\r\n{}"""
347
348
349NO_BODY_EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
350Content-Type: application/json
351MIME-Version: 1.0
352Host: www.googleapis.com
353content-length: 0\r\n\r\n"""
Joe Gregorio66f57522011-11-30 11:00:00 -0500354
355
356RESPONSE = """HTTP/1.1 200 OK
357Content-Type application/json
358Content-Length: 14
359ETag: "etag/pony"\r\n\r\n{"answer": 42}"""
360
361
362BATCH_RESPONSE = """--batch_foobarbaz
363Content-Type: application/http
364Content-Transfer-Encoding: binary
365Content-ID: <randomness+1>
366
367HTTP/1.1 200 OK
368Content-Type application/json
369Content-Length: 14
370ETag: "etag/pony"\r\n\r\n{"foo": 42}
371
372--batch_foobarbaz
373Content-Type: application/http
374Content-Transfer-Encoding: binary
375Content-ID: <randomness+2>
376
377HTTP/1.1 200 OK
378Content-Type application/json
379Content-Length: 14
380ETag: "etag/sheep"\r\n\r\n{"baz": "qux"}
381--batch_foobarbaz--"""
382
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500383
Joe Gregorio654f4a22012-02-09 14:15:44 -0500384BATCH_RESPONSE_WITH_401 = """--batch_foobarbaz
385Content-Type: application/http
386Content-Transfer-Encoding: binary
387Content-ID: <randomness+1>
388
389HTTP/1.1 401 Authoration Required
390Content-Type application/json
391Content-Length: 14
392ETag: "etag/pony"\r\n\r\n{"error": {"message":
393 "Authorizaton failed."}}
394
395--batch_foobarbaz
396Content-Type: application/http
397Content-Transfer-Encoding: binary
398Content-ID: <randomness+2>
399
400HTTP/1.1 200 OK
401Content-Type application/json
402Content-Length: 14
403ETag: "etag/sheep"\r\n\r\n{"baz": "qux"}
404--batch_foobarbaz--"""
405
406
407BATCH_SINGLE_RESPONSE = """--batch_foobarbaz
408Content-Type: application/http
409Content-Transfer-Encoding: binary
410Content-ID: <randomness+1>
411
412HTTP/1.1 200 OK
413Content-Type application/json
414Content-Length: 14
415ETag: "etag/pony"\r\n\r\n{"foo": 42}
416--batch_foobarbaz--"""
417
418class Callbacks(object):
419 def __init__(self):
420 self.responses = {}
421 self.exceptions = {}
422
423 def f(self, request_id, response, exception):
424 self.responses[request_id] = response
425 self.exceptions[request_id] = exception
426
427
Joe Gregorio66f57522011-11-30 11:00:00 -0500428class TestBatch(unittest.TestCase):
429
430 def setUp(self):
431 model = JsonModel()
432 self.request1 = HttpRequest(
433 None,
434 model.response,
435 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
436 method='POST',
437 body='{}',
438 headers={'content-type': 'application/json'})
439
440 self.request2 = HttpRequest(
441 None,
442 model.response,
443 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500444 method='GET',
445 body='',
Joe Gregorio66f57522011-11-30 11:00:00 -0500446 headers={'content-type': 'application/json'})
447
448
449 def test_id_to_from_content_id_header(self):
450 batch = BatchHttpRequest()
451 self.assertEquals('12', batch._header_to_id(batch._id_to_header('12')))
452
453 def test_invalid_content_id_header(self):
454 batch = BatchHttpRequest()
455 self.assertRaises(BatchError, batch._header_to_id, '[foo+x]')
456 self.assertRaises(BatchError, batch._header_to_id, 'foo+1')
457 self.assertRaises(BatchError, batch._header_to_id, '<foo>')
458
459 def test_serialize_request(self):
460 batch = BatchHttpRequest()
461 request = HttpRequest(
462 None,
463 None,
464 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
465 method='POST',
466 body='{}',
467 headers={'content-type': 'application/json'},
468 methodId=None,
469 resumable=None)
470 s = batch._serialize_request(request).splitlines()
Joe Gregorio654f4a22012-02-09 14:15:44 -0500471 self.assertEqual(EXPECTED.splitlines(), s)
Joe Gregorio66f57522011-11-30 11:00:00 -0500472
Joe Gregoriodd813822012-01-25 10:32:47 -0500473 def test_serialize_request_media_body(self):
474 batch = BatchHttpRequest()
475 f = open(datafile('small.png'))
476 body = f.read()
477 f.close()
478
479 request = HttpRequest(
480 None,
481 None,
482 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
483 method='POST',
484 body=body,
485 headers={'content-type': 'application/json'},
486 methodId=None,
487 resumable=None)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500488 # Just testing it shouldn't raise an exception.
Joe Gregoriodd813822012-01-25 10:32:47 -0500489 s = batch._serialize_request(request).splitlines()
490
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500491 def test_serialize_request_no_body(self):
492 batch = BatchHttpRequest()
493 request = HttpRequest(
494 None,
495 None,
496 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
497 method='POST',
498 body='',
499 headers={'content-type': 'application/json'},
500 methodId=None,
501 resumable=None)
502 s = batch._serialize_request(request).splitlines()
Joe Gregorio654f4a22012-02-09 14:15:44 -0500503 self.assertEqual(NO_BODY_EXPECTED.splitlines(), s)
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500504
Joe Gregorio66f57522011-11-30 11:00:00 -0500505 def test_deserialize_response(self):
506 batch = BatchHttpRequest()
507 resp, content = batch._deserialize_response(RESPONSE)
508
Joe Gregorio654f4a22012-02-09 14:15:44 -0500509 self.assertEqual(200, resp.status)
510 self.assertEqual('OK', resp.reason)
511 self.assertEqual(11, resp.version)
512 self.assertEqual('{"answer": 42}', content)
Joe Gregorio66f57522011-11-30 11:00:00 -0500513
514 def test_new_id(self):
515 batch = BatchHttpRequest()
516
517 id_ = batch._new_id()
Joe Gregorio654f4a22012-02-09 14:15:44 -0500518 self.assertEqual('1', id_)
Joe Gregorio66f57522011-11-30 11:00:00 -0500519
520 id_ = batch._new_id()
Joe Gregorio654f4a22012-02-09 14:15:44 -0500521 self.assertEqual('2', id_)
Joe Gregorio66f57522011-11-30 11:00:00 -0500522
523 batch.add(self.request1, request_id='3')
524
525 id_ = batch._new_id()
Joe Gregorio654f4a22012-02-09 14:15:44 -0500526 self.assertEqual('4', id_)
Joe Gregorio66f57522011-11-30 11:00:00 -0500527
528 def test_add(self):
529 batch = BatchHttpRequest()
530 batch.add(self.request1, request_id='1')
531 self.assertRaises(KeyError, batch.add, self.request1, request_id='1')
532
533 def test_add_fail_for_resumable(self):
534 batch = BatchHttpRequest()
535
536 upload = MediaFileUpload(
537 datafile('small.png'), chunksize=500, resumable=True)
538 self.request1.resumable = upload
539 self.assertRaises(BatchError, batch.add, self.request1, request_id='1')
540
541 def test_execute(self):
Joe Gregorio66f57522011-11-30 11:00:00 -0500542 batch = BatchHttpRequest()
543 callbacks = Callbacks()
544
545 batch.add(self.request1, callback=callbacks.f)
546 batch.add(self.request2, callback=callbacks.f)
547 http = HttpMockSequence([
548 ({'status': '200',
549 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
550 BATCH_RESPONSE),
551 ])
552 batch.execute(http)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500553 self.assertEqual({'foo': 42}, callbacks.responses['1'])
554 self.assertEqual(None, callbacks.exceptions['1'])
555 self.assertEqual({'baz': 'qux'}, callbacks.responses['2'])
556 self.assertEqual(None, callbacks.exceptions['2'])
Joe Gregorio66f57522011-11-30 11:00:00 -0500557
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500558 def test_execute_request_body(self):
559 batch = BatchHttpRequest()
560
561 batch.add(self.request1)
562 batch.add(self.request2)
563 http = HttpMockSequence([
564 ({'status': '200',
565 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
566 'echo_request_body'),
567 ])
568 try:
569 batch.execute(http)
570 self.fail('Should raise exception')
571 except BatchError, e:
572 boundary, _ = e.content.split(None, 1)
573 self.assertEqual('--', boundary[:2])
574 parts = e.content.split(boundary)
575 self.assertEqual(4, len(parts))
576 self.assertEqual('', parts[0])
577 self.assertEqual('--', parts[3])
578 header = parts[1].splitlines()[1]
579 self.assertEqual('Content-Type: application/http', header)
580
Joe Gregorio654f4a22012-02-09 14:15:44 -0500581 def test_execute_refresh_and_retry_on_401(self):
582 batch = BatchHttpRequest()
583 callbacks = Callbacks()
584 cred_1 = MockCredentials('Foo')
585 cred_2 = MockCredentials('Bar')
586
587 http = HttpMockSequence([
588 ({'status': '200',
589 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
590 BATCH_RESPONSE_WITH_401),
591 ({'status': '200',
592 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
593 BATCH_SINGLE_RESPONSE),
594 ])
595
596 creds_http_1 = HttpMockSequence([])
597 cred_1.authorize(creds_http_1)
598
599 creds_http_2 = HttpMockSequence([])
600 cred_2.authorize(creds_http_2)
601
602 self.request1.http = creds_http_1
603 self.request2.http = creds_http_2
604
605 batch.add(self.request1, callback=callbacks.f)
606 batch.add(self.request2, callback=callbacks.f)
607 batch.execute(http)
608
609 self.assertEqual({'foo': 42}, callbacks.responses['1'])
610 self.assertEqual(None, callbacks.exceptions['1'])
611 self.assertEqual({'baz': 'qux'}, callbacks.responses['2'])
612 self.assertEqual(None, callbacks.exceptions['2'])
613
614 self.assertEqual(1, cred_1._refreshed)
615 self.assertEqual(0, cred_2._refreshed)
616
617 self.assertEqual(1, cred_1._authorized)
618 self.assertEqual(1, cred_2._authorized)
619
620 self.assertEqual(1, cred_2._applied)
621 self.assertEqual(2, cred_1._applied)
622
623 def test_http_errors_passed_to_callback(self):
624 batch = BatchHttpRequest()
625 callbacks = Callbacks()
626 cred_1 = MockCredentials('Foo')
627 cred_2 = MockCredentials('Bar')
628
629 http = HttpMockSequence([
630 ({'status': '200',
631 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
632 BATCH_RESPONSE_WITH_401),
633 ({'status': '200',
634 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
635 BATCH_RESPONSE_WITH_401),
636 ])
637
638 creds_http_1 = HttpMockSequence([])
639 cred_1.authorize(creds_http_1)
640
641 creds_http_2 = HttpMockSequence([])
642 cred_2.authorize(creds_http_2)
643
644 self.request1.http = creds_http_1
645 self.request2.http = creds_http_2
646
647 batch.add(self.request1, callback=callbacks.f)
648 batch.add(self.request2, callback=callbacks.f)
649 batch.execute(http)
650
651 self.assertEqual(None, callbacks.responses['1'])
652 self.assertEqual(401, callbacks.exceptions['1'].resp.status)
653 self.assertEqual({u'baz': u'qux'}, callbacks.responses['2'])
654 self.assertEqual(None, callbacks.exceptions['2'])
655
Joe Gregorio66f57522011-11-30 11:00:00 -0500656 def test_execute_global_callback(self):
Joe Gregorio66f57522011-11-30 11:00:00 -0500657 callbacks = Callbacks()
658 batch = BatchHttpRequest(callback=callbacks.f)
659
660 batch.add(self.request1)
661 batch.add(self.request2)
662 http = HttpMockSequence([
663 ({'status': '200',
664 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
665 BATCH_RESPONSE),
666 ])
667 batch.execute(http)
Joe Gregorio654f4a22012-02-09 14:15:44 -0500668 self.assertEqual({'foo': 42}, callbacks.responses['1'])
669 self.assertEqual({'baz': 'qux'}, callbacks.responses['2'])
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500670
Ali Afshar6f11ea12012-02-07 10:32:14 -0500671
Joe Gregorio6bcbcea2011-03-10 15:26:05 -0500672if __name__ == '__main__':
673 unittest.main()