blob: a07e861222a7ab5914a39d96519d313037938635 [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
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070026__author__ = "jcgregorio@google.com (Joe Gregorio)"
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040027
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
Phil Ruffwind26178fc2015-10-13 19:00:33 -040038import re
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040039import sys
Pat Ferate497a90f2015-03-09 09:52:54 -070040import unittest2 as unittest
Joe Gregoriodc106fc2012-11-20 14:30:14 -050041
Takashi Matsuo30125122015-08-19 11:42:32 -070042import mock
43
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080044import google.auth.credentials
45import google_auth_httplib2
John Asmuth864311d2014-04-24 15:46:08 -040046from googleapiclient.discovery import _fix_up_media_upload
47from googleapiclient.discovery import _fix_up_method_description
48from googleapiclient.discovery import _fix_up_parameters
Craig Citro7ee535d2015-02-23 10:11:14 -080049from googleapiclient.discovery import _urljoin
John Asmuth864311d2014-04-24 15:46:08 -040050from googleapiclient.discovery import build
51from googleapiclient.discovery import build_from_document
52from googleapiclient.discovery import DISCOVERY_URI
53from googleapiclient.discovery import key2param
54from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
Brian J. Watson38051ac2016-10-25 07:53:08 -070055from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE
John Asmuth864311d2014-04-24 15:46:08 -040056from googleapiclient.discovery import ResourceMethodParameters
57from googleapiclient.discovery import STACK_QUERY_PARAMETERS
58from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
Takashi Matsuo30125122015-08-19 11:42:32 -070059from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE
60from googleapiclient.discovery_cache.base import Cache
John Asmuth864311d2014-04-24 15:46:08 -040061from googleapiclient.errors import HttpError
62from googleapiclient.errors import InvalidJsonError
63from googleapiclient.errors import MediaUploadSizeError
64from googleapiclient.errors import ResumableUploadError
65from googleapiclient.errors import UnacceptableMimeTypeError
Takashi Matsuo3772f9d2015-09-04 12:25:55 -070066from googleapiclient.errors import UnknownApiNameOrVersion
Brian J. Watson38051ac2016-10-25 07:53:08 -070067from googleapiclient.errors import UnknownFileType
Igor Maravić22435292017-01-19 22:28:22 +010068from googleapiclient.http import build_http
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -040069from googleapiclient.http import BatchHttpRequest
John Asmuth864311d2014-04-24 15:46:08 -040070from googleapiclient.http import HttpMock
71from googleapiclient.http import HttpMockSequence
72from googleapiclient.http import MediaFileUpload
73from googleapiclient.http import MediaIoBaseUpload
74from googleapiclient.http import MediaUpload
75from googleapiclient.http import MediaUploadProgress
76from googleapiclient.http import tunnel_patch
Thomas Coffee20af04d2017-02-10 15:24:44 -080077from googleapiclient.model import JsonModel
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +090078from googleapiclient.schema import Schemas
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080079from oauth2client import GOOGLE_TOKEN_URI
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080080from oauth2client.client import OAuth2Credentials, GoogleCredentials
Joe Gregorio79daca02013-03-29 16:25:52 -040081
Helen Koikede13e3b2018-04-26 16:05:16 -030082from googleapiclient import _helpers as util
Jon Wayne Parrott36d4e1b2016-10-17 13:31:33 -070083
Joe Gregoriodc106fc2012-11-20 14:30:14 -050084import uritemplate
85
Joe Gregoriocb8103d2011-02-11 23:20:52 -050086
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070087DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
Joe Gregoriocb8103d2011-02-11 23:20:52 -050088
Joe Gregorioa98733f2011-09-16 10:12:28 -040089
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050090def assertUrisEqual(testcase, expected, actual):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070091 """Test that URIs are the same, up to reordering of query parameters."""
92 expected = urlparse(expected)
93 actual = urlparse(actual)
94 testcase.assertEqual(expected.scheme, actual.scheme)
95 testcase.assertEqual(expected.netloc, actual.netloc)
96 testcase.assertEqual(expected.path, actual.path)
97 testcase.assertEqual(expected.params, actual.params)
98 testcase.assertEqual(expected.fragment, actual.fragment)
99 expected_query = parse_qs(expected.query)
100 actual_query = parse_qs(actual.query)
101 for name in list(expected_query.keys()):
102 testcase.assertEqual(expected_query[name], actual_query[name])
103 for name in list(actual_query.keys()):
104 testcase.assertEqual(expected_query[name], actual_query[name])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500105
106
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500107def datafile(filename):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700108 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400109
110
Joe Gregorio504a17f2012-12-07 14:14:26 -0500111class SetupHttplib2(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700112 def test_retries(self):
113 # Merely loading googleapiclient.discovery should set the RETRIES to 1.
114 self.assertEqual(1, httplib2.RETRIES)
Joe Gregorio504a17f2012-12-07 14:14:26 -0500115
116
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400117class Utilities(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700118 def setUp(self):
119 with open(datafile("zoo.json"), "r") as fh:
120 self.zoo_root_desc = json.loads(fh.read())
121 self.zoo_get_method_desc = self.zoo_root_desc["methods"]["query"]
122 self.zoo_animals_resource = self.zoo_root_desc["resources"]["animals"]
123 self.zoo_insert_method_desc = self.zoo_animals_resource["methods"]["insert"]
124 self.zoo_schema = Schemas(self.zoo_root_desc)
Daniel Hermesc2113242013-02-27 10:16:13 -0800125
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700126 def test_key2param(self):
127 self.assertEqual("max_results", key2param("max-results"))
128 self.assertEqual("x007_bond", key2param("007-bond"))
Daniel Hermesc2113242013-02-27 10:16:13 -0800129
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700130 def _base_fix_up_parameters_test(self, method_desc, http_method, root_desc, schema):
131 self.assertEqual(method_desc["httpMethod"], http_method)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400132
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700133 method_desc_copy = copy.deepcopy(method_desc)
134 self.assertEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800135
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700136 parameters = _fix_up_parameters(
137 method_desc_copy, root_desc, http_method, schema
138 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800139
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700140 self.assertNotEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800141
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700142 for param_name in STACK_QUERY_PARAMETERS:
143 self.assertEqual(
144 STACK_QUERY_PARAMETER_DEFAULT_VALUE, parameters[param_name]
145 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800146
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700147 for param_name, value in six.iteritems(root_desc.get("parameters", {})):
148 self.assertEqual(value, parameters[param_name])
Daniel Hermesc2113242013-02-27 10:16:13 -0800149
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700150 return parameters
Daniel Hermesc2113242013-02-27 10:16:13 -0800151
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700152 def test_fix_up_parameters_get(self):
153 parameters = self._base_fix_up_parameters_test(
154 self.zoo_get_method_desc, "GET", self.zoo_root_desc, self.zoo_schema
155 )
156 # Since http_method is 'GET'
157 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800158
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700159 def test_fix_up_parameters_insert(self):
160 parameters = self._base_fix_up_parameters_test(
161 self.zoo_insert_method_desc, "POST", self.zoo_root_desc, self.zoo_schema
162 )
163 body = {"description": "The request body.", "type": "object", "$ref": "Animal"}
164 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800165
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700166 def test_fix_up_parameters_check_body(self):
167 dummy_root_desc = {}
168 dummy_schema = {
169 "Request": {
170 "properties": {
171 "description": "Required. Dummy parameter.",
172 "type": "string",
173 }
174 }
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900175 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700176 no_payload_http_method = "DELETE"
177 with_payload_http_method = "PUT"
Daniel Hermesc2113242013-02-27 10:16:13 -0800178
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700179 invalid_method_desc = {"response": "Who cares"}
180 valid_method_desc = {
181 "request": {"key1": "value1", "key2": "value2", "$ref": "Request"}
182 }
Daniel Hermesc2113242013-02-27 10:16:13 -0800183
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700184 parameters = _fix_up_parameters(
185 invalid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
186 )
187 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800188
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700189 parameters = _fix_up_parameters(
190 valid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
191 )
192 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800193
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700194 parameters = _fix_up_parameters(
195 invalid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
196 )
197 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800198
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700199 parameters = _fix_up_parameters(
200 valid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
201 )
202 body = {
203 "description": "The request body.",
204 "type": "object",
205 "$ref": "Request",
206 "key1": "value1",
207 "key2": "value2",
208 }
209 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800210
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700211 def test_fix_up_parameters_optional_body(self):
212 # Request with no parameters
213 dummy_schema = {"Request": {"properties": {}}}
214 method_desc = {"request": {"$ref": "Request"}}
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900215
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700216 parameters = _fix_up_parameters(method_desc, {}, "POST", dummy_schema)
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900217
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700218 def _base_fix_up_method_description_test(
219 self,
220 method_desc,
221 initial_parameters,
222 final_parameters,
223 final_accept,
224 final_max_size,
225 final_media_path_url,
226 ):
227 fake_root_desc = {"rootUrl": "http://root/", "servicePath": "fake/"}
228 fake_path_url = "fake-path/"
Daniel Hermesc2113242013-02-27 10:16:13 -0800229
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700230 accept, max_size, media_path_url = _fix_up_media_upload(
231 method_desc, fake_root_desc, fake_path_url, initial_parameters
232 )
233 self.assertEqual(accept, final_accept)
234 self.assertEqual(max_size, final_max_size)
235 self.assertEqual(media_path_url, final_media_path_url)
236 self.assertEqual(initial_parameters, final_parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800237
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700238 def test_fix_up_media_upload_no_initial_invalid(self):
239 invalid_method_desc = {"response": "Who cares"}
240 self._base_fix_up_method_description_test(
241 invalid_method_desc, {}, {}, [], 0, None
242 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800243
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700244 def test_fix_up_media_upload_no_initial_valid_minimal(self):
245 valid_method_desc = {"mediaUpload": {"accept": []}}
246 final_parameters = {
247 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
248 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
249 }
250 self._base_fix_up_method_description_test(
251 valid_method_desc,
252 {},
253 final_parameters,
254 [],
255 0,
256 "http://root/upload/fake/fake-path/",
257 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800258
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700259 def test_fix_up_media_upload_no_initial_valid_full(self):
260 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
261 final_parameters = {
262 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
263 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
264 }
265 ten_gb = 10 * 2 ** 30
266 self._base_fix_up_method_description_test(
267 valid_method_desc,
268 {},
269 final_parameters,
270 ["*/*"],
271 ten_gb,
272 "http://root/upload/fake/fake-path/",
273 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800274
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700275 def test_fix_up_media_upload_with_initial_invalid(self):
276 invalid_method_desc = {"response": "Who cares"}
277 initial_parameters = {"body": {}}
278 self._base_fix_up_method_description_test(
279 invalid_method_desc, initial_parameters, initial_parameters, [], 0, None
280 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800281
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700282 def test_fix_up_media_upload_with_initial_valid_minimal(self):
283 valid_method_desc = {"mediaUpload": {"accept": []}}
284 initial_parameters = {"body": {}}
285 final_parameters = {
286 "body": {},
287 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
288 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
289 }
290 self._base_fix_up_method_description_test(
291 valid_method_desc,
292 initial_parameters,
293 final_parameters,
294 [],
295 0,
296 "http://root/upload/fake/fake-path/",
297 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800298
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700299 def test_fix_up_media_upload_with_initial_valid_full(self):
300 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
301 initial_parameters = {"body": {}}
302 final_parameters = {
303 "body": {},
304 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
305 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
306 }
307 ten_gb = 10 * 2 ** 30
308 self._base_fix_up_method_description_test(
309 valid_method_desc,
310 initial_parameters,
311 final_parameters,
312 ["*/*"],
313 ten_gb,
314 "http://root/upload/fake/fake-path/",
315 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800316
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700317 def test_fix_up_method_description_get(self):
318 result = _fix_up_method_description(
319 self.zoo_get_method_desc, self.zoo_root_desc, self.zoo_schema
320 )
321 path_url = "query"
322 http_method = "GET"
323 method_id = "bigquery.query"
324 accept = []
325 max_size = 0
326 media_path_url = None
327 self.assertEqual(
328 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
329 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800330
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700331 def test_fix_up_method_description_insert(self):
332 result = _fix_up_method_description(
333 self.zoo_insert_method_desc, self.zoo_root_desc, self.zoo_schema
334 )
335 path_url = "animals"
336 http_method = "POST"
337 method_id = "zoo.animals.insert"
338 accept = ["image/png"]
339 max_size = 1024
340 media_path_url = "https://www.googleapis.com/upload/zoo/v1/animals"
341 self.assertEqual(
342 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
343 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800344
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700345 def test_urljoin(self):
346 # We want to exhaustively test various URL combinations.
347 simple_bases = ["https://www.googleapis.com", "https://www.googleapis.com/"]
348 long_urls = ["foo/v1/bar:custom?alt=json", "/foo/v1/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800349
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700350 long_bases = [
351 "https://www.googleapis.com/foo/v1",
352 "https://www.googleapis.com/foo/v1/",
353 ]
354 simple_urls = ["bar:custom?alt=json", "/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800355
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700356 final_url = "https://www.googleapis.com/foo/v1/bar:custom?alt=json"
357 for base, url in itertools.product(simple_bases, long_urls):
358 self.assertEqual(final_url, _urljoin(base, url))
359 for base, url in itertools.product(long_bases, simple_urls):
360 self.assertEqual(final_url, _urljoin(base, url))
Craig Citro7ee535d2015-02-23 10:11:14 -0800361
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700362 def test_ResourceMethodParameters_zoo_get(self):
363 parameters = ResourceMethodParameters(self.zoo_get_method_desc)
Craig Citro7ee535d2015-02-23 10:11:14 -0800364
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700365 param_types = {
366 "a": "any",
367 "b": "boolean",
368 "e": "string",
369 "er": "string",
370 "i": "integer",
371 "n": "number",
372 "o": "object",
373 "q": "string",
374 "rr": "string",
375 }
376 keys = list(param_types.keys())
377 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
378 self.assertEqual(parameters.required_params, [])
379 self.assertEqual(sorted(parameters.repeated_params), ["er", "rr"])
380 self.assertEqual(parameters.pattern_params, {"rr": "[a-z]+"})
381 self.assertEqual(
382 sorted(parameters.query_params),
383 ["a", "b", "e", "er", "i", "n", "o", "q", "rr"],
384 )
385 self.assertEqual(parameters.path_params, set())
386 self.assertEqual(parameters.param_types, param_types)
387 enum_params = {"e": ["foo", "bar"], "er": ["one", "two", "three"]}
388 self.assertEqual(parameters.enum_params, enum_params)
Daniel Hermes954e1242013-02-28 09:28:37 -0800389
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700390 def test_ResourceMethodParameters_zoo_animals_patch(self):
391 method_desc = self.zoo_animals_resource["methods"]["patch"]
392 parameters = ResourceMethodParameters(method_desc)
Daniel Hermes954e1242013-02-28 09:28:37 -0800393
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700394 param_types = {"name": "string"}
395 keys = list(param_types.keys())
396 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
397 self.assertEqual(parameters.required_params, ["name"])
398 self.assertEqual(parameters.repeated_params, [])
399 self.assertEqual(parameters.pattern_params, {})
400 self.assertEqual(parameters.query_params, [])
401 self.assertEqual(parameters.path_params, set(["name"]))
402 self.assertEqual(parameters.param_types, param_types)
403 self.assertEqual(parameters.enum_params, {})
Daniel Hermes954e1242013-02-28 09:28:37 -0800404
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400405
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500406class DiscoveryErrors(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700407 def test_tests_should_be_run_with_strict_positional_enforcement(self):
408 try:
409 plus = build("plus", "v1", None)
410 self.fail("should have raised a TypeError exception over missing http=.")
411 except TypeError:
412 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500413
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700414 def test_failed_to_parse_discovery_json(self):
415 self.http = HttpMock(datafile("malformed.json"), {"status": "200"})
416 try:
417 plus = build("plus", "v1", http=self.http, cache_discovery=False)
418 self.fail("should have raised an exception over malformed JSON.")
419 except InvalidJsonError:
420 pass
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400421
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700422 def test_unknown_api_name_or_version(self):
423 http = HttpMockSequence(
424 [
425 ({"status": "404"}, open(datafile("zoo.json"), "rb").read()),
426 ({"status": "404"}, open(datafile("zoo.json"), "rb").read()),
427 ]
428 )
429 with self.assertRaises(UnknownApiNameOrVersion):
430 plus = build("plus", "v1", http=http, cache_discovery=False)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500431
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700432 def test_credentials_and_http_mutually_exclusive(self):
433 http = HttpMock(datafile("plus.json"), {"status": "200"})
434 with self.assertRaises(ValueError):
435 build("plus", "v1", http=http, credentials=mock.sentinel.credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800436
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500437
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100438class DiscoveryFromDocument(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700439 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400440
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700441 def test_can_build_from_local_document(self):
442 discovery = open(datafile("plus.json")).read()
443 plus = build_from_document(
444 discovery,
445 base="https://www.googleapis.com/",
446 credentials=self.MOCK_CREDENTIALS,
447 )
448 self.assertTrue(plus is not None)
449 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500450
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700451 def test_can_build_from_local_deserialized_document(self):
452 discovery = open(datafile("plus.json")).read()
453 discovery = json.loads(discovery)
454 plus = build_from_document(
455 discovery,
456 base="https://www.googleapis.com/",
457 credentials=self.MOCK_CREDENTIALS,
458 )
459 self.assertTrue(plus is not None)
460 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400461
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700462 def test_building_with_base_remembers_base(self):
463 discovery = open(datafile("plus.json")).read()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400464
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700465 base = "https://www.example.com/"
466 plus = build_from_document(
467 discovery, base=base, credentials=self.MOCK_CREDENTIALS
468 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700469 self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100470
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700471 def test_building_with_optional_http_with_authorization(self):
472 discovery = open(datafile("plus.json")).read()
473 plus = build_from_document(
474 discovery,
475 base="https://www.googleapis.com/",
476 credentials=self.MOCK_CREDENTIALS,
477 )
Igor Maravić22435292017-01-19 22:28:22 +0100478
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700479 # plus service requires Authorization, hence we expect to see AuthorizedHttp object here
480 self.assertIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
481 self.assertIsInstance(plus._http.http, httplib2.Http)
482 self.assertIsInstance(plus._http.http.timeout, int)
483 self.assertGreater(plus._http.http.timeout, 0)
Igor Maravić22435292017-01-19 22:28:22 +0100484
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700485 def test_building_with_optional_http_with_no_authorization(self):
486 discovery = open(datafile("plus.json")).read()
487 # Cleanup auth field, so we would use plain http client
488 discovery = json.loads(discovery)
489 discovery["auth"] = {}
490 discovery = json.dumps(discovery)
Igor Maravić22435292017-01-19 22:28:22 +0100491
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700492 plus = build_from_document(
493 discovery, base="https://www.googleapis.com/", credentials=None
494 )
495 # plus service requires Authorization
496 self.assertIsInstance(plus._http, httplib2.Http)
497 self.assertIsInstance(plus._http.timeout, int)
498 self.assertGreater(plus._http.timeout, 0)
Jonathan Wayne Parrotta6e6fbd2015-07-16 15:33:57 -0700499
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700500 def test_building_with_explicit_http(self):
501 http = HttpMock()
502 discovery = open(datafile("plus.json")).read()
503 plus = build_from_document(
504 discovery, base="https://www.googleapis.com/", http=http
505 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700506 self.assertEqual(plus._http, http)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100507
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700508 def test_building_with_developer_key_skips_adc(self):
509 discovery = open(datafile("plus.json")).read()
510 plus = build_from_document(
511 discovery, base="https://www.googleapis.com/", developerKey="123"
512 )
513 self.assertIsInstance(plus._http, httplib2.Http)
514 # It should not be an AuthorizedHttp, because that would indicate that
515 # application default credentials were used.
516 self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800517
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700518 def test_api_endpoint_override_from_client_options(self):
519 discovery = open(datafile("plus.json")).read()
520 api_endpoint = "https://foo.googleapis.com/"
521 options = google.api_core.client_options.ClientOptions(
522 api_endpoint=api_endpoint
523 )
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700524 plus = build_from_document(
525 discovery,
526 client_options=options,
527 credentials=self.MOCK_CREDENTIALS
528 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700529
530 self.assertEqual(plus._baseUrl, api_endpoint)
531
532 def test_api_endpoint_override_from_client_options_dict(self):
533 discovery = open(datafile("plus.json")).read()
534 api_endpoint = "https://foo.googleapis.com/"
535 plus = build_from_document(
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700536 discovery,
537 client_options={"api_endpoint": api_endpoint},
538 credentials=self.MOCK_CREDENTIALS
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700539 )
540
541 self.assertEqual(plus._baseUrl, api_endpoint)
542
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800543
Joe Gregorioa98733f2011-09-16 10:12:28 -0400544class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700545 def setUp(self):
546 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400547
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700548 def tearDown(self):
549 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400550
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700551 def test_userip_is_added_to_discovery_uri(self):
552 # build() will raise an HttpError on a 400, use this to pick the request uri
553 # out of the raised exception.
554 os.environ["REMOTE_ADDR"] = "10.0.0.1"
555 try:
556 http = HttpMockSequence(
557 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
558 )
559 zoo = build(
560 "zoo",
561 "v1",
562 http=http,
563 developerKey=None,
564 discoveryServiceUrl="http://example.com",
565 )
566 self.fail("Should have raised an exception.")
567 except HttpError as e:
568 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400569
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700570 def test_userip_missing_is_not_added_to_discovery_uri(self):
571 # build() will raise an HttpError on a 400, use this to pick the request uri
572 # out of the raised exception.
573 try:
574 http = HttpMockSequence(
575 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
576 )
577 zoo = build(
578 "zoo",
579 "v1",
580 http=http,
581 developerKey=None,
582 discoveryServiceUrl="http://example.com",
583 )
584 self.fail("Should have raised an exception.")
585 except HttpError as e:
586 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400587
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700588 def test_key_is_added_to_discovery_uri(self):
589 # build() will raise an HttpError on a 400, use this to pick the request uri
590 # out of the raised exception.
591 try:
592 http = HttpMockSequence(
593 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
594 )
595 zoo = build(
596 "zoo",
597 "v1",
598 http=http,
599 developerKey="foo",
600 discoveryServiceUrl="http://example.com",
601 )
602 self.fail("Should have raised an exception.")
603 except HttpError as e:
604 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800605
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700606 def test_discovery_loading_from_v2_discovery_uri(self):
607 http = HttpMockSequence(
608 [
609 ({"status": "404"}, "Not found"),
610 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
611 ]
612 )
613 zoo = build("zoo", "v1", http=http, cache_discovery=False)
614 self.assertTrue(hasattr(zoo, "animals"))
615
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700616 def test_api_endpoint_override_from_client_options(self):
617 http = HttpMockSequence(
618 [
619 ({"status": "404"}, "Not found"),
620 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
621 ]
622 )
623 api_endpoint = "https://foo.googleapis.com/"
624 options = google.api_core.client_options.ClientOptions(
625 api_endpoint=api_endpoint
626 )
627 zoo = build(
628 "zoo", "v1", http=http, cache_discovery=False, client_options=options
629 )
630 self.assertEqual(zoo._baseUrl, api_endpoint)
631
632 def test_api_endpoint_override_from_client_options_dict(self):
633 http = HttpMockSequence(
634 [
635 ({"status": "404"}, "Not found"),
636 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
637 ]
638 )
639 api_endpoint = "https://foo.googleapis.com/"
640 zoo = build(
641 "zoo",
642 "v1",
643 http=http,
644 cache_discovery=False,
645 client_options={"api_endpoint": api_endpoint},
646 )
647 self.assertEqual(zoo._baseUrl, api_endpoint)
648
Joe Gregorioa98733f2011-09-16 10:12:28 -0400649
Takashi Matsuo30125122015-08-19 11:42:32 -0700650class DiscoveryFromAppEngineCache(unittest.TestCase):
Zev Goldstein09e64472020-05-14 16:29:20 -0400651
652 def setUp(self):
653 self.old_environ = os.environ.copy()
654 os.environ["APPENGINE_RUNTIME"] = "python27"
655
656 def tearDown(self):
657 os.environ = self.old_environ
658
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700659 def test_appengine_memcache(self):
660 # Hack module import
661 self.orig_import = __import__
662 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -0700663
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700664 def import_mock(name, *args, **kwargs):
665 if name == "google.appengine.api":
666 return self.mocked_api
667 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -0700668
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700669 import_fullname = "__builtin__.__import__"
670 if sys.version_info[0] >= 3:
671 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -0700672
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700673 with mock.patch(import_fullname, side_effect=import_mock):
674 namespace = "google-api-client"
675 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700676
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700677 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -0700678
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700679 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700680
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700681 # memcache.get is called once
682 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
683 self.mocked_api.memcache.get.assert_called_once_with(
684 url, namespace=namespace
685 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700686
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700687 # memcache.set is called once
688 with open(datafile("plus.json")) as f:
689 content = f.read()
690 self.mocked_api.memcache.set.assert_called_once_with(
691 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
692 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700693
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700694 # Returns the cached content this time.
695 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -0700696
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700697 # Make sure the contents are returned from the cache.
698 # (Otherwise it should through an error)
699 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700700
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700701 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700702
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700703 # memcache.get is called twice
704 self.mocked_api.memcache.get.assert_has_calls(
705 [
706 mock.call(url, namespace=namespace),
707 mock.call(url, namespace=namespace),
708 ]
709 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700710
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700711 # memcahce.set is called just once
712 self.mocked_api.memcache.set.assert_called_once_with(
713 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
714 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700715
716
717class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700718 def __init__(self):
719 self.d = {}
720
721 def get(self, url):
722 return self.d.get(url, None)
723
724 def set(self, url, content):
725 self.d[url] = content
726
727 def contains(self, url):
728 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -0700729
730
731class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700732 def test_file_based_cache(self):
733 cache = mock.Mock(wraps=DictCache())
734 with mock.patch(
735 "googleapiclient.discovery_cache.autodetect", return_value=cache
736 ):
737 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700738
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700739 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700740
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700741 # cache.get is called once
742 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
743 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -0700744
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700745 # cache.set is called once
746 with open(datafile("plus.json")) as f:
747 content = f.read()
748 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -0700749
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700750 # Make sure there is a cache entry for the plus v1 discovery doc.
751 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -0700752
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700753 # Make sure the contents are returned from the cache.
754 # (Otherwise it should through an error)
755 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700756
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700757 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700758
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700759 # cache.get is called twice
760 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -0700761
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700762 # cahce.set is called just once
763 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -0700764
765
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400766class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700767 def test_method_error_checking(self):
768 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
769 plus = build("plus", "v1", http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400770
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700771 # Missing required parameters
772 try:
773 plus.activities().list()
774 self.fail()
775 except TypeError as e:
776 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400777
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700778 # Missing required parameters even if supplied as None.
779 try:
780 plus.activities().list(collection=None, userId=None)
781 self.fail()
782 except TypeError as e:
783 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400784
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700785 # Parameter doesn't match regex
786 try:
787 plus.activities().list(collection="not_a_collection_name", userId="me")
788 self.fail()
789 except TypeError as e:
790 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -0400791
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700792 # Unexpected parameter
793 try:
794 plus.activities().list(flubber=12)
795 self.fail()
796 except TypeError as e:
797 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400798
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700799 def _check_query_types(self, request):
800 parsed = urlparse(request.uri)
801 q = parse_qs(parsed[4])
802 self.assertEqual(q["q"], ["foo"])
803 self.assertEqual(q["i"], ["1"])
804 self.assertEqual(q["n"], ["1.0"])
805 self.assertEqual(q["b"], ["false"])
806 self.assertEqual(q["a"], ["[1, 2, 3]"])
807 self.assertEqual(q["o"], ["{'a': 1}"])
808 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400809
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700810 def test_type_coercion(self):
811 http = HttpMock(datafile("zoo.json"), {"status": "200"})
812 zoo = build("zoo", "v1", http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500813
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700814 request = zoo.query(
815 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
816 )
817 self._check_query_types(request)
818 request = zoo.query(
819 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
820 )
821 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -0500822
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700823 request = zoo.query(
824 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
825 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500826
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700827 request = zoo.query(
828 q="foo",
829 i="1",
830 n="1",
831 b="",
832 a=[1, 2, 3],
833 o={"a": 1},
834 e="bar",
835 er=["one", "three"],
836 rr=["foo", "bar"],
837 )
838 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -0500839
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700840 # Five is right out.
841 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -0500842
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700843 def test_optional_stack_query_parameters(self):
844 http = HttpMock(datafile("zoo.json"), {"status": "200"})
845 zoo = build("zoo", "v1", http=http)
846 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -0800847
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700848 parsed = urlparse(request.uri)
849 q = parse_qs(parsed[4])
850 self.assertEqual(q["trace"], ["html"])
851 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -0500852
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700853 def test_string_params_value_of_none_get_dropped(self):
854 http = HttpMock(datafile("zoo.json"), {"status": "200"})
855 zoo = build("zoo", "v1", http=http)
856 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -0400857
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700858 parsed = urlparse(request.uri)
859 q = parse_qs(parsed[4])
860 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400861
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700862 def test_model_added_query_parameters(self):
863 http = HttpMock(datafile("zoo.json"), {"status": "200"})
864 zoo = build("zoo", "v1", http=http)
865 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400866
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700867 parsed = urlparse(request.uri)
868 q = parse_qs(parsed[4])
869 self.assertEqual(q["alt"], ["json"])
870 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500871
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700872 def test_fallback_to_raw_model(self):
873 http = HttpMock(datafile("zoo.json"), {"status": "200"})
874 zoo = build("zoo", "v1", http=http)
875 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500876
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700877 parsed = urlparse(request.uri)
878 q = parse_qs(parsed[4])
879 self.assertTrue("alt" not in q)
880 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500881
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700882 def test_patch(self):
883 http = HttpMock(datafile("zoo.json"), {"status": "200"})
884 zoo = build("zoo", "v1", http=http)
885 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -0500886
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700887 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -0400888
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700889 def test_batch_request_from_discovery(self):
890 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
891 # zoo defines a batchPath
892 zoo = build("zoo", "v1", http=self.http)
893 batch_request = zoo.new_batch_http_request()
894 self.assertEqual(
895 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
896 )
Joe Gregoriof4153422011-03-18 22:45:18 -0400897
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700898 def test_batch_request_from_default(self):
899 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
900 # plus does not define a batchPath
901 plus = build("plus", "v1", http=self.http)
902 batch_request = plus.new_batch_http_request()
903 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -0400904
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700905 def test_tunnel_patch(self):
906 http = HttpMockSequence(
907 [
908 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
909 ({"status": "200"}, "echo_request_headers_as_json"),
910 ]
911 )
912 http = tunnel_patch(http)
913 zoo = build("zoo", "v1", http=http, cache_discovery=False)
914 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -0400915
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700916 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -0400917
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700918 def test_plus_resources(self):
919 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
920 plus = build("plus", "v1", http=self.http)
921 self.assertTrue(getattr(plus, "activities"))
922 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -0500923
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700924 def test_oauth2client_credentials(self):
925 credentials = mock.Mock(spec=GoogleCredentials)
926 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400927
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700928 discovery = open(datafile("plus.json")).read()
929 service = build_from_document(discovery, credentials=credentials)
930 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -0700931
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700932 def test_google_auth_credentials(self):
933 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
934 discovery = open(datafile("plus.json")).read()
935 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -0700936
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700937 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
938 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800939
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700940 def test_no_scopes_no_credentials(self):
941 # Zoo doesn't have scopes
942 discovery = open(datafile("zoo.json")).read()
943 service = build_from_document(discovery)
944 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
945 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800946
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700947 def test_full_featured(self):
948 # Zoo should exercise all discovery facets
949 # and should also have no future.json file.
950 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
951 zoo = build("zoo", "v1", http=self.http)
952 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -0700953
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700954 request = zoo.animals().list(name="bat", projection="full")
955 parsed = urlparse(request.uri)
956 q = parse_qs(parsed[4])
957 self.assertEqual(q["name"], ["bat"])
958 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500959
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700960 def test_nested_resources(self):
961 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
962 zoo = build("zoo", "v1", http=self.http)
963 self.assertTrue(getattr(zoo, "animals"))
964 request = zoo.my().favorites().list(max_results="5")
965 parsed = urlparse(request.uri)
966 q = parse_qs(parsed[4])
967 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400968
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700969 @unittest.skipIf(six.PY3, "print is not a reserved name in Python 3")
970 def test_methods_with_reserved_names(self):
971 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
972 zoo = build("zoo", "v1", http=self.http)
973 self.assertTrue(getattr(zoo, "animals"))
974 request = zoo.global_().print_().assert_(max_results="5")
975 parsed = urlparse(request.uri)
976 self.assertEqual(parsed[2], "/zoo/v1/global/print/assert")
Joe Gregorio3fada332011-01-07 17:07:45 -0500977
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700978 def test_top_level_functions(self):
979 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
980 zoo = build("zoo", "v1", http=self.http)
981 self.assertTrue(getattr(zoo, "query"))
982 request = zoo.query(q="foo")
983 parsed = urlparse(request.uri)
984 q = parse_qs(parsed[4])
985 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -0400986
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700987 def test_simple_media_uploads(self):
988 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
989 zoo = build("zoo", "v1", http=self.http)
990 doc = getattr(zoo.animals().insert, "__doc__")
991 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400992
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700993 def test_simple_media_upload_no_max_size_provided(self):
994 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
995 zoo = build("zoo", "v1", http=self.http)
996 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700997 self.assertEqual("image/png", request.headers["content-type"])
998 self.assertEqual(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400999
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001000 def test_simple_media_raise_correct_exceptions(self):
1001 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1002 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -04001003
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001004 try:
1005 zoo.animals().insert(media_body=datafile("smiley.png"))
1006 self.fail("should throw exception if media is too large.")
1007 except MediaUploadSizeError:
1008 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001009
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001010 try:
1011 zoo.animals().insert(media_body=datafile("small.jpg"))
1012 self.fail("should throw exception if mimetype is unacceptable.")
1013 except UnacceptableMimeTypeError:
1014 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001015
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001016 def test_simple_media_good_upload(self):
1017 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1018 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001019
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001020 request = zoo.animals().insert(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001021 self.assertEqual("image/png", request.headers["content-type"])
1022 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001023 assertUrisEqual(
1024 self,
1025 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1026 request.uri,
1027 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001028
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001029 def test_simple_media_unknown_mimetype(self):
1030 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1031 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001032
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001033 try:
1034 zoo.animals().insert(media_body=datafile("small-png"))
1035 self.fail("should throw exception if mimetype is unknown.")
1036 except UnknownFileType:
1037 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -07001038
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001039 request = zoo.animals().insert(
1040 media_body=datafile("small-png"), media_mime_type="image/png"
1041 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001042 self.assertEqual("image/png", request.headers["content-type"])
1043 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001044 assertUrisEqual(
1045 self,
1046 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1047 request.uri,
1048 )
Brian J. Watson38051ac2016-10-25 07:53:08 -07001049
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001050 def test_multipart_media_raise_correct_exceptions(self):
1051 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1052 zoo = build("zoo", "v1", http=self.http)
Brian J. Watson38051ac2016-10-25 07:53:08 -07001053
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001054 try:
1055 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
1056 self.fail("should throw exception if media is too large.")
1057 except MediaUploadSizeError:
1058 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001059
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001060 try:
1061 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
1062 self.fail("should throw exception if mimetype is unacceptable.")
1063 except UnacceptableMimeTypeError:
1064 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001065
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001066 def test_multipart_media_good_upload(self):
1067 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1068 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001069
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001070 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1071 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
1072 with open(datafile("small.png"), "rb") as f:
1073 contents = f.read()
1074 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1075 self.assertEqual(
1076 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1077 b"--==============="
1078 + boundary
1079 + b"==\n"
1080 + b"Content-Type: application/json\n"
1081 + b"MIME-Version: 1.0\n\n"
1082 + b'{"data": {}}\n'
1083 + b"--==============="
1084 + boundary
1085 + b"==\n"
1086 + b"Content-Type: image/png\n"
1087 + b"MIME-Version: 1.0\n"
1088 + b"Content-Transfer-Encoding: binary\n\n"
1089 + contents
1090 + b"\n--==============="
1091 + boundary
1092 + b"==--",
1093 )
1094 assertUrisEqual(
1095 self,
1096 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1097 request.uri,
1098 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001099
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001100 def test_media_capable_method_without_media(self):
1101 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1102 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001103
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001104 request = zoo.animals().insert(body={})
1105 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001106
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001107 def test_resumable_multipart_media_good_upload(self):
1108 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1109 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001110
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001111 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1112 request = zoo.animals().insert(media_body=media_upload, body={})
1113 self.assertTrue(request.headers["content-type"].startswith("application/json"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001114 self.assertEqual('{"data": {}}', request.body)
1115 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001116
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001117 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001118
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001119 self.assertNotEquals(request.body, None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001120 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001121
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001122 http = HttpMockSequence(
1123 [
1124 ({"status": "200", "location": "http://upload.example.com"}, ""),
1125 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1126 (
1127 {
1128 "status": "308",
1129 "location": "http://upload.example.com/3",
1130 "range": "0-12",
1131 },
1132 "",
1133 ),
1134 (
1135 {
1136 "status": "308",
1137 "location": "http://upload.example.com/4",
1138 "range": "0-%d" % (media_upload.size() - 2),
1139 },
1140 "",
1141 ),
1142 ({"status": "200"}, '{"foo": "bar"}'),
1143 ]
1144 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001145
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001146 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001147 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001148 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001149 self.assertEqual(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001150
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001151 # Two requests should have been made and the resumable_uri should have been
1152 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001153 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
1154 self.assertEqual(media_upload, request.resumable)
1155 self.assertEqual(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001156
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001157 # This next chuck call should upload the first chunk
1158 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001159 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1160 self.assertEqual(media_upload, request.resumable)
1161 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001162
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001163 # This call will upload the next chunk
1164 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001165 self.assertEqual(request.resumable_uri, "http://upload.example.com/4")
1166 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1167 self.assertEqual('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001168
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001169 # Final call to next_chunk should complete the upload.
1170 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001171 self.assertEqual(body, {"foo": "bar"})
1172 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001173
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001174 def test_resumable_media_good_upload(self):
1175 """Not a multipart upload."""
1176 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1177 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001178
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001179 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1180 request = zoo.animals().insert(media_body=media_upload, body=None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001181 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001182
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001183 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001184
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001185 self.assertEqual(request.body, None)
1186 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001187
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001188 http = HttpMockSequence(
1189 [
1190 ({"status": "200", "location": "http://upload.example.com"}, ""),
1191 (
1192 {
1193 "status": "308",
1194 "location": "http://upload.example.com/2",
1195 "range": "0-12",
1196 },
1197 "",
1198 ),
1199 (
1200 {
1201 "status": "308",
1202 "location": "http://upload.example.com/3",
1203 "range": "0-%d" % (media_upload.size() - 2),
1204 },
1205 "",
1206 ),
1207 ({"status": "200"}, '{"foo": "bar"}'),
1208 ]
1209 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001210
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001211 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001212 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001213 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001214 self.assertEqual(13, status.resumable_progress)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001215
1216 # Two requests should have been made and the resumable_uri should have been
1217 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001218 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001219
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001220 self.assertEqual(media_upload, request.resumable)
1221 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001222
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001223 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001224 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1225 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1226 self.assertEqual(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001227
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001228 # Final call to next_chunk should complete the upload.
1229 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001230 self.assertEqual(body, {"foo": "bar"})
1231 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001232
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001233 def test_resumable_media_good_upload_from_execute(self):
1234 """Not a multipart upload."""
1235 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1236 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001237
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001238 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1239 request = zoo.animals().insert(media_body=media_upload, body=None)
1240 assertUrisEqual(
1241 self,
1242 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1243 request.uri,
1244 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001245
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001246 http = HttpMockSequence(
1247 [
1248 ({"status": "200", "location": "http://upload.example.com"}, ""),
1249 (
1250 {
1251 "status": "308",
1252 "location": "http://upload.example.com/2",
1253 "range": "0-12",
1254 },
1255 "",
1256 ),
1257 (
1258 {
1259 "status": "308",
1260 "location": "http://upload.example.com/3",
1261 "range": "0-%d" % media_upload.size(),
1262 },
1263 "",
1264 ),
1265 ({"status": "200"}, '{"foo": "bar"}'),
1266 ]
1267 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001268
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001269 body = request.execute(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001270 self.assertEqual(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001271
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001272 def test_resumable_media_fail_unknown_response_code_first_request(self):
1273 """Not a multipart upload."""
1274 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1275 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001276
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001277 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1278 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001279
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001280 http = HttpMockSequence(
1281 [({"status": "400", "location": "http://upload.example.com"}, "")]
1282 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001283
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001284 try:
1285 request.execute(http=http)
1286 self.fail("Should have raised ResumableUploadError.")
1287 except ResumableUploadError as e:
1288 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001289
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001290 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1291 """Not a multipart upload."""
1292 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1293 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001294
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001295 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1296 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001297
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001298 http = HttpMockSequence(
1299 [
1300 ({"status": "200", "location": "http://upload.example.com"}, ""),
1301 ({"status": "400"}, ""),
1302 ]
1303 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001304
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001305 self.assertRaises(HttpError, request.execute, http=http)
1306 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001307
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001308 http = HttpMockSequence(
1309 [
1310 ({"status": "308", "range": "0-5"}, ""),
1311 ({"status": "308", "range": "0-6"}, ""),
1312 ]
1313 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001314
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001315 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001316 self.assertEqual(
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001317 status.resumable_progress,
1318 7,
1319 "Should have first checked length and then tried to PUT more.",
1320 )
1321 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001322
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001323 # Put it back in an error state.
1324 http = HttpMockSequence([({"status": "400"}, "")])
1325 self.assertRaises(HttpError, request.execute, http=http)
1326 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001327
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001328 # Pretend the last request that 400'd actually succeeded.
1329 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1330 status, body = request.next_chunk(http=http)
1331 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001332
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001333 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1334 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1335 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001336
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001337 # Set up a seekable stream and try to upload in single chunk.
1338 fd = BytesIO(b'01234"56789"')
1339 media_upload = MediaIoBaseUpload(
1340 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1341 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001342
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001343 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001344
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001345 # The single chunk fails, restart at the right point.
1346 http = HttpMockSequence(
1347 [
1348 ({"status": "200", "location": "http://upload.example.com"}, ""),
1349 (
1350 {
1351 "status": "308",
1352 "location": "http://upload.example.com/2",
1353 "range": "0-4",
1354 },
1355 "",
1356 ),
1357 ({"status": "200"}, "echo_request_body"),
1358 ]
1359 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001360
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001361 body = request.execute(http=http)
1362 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001363
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001364 def test_media_io_base_stream_chunksize_resume(self):
1365 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1366 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001367
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001368 # Set up a seekable stream and try to upload in chunks.
1369 fd = BytesIO(b"0123456789")
1370 media_upload = MediaIoBaseUpload(
1371 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1372 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001373
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001374 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001375
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001376 # The single chunk fails, pull the content sent out of the exception.
1377 http = HttpMockSequence(
1378 [
1379 ({"status": "200", "location": "http://upload.example.com"}, ""),
1380 ({"status": "400"}, "echo_request_body"),
1381 ]
1382 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001383
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001384 try:
1385 body = request.execute(http=http)
1386 except HttpError as e:
1387 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001388
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001389 def test_resumable_media_handle_uploads_of_unknown_size(self):
1390 http = HttpMockSequence(
1391 [
1392 ({"status": "200", "location": "http://upload.example.com"}, ""),
1393 ({"status": "200"}, "echo_request_headers_as_json"),
1394 ]
1395 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001396
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001397 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1398 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001399
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001400 # Create an upload that doesn't know the full size of the media.
1401 class IoBaseUnknownLength(MediaUpload):
1402 def chunksize(self):
1403 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001404
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001405 def mimetype(self):
1406 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001407
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001408 def size(self):
1409 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001410
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001411 def resumable(self):
1412 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001413
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001414 def getbytes(self, begin, length):
1415 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001416
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001417 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001418
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001419 request = zoo.animals().insert(media_body=upload, body=None)
1420 status, body = request.next_chunk(http=http)
1421 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001422
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001423 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1424 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1425 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001426
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001427 class IoBaseHasStream(MediaUpload):
1428 def chunksize(self):
1429 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001430
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001431 def mimetype(self):
1432 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001433
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001434 def size(self):
1435 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001436
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001437 def resumable(self):
1438 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001439
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001440 def getbytes(self, begin, length):
1441 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001442
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001443 def has_stream(self):
1444 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001445
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001446 def stream(self):
1447 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001448
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001449 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001450
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001451 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001452
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001453 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001454
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001455 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001456
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001457 # This should raise an exception because stream() will be called.
1458 http = HttpMockSequence(
1459 [
1460 ({"status": "200", "location": "http://upload.example.com"}, ""),
1461 ({"status": "200"}, "echo_request_headers_as_json"),
1462 ]
1463 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001464
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001465 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001466
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001467 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001468
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001469 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1470 http = HttpMockSequence(
1471 [
1472 ({"status": "200", "location": "http://upload.example.com"}, ""),
1473 ({"status": "200"}, "echo_request_headers_as_json"),
1474 ]
1475 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001476
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001477 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1478 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001479
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001480 fd = BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001481
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001482 # Create an upload that doesn't know the full size of the media.
1483 upload = MediaIoBaseUpload(
1484 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1485 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001486
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001487 request = zoo.animals().insert(media_body=upload, body=None)
1488 status, body = request.next_chunk(http=http)
1489 self.assertEqual(
1490 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1491 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001492
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001493 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1494 http = HttpMockSequence(
1495 [
1496 ({"status": "200", "location": "http://upload.example.com"}, ""),
1497 ({"status": "400"}, ""),
1498 ]
1499 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001500
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001501 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1502 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001503
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001504 # Create an upload that doesn't know the full size of the media.
1505 fd = BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001506
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001507 upload = MediaIoBaseUpload(
1508 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1509 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001510
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001511 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001512
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001513 # Put it in an error state.
1514 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001515
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001516 http = HttpMockSequence(
1517 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1518 )
1519 try:
1520 # Should resume the upload by first querying the status of the upload.
1521 request.next_chunk(http=http)
1522 except HttpError as e:
1523 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1524 self.assertEqual(
1525 expected,
1526 json.loads(e.content.decode("utf-8")),
1527 "Should send an empty body when requesting the current upload status.",
1528 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001529
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001530 def test_pickle(self):
1531 sorted_resource_keys = [
1532 "_baseUrl",
1533 "_developerKey",
1534 "_dynamic_attrs",
1535 "_http",
1536 "_model",
1537 "_requestBuilder",
1538 "_resourceDesc",
1539 "_rootDesc",
1540 "_schema",
1541 "animals",
1542 "global_",
1543 "load",
1544 "loadNoTemplate",
1545 "my",
1546 "new_batch_http_request",
1547 "query",
1548 "scopedAnimals",
1549 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04001550
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001551 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1552 zoo = build("zoo", "v1", http=http)
1553 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001554
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001555 pickled_zoo = pickle.dumps(zoo)
1556 new_zoo = pickle.loads(pickled_zoo)
1557 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
1558 self.assertTrue(hasattr(new_zoo, "animals"))
1559 self.assertTrue(callable(new_zoo.animals))
1560 self.assertTrue(hasattr(new_zoo, "global_"))
1561 self.assertTrue(callable(new_zoo.global_))
1562 self.assertTrue(hasattr(new_zoo, "load"))
1563 self.assertTrue(callable(new_zoo.load))
1564 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
1565 self.assertTrue(callable(new_zoo.loadNoTemplate))
1566 self.assertTrue(hasattr(new_zoo, "my"))
1567 self.assertTrue(callable(new_zoo.my))
1568 self.assertTrue(hasattr(new_zoo, "query"))
1569 self.assertTrue(callable(new_zoo.query))
1570 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
1571 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001572
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001573 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
1574 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
1575 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
1576 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
1577 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
1578 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
1579 # _http, _model and _schema won't be equal since we will get new
1580 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001581
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001582 def _dummy_zoo_request(self):
1583 with open(os.path.join(DATA_DIR, "zoo.json"), "rU") as fh:
1584 zoo_contents = fh.read()
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001585
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001586 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
1587 if "REMOTE_ADDR" in os.environ:
1588 zoo_uri = util._add_query_parameter(
1589 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
1590 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001591
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001592 http = build_http()
1593 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001594
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001595 def wrapped_request(uri, method="GET", *args, **kwargs):
1596 if uri == zoo_uri:
1597 return httplib2.Response({"status": "200"}), zoo_contents
1598 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001599
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001600 http.request = wrapped_request
1601 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001602
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001603 def _dummy_token(self):
1604 access_token = "foo"
1605 client_id = "some_client_id"
1606 client_secret = "cOuDdkfjxxnv+"
1607 refresh_token = "1/0/a.df219fjls0"
1608 token_expiry = datetime.datetime.utcnow()
1609 user_agent = "refresh_checker/1.0"
1610 return OAuth2Credentials(
1611 access_token,
1612 client_id,
1613 client_secret,
1614 refresh_token,
1615 token_expiry,
1616 GOOGLE_TOKEN_URI,
1617 user_agent,
1618 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001619
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001620 def test_pickle_with_credentials(self):
1621 credentials = self._dummy_token()
1622 http = self._dummy_zoo_request()
1623 http = credentials.authorize(http)
1624 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001625
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001626 zoo = build("zoo", "v1", http=http)
1627 pickled_zoo = pickle.dumps(zoo)
1628 new_zoo = pickle.loads(pickled_zoo)
1629 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
1630 new_http = new_zoo._http
1631 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001632
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001633 def test_resumable_media_upload_no_content(self):
1634 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1635 zoo = build("zoo", "v1", http=self.http)
andrewnestera4a44cf2017-03-31 16:09:31 +03001636
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001637 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
1638 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03001639
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001640 self.assertEqual(media_upload, request.resumable)
1641 self.assertEqual(request.body, None)
1642 self.assertEqual(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03001643
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001644 http = HttpMockSequence(
1645 [
1646 ({"status": "200", "location": "http://upload.example.com"}, ""),
1647 (
1648 {
1649 "status": "308",
1650 "location": "http://upload.example.com/2",
1651 "range": "0-0",
1652 },
1653 "",
1654 ),
1655 ]
1656 )
andrewnestera4a44cf2017-03-31 16:09:31 +03001657
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001658 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001659 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001660 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001661 self.assertEqual(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03001662
Joe Gregorio708388c2012-06-15 13:43:04 -04001663
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001664class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001665 def test_next_successful_none_on_no_next_page_token(self):
1666 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1667 tasks = build("tasks", "v1", http=self.http)
1668 request = tasks.tasklists().list()
1669 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001670
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001671 def test_next_successful_none_on_empty_page_token(self):
1672 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1673 tasks = build("tasks", "v1", http=self.http)
1674 request = tasks.tasklists().list()
1675 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
1676 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001677
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001678 def test_next_successful_with_next_page_token(self):
1679 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1680 tasks = build("tasks", "v1", http=self.http)
1681 request = tasks.tasklists().list()
1682 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
1683 parsed = list(urlparse(next_request.uri))
1684 q = parse_qs(parsed[4])
1685 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00001686
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001687 def test_next_successful_with_next_page_token_alternate_name(self):
1688 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
1689 bigquery = build("bigquery", "v2", http=self.http)
1690 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
1691 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
1692 parsed = list(urlparse(next_request.uri))
1693 q = parse_qs(parsed[4])
1694 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04001695
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001696 def test_next_successful_with_next_page_token_in_body(self):
1697 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
1698 logging = build("logging", "v2", http=self.http)
1699 request = logging.entries().list(body={})
1700 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
1701 body = JsonModel().deserialize(next_request.body)
1702 self.assertEqual(body["pageToken"], "123abc")
Thomas Coffee20af04d2017-02-10 15:24:44 -08001703
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001704 def test_next_with_method_with_no_properties(self):
1705 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
1706 service = build("latitude", "v1", http=self.http)
1707 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08001708
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001709 def test_next_nonexistent_with_no_next_page_token(self):
1710 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
1711 drive = build("drive", "v3", http=self.http)
1712 drive.changes().watch(body={})
1713 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08001714
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001715 def test_next_successful_with_next_page_token_required(self):
1716 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
1717 drive = build("drive", "v3", http=self.http)
1718 request = drive.changes().list(pageToken="startPageToken")
1719 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
1720 parsed = list(urlparse(next_request.uri))
1721 q = parse_qs(parsed[4])
1722 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001723
Joe Gregorioa98733f2011-09-16 10:12:28 -04001724
Joe Gregorio708388c2012-06-15 13:43:04 -04001725class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001726 def test_get_media(self):
1727 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1728 zoo = build("zoo", "v1", http=http)
1729 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04001730
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001731 parsed = urlparse(request.uri)
1732 q = parse_qs(parsed[4])
1733 self.assertEqual(q["alt"], ["media"])
1734 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04001735
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001736 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
1737 response = request.execute(http=http)
1738 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04001739
1740
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001741if __name__ == "__main__":
1742 unittest.main()