blob: 1f2b38ca76a38447920c1e4e77676b22c5d25e23 [file] [log] [blame]
Craig Citro15744b12015-03-02 13:34:32 -08001#!/usr/bin/env python
Joe Gregoriof863f7a2011-02-24 03:24:44 -05002# -*- coding: utf-8 -*-
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04003#
Craig Citro751b7fb2014-09-23 11:20:38 -07004# Copyright 2014 Google Inc. All Rights Reserved.
Joe Gregorio6d5e94f2010-08-25 23:49:30 -04005#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040018
19"""Discovery document tests
20
21Unit tests for objects created from discovery documents.
22"""
INADA Naokid898a372015-03-04 03:52:46 +090023from __future__ import absolute_import
24import six
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040025
26__author__ = 'jcgregorio@google.com (Joe Gregorio)'
27
Pat Ferateed9affd2015-03-03 16:03:15 -080028from six import BytesIO, StringIO
Pat Ferated5b61bd2015-03-03 16:04:11 -080029from six.moves.urllib.parse import urlparse, parse_qs
Pat Ferateed9affd2015-03-03 16:03:15 -080030
Daniel Hermesc2113242013-02-27 10:16:13 -080031import copy
Joe Gregoriodc106fc2012-11-20 14:30:14 -050032import datetime
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040033import httplib2
Craig Citro7ee535d2015-02-23 10:11:14 -080034import itertools
Craig Citro6ae34d72014-08-18 23:10:09 -070035import json
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040036import os
Joe Gregoriodc106fc2012-11-20 14:30:14 -050037import pickle
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040038import sys
Pat Ferate497a90f2015-03-09 09:52:54 -070039import unittest2 as unittest
Joe Gregoriodc106fc2012-11-20 14:30:14 -050040
John Asmuth864311d2014-04-24 15:46:08 -040041from googleapiclient.discovery import _fix_up_media_upload
42from googleapiclient.discovery import _fix_up_method_description
43from googleapiclient.discovery import _fix_up_parameters
Craig Citro7ee535d2015-02-23 10:11:14 -080044from googleapiclient.discovery import _urljoin
John Asmuth864311d2014-04-24 15:46:08 -040045from googleapiclient.discovery import build
46from googleapiclient.discovery import build_from_document
47from googleapiclient.discovery import DISCOVERY_URI
48from googleapiclient.discovery import key2param
49from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
50from googleapiclient.discovery import ResourceMethodParameters
51from googleapiclient.discovery import STACK_QUERY_PARAMETERS
52from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
53from googleapiclient.errors import HttpError
54from googleapiclient.errors import InvalidJsonError
55from googleapiclient.errors import MediaUploadSizeError
56from googleapiclient.errors import ResumableUploadError
57from googleapiclient.errors import UnacceptableMimeTypeError
58from googleapiclient.http import HttpMock
59from googleapiclient.http import HttpMockSequence
60from googleapiclient.http import MediaFileUpload
61from googleapiclient.http import MediaIoBaseUpload
62from googleapiclient.http import MediaUpload
63from googleapiclient.http import MediaUploadProgress
64from googleapiclient.http import tunnel_patch
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080065from oauth2client import GOOGLE_TOKEN_URI
Joe Gregorio79daca02013-03-29 16:25:52 -040066from oauth2client import util
Joe Gregoriodc106fc2012-11-20 14:30:14 -050067from oauth2client.client import OAuth2Credentials
Joe Gregorio79daca02013-03-29 16:25:52 -040068
Joe Gregoriodc106fc2012-11-20 14:30:14 -050069import uritemplate
70
Joe Gregoriocb8103d2011-02-11 23:20:52 -050071
72DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
73
Joe Gregorio79daca02013-03-29 16:25:52 -040074util.positional_parameters_enforcement = util.POSITIONAL_EXCEPTION
Joe Gregorio32f048f2012-08-27 16:31:27 -040075
Joe Gregorioa98733f2011-09-16 10:12:28 -040076
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050077def assertUrisEqual(testcase, expected, actual):
78 """Test that URIs are the same, up to reordering of query parameters."""
Pat Ferated5b61bd2015-03-03 16:04:11 -080079 expected = urlparse(expected)
80 actual = urlparse(actual)
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050081 testcase.assertEqual(expected.scheme, actual.scheme)
82 testcase.assertEqual(expected.netloc, actual.netloc)
83 testcase.assertEqual(expected.path, actual.path)
84 testcase.assertEqual(expected.params, actual.params)
85 testcase.assertEqual(expected.fragment, actual.fragment)
86 expected_query = parse_qs(expected.query)
87 actual_query = parse_qs(actual.query)
INADA Naokid898a372015-03-04 03:52:46 +090088 for name in list(expected_query.keys()):
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050089 testcase.assertEqual(expected_query[name], actual_query[name])
INADA Naokid898a372015-03-04 03:52:46 +090090 for name in list(actual_query.keys()):
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050091 testcase.assertEqual(expected_query[name], actual_query[name])
92
93
Joe Gregoriocb8103d2011-02-11 23:20:52 -050094def datafile(filename):
95 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040096
97
Joe Gregorio504a17f2012-12-07 14:14:26 -050098class SetupHttplib2(unittest.TestCase):
Daniel Hermesc2113242013-02-27 10:16:13 -080099
Joe Gregorio504a17f2012-12-07 14:14:26 -0500100 def test_retries(self):
John Asmuth864311d2014-04-24 15:46:08 -0400101 # Merely loading googleapiclient.discovery should set the RETRIES to 1.
Joe Gregorio504a17f2012-12-07 14:14:26 -0500102 self.assertEqual(1, httplib2.RETRIES)
103
104
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400105class Utilities(unittest.TestCase):
Daniel Hermesc2113242013-02-27 10:16:13 -0800106
107 def setUp(self):
108 with open(datafile('zoo.json'), 'r') as fh:
Craig Citro6ae34d72014-08-18 23:10:09 -0700109 self.zoo_root_desc = json.loads(fh.read())
Daniel Hermesc2113242013-02-27 10:16:13 -0800110 self.zoo_get_method_desc = self.zoo_root_desc['methods']['query']
Daniel Hermes954e1242013-02-28 09:28:37 -0800111 self.zoo_animals_resource = self.zoo_root_desc['resources']['animals']
112 self.zoo_insert_method_desc = self.zoo_animals_resource['methods']['insert']
Daniel Hermesc2113242013-02-27 10:16:13 -0800113
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400114 def test_key2param(self):
115 self.assertEqual('max_results', key2param('max-results'))
116 self.assertEqual('x007_bond', key2param('007-bond'))
117
Daniel Hermesc2113242013-02-27 10:16:13 -0800118 def _base_fix_up_parameters_test(self, method_desc, http_method, root_desc):
119 self.assertEqual(method_desc['httpMethod'], http_method)
120
121 method_desc_copy = copy.deepcopy(method_desc)
122 self.assertEqual(method_desc, method_desc_copy)
123
124 parameters = _fix_up_parameters(method_desc_copy, root_desc, http_method)
125
126 self.assertNotEqual(method_desc, method_desc_copy)
127
128 for param_name in STACK_QUERY_PARAMETERS:
129 self.assertEqual(STACK_QUERY_PARAMETER_DEFAULT_VALUE,
130 parameters[param_name])
131
INADA Naokid898a372015-03-04 03:52:46 +0900132 for param_name, value in six.iteritems(root_desc.get('parameters', {})):
Daniel Hermesc2113242013-02-27 10:16:13 -0800133 self.assertEqual(value, parameters[param_name])
134
135 return parameters
136
137 def test_fix_up_parameters_get(self):
138 parameters = self._base_fix_up_parameters_test(self.zoo_get_method_desc,
139 'GET', self.zoo_root_desc)
140 # Since http_method is 'GET'
INADA Naoki0bceb332014-08-20 15:27:52 +0900141 self.assertFalse('body' in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800142
143 def test_fix_up_parameters_insert(self):
144 parameters = self._base_fix_up_parameters_test(self.zoo_insert_method_desc,
145 'POST', self.zoo_root_desc)
146 body = {
147 'description': 'The request body.',
148 'type': 'object',
149 'required': True,
150 '$ref': 'Animal',
151 }
152 self.assertEqual(parameters['body'], body)
153
154 def test_fix_up_parameters_check_body(self):
155 dummy_root_desc = {}
156 no_payload_http_method = 'DELETE'
157 with_payload_http_method = 'PUT'
158
159 invalid_method_desc = {'response': 'Who cares'}
160 valid_method_desc = {'request': {'key1': 'value1', 'key2': 'value2'}}
161
162 parameters = _fix_up_parameters(invalid_method_desc, dummy_root_desc,
163 no_payload_http_method)
INADA Naoki0bceb332014-08-20 15:27:52 +0900164 self.assertFalse('body' in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800165
166 parameters = _fix_up_parameters(valid_method_desc, dummy_root_desc,
167 no_payload_http_method)
INADA Naoki0bceb332014-08-20 15:27:52 +0900168 self.assertFalse('body' in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800169
170 parameters = _fix_up_parameters(invalid_method_desc, dummy_root_desc,
171 with_payload_http_method)
INADA Naoki0bceb332014-08-20 15:27:52 +0900172 self.assertFalse('body' in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800173
174 parameters = _fix_up_parameters(valid_method_desc, dummy_root_desc,
175 with_payload_http_method)
176 body = {
177 'description': 'The request body.',
178 'type': 'object',
179 'required': True,
180 'key1': 'value1',
181 'key2': 'value2',
182 }
183 self.assertEqual(parameters['body'], body)
184
185 def _base_fix_up_method_description_test(
186 self, method_desc, initial_parameters, final_parameters,
187 final_accept, final_max_size, final_media_path_url):
188 fake_root_desc = {'rootUrl': 'http://root/',
189 'servicePath': 'fake/'}
190 fake_path_url = 'fake-path/'
191
192 accept, max_size, media_path_url = _fix_up_media_upload(
193 method_desc, fake_root_desc, fake_path_url, initial_parameters)
194 self.assertEqual(accept, final_accept)
195 self.assertEqual(max_size, final_max_size)
196 self.assertEqual(media_path_url, final_media_path_url)
197 self.assertEqual(initial_parameters, final_parameters)
198
199 def test_fix_up_media_upload_no_initial_invalid(self):
200 invalid_method_desc = {'response': 'Who cares'}
201 self._base_fix_up_method_description_test(invalid_method_desc, {}, {},
202 [], 0, None)
203
204 def test_fix_up_media_upload_no_initial_valid_minimal(self):
205 valid_method_desc = {'mediaUpload': {'accept': []}}
206 final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
207 self._base_fix_up_method_description_test(
208 valid_method_desc, {}, final_parameters, [], 0,
209 'http://root/upload/fake/fake-path/')
210
211 def test_fix_up_media_upload_no_initial_valid_full(self):
212 valid_method_desc = {'mediaUpload': {'accept': ['*/*'], 'maxSize': '10GB'}}
213 final_parameters = {'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
214 ten_gb = 10 * 2**30
215 self._base_fix_up_method_description_test(
216 valid_method_desc, {}, final_parameters, ['*/*'],
217 ten_gb, 'http://root/upload/fake/fake-path/')
218
219 def test_fix_up_media_upload_with_initial_invalid(self):
220 invalid_method_desc = {'response': 'Who cares'}
221 initial_parameters = {'body': {}}
222 self._base_fix_up_method_description_test(
223 invalid_method_desc, initial_parameters,
224 initial_parameters, [], 0, None)
225
226 def test_fix_up_media_upload_with_initial_valid_minimal(self):
227 valid_method_desc = {'mediaUpload': {'accept': []}}
228 initial_parameters = {'body': {}}
229 final_parameters = {'body': {'required': False},
230 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
231 self._base_fix_up_method_description_test(
232 valid_method_desc, initial_parameters, final_parameters, [], 0,
233 'http://root/upload/fake/fake-path/')
234
235 def test_fix_up_media_upload_with_initial_valid_full(self):
236 valid_method_desc = {'mediaUpload': {'accept': ['*/*'], 'maxSize': '10GB'}}
237 initial_parameters = {'body': {}}
238 final_parameters = {'body': {'required': False},
239 'media_body': MEDIA_BODY_PARAMETER_DEFAULT_VALUE}
240 ten_gb = 10 * 2**30
241 self._base_fix_up_method_description_test(
242 valid_method_desc, initial_parameters, final_parameters, ['*/*'],
243 ten_gb, 'http://root/upload/fake/fake-path/')
244
245 def test_fix_up_method_description_get(self):
246 result = _fix_up_method_description(self.zoo_get_method_desc,
247 self.zoo_root_desc)
248 path_url = 'query'
249 http_method = 'GET'
250 method_id = 'bigquery.query'
251 accept = []
INADA Naoki0bceb332014-08-20 15:27:52 +0900252 max_size = 0
Daniel Hermesc2113242013-02-27 10:16:13 -0800253 media_path_url = None
254 self.assertEqual(result, (path_url, http_method, method_id, accept,
255 max_size, media_path_url))
256
257 def test_fix_up_method_description_insert(self):
258 result = _fix_up_method_description(self.zoo_insert_method_desc,
259 self.zoo_root_desc)
260 path_url = 'animals'
261 http_method = 'POST'
262 method_id = 'zoo.animals.insert'
263 accept = ['image/png']
INADA Naoki0bceb332014-08-20 15:27:52 +0900264 max_size = 1024
Daniel Hermesc2113242013-02-27 10:16:13 -0800265 media_path_url = 'https://www.googleapis.com/upload/zoo/v1/animals'
266 self.assertEqual(result, (path_url, http_method, method_id, accept,
267 max_size, media_path_url))
268
Craig Citro7ee535d2015-02-23 10:11:14 -0800269 def test_urljoin(self):
270 # We want to exhaustively test various URL combinations.
271 simple_bases = ['https://www.googleapis.com', 'https://www.googleapis.com/']
272 long_urls = ['foo/v1/bar:custom?alt=json', '/foo/v1/bar:custom?alt=json']
273
274 long_bases = [
275 'https://www.googleapis.com/foo/v1',
276 'https://www.googleapis.com/foo/v1/',
277 ]
278 simple_urls = ['bar:custom?alt=json', '/bar:custom?alt=json']
279
280 final_url = 'https://www.googleapis.com/foo/v1/bar:custom?alt=json'
281 for base, url in itertools.product(simple_bases, long_urls):
282 self.assertEqual(final_url, _urljoin(base, url))
283 for base, url in itertools.product(long_bases, simple_urls):
284 self.assertEqual(final_url, _urljoin(base, url))
285
286
Daniel Hermes954e1242013-02-28 09:28:37 -0800287 def test_ResourceMethodParameters_zoo_get(self):
288 parameters = ResourceMethodParameters(self.zoo_get_method_desc)
289
290 param_types = {'a': 'any',
291 'b': 'boolean',
292 'e': 'string',
293 'er': 'string',
294 'i': 'integer',
295 'n': 'number',
296 'o': 'object',
297 'q': 'string',
298 'rr': 'string'}
INADA Naokid898a372015-03-04 03:52:46 +0900299 keys = list(param_types.keys())
Daniel Hermes954e1242013-02-28 09:28:37 -0800300 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
301 self.assertEqual(parameters.required_params, [])
302 self.assertEqual(sorted(parameters.repeated_params), ['er', 'rr'])
303 self.assertEqual(parameters.pattern_params, {'rr': '[a-z]+'})
304 self.assertEqual(sorted(parameters.query_params),
305 ['a', 'b', 'e', 'er', 'i', 'n', 'o', 'q', 'rr'])
306 self.assertEqual(parameters.path_params, set())
307 self.assertEqual(parameters.param_types, param_types)
308 enum_params = {'e': ['foo', 'bar'],
309 'er': ['one', 'two', 'three']}
310 self.assertEqual(parameters.enum_params, enum_params)
311
312 def test_ResourceMethodParameters_zoo_animals_patch(self):
313 method_desc = self.zoo_animals_resource['methods']['patch']
314 parameters = ResourceMethodParameters(method_desc)
315
316 param_types = {'name': 'string'}
INADA Naokid898a372015-03-04 03:52:46 +0900317 keys = list(param_types.keys())
Daniel Hermes954e1242013-02-28 09:28:37 -0800318 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
319 self.assertEqual(parameters.required_params, ['name'])
320 self.assertEqual(parameters.repeated_params, [])
321 self.assertEqual(parameters.pattern_params, {})
322 self.assertEqual(parameters.query_params, [])
323 self.assertEqual(parameters.path_params, set(['name']))
324 self.assertEqual(parameters.param_types, param_types)
325 self.assertEqual(parameters.enum_params, {})
326
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400327
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500328class DiscoveryErrors(unittest.TestCase):
329
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400330 def test_tests_should_be_run_with_strict_positional_enforcement(self):
331 try:
332 plus = build('plus', 'v1', None)
333 self.fail("should have raised a TypeError exception over missing http=.")
334 except TypeError:
335 pass
336
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500337 def test_failed_to_parse_discovery_json(self):
338 self.http = HttpMock(datafile('malformed.json'), {'status': '200'})
339 try:
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400340 plus = build('plus', 'v1', http=self.http)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500341 self.fail("should have raised an exception over malformed JSON.")
Joe Gregorio49396552011-03-08 10:39:00 -0500342 except InvalidJsonError:
343 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500344
345
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100346class DiscoveryFromDocument(unittest.TestCase):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400347
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100348 def test_can_build_from_local_document(self):
Joe Gregorio79daca02013-03-29 16:25:52 -0400349 discovery = open(datafile('plus.json')).read()
Joe Gregorio7b70f432011-11-09 10:18:51 -0500350 plus = build_from_document(discovery, base="https://www.googleapis.com/")
351 self.assertTrue(plus is not None)
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500352 self.assertTrue(hasattr(plus, 'activities'))
353
354 def test_can_build_from_local_deserialized_document(self):
Joe Gregorio79daca02013-03-29 16:25:52 -0400355 discovery = open(datafile('plus.json')).read()
Craig Citro6ae34d72014-08-18 23:10:09 -0700356 discovery = json.loads(discovery)
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500357 plus = build_from_document(discovery, base="https://www.googleapis.com/")
358 self.assertTrue(plus is not None)
359 self.assertTrue(hasattr(plus, 'activities'))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400360
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100361 def test_building_with_base_remembers_base(self):
Joe Gregorio79daca02013-03-29 16:25:52 -0400362 discovery = open(datafile('plus.json')).read()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400363
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100364 base = "https://www.example.com/"
Joe Gregorio7b70f432011-11-09 10:18:51 -0500365 plus = build_from_document(discovery, base=base)
Joe Gregorioa2838152012-07-16 11:52:17 -0400366 self.assertEquals("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100367
368
Joe Gregorioa98733f2011-09-16 10:12:28 -0400369class DiscoveryFromHttp(unittest.TestCase):
Joe Gregorio583d9e42011-09-16 15:54:15 -0400370 def setUp(self):
Joe Bedafb463cb2011-09-19 17:39:49 -0700371 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400372
Joe Gregorio583d9e42011-09-16 15:54:15 -0400373 def tearDown(self):
374 os.environ = self.old_environ
375
376 def test_userip_is_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400377 # build() will raise an HttpError on a 400, use this to pick the request uri
378 # out of the raised exception.
Joe Gregorio583d9e42011-09-16 15:54:15 -0400379 os.environ['REMOTE_ADDR'] = '10.0.0.1'
Joe Gregorioa98733f2011-09-16 10:12:28 -0400380 try:
381 http = HttpMockSequence([
Joe Gregorio79daca02013-03-29 16:25:52 -0400382 ({'status': '400'}, open(datafile('zoo.json'), 'rb').read()),
Joe Gregorioa98733f2011-09-16 10:12:28 -0400383 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400384 zoo = build('zoo', 'v1', http=http, developerKey='foo',
Joe Gregorioa98733f2011-09-16 10:12:28 -0400385 discoveryServiceUrl='http://example.com')
386 self.fail('Should have raised an exception.')
INADA Naokic1505df2014-08-20 15:19:53 +0900387 except HttpError as e:
Joe Gregorio583d9e42011-09-16 15:54:15 -0400388 self.assertEqual(e.uri, 'http://example.com?userIp=10.0.0.1')
Joe Gregorioa98733f2011-09-16 10:12:28 -0400389
Joe Gregorio583d9e42011-09-16 15:54:15 -0400390 def test_userip_missing_is_not_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400391 # build() will raise an HttpError on a 400, use this to pick the request uri
392 # out of the raised exception.
393 try:
394 http = HttpMockSequence([
Joe Gregorio79daca02013-03-29 16:25:52 -0400395 ({'status': '400'}, open(datafile('zoo.json'), 'rb').read()),
Joe Gregorioa98733f2011-09-16 10:12:28 -0400396 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400397 zoo = build('zoo', 'v1', http=http, developerKey=None,
Joe Gregorioa98733f2011-09-16 10:12:28 -0400398 discoveryServiceUrl='http://example.com')
399 self.fail('Should have raised an exception.')
INADA Naokic1505df2014-08-20 15:19:53 +0900400 except HttpError as e:
Joe Gregorioa98733f2011-09-16 10:12:28 -0400401 self.assertEqual(e.uri, 'http://example.com')
402
403
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400404class Discovery(unittest.TestCase):
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400405
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400406 def test_method_error_checking(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500407 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400408 plus = build('plus', 'v1', http=self.http)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400409
410 # Missing required parameters
411 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500412 plus.activities().list()
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400413 self.fail()
INADA Naokic1505df2014-08-20 15:19:53 +0900414 except TypeError as e:
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400415 self.assertTrue('Missing' in str(e))
416
Joe Gregorio2467afa2012-06-20 12:21:25 -0400417 # Missing required parameters even if supplied as None.
418 try:
419 plus.activities().list(collection=None, userId=None)
420 self.fail()
INADA Naokic1505df2014-08-20 15:19:53 +0900421 except TypeError as e:
Joe Gregorio2467afa2012-06-20 12:21:25 -0400422 self.assertTrue('Missing' in str(e))
423
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400424 # Parameter doesn't match regex
425 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500426 plus.activities().list(collection='not_a_collection_name', userId='me')
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400427 self.fail()
INADA Naokic1505df2014-08-20 15:19:53 +0900428 except TypeError as e:
Joe Gregorioca876e42011-02-22 19:39:42 -0500429 self.assertTrue('not an allowed value' in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400430
431 # Unexpected parameter
432 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500433 plus.activities().list(flubber=12)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400434 self.fail()
INADA Naokic1505df2014-08-20 15:19:53 +0900435 except TypeError as e:
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400436 self.assertTrue('unexpected' in str(e))
437
Joe Gregoriobee86832011-02-22 10:00:19 -0500438 def _check_query_types(self, request):
Pat Ferated5b61bd2015-03-03 16:04:11 -0800439 parsed = urlparse(request.uri)
Joe Gregoriobee86832011-02-22 10:00:19 -0500440 q = parse_qs(parsed[4])
441 self.assertEqual(q['q'], ['foo'])
442 self.assertEqual(q['i'], ['1'])
443 self.assertEqual(q['n'], ['1.0'])
444 self.assertEqual(q['b'], ['false'])
445 self.assertEqual(q['a'], ['[1, 2, 3]'])
446 self.assertEqual(q['o'], ['{\'a\': 1}'])
447 self.assertEqual(q['e'], ['bar'])
448
449 def test_type_coercion(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400450 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400451 zoo = build('zoo', 'v1', http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500452
Joe Gregorioa98733f2011-09-16 10:12:28 -0400453 request = zoo.query(
454 q="foo", i=1.0, n=1.0, b=0, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500455 self._check_query_types(request)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400456 request = zoo.query(
457 q="foo", i=1, n=1, b=False, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500458 self._check_query_types(request)
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500459
Joe Gregorioa98733f2011-09-16 10:12:28 -0400460 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800461 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar', er='two')
Joe Gregorio6804c7a2011-11-18 14:30:32 -0500462
463 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800464 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar',
465 er=['one', 'three'], rr=['foo', 'bar'])
Joe Gregoriobee86832011-02-22 10:00:19 -0500466 self._check_query_types(request)
467
Craig Citro1e742822012-03-01 12:59:22 -0800468 # Five is right out.
Joe Gregorio20c26e52012-03-02 15:58:31 -0500469 self.assertRaises(TypeError, zoo.query, er=['one', 'five'])
Craig Citro1e742822012-03-01 12:59:22 -0800470
Joe Gregorio13217952011-02-22 15:37:38 -0500471 def test_optional_stack_query_parameters(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400472 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400473 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400474 request = zoo.query(trace='html', fields='description')
Joe Gregorio13217952011-02-22 15:37:38 -0500475
Pat Ferated5b61bd2015-03-03 16:04:11 -0800476 parsed = urlparse(request.uri)
Joe Gregorioca876e42011-02-22 19:39:42 -0500477 q = parse_qs(parsed[4])
478 self.assertEqual(q['trace'], ['html'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400479 self.assertEqual(q['fields'], ['description'])
480
Joe Gregorio2467afa2012-06-20 12:21:25 -0400481 def test_string_params_value_of_none_get_dropped(self):
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400482 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400483 zoo = build('zoo', 'v1', http=http)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400484 request = zoo.query(trace=None, fields='description')
485
Pat Ferated5b61bd2015-03-03 16:04:11 -0800486 parsed = urlparse(request.uri)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400487 q = parse_qs(parsed[4])
488 self.assertFalse('trace' in q)
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400489
Joe Gregorioe08a1662011-12-07 09:48:22 -0500490 def test_model_added_query_parameters(self):
491 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400492 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500493 request = zoo.animals().get(name='Lion')
494
Pat Ferated5b61bd2015-03-03 16:04:11 -0800495 parsed = urlparse(request.uri)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500496 q = parse_qs(parsed[4])
497 self.assertEqual(q['alt'], ['json'])
498 self.assertEqual(request.headers['accept'], 'application/json')
499
500 def test_fallback_to_raw_model(self):
501 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400502 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500503 request = zoo.animals().getmedia(name='Lion')
504
Pat Ferated5b61bd2015-03-03 16:04:11 -0800505 parsed = urlparse(request.uri)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500506 q = parse_qs(parsed[4])
507 self.assertTrue('alt' not in q)
508 self.assertEqual(request.headers['accept'], '*/*')
509
Joe Gregoriof4153422011-03-18 22:45:18 -0400510 def test_patch(self):
511 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400512 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400513 request = zoo.animals().patch(name='lion', body='{"description": "foo"}')
514
515 self.assertEqual(request.method, 'PATCH')
516
517 def test_tunnel_patch(self):
518 http = HttpMockSequence([
Joe Gregorio79daca02013-03-29 16:25:52 -0400519 ({'status': '200'}, open(datafile('zoo.json'), 'rb').read()),
Joe Gregoriof4153422011-03-18 22:45:18 -0400520 ({'status': '200'}, 'echo_request_headers_as_json'),
521 ])
522 http = tunnel_patch(http)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400523 zoo = build('zoo', 'v1', http=http)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400524 resp = zoo.animals().patch(
525 name='lion', body='{"description": "foo"}').execute()
Joe Gregoriof4153422011-03-18 22:45:18 -0400526
527 self.assertTrue('x-http-method-override' in resp)
Joe Gregorioca876e42011-02-22 19:39:42 -0500528
Joe Gregorio7b70f432011-11-09 10:18:51 -0500529 def test_plus_resources(self):
530 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400531 plus = build('plus', 'v1', http=self.http)
Joe Gregorio7b70f432011-11-09 10:18:51 -0500532 self.assertTrue(getattr(plus, 'activities'))
533 self.assertTrue(getattr(plus, 'people'))
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400534
Orest Bolohane92c9002014-05-30 11:15:43 -0700535 def test_credentials(self):
536 class CredentialsMock:
537 def create_scoped_required(self):
538 return False
539
540 def authorize(self, http):
541 http.orest = True
542
543 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
544 build('plus', 'v1', http=self.http, credentials=None)
545 self.assertFalse(hasattr(self.http, 'orest'))
546 build('plus', 'v1', http=self.http, credentials=CredentialsMock())
547 self.assertTrue(hasattr(self.http, 'orest'))
548
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400549 def test_full_featured(self):
550 # Zoo should exercise all discovery facets
551 # and should also have no future.json file.
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500552 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400553 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400554 self.assertTrue(getattr(zoo, 'animals'))
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500555
Joe Gregoriof4153422011-03-18 22:45:18 -0400556 request = zoo.animals().list(name='bat', projection="full")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800557 parsed = urlparse(request.uri)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400558 q = parse_qs(parsed[4])
559 self.assertEqual(q['name'], ['bat'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400560 self.assertEqual(q['projection'], ['full'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400561
Joe Gregorio3fada332011-01-07 17:07:45 -0500562 def test_nested_resources(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500563 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400564 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio3fada332011-01-07 17:07:45 -0500565 self.assertTrue(getattr(zoo, 'animals'))
566 request = zoo.my().favorites().list(max_results="5")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800567 parsed = urlparse(request.uri)
Joe Gregorio3fada332011-01-07 17:07:45 -0500568 q = parse_qs(parsed[4])
569 self.assertEqual(q['max-results'], ['5'])
570
Pat Feratec6050872015-03-03 18:24:59 -0800571 @unittest.skipIf(six.PY3, 'print is not a reserved name in Python 3')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400572 def test_methods_with_reserved_names(self):
573 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400574 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod92897c2011-07-07 11:44:56 -0400575 self.assertTrue(getattr(zoo, 'animals'))
576 request = zoo.global_().print_().assert_(max_results="5")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800577 parsed = urlparse(request.uri)
Joe Gregorioa2838152012-07-16 11:52:17 -0400578 self.assertEqual(parsed[2], '/zoo/v1/global/print/assert')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400579
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500580 def test_top_level_functions(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500581 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400582 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500583 self.assertTrue(getattr(zoo, 'query'))
584 request = zoo.query(q="foo")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800585 parsed = urlparse(request.uri)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500586 q = parse_qs(parsed[4])
587 self.assertEqual(q['q'], ['foo'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400588
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400589 def test_simple_media_uploads(self):
590 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400591 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400592 doc = getattr(zoo.animals().insert, '__doc__')
593 self.assertTrue('media_body' in doc)
594
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400595 def test_simple_media_upload_no_max_size_provided(self):
596 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400597 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400598 request = zoo.animals().crossbreed(media_body=datafile('small.png'))
599 self.assertEquals('image/png', request.headers['content-type'])
Pat Ferate2b140222015-03-03 18:05:11 -0800600 self.assertEquals(b'PNG', request.body[1:4])
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400601
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400602 def test_simple_media_raise_correct_exceptions(self):
603 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400604 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400605
606 try:
607 zoo.animals().insert(media_body=datafile('smiley.png'))
608 self.fail("should throw exception if media is too large.")
609 except MediaUploadSizeError:
610 pass
611
612 try:
613 zoo.animals().insert(media_body=datafile('small.jpg'))
614 self.fail("should throw exception if mimetype is unacceptable.")
615 except UnacceptableMimeTypeError:
616 pass
617
618 def test_simple_media_good_upload(self):
619 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400620 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400621
622 request = zoo.animals().insert(media_body=datafile('small.png'))
623 self.assertEquals('image/png', request.headers['content-type'])
Pat Ferate2b140222015-03-03 18:05:11 -0800624 self.assertEquals(b'PNG', request.body[1:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500625 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400626 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500627 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400628
629 def test_multipart_media_raise_correct_exceptions(self):
630 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400631 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400632
633 try:
634 zoo.animals().insert(media_body=datafile('smiley.png'), body={})
635 self.fail("should throw exception if media is too large.")
636 except MediaUploadSizeError:
637 pass
638
639 try:
640 zoo.animals().insert(media_body=datafile('small.jpg'), body={})
641 self.fail("should throw exception if mimetype is unacceptable.")
642 except UnacceptableMimeTypeError:
643 pass
644
645 def test_multipart_media_good_upload(self):
646 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400647 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400648
649 request = zoo.animals().insert(media_body=datafile('small.png'), body={})
Joe Gregorioa98733f2011-09-16 10:12:28 -0400650 self.assertTrue(request.headers['content-type'].startswith(
651 'multipart/related'))
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400652 self.assertEquals('--==', request.body[0:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500653 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400654 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500655 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400656
657 def test_media_capable_method_without_media(self):
658 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400659 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400660
661 request = zoo.animals().insert(body={})
662 self.assertTrue(request.headers['content-type'], 'application/json')
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400663
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500664 def test_resumable_multipart_media_good_upload(self):
665 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400666 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500667
668 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
669 request = zoo.animals().insert(media_body=media_upload, body={})
670 self.assertTrue(request.headers['content-type'].startswith(
Joe Gregorio945be3e2012-01-27 17:01:06 -0500671 'application/json'))
672 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500673 self.assertEquals(media_upload, request.resumable)
674
675 self.assertEquals('image/png', request.resumable.mimetype())
676
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500677 self.assertNotEquals(request.body, None)
678 self.assertEquals(request.resumable_uri, None)
679
680 http = HttpMockSequence([
681 ({'status': '200',
682 'location': 'http://upload.example.com'}, ''),
683 ({'status': '308',
684 'location': 'http://upload.example.com/2',
685 'range': '0-12'}, ''),
686 ({'status': '308',
687 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500688 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500689 ({'status': '200'}, '{"foo": "bar"}'),
690 ])
691
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400692 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500693 self.assertEquals(None, body)
694 self.assertTrue(isinstance(status, MediaUploadProgress))
695 self.assertEquals(13, status.resumable_progress)
696
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500697 # Two requests should have been made and the resumable_uri should have been
698 # updated for each one.
699 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
700
701 self.assertEquals(media_upload, request.resumable)
702 self.assertEquals(13, request.resumable_progress)
703
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400704 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500705 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500706 self.assertEquals(media_upload.size()-1, request.resumable_progress)
707 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500708
709 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400710 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500711 self.assertEquals(body, {"foo": "bar"})
712 self.assertEquals(status, None)
713
714
715 def test_resumable_media_good_upload(self):
716 """Not a multipart upload."""
717 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400718 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500719
720 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
721 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500722 self.assertEquals(media_upload, request.resumable)
723
724 self.assertEquals('image/png', request.resumable.mimetype())
725
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500726 self.assertEquals(request.body, None)
727 self.assertEquals(request.resumable_uri, None)
728
729 http = HttpMockSequence([
730 ({'status': '200',
731 'location': 'http://upload.example.com'}, ''),
732 ({'status': '308',
733 'location': 'http://upload.example.com/2',
734 'range': '0-12'}, ''),
735 ({'status': '308',
736 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500737 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500738 ({'status': '200'}, '{"foo": "bar"}'),
739 ])
740
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400741 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500742 self.assertEquals(None, body)
743 self.assertTrue(isinstance(status, MediaUploadProgress))
744 self.assertEquals(13, status.resumable_progress)
745
746 # Two requests should have been made and the resumable_uri should have been
747 # updated for each one.
748 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
749
750 self.assertEquals(media_upload, request.resumable)
751 self.assertEquals(13, request.resumable_progress)
752
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400753 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500754 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500755 self.assertEquals(media_upload.size()-1, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500756 self.assertEquals(request.body, None)
757
758 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400759 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500760 self.assertEquals(body, {"foo": "bar"})
761 self.assertEquals(status, None)
762
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500763 def test_resumable_media_good_upload_from_execute(self):
764 """Not a multipart upload."""
765 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400766 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500767
768 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
769 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500770 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400771 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500772 request.uri)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500773
774 http = HttpMockSequence([
775 ({'status': '200',
776 'location': 'http://upload.example.com'}, ''),
777 ({'status': '308',
778 'location': 'http://upload.example.com/2',
779 'range': '0-12'}, ''),
780 ({'status': '308',
781 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500782 'range': '0-%d' % media_upload.size()}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500783 ({'status': '200'}, '{"foo": "bar"}'),
784 ])
785
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400786 body = request.execute(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500787 self.assertEquals(body, {"foo": "bar"})
788
789 def test_resumable_media_fail_unknown_response_code_first_request(self):
790 """Not a multipart upload."""
791 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400792 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500793
794 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
795 request = zoo.animals().insert(media_body=media_upload, body=None)
796
797 http = HttpMockSequence([
798 ({'status': '400',
799 'location': 'http://upload.example.com'}, ''),
800 ])
801
Joe Gregoriobaf04802013-03-01 12:27:06 -0500802 try:
803 request.execute(http=http)
804 self.fail('Should have raised ResumableUploadError.')
INADA Naokic1505df2014-08-20 15:19:53 +0900805 except ResumableUploadError as e:
Joe Gregoriobaf04802013-03-01 12:27:06 -0500806 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500807
808 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
809 """Not a multipart upload."""
810 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400811 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500812
813 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
814 request = zoo.animals().insert(media_body=media_upload, body=None)
815
816 http = HttpMockSequence([
817 ({'status': '200',
818 'location': 'http://upload.example.com'}, ''),
819 ({'status': '400'}, ''),
820 ])
821
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400822 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400823 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500824
Joe Gregorio910b9b12012-06-12 09:36:30 -0400825 http = HttpMockSequence([
826 ({'status': '308',
827 'range': '0-5'}, ''),
828 ({'status': '308',
829 'range': '0-6'}, ''),
830 ])
831
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400832 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400833 self.assertEquals(status.resumable_progress, 7,
834 'Should have first checked length and then tried to PUT more.')
835 self.assertFalse(request._in_error_state)
836
837 # Put it back in an error state.
838 http = HttpMockSequence([
839 ({'status': '400'}, ''),
840 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400841 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400842 self.assertTrue(request._in_error_state)
843
844 # Pretend the last request that 400'd actually succeeded.
845 http = HttpMockSequence([
846 ({'status': '200'}, '{"foo": "bar"}'),
847 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400848 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400849 self.assertEqual(body, {'foo': 'bar'})
850
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400851 def test_media_io_base_stream_unlimited_chunksize_resume(self):
852 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
853 zoo = build('zoo', 'v1', http=self.http)
854
Pat Ferateed9affd2015-03-03 16:03:15 -0800855 # Set up a seekable stream and try to upload in single chunk.
Pat Ferate2b140222015-03-03 18:05:11 -0800856 fd = BytesIO(b'01234"56789"')
Pat Ferateed9affd2015-03-03 16:03:15 -0800857 media_upload = MediaIoBaseUpload(
858 fd=fd, mimetype='text/plain', chunksize=-1, resumable=True)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400859
Pat Ferateed9affd2015-03-03 16:03:15 -0800860 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400861
Pat Ferateed9affd2015-03-03 16:03:15 -0800862 # The single chunk fails, restart at the right point.
863 http = HttpMockSequence([
864 ({'status': '200',
865 'location': 'http://upload.example.com'}, ''),
866 ({'status': '308',
867 'location': 'http://upload.example.com/2',
868 'range': '0-4'}, ''),
869 ({'status': '200'}, 'echo_request_body'),
870 ])
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400871
Pat Ferateed9affd2015-03-03 16:03:15 -0800872 body = request.execute(http=http)
873 self.assertEqual('56789', body)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400874
875 def test_media_io_base_stream_chunksize_resume(self):
876 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
877 zoo = build('zoo', 'v1', http=self.http)
878
Pat Ferateed9affd2015-03-03 16:03:15 -0800879 # Set up a seekable stream and try to upload in chunks.
Pat Ferate2b140222015-03-03 18:05:11 -0800880 fd = BytesIO(b'0123456789')
Pat Ferateed9affd2015-03-03 16:03:15 -0800881 media_upload = MediaIoBaseUpload(
882 fd=fd, mimetype='text/plain', chunksize=5, resumable=True)
883
884 request = zoo.animals().insert(media_body=media_upload, body=None)
885
886 # The single chunk fails, pull the content sent out of the exception.
887 http = HttpMockSequence([
888 ({'status': '200',
889 'location': 'http://upload.example.com'}, ''),
890 ({'status': '400'}, 'echo_request_body'),
891 ])
892
Joe Gregorio5c120db2012-08-23 09:13:55 -0400893 try:
Pat Ferateed9affd2015-03-03 16:03:15 -0800894 body = request.execute(http=http)
895 except HttpError as e:
Pat Ferate2b140222015-03-03 18:05:11 -0800896 self.assertEqual(b'01234', e.content)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400897
Joe Gregorio910b9b12012-06-12 09:36:30 -0400898 def test_resumable_media_handle_uploads_of_unknown_size(self):
899 http = HttpMockSequence([
900 ({'status': '200',
901 'location': 'http://upload.example.com'}, ''),
902 ({'status': '200'}, 'echo_request_headers_as_json'),
903 ])
904
905 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400906 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400907
Joe Gregorio910b9b12012-06-12 09:36:30 -0400908 # Create an upload that doesn't know the full size of the media.
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400909 class IoBaseUnknownLength(MediaUpload):
910 def chunksize(self):
911 return 10
912
913 def mimetype(self):
914 return 'image/png'
915
916 def size(self):
917 return None
918
919 def resumable(self):
920 return True
921
922 def getbytes(self, begin, length):
923 return '0123456789'
924
925 upload = IoBaseUnknownLength()
Joe Gregorio910b9b12012-06-12 09:36:30 -0400926
927 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400928 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400929 self.assertEqual(body, {
930 'Content-Range': 'bytes 0-9/*',
931 'Content-Length': '10',
932 })
Joe Gregorio44454e42012-06-15 08:38:53 -0400933
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400934 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
935 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
936 zoo = build('zoo', 'v1', http=self.http)
937
938 class IoBaseHasStream(MediaUpload):
939 def chunksize(self):
940 return 10
941
942 def mimetype(self):
943 return 'image/png'
944
945 def size(self):
946 return None
947
948 def resumable(self):
949 return True
950
951 def getbytes(self, begin, length):
952 return '0123456789'
953
954 def has_stream(self):
955 return True
956
957 def stream(self):
958 raise NotImplementedError()
959
960 upload = IoBaseHasStream()
961
962 orig_version = sys.version_info
963 sys.version_info = (2, 5, 5, 'final', 0)
964
965 request = zoo.animals().insert(media_body=upload, body=None)
966
967 http = HttpMockSequence([
968 ({'status': '200',
969 'location': 'http://upload.example.com'}, ''),
970 ({'status': '200'}, 'echo_request_headers_as_json'),
971 ])
972
973 # This should not raise an exception because stream() shouldn't be called.
974 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400975 self.assertEqual(body, {
976 'Content-Range': 'bytes 0-9/*',
977 'Content-Length': '10'
978 })
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400979
980 sys.version_info = (2, 6, 5, 'final', 0)
981
982 request = zoo.animals().insert(media_body=upload, body=None)
983
984 # This should raise an exception because stream() will be called.
985 http = HttpMockSequence([
986 ({'status': '200',
987 'location': 'http://upload.example.com'}, ''),
988 ({'status': '200'}, 'echo_request_headers_as_json'),
989 ])
990
991 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
992
993 sys.version_info = orig_version
994
Joe Gregorio44454e42012-06-15 08:38:53 -0400995 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
996 http = HttpMockSequence([
997 ({'status': '200',
998 'location': 'http://upload.example.com'}, ''),
999 ({'status': '200'}, 'echo_request_headers_as_json'),
1000 ])
1001
1002 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001003 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio44454e42012-06-15 08:38:53 -04001004
Pat Ferate2b140222015-03-03 18:05:11 -08001005 fd = BytesIO(b'data goes here')
Joe Gregorio44454e42012-06-15 08:38:53 -04001006
1007 # Create an upload that doesn't know the full size of the media.
1008 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -04001009 fd=fd, mimetype='image/png', chunksize=15, resumable=True)
Joe Gregorio44454e42012-06-15 08:38:53 -04001010
1011 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001012 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001013 self.assertEqual(body, {
1014 'Content-Range': 'bytes 0-13/14',
1015 'Content-Length': '14',
1016 })
Joe Gregorio910b9b12012-06-12 09:36:30 -04001017
1018 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1019 http = HttpMockSequence([
1020 ({'status': '200',
1021 'location': 'http://upload.example.com'}, ''),
1022 ({'status': '400'}, ''),
1023 ])
1024
1025 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001026 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001027
1028 # Create an upload that doesn't know the full size of the media.
Pat Ferate2b140222015-03-03 18:05:11 -08001029 fd = BytesIO(b'data goes here')
Joe Gregorio910b9b12012-06-12 09:36:30 -04001030
1031 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -04001032 fd=fd, mimetype='image/png', chunksize=500, resumable=True)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001033
1034 request = zoo.animals().insert(media_body=upload, body=None)
1035
1036 # Put it in an error state.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001037 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001038
1039 http = HttpMockSequence([
1040 ({'status': '400',
1041 'range': '0-5'}, 'echo_request_headers_as_json'),
1042 ])
1043 try:
1044 # Should resume the upload by first querying the status of the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001045 request.next_chunk(http=http)
INADA Naokic1505df2014-08-20 15:19:53 +09001046 except HttpError as e:
Joe Gregorio910b9b12012-06-12 09:36:30 -04001047 expected = {
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001048 'Content-Range': 'bytes */14',
Joe Gregorio910b9b12012-06-12 09:36:30 -04001049 'content-length': '0'
1050 }
INADA Naoki09157612015-03-25 01:51:03 +09001051 self.assertEqual(expected, json.loads(e.content.decode('utf-8')),
Joe Gregorio910b9b12012-06-12 09:36:30 -04001052 'Should send an empty body when requesting the current upload status.')
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001053
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001054 def test_pickle(self):
1055 sorted_resource_keys = ['_baseUrl',
1056 '_developerKey',
1057 '_dynamic_attrs',
1058 '_http',
1059 '_model',
1060 '_requestBuilder',
1061 '_resourceDesc',
1062 '_rootDesc',
1063 '_schema',
1064 'animals',
1065 'global_',
1066 'load',
1067 'loadNoTemplate',
1068 'my',
1069 'query',
1070 'scopedAnimals']
1071
1072 http = HttpMock(datafile('zoo.json'), {'status': '200'})
1073 zoo = build('zoo', 'v1', http=http)
1074 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
1075
1076 pickled_zoo = pickle.dumps(zoo)
1077 new_zoo = pickle.loads(pickled_zoo)
1078 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
1079 self.assertTrue(hasattr(new_zoo, 'animals'))
1080 self.assertTrue(callable(new_zoo.animals))
1081 self.assertTrue(hasattr(new_zoo, 'global_'))
1082 self.assertTrue(callable(new_zoo.global_))
1083 self.assertTrue(hasattr(new_zoo, 'load'))
1084 self.assertTrue(callable(new_zoo.load))
1085 self.assertTrue(hasattr(new_zoo, 'loadNoTemplate'))
1086 self.assertTrue(callable(new_zoo.loadNoTemplate))
1087 self.assertTrue(hasattr(new_zoo, 'my'))
1088 self.assertTrue(callable(new_zoo.my))
1089 self.assertTrue(hasattr(new_zoo, 'query'))
1090 self.assertTrue(callable(new_zoo.query))
1091 self.assertTrue(hasattr(new_zoo, 'scopedAnimals'))
1092 self.assertTrue(callable(new_zoo.scopedAnimals))
1093
Joe Gregorio003b6e42013-02-13 15:42:19 -05001094 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001095 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
1096 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
1097 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
1098 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
1099 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
1100 # _http, _model and _schema won't be equal since we will get new
1101 # instances upon un-pickling
1102
1103 def _dummy_zoo_request(self):
1104 with open(os.path.join(DATA_DIR, 'zoo.json'), 'rU') as fh:
1105 zoo_contents = fh.read()
1106
1107 zoo_uri = uritemplate.expand(DISCOVERY_URI,
1108 {'api': 'zoo', 'apiVersion': 'v1'})
1109 if 'REMOTE_ADDR' in os.environ:
Joe Gregorio79daca02013-03-29 16:25:52 -04001110 zoo_uri = util._add_query_parameter(zoo_uri, 'userIp',
1111 os.environ['REMOTE_ADDR'])
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001112
1113 http = httplib2.Http()
1114 original_request = http.request
1115 def wrapped_request(uri, method='GET', *args, **kwargs):
1116 if uri == zoo_uri:
1117 return httplib2.Response({'status': '200'}), zoo_contents
1118 return original_request(uri, method=method, *args, **kwargs)
1119 http.request = wrapped_request
1120 return http
1121
1122 def _dummy_token(self):
1123 access_token = 'foo'
1124 client_id = 'some_client_id'
1125 client_secret = 'cOuDdkfjxxnv+'
1126 refresh_token = '1/0/a.df219fjls0'
1127 token_expiry = datetime.datetime.utcnow()
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001128 user_agent = 'refresh_checker/1.0'
1129 return OAuth2Credentials(
1130 access_token, client_id, client_secret,
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -08001131 refresh_token, token_expiry, GOOGLE_TOKEN_URI,
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001132 user_agent)
1133
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001134 def test_pickle_with_credentials(self):
1135 credentials = self._dummy_token()
1136 http = self._dummy_zoo_request()
1137 http = credentials.authorize(http)
1138 self.assertTrue(hasattr(http.request, 'credentials'))
1139
1140 zoo = build('zoo', 'v1', http=http)
1141 pickled_zoo = pickle.dumps(zoo)
1142 new_zoo = pickle.loads(pickled_zoo)
1143 self.assertEqual(sorted(zoo.__dict__.keys()),
1144 sorted(new_zoo.__dict__.keys()))
1145 new_http = new_zoo._http
1146 self.assertFalse(hasattr(new_http.request, 'credentials'))
1147
Joe Gregorio708388c2012-06-15 13:43:04 -04001148
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001149class Next(unittest.TestCase):
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001150
Joe Gregorio3c676f92011-07-25 10:38:14 -04001151 def test_next_successful_none_on_no_next_page_token(self):
1152 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001153 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001154 request = tasks.tasklists().list()
1155 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
1156
1157 def test_next_successful_with_next_page_token(self):
1158 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001159 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001160 request = tasks.tasklists().list()
Joe Gregorioa98733f2011-09-16 10:12:28 -04001161 next_request = tasks.tasklists().list_next(
1162 request, {'nextPageToken': '123abc'})
Pat Ferated5b61bd2015-03-03 16:04:11 -08001163 parsed = list(urlparse(next_request.uri))
Joe Gregorio3c676f92011-07-25 10:38:14 -04001164 q = parse_qs(parsed[4])
1165 self.assertEqual(q['pageToken'][0], '123abc')
1166
Joe Gregorio555f33c2011-08-19 14:56:07 -04001167 def test_next_with_method_with_no_properties(self):
1168 self.http = HttpMock(datafile('latitude.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001169 service = build('latitude', 'v1', http=self.http)
Joe Gregorio555f33c2011-08-19 14:56:07 -04001170 request = service.currentLocation().get()
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001171
Joe Gregorioa98733f2011-09-16 10:12:28 -04001172
Joe Gregorio708388c2012-06-15 13:43:04 -04001173class MediaGet(unittest.TestCase):
1174
1175 def test_get_media(self):
1176 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001177 zoo = build('zoo', 'v1', http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -04001178 request = zoo.animals().get_media(name='Lion')
1179
Pat Ferated5b61bd2015-03-03 16:04:11 -08001180 parsed = urlparse(request.uri)
Joe Gregorio708388c2012-06-15 13:43:04 -04001181 q = parse_qs(parsed[4])
1182 self.assertEqual(q['alt'], ['media'])
1183 self.assertEqual(request.headers['accept'], '*/*')
1184
1185 http = HttpMockSequence([
1186 ({'status': '200'}, 'standing in for media'),
1187 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001188 response = request.execute(http=http)
INADA Naoki09157612015-03-25 01:51:03 +09001189 self.assertEqual(b'standing in for media', response)
Joe Gregorio708388c2012-06-15 13:43:04 -04001190
1191
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001192if __name__ == '__main__':
1193 unittest.main()