blob: 0a71cfcd23e81dc689bd05c6f1ccc7a252ccf892 [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
Joe Gregoriod92897c2011-07-07 11:44:56 -0400571 def test_methods_with_reserved_names(self):
572 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400573 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod92897c2011-07-07 11:44:56 -0400574 self.assertTrue(getattr(zoo, 'animals'))
575 request = zoo.global_().print_().assert_(max_results="5")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800576 parsed = urlparse(request.uri)
Joe Gregorioa2838152012-07-16 11:52:17 -0400577 self.assertEqual(parsed[2], '/zoo/v1/global/print/assert')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400578
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500579 def test_top_level_functions(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500580 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400581 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500582 self.assertTrue(getattr(zoo, 'query'))
583 request = zoo.query(q="foo")
Pat Ferated5b61bd2015-03-03 16:04:11 -0800584 parsed = urlparse(request.uri)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500585 q = parse_qs(parsed[4])
586 self.assertEqual(q['q'], ['foo'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400587
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400588 def test_simple_media_uploads(self):
589 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400590 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400591 doc = getattr(zoo.animals().insert, '__doc__')
592 self.assertTrue('media_body' in doc)
593
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400594 def test_simple_media_upload_no_max_size_provided(self):
595 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400596 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400597 request = zoo.animals().crossbreed(media_body=datafile('small.png'))
598 self.assertEquals('image/png', request.headers['content-type'])
599 self.assertEquals('PNG', request.body[1:4])
600
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400601 def test_simple_media_raise_correct_exceptions(self):
602 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400603 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400604
605 try:
606 zoo.animals().insert(media_body=datafile('smiley.png'))
607 self.fail("should throw exception if media is too large.")
608 except MediaUploadSizeError:
609 pass
610
611 try:
612 zoo.animals().insert(media_body=datafile('small.jpg'))
613 self.fail("should throw exception if mimetype is unacceptable.")
614 except UnacceptableMimeTypeError:
615 pass
616
617 def test_simple_media_good_upload(self):
618 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400619 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400620
621 request = zoo.animals().insert(media_body=datafile('small.png'))
622 self.assertEquals('image/png', request.headers['content-type'])
623 self.assertEquals('PNG', request.body[1:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500624 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400625 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500626 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400627
628 def test_multipart_media_raise_correct_exceptions(self):
629 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400630 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400631
632 try:
633 zoo.animals().insert(media_body=datafile('smiley.png'), body={})
634 self.fail("should throw exception if media is too large.")
635 except MediaUploadSizeError:
636 pass
637
638 try:
639 zoo.animals().insert(media_body=datafile('small.jpg'), body={})
640 self.fail("should throw exception if mimetype is unacceptable.")
641 except UnacceptableMimeTypeError:
642 pass
643
644 def test_multipart_media_good_upload(self):
645 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400646 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400647
648 request = zoo.animals().insert(media_body=datafile('small.png'), body={})
Joe Gregorioa98733f2011-09-16 10:12:28 -0400649 self.assertTrue(request.headers['content-type'].startswith(
650 'multipart/related'))
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400651 self.assertEquals('--==', request.body[0:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500652 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400653 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500654 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400655
656 def test_media_capable_method_without_media(self):
657 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400658 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400659
660 request = zoo.animals().insert(body={})
661 self.assertTrue(request.headers['content-type'], 'application/json')
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400662
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500663 def test_resumable_multipart_media_good_upload(self):
664 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400665 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500666
667 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
668 request = zoo.animals().insert(media_body=media_upload, body={})
669 self.assertTrue(request.headers['content-type'].startswith(
Joe Gregorio945be3e2012-01-27 17:01:06 -0500670 'application/json'))
671 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500672 self.assertEquals(media_upload, request.resumable)
673
674 self.assertEquals('image/png', request.resumable.mimetype())
675
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500676 self.assertNotEquals(request.body, None)
677 self.assertEquals(request.resumable_uri, None)
678
679 http = HttpMockSequence([
680 ({'status': '200',
681 'location': 'http://upload.example.com'}, ''),
682 ({'status': '308',
683 'location': 'http://upload.example.com/2',
684 'range': '0-12'}, ''),
685 ({'status': '308',
686 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500687 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500688 ({'status': '200'}, '{"foo": "bar"}'),
689 ])
690
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400691 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500692 self.assertEquals(None, body)
693 self.assertTrue(isinstance(status, MediaUploadProgress))
694 self.assertEquals(13, status.resumable_progress)
695
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500696 # Two requests should have been made and the resumable_uri should have been
697 # updated for each one.
698 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
699
700 self.assertEquals(media_upload, request.resumable)
701 self.assertEquals(13, request.resumable_progress)
702
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400703 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500704 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500705 self.assertEquals(media_upload.size()-1, request.resumable_progress)
706 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500707
708 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400709 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500710 self.assertEquals(body, {"foo": "bar"})
711 self.assertEquals(status, None)
712
713
714 def test_resumable_media_good_upload(self):
715 """Not a multipart upload."""
716 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400717 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500718
719 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
720 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500721 self.assertEquals(media_upload, request.resumable)
722
723 self.assertEquals('image/png', request.resumable.mimetype())
724
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500725 self.assertEquals(request.body, None)
726 self.assertEquals(request.resumable_uri, None)
727
728 http = HttpMockSequence([
729 ({'status': '200',
730 'location': 'http://upload.example.com'}, ''),
731 ({'status': '308',
732 'location': 'http://upload.example.com/2',
733 'range': '0-12'}, ''),
734 ({'status': '308',
735 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500736 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500737 ({'status': '200'}, '{"foo": "bar"}'),
738 ])
739
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400740 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500741 self.assertEquals(None, body)
742 self.assertTrue(isinstance(status, MediaUploadProgress))
743 self.assertEquals(13, status.resumable_progress)
744
745 # Two requests should have been made and the resumable_uri should have been
746 # updated for each one.
747 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
748
749 self.assertEquals(media_upload, request.resumable)
750 self.assertEquals(13, request.resumable_progress)
751
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400752 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500753 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500754 self.assertEquals(media_upload.size()-1, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500755 self.assertEquals(request.body, None)
756
757 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400758 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500759 self.assertEquals(body, {"foo": "bar"})
760 self.assertEquals(status, None)
761
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500762 def test_resumable_media_good_upload_from_execute(self):
763 """Not a multipart upload."""
764 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400765 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500766
767 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
768 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500769 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400770 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500771 request.uri)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500772
773 http = HttpMockSequence([
774 ({'status': '200',
775 'location': 'http://upload.example.com'}, ''),
776 ({'status': '308',
777 'location': 'http://upload.example.com/2',
778 'range': '0-12'}, ''),
779 ({'status': '308',
780 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500781 'range': '0-%d' % media_upload.size()}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500782 ({'status': '200'}, '{"foo": "bar"}'),
783 ])
784
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400785 body = request.execute(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500786 self.assertEquals(body, {"foo": "bar"})
787
788 def test_resumable_media_fail_unknown_response_code_first_request(self):
789 """Not a multipart upload."""
790 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400791 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500792
793 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
794 request = zoo.animals().insert(media_body=media_upload, body=None)
795
796 http = HttpMockSequence([
797 ({'status': '400',
798 'location': 'http://upload.example.com'}, ''),
799 ])
800
Joe Gregoriobaf04802013-03-01 12:27:06 -0500801 try:
802 request.execute(http=http)
803 self.fail('Should have raised ResumableUploadError.')
INADA Naokic1505df2014-08-20 15:19:53 +0900804 except ResumableUploadError as e:
Joe Gregoriobaf04802013-03-01 12:27:06 -0500805 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500806
807 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
808 """Not a multipart upload."""
809 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400810 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500811
812 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
813 request = zoo.animals().insert(media_body=media_upload, body=None)
814
815 http = HttpMockSequence([
816 ({'status': '200',
817 'location': 'http://upload.example.com'}, ''),
818 ({'status': '400'}, ''),
819 ])
820
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400821 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400822 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500823
Joe Gregorio910b9b12012-06-12 09:36:30 -0400824 http = HttpMockSequence([
825 ({'status': '308',
826 'range': '0-5'}, ''),
827 ({'status': '308',
828 'range': '0-6'}, ''),
829 ])
830
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400831 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400832 self.assertEquals(status.resumable_progress, 7,
833 'Should have first checked length and then tried to PUT more.')
834 self.assertFalse(request._in_error_state)
835
836 # Put it back in an error state.
837 http = HttpMockSequence([
838 ({'status': '400'}, ''),
839 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400840 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400841 self.assertTrue(request._in_error_state)
842
843 # Pretend the last request that 400'd actually succeeded.
844 http = HttpMockSequence([
845 ({'status': '200'}, '{"foo": "bar"}'),
846 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400847 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400848 self.assertEqual(body, {'foo': 'bar'})
849
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400850 def test_media_io_base_stream_unlimited_chunksize_resume(self):
851 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
852 zoo = build('zoo', 'v1', http=self.http)
853
Pat Ferateed9affd2015-03-03 16:03:15 -0800854 # Set up a seekable stream and try to upload in single chunk.
855 fd = BytesIO('01234"56789"')
856 media_upload = MediaIoBaseUpload(
857 fd=fd, mimetype='text/plain', chunksize=-1, resumable=True)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400858
Pat Ferateed9affd2015-03-03 16:03:15 -0800859 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400860
Pat Ferateed9affd2015-03-03 16:03:15 -0800861 # The single chunk fails, restart at the right point.
862 http = HttpMockSequence([
863 ({'status': '200',
864 'location': 'http://upload.example.com'}, ''),
865 ({'status': '308',
866 'location': 'http://upload.example.com/2',
867 'range': '0-4'}, ''),
868 ({'status': '200'}, 'echo_request_body'),
869 ])
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400870
Pat Ferateed9affd2015-03-03 16:03:15 -0800871 body = request.execute(http=http)
872 self.assertEqual('56789', body)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400873
874 def test_media_io_base_stream_chunksize_resume(self):
875 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
876 zoo = build('zoo', 'v1', http=self.http)
877
Pat Ferateed9affd2015-03-03 16:03:15 -0800878 # Set up a seekable stream and try to upload in chunks.
879 fd = BytesIO('0123456789')
880 media_upload = MediaIoBaseUpload(
881 fd=fd, mimetype='text/plain', chunksize=5, resumable=True)
882
883 request = zoo.animals().insert(media_body=media_upload, body=None)
884
885 # The single chunk fails, pull the content sent out of the exception.
886 http = HttpMockSequence([
887 ({'status': '200',
888 'location': 'http://upload.example.com'}, ''),
889 ({'status': '400'}, 'echo_request_body'),
890 ])
891
Joe Gregorio5c120db2012-08-23 09:13:55 -0400892 try:
Pat Ferateed9affd2015-03-03 16:03:15 -0800893 body = request.execute(http=http)
894 except HttpError as e:
895 self.assertEqual('01234', e.content)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400896
Joe Gregorio910b9b12012-06-12 09:36:30 -0400897 def test_resumable_media_handle_uploads_of_unknown_size(self):
898 http = HttpMockSequence([
899 ({'status': '200',
900 'location': 'http://upload.example.com'}, ''),
901 ({'status': '200'}, 'echo_request_headers_as_json'),
902 ])
903
904 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400905 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400906
Joe Gregorio910b9b12012-06-12 09:36:30 -0400907 # Create an upload that doesn't know the full size of the media.
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400908 class IoBaseUnknownLength(MediaUpload):
909 def chunksize(self):
910 return 10
911
912 def mimetype(self):
913 return 'image/png'
914
915 def size(self):
916 return None
917
918 def resumable(self):
919 return True
920
921 def getbytes(self, begin, length):
922 return '0123456789'
923
924 upload = IoBaseUnknownLength()
Joe Gregorio910b9b12012-06-12 09:36:30 -0400925
926 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400927 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400928 self.assertEqual(body, {
929 'Content-Range': 'bytes 0-9/*',
930 'Content-Length': '10',
931 })
Joe Gregorio44454e42012-06-15 08:38:53 -0400932
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400933 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
934 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
935 zoo = build('zoo', 'v1', http=self.http)
936
937 class IoBaseHasStream(MediaUpload):
938 def chunksize(self):
939 return 10
940
941 def mimetype(self):
942 return 'image/png'
943
944 def size(self):
945 return None
946
947 def resumable(self):
948 return True
949
950 def getbytes(self, begin, length):
951 return '0123456789'
952
953 def has_stream(self):
954 return True
955
956 def stream(self):
957 raise NotImplementedError()
958
959 upload = IoBaseHasStream()
960
961 orig_version = sys.version_info
962 sys.version_info = (2, 5, 5, 'final', 0)
963
964 request = zoo.animals().insert(media_body=upload, body=None)
965
966 http = HttpMockSequence([
967 ({'status': '200',
968 'location': 'http://upload.example.com'}, ''),
969 ({'status': '200'}, 'echo_request_headers_as_json'),
970 ])
971
972 # This should not raise an exception because stream() shouldn't be called.
973 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400974 self.assertEqual(body, {
975 'Content-Range': 'bytes 0-9/*',
976 'Content-Length': '10'
977 })
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400978
979 sys.version_info = (2, 6, 5, 'final', 0)
980
981 request = zoo.animals().insert(media_body=upload, body=None)
982
983 # This should raise an exception because stream() will be called.
984 http = HttpMockSequence([
985 ({'status': '200',
986 'location': 'http://upload.example.com'}, ''),
987 ({'status': '200'}, 'echo_request_headers_as_json'),
988 ])
989
990 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
991
992 sys.version_info = orig_version
993
Joe Gregorio44454e42012-06-15 08:38:53 -0400994 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
995 http = HttpMockSequence([
996 ({'status': '200',
997 'location': 'http://upload.example.com'}, ''),
998 ({'status': '200'}, 'echo_request_headers_as_json'),
999 ])
1000
1001 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001002 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio44454e42012-06-15 08:38:53 -04001003
Pat Ferateed9affd2015-03-03 16:03:15 -08001004 fd = BytesIO('data goes here')
Joe Gregorio44454e42012-06-15 08:38:53 -04001005
1006 # Create an upload that doesn't know the full size of the media.
1007 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -04001008 fd=fd, mimetype='image/png', chunksize=15, resumable=True)
Joe Gregorio44454e42012-06-15 08:38:53 -04001009
1010 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001011 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001012 self.assertEqual(body, {
1013 'Content-Range': 'bytes 0-13/14',
1014 'Content-Length': '14',
1015 })
Joe Gregorio910b9b12012-06-12 09:36:30 -04001016
1017 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1018 http = HttpMockSequence([
1019 ({'status': '200',
1020 'location': 'http://upload.example.com'}, ''),
1021 ({'status': '400'}, ''),
1022 ])
1023
1024 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001025 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001026
1027 # Create an upload that doesn't know the full size of the media.
Pat Ferateed9affd2015-03-03 16:03:15 -08001028 fd = BytesIO('data goes here')
Joe Gregorio910b9b12012-06-12 09:36:30 -04001029
1030 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -04001031 fd=fd, mimetype='image/png', chunksize=500, resumable=True)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001032
1033 request = zoo.animals().insert(media_body=upload, body=None)
1034
1035 # Put it in an error state.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001036 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001037
1038 http = HttpMockSequence([
1039 ({'status': '400',
1040 'range': '0-5'}, 'echo_request_headers_as_json'),
1041 ])
1042 try:
1043 # Should resume the upload by first querying the status of the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001044 request.next_chunk(http=http)
INADA Naokic1505df2014-08-20 15:19:53 +09001045 except HttpError as e:
Joe Gregorio910b9b12012-06-12 09:36:30 -04001046 expected = {
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001047 'Content-Range': 'bytes */14',
Joe Gregorio910b9b12012-06-12 09:36:30 -04001048 'content-length': '0'
1049 }
Craig Citro6ae34d72014-08-18 23:10:09 -07001050 self.assertEqual(expected, json.loads(e.content),
Joe Gregorio910b9b12012-06-12 09:36:30 -04001051 'Should send an empty body when requesting the current upload status.')
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001052
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001053 def test_pickle(self):
1054 sorted_resource_keys = ['_baseUrl',
1055 '_developerKey',
1056 '_dynamic_attrs',
1057 '_http',
1058 '_model',
1059 '_requestBuilder',
1060 '_resourceDesc',
1061 '_rootDesc',
1062 '_schema',
1063 'animals',
1064 'global_',
1065 'load',
1066 'loadNoTemplate',
1067 'my',
1068 'query',
1069 'scopedAnimals']
1070
1071 http = HttpMock(datafile('zoo.json'), {'status': '200'})
1072 zoo = build('zoo', 'v1', http=http)
1073 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
1074
1075 pickled_zoo = pickle.dumps(zoo)
1076 new_zoo = pickle.loads(pickled_zoo)
1077 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
1078 self.assertTrue(hasattr(new_zoo, 'animals'))
1079 self.assertTrue(callable(new_zoo.animals))
1080 self.assertTrue(hasattr(new_zoo, 'global_'))
1081 self.assertTrue(callable(new_zoo.global_))
1082 self.assertTrue(hasattr(new_zoo, 'load'))
1083 self.assertTrue(callable(new_zoo.load))
1084 self.assertTrue(hasattr(new_zoo, 'loadNoTemplate'))
1085 self.assertTrue(callable(new_zoo.loadNoTemplate))
1086 self.assertTrue(hasattr(new_zoo, 'my'))
1087 self.assertTrue(callable(new_zoo.my))
1088 self.assertTrue(hasattr(new_zoo, 'query'))
1089 self.assertTrue(callable(new_zoo.query))
1090 self.assertTrue(hasattr(new_zoo, 'scopedAnimals'))
1091 self.assertTrue(callable(new_zoo.scopedAnimals))
1092
Joe Gregorio003b6e42013-02-13 15:42:19 -05001093 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001094 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
1095 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
1096 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
1097 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
1098 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
1099 # _http, _model and _schema won't be equal since we will get new
1100 # instances upon un-pickling
1101
1102 def _dummy_zoo_request(self):
1103 with open(os.path.join(DATA_DIR, 'zoo.json'), 'rU') as fh:
1104 zoo_contents = fh.read()
1105
1106 zoo_uri = uritemplate.expand(DISCOVERY_URI,
1107 {'api': 'zoo', 'apiVersion': 'v1'})
1108 if 'REMOTE_ADDR' in os.environ:
Joe Gregorio79daca02013-03-29 16:25:52 -04001109 zoo_uri = util._add_query_parameter(zoo_uri, 'userIp',
1110 os.environ['REMOTE_ADDR'])
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001111
1112 http = httplib2.Http()
1113 original_request = http.request
1114 def wrapped_request(uri, method='GET', *args, **kwargs):
1115 if uri == zoo_uri:
1116 return httplib2.Response({'status': '200'}), zoo_contents
1117 return original_request(uri, method=method, *args, **kwargs)
1118 http.request = wrapped_request
1119 return http
1120
1121 def _dummy_token(self):
1122 access_token = 'foo'
1123 client_id = 'some_client_id'
1124 client_secret = 'cOuDdkfjxxnv+'
1125 refresh_token = '1/0/a.df219fjls0'
1126 token_expiry = datetime.datetime.utcnow()
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001127 user_agent = 'refresh_checker/1.0'
1128 return OAuth2Credentials(
1129 access_token, client_id, client_secret,
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -08001130 refresh_token, token_expiry, GOOGLE_TOKEN_URI,
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001131 user_agent)
1132
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001133 def test_pickle_with_credentials(self):
1134 credentials = self._dummy_token()
1135 http = self._dummy_zoo_request()
1136 http = credentials.authorize(http)
1137 self.assertTrue(hasattr(http.request, 'credentials'))
1138
1139 zoo = build('zoo', 'v1', http=http)
1140 pickled_zoo = pickle.dumps(zoo)
1141 new_zoo = pickle.loads(pickled_zoo)
1142 self.assertEqual(sorted(zoo.__dict__.keys()),
1143 sorted(new_zoo.__dict__.keys()))
1144 new_http = new_zoo._http
1145 self.assertFalse(hasattr(new_http.request, 'credentials'))
1146
Joe Gregorio708388c2012-06-15 13:43:04 -04001147
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001148class Next(unittest.TestCase):
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001149
Joe Gregorio3c676f92011-07-25 10:38:14 -04001150 def test_next_successful_none_on_no_next_page_token(self):
1151 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001152 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001153 request = tasks.tasklists().list()
1154 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
1155
1156 def test_next_successful_with_next_page_token(self):
1157 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001158 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001159 request = tasks.tasklists().list()
Joe Gregorioa98733f2011-09-16 10:12:28 -04001160 next_request = tasks.tasklists().list_next(
1161 request, {'nextPageToken': '123abc'})
Pat Ferated5b61bd2015-03-03 16:04:11 -08001162 parsed = list(urlparse(next_request.uri))
Joe Gregorio3c676f92011-07-25 10:38:14 -04001163 q = parse_qs(parsed[4])
1164 self.assertEqual(q['pageToken'][0], '123abc')
1165
Joe Gregorio555f33c2011-08-19 14:56:07 -04001166 def test_next_with_method_with_no_properties(self):
1167 self.http = HttpMock(datafile('latitude.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001168 service = build('latitude', 'v1', http=self.http)
Joe Gregorio555f33c2011-08-19 14:56:07 -04001169 request = service.currentLocation().get()
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001170
Joe Gregorioa98733f2011-09-16 10:12:28 -04001171
Joe Gregorio708388c2012-06-15 13:43:04 -04001172class MediaGet(unittest.TestCase):
1173
1174 def test_get_media(self):
1175 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001176 zoo = build('zoo', 'v1', http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -04001177 request = zoo.animals().get_media(name='Lion')
1178
Pat Ferated5b61bd2015-03-03 16:04:11 -08001179 parsed = urlparse(request.uri)
Joe Gregorio708388c2012-06-15 13:43:04 -04001180 q = parse_qs(parsed[4])
1181 self.assertEqual(q['alt'], ['media'])
1182 self.assertEqual(request.headers['accept'], '*/*')
1183
1184 http = HttpMockSequence([
1185 ({'status': '200'}, 'standing in for media'),
1186 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -04001187 response = request.execute(http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -04001188 self.assertEqual('standing in for media', response)
1189
1190
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001191if __name__ == '__main__':
1192 unittest.main()