Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(180)

Side by Side Diff: third_party/google-endpoints/apitools/base/py/batch_test.py

Issue 2666783008: Add google-endpoints to third_party/. (Closed)
Patch Set: Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 #
2 # Copyright 2015 Google Inc.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 """Tests for apitools.base.py.batch."""
17
18 import textwrap
19
20 import mock
21 from six.moves import http_client
22 from six.moves.urllib import parse
23 import unittest2
24
25 from apitools.base.py import batch
26 from apitools.base.py import exceptions
27 from apitools.base.py import http_wrapper
28
29
30 class FakeCredentials(object):
31
32 def __init__(self):
33 self.num_refreshes = 0
34
35 def refresh(self, _):
36 self.num_refreshes += 1
37
38
39 class FakeHttp(object):
40
41 class FakeRequest(object):
42
43 def __init__(self, credentials=None):
44 if credentials is not None:
45 self.credentials = credentials
46
47 def __init__(self, credentials=None):
48 self.request = FakeHttp.FakeRequest(credentials=credentials)
49
50
51 class FakeService(object):
52
53 """A service for testing."""
54
55 def GetMethodConfig(self, _):
56 return {}
57
58 def GetUploadConfig(self, _):
59 return {}
60
61 # pylint: disable=unused-argument
62 def PrepareHttpRequest(
63 self, method_config, request, global_params, upload_config):
64 return global_params['desired_request']
65 # pylint: enable=unused-argument
66
67 def ProcessHttpResponse(self, _, http_response):
68 return http_response
69
70
71 class BatchTest(unittest2.TestCase):
72
73 def assertUrlEqual(self, expected_url, provided_url):
74
75 def parse_components(url):
76 parsed = parse.urlsplit(url)
77 query = parse.parse_qs(parsed.query)
78 return parsed._replace(query=''), query
79
80 expected_parse, expected_query = parse_components(expected_url)
81 provided_parse, provided_query = parse_components(provided_url)
82
83 self.assertEqual(expected_parse, provided_parse)
84 self.assertEqual(expected_query, provided_query)
85
86 def __ConfigureMock(self, mock_request, expected_request, response):
87
88 if isinstance(response, list):
89 response = list(response)
90
91 def CheckRequest(_, request, **unused_kwds):
92 self.assertUrlEqual(expected_request.url, request.url)
93 self.assertEqual(expected_request.http_method, request.http_method)
94 if isinstance(response, list):
95 return response.pop(0)
96 else:
97 return response
98
99 mock_request.side_effect = CheckRequest
100
101 def testRequestServiceUnavailable(self):
102 mock_service = FakeService()
103
104 desired_url = 'https://www.example.com'
105 batch_api_request = batch.BatchApiRequest(batch_url=desired_url,
106 retryable_codes=[])
107 # The request to be added. The actual request sent will be somewhat
108 # larger, as this is added to a batch.
109 desired_request = http_wrapper.Request(desired_url, 'POST', {
110 'content-type': 'multipart/mixed; boundary="None"',
111 'content-length': 80,
112 }, 'x' * 80)
113
114 with mock.patch.object(http_wrapper, 'MakeRequest',
115 autospec=True) as mock_request:
116 self.__ConfigureMock(
117 mock_request,
118 http_wrapper.Request(desired_url, 'POST', {
119 'content-type': 'multipart/mixed; boundary="None"',
120 'content-length': 419,
121 }, 'x' * 419),
122 http_wrapper.Response({
123 'status': '200',
124 'content-type': 'multipart/mixed; boundary="boundary"',
125 }, textwrap.dedent("""\
126 --boundary
127 content-type: text/plain
128 content-id: <id+0>
129
130 HTTP/1.1 503 SERVICE UNAVAILABLE
131 nope
132 --boundary--"""), None))
133
134 batch_api_request.Add(
135 mock_service, 'unused', None,
136 global_params={'desired_request': desired_request})
137
138 api_request_responses = batch_api_request.Execute(
139 FakeHttp(), sleep_between_polls=0)
140
141 self.assertEqual(1, len(api_request_responses))
142
143 # Make sure we didn't retry non-retryable code 503.
144 self.assertEqual(1, mock_request.call_count)
145
146 self.assertTrue(api_request_responses[0].is_error)
147 self.assertIsNone(api_request_responses[0].response)
148 self.assertIsInstance(api_request_responses[0].exception,
149 exceptions.HttpError)
150
151 def testSingleRequestInBatch(self):
152 mock_service = FakeService()
153
154 desired_url = 'https://www.example.com'
155 batch_api_request = batch.BatchApiRequest(batch_url=desired_url)
156 # The request to be added. The actual request sent will be somewhat
157 # larger, as this is added to a batch.
158 desired_request = http_wrapper.Request(desired_url, 'POST', {
159 'content-type': 'multipart/mixed; boundary="None"',
160 'content-length': 80,
161 }, 'x' * 80)
162
163 with mock.patch.object(http_wrapper, 'MakeRequest',
164 autospec=True) as mock_request:
165 self.__ConfigureMock(
166 mock_request,
167 http_wrapper.Request(desired_url, 'POST', {
168 'content-type': 'multipart/mixed; boundary="None"',
169 'content-length': 419,
170 }, 'x' * 419),
171 http_wrapper.Response({
172 'status': '200',
173 'content-type': 'multipart/mixed; boundary="boundary"',
174 }, textwrap.dedent("""\
175 --boundary
176 content-type: text/plain
177 content-id: <id+0>
178
179 HTTP/1.1 200 OK
180 content
181 --boundary--"""), None))
182
183 batch_api_request.Add(mock_service, 'unused', None, {
184 'desired_request': desired_request,
185 })
186
187 api_request_responses = batch_api_request.Execute(FakeHttp())
188
189 self.assertEqual(1, len(api_request_responses))
190 self.assertEqual(1, mock_request.call_count)
191
192 self.assertFalse(api_request_responses[0].is_error)
193
194 response = api_request_responses[0].response
195 self.assertEqual({'status': '200'}, response.info)
196 self.assertEqual('content', response.content)
197 self.assertEqual(desired_url, response.request_url)
198
199 def testRefreshOnAuthFailure(self):
200 mock_service = FakeService()
201
202 desired_url = 'https://www.example.com'
203 batch_api_request = batch.BatchApiRequest(batch_url=desired_url)
204 # The request to be added. The actual request sent will be somewhat
205 # larger, as this is added to a batch.
206 desired_request = http_wrapper.Request(desired_url, 'POST', {
207 'content-type': 'multipart/mixed; boundary="None"',
208 'content-length': 80,
209 }, 'x' * 80)
210
211 with mock.patch.object(http_wrapper, 'MakeRequest',
212 autospec=True) as mock_request:
213 self.__ConfigureMock(
214 mock_request,
215 http_wrapper.Request(desired_url, 'POST', {
216 'content-type': 'multipart/mixed; boundary="None"',
217 'content-length': 419,
218 }, 'x' * 419), [
219 http_wrapper.Response({
220 'status': '200',
221 'content-type': 'multipart/mixed; boundary="boundary"',
222 }, textwrap.dedent("""\
223 --boundary
224 content-type: text/plain
225 content-id: <id+0>
226
227 HTTP/1.1 401 UNAUTHORIZED
228 Invalid grant
229
230 --boundary--"""), None),
231 http_wrapper.Response({
232 'status': '200',
233 'content-type': 'multipart/mixed; boundary="boundary"',
234 }, textwrap.dedent("""\
235 --boundary
236 content-type: text/plain
237 content-id: <id+0>
238
239 HTTP/1.1 200 OK
240 content
241 --boundary--"""), None)
242 ])
243
244 batch_api_request.Add(mock_service, 'unused', None, {
245 'desired_request': desired_request,
246 })
247
248 credentials = FakeCredentials()
249 api_request_responses = batch_api_request.Execute(
250 FakeHttp(credentials=credentials), sleep_between_polls=0)
251
252 self.assertEqual(1, len(api_request_responses))
253 self.assertEqual(2, mock_request.call_count)
254 self.assertEqual(1, credentials.num_refreshes)
255
256 self.assertFalse(api_request_responses[0].is_error)
257
258 response = api_request_responses[0].response
259 self.assertEqual({'status': '200'}, response.info)
260 self.assertEqual('content', response.content)
261 self.assertEqual(desired_url, response.request_url)
262
263 def testNoAttempts(self):
264 desired_url = 'https://www.example.com'
265 batch_api_request = batch.BatchApiRequest(batch_url=desired_url)
266 batch_api_request.Add(FakeService(), 'unused', None, {
267 'desired_request': http_wrapper.Request(desired_url, 'POST', {
268 'content-type': 'multipart/mixed; boundary="None"',
269 'content-length': 80,
270 }, 'x' * 80),
271 })
272 api_request_responses = batch_api_request.Execute(None, max_retries=0)
273 self.assertEqual(1, len(api_request_responses))
274 self.assertIsNone(api_request_responses[0].response)
275 self.assertIsNone(api_request_responses[0].exception)
276
277 def _DoTestConvertIdToHeader(self, test_id, expected_result):
278 batch_request = batch.BatchHttpRequest('https://www.example.com')
279 self.assertEqual(
280 expected_result % batch_request._BatchHttpRequest__base_id,
281 batch_request._ConvertIdToHeader(test_id))
282
283 def testConvertIdSimple(self):
284 self._DoTestConvertIdToHeader('blah', '<%s+blah>')
285
286 def testConvertIdThatNeedsEscaping(self):
287 self._DoTestConvertIdToHeader('~tilde1', '<%s+%%7Etilde1>')
288
289 def _DoTestConvertHeaderToId(self, header, expected_id):
290 batch_request = batch.BatchHttpRequest('https://www.example.com')
291 self.assertEqual(expected_id,
292 batch_request._ConvertHeaderToId(header))
293
294 def testConvertHeaderToIdSimple(self):
295 self._DoTestConvertHeaderToId('<hello+blah>', 'blah')
296
297 def testConvertHeaderToIdWithLotsOfPlus(self):
298 self._DoTestConvertHeaderToId('<a+++++plus>', 'plus')
299
300 def _DoTestConvertInvalidHeaderToId(self, invalid_header):
301 batch_request = batch.BatchHttpRequest('https://www.example.com')
302 self.assertRaises(exceptions.BatchError,
303 batch_request._ConvertHeaderToId, invalid_header)
304
305 def testHeaderWithoutAngleBrackets(self):
306 self._DoTestConvertInvalidHeaderToId('1+1')
307
308 def testHeaderWithoutPlus(self):
309 self._DoTestConvertInvalidHeaderToId('<HEADER>')
310
311 def testSerializeRequest(self):
312 request = http_wrapper.Request(body='Hello World', headers={
313 'content-type': 'protocol/version',
314 })
315 expected_serialized_request = '\n'.join([
316 'GET HTTP/1.1',
317 'Content-Type: protocol/version',
318 'MIME-Version: 1.0',
319 'content-length: 11',
320 'Host: ',
321 '',
322 'Hello World',
323 ])
324 batch_request = batch.BatchHttpRequest('https://www.example.com')
325 self.assertEqual(expected_serialized_request,
326 batch_request._SerializeRequest(request))
327
328 def testSerializeRequestPreservesHeaders(self):
329 # Now confirm that if an additional, arbitrary header is added
330 # that it is successfully serialized to the request. Merely
331 # check that it is included, because the order of the headers
332 # in the request is arbitrary.
333 request = http_wrapper.Request(body='Hello World', headers={
334 'content-type': 'protocol/version',
335 'key': 'value',
336 })
337 batch_request = batch.BatchHttpRequest('https://www.example.com')
338 self.assertTrue(
339 'key: value\n' in batch_request._SerializeRequest(request))
340
341 def testSerializeRequestNoBody(self):
342 request = http_wrapper.Request(body=None, headers={
343 'content-type': 'protocol/version',
344 })
345 expected_serialized_request = '\n'.join([
346 'GET HTTP/1.1',
347 'Content-Type: protocol/version',
348 'MIME-Version: 1.0',
349 'Host: ',
350 '',
351 '',
352 ])
353 batch_request = batch.BatchHttpRequest('https://www.example.com')
354 self.assertEqual(expected_serialized_request,
355 batch_request._SerializeRequest(request))
356
357 def testDeserializeRequest(self):
358 serialized_payload = '\n'.join([
359 'GET HTTP/1.1',
360 'Content-Type: protocol/version',
361 'MIME-Version: 1.0',
362 'content-length: 11',
363 'key: value',
364 'Host: ',
365 '',
366 'Hello World',
367 ])
368 example_url = 'https://www.example.com'
369 expected_response = http_wrapper.Response({
370 'content-length': str(len('Hello World')),
371 'Content-Type': 'protocol/version',
372 'key': 'value',
373 'MIME-Version': '1.0',
374 'status': '',
375 'Host': ''
376 }, 'Hello World', example_url)
377
378 batch_request = batch.BatchHttpRequest(example_url)
379 self.assertEqual(
380 expected_response,
381 batch_request._DeserializeResponse(serialized_payload))
382
383 def testNewId(self):
384 batch_request = batch.BatchHttpRequest('https://www.example.com')
385
386 for i in range(100):
387 self.assertEqual(str(i), batch_request._NewId())
388
389 def testAdd(self):
390 batch_request = batch.BatchHttpRequest('https://www.example.com')
391
392 for x in range(100):
393 batch_request.Add(http_wrapper.Request(body=str(x)))
394
395 for key in batch_request._BatchHttpRequest__request_response_handlers:
396 value = batch_request._BatchHttpRequest__request_response_handlers[
397 key]
398 self.assertEqual(key, value.request.body)
399 self.assertFalse(value.request.url)
400 self.assertEqual('GET', value.request.http_method)
401 self.assertIsNone(value.response)
402 self.assertIsNone(value.handler)
403
404 def testInternalExecuteWithFailedRequest(self):
405 with mock.patch.object(http_wrapper, 'MakeRequest',
406 autospec=True) as mock_request:
407 self.__ConfigureMock(
408 mock_request,
409 http_wrapper.Request('https://www.example.com', 'POST', {
410 'content-type': 'multipart/mixed; boundary="None"',
411 'content-length': 80,
412 }, 'x' * 80),
413 http_wrapper.Response({'status': '300'}, None, None))
414
415 batch_request = batch.BatchHttpRequest('https://www.example.com')
416
417 self.assertRaises(
418 exceptions.HttpError, batch_request._Execute, None)
419
420 def testInternalExecuteWithNonMultipartResponse(self):
421 with mock.patch.object(http_wrapper, 'MakeRequest',
422 autospec=True) as mock_request:
423 self.__ConfigureMock(
424 mock_request,
425 http_wrapper.Request('https://www.example.com', 'POST', {
426 'content-type': 'multipart/mixed; boundary="None"',
427 'content-length': 80,
428 }, 'x' * 80),
429 http_wrapper.Response({
430 'status': '200',
431 'content-type': 'blah/blah'
432 }, '', None))
433
434 batch_request = batch.BatchHttpRequest('https://www.example.com')
435
436 self.assertRaises(
437 exceptions.BatchError, batch_request._Execute, None)
438
439 def testInternalExecute(self):
440 with mock.patch.object(http_wrapper, 'MakeRequest',
441 autospec=True) as mock_request:
442 self.__ConfigureMock(
443 mock_request,
444 http_wrapper.Request('https://www.example.com', 'POST', {
445 'content-type': 'multipart/mixed; boundary="None"',
446 'content-length': 583,
447 }, 'x' * 583),
448 http_wrapper.Response({
449 'status': '200',
450 'content-type': 'multipart/mixed; boundary="boundary"',
451 }, textwrap.dedent("""\
452 --boundary
453 content-type: text/plain
454 content-id: <id+2>
455
456 HTTP/1.1 200 OK
457 Second response
458
459 --boundary
460 content-type: text/plain
461 content-id: <id+1>
462
463 HTTP/1.1 401 UNAUTHORIZED
464 First response
465
466 --boundary--"""), None))
467
468 test_requests = {
469 '1': batch.RequestResponseAndHandler(
470 http_wrapper.Request(body='first'), None, None),
471 '2': batch.RequestResponseAndHandler(
472 http_wrapper.Request(body='second'), None, None),
473 }
474
475 batch_request = batch.BatchHttpRequest('https://www.example.com')
476 batch_request._BatchHttpRequest__request_response_handlers = (
477 test_requests)
478
479 batch_request._Execute(FakeHttp())
480
481 test_responses = (
482 batch_request._BatchHttpRequest__request_response_handlers)
483
484 self.assertEqual(http_client.UNAUTHORIZED,
485 test_responses['1'].response.status_code)
486 self.assertEqual(http_client.OK,
487 test_responses['2'].response.status_code)
488
489 self.assertIn(
490 'First response', test_responses['1'].response.content)
491 self.assertIn(
492 'Second response', test_responses['2'].response.content)
493
494 def testPublicExecute(self):
495
496 def LocalCallback(response, exception):
497 self.assertEqual({'status': '418'}, response.info)
498 self.assertEqual('Teapot', response.content)
499 self.assertIsNone(response.request_url)
500 self.assertIsInstance(exception, exceptions.HttpError)
501
502 global_callback = mock.Mock()
503 batch_request = batch.BatchHttpRequest(
504 'https://www.example.com', global_callback)
505
506 with mock.patch.object(batch.BatchHttpRequest, '_Execute',
507 autospec=True) as mock_execute:
508 mock_execute.return_value = None
509
510 test_requests = {
511 '0': batch.RequestResponseAndHandler(
512 None,
513 http_wrapper.Response({'status': '200'}, 'Hello!', None),
514 None),
515 '1': batch.RequestResponseAndHandler(
516 None,
517 http_wrapper.Response({'status': '418'}, 'Teapot', None),
518 LocalCallback),
519 }
520
521 batch_request._BatchHttpRequest__request_response_handlers = (
522 test_requests)
523 batch_request.Execute(None)
524
525 # Global callback was called once per handler.
526 self.assertEqual(len(test_requests), global_callback.call_count)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698