blob: f85035ef424d3d4015a468d9913be4946f0144a7 [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 )
469 self.assertEquals("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 )
506 self.assertEquals(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
518
Joe Gregorioa98733f2011-09-16 10:12:28 -0400519class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700520 def setUp(self):
521 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400522
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700523 def tearDown(self):
524 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400525
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700526 def test_userip_is_added_to_discovery_uri(self):
527 # build() will raise an HttpError on a 400, use this to pick the request uri
528 # out of the raised exception.
529 os.environ["REMOTE_ADDR"] = "10.0.0.1"
530 try:
531 http = HttpMockSequence(
532 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
533 )
534 zoo = build(
535 "zoo",
536 "v1",
537 http=http,
538 developerKey=None,
539 discoveryServiceUrl="http://example.com",
540 )
541 self.fail("Should have raised an exception.")
542 except HttpError as e:
543 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400544
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700545 def test_userip_missing_is_not_added_to_discovery_uri(self):
546 # build() will raise an HttpError on a 400, use this to pick the request uri
547 # out of the raised exception.
548 try:
549 http = HttpMockSequence(
550 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
551 )
552 zoo = build(
553 "zoo",
554 "v1",
555 http=http,
556 developerKey=None,
557 discoveryServiceUrl="http://example.com",
558 )
559 self.fail("Should have raised an exception.")
560 except HttpError as e:
561 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400562
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700563 def test_key_is_added_to_discovery_uri(self):
564 # build() will raise an HttpError on a 400, use this to pick the request uri
565 # out of the raised exception.
566 try:
567 http = HttpMockSequence(
568 [({"status": "400"}, open(datafile("zoo.json"), "rb").read())]
569 )
570 zoo = build(
571 "zoo",
572 "v1",
573 http=http,
574 developerKey="foo",
575 discoveryServiceUrl="http://example.com",
576 )
577 self.fail("Should have raised an exception.")
578 except HttpError as e:
579 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800580
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700581 def test_discovery_loading_from_v2_discovery_uri(self):
582 http = HttpMockSequence(
583 [
584 ({"status": "404"}, "Not found"),
585 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
586 ]
587 )
588 zoo = build("zoo", "v1", http=http, cache_discovery=False)
589 self.assertTrue(hasattr(zoo, "animals"))
590
Joe Gregorioa98733f2011-09-16 10:12:28 -0400591
Takashi Matsuo30125122015-08-19 11:42:32 -0700592class DiscoveryFromAppEngineCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700593 def test_appengine_memcache(self):
594 # Hack module import
595 self.orig_import = __import__
596 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -0700597
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700598 def import_mock(name, *args, **kwargs):
599 if name == "google.appengine.api":
600 return self.mocked_api
601 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -0700602
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700603 import_fullname = "__builtin__.__import__"
604 if sys.version_info[0] >= 3:
605 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -0700606
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700607 with mock.patch(import_fullname, side_effect=import_mock):
608 namespace = "google-api-client"
609 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700610
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700611 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -0700612
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700613 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700614
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700615 # memcache.get is called once
616 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
617 self.mocked_api.memcache.get.assert_called_once_with(
618 url, namespace=namespace
619 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700620
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700621 # memcache.set is called once
622 with open(datafile("plus.json")) as f:
623 content = f.read()
624 self.mocked_api.memcache.set.assert_called_once_with(
625 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
626 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700627
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700628 # Returns the cached content this time.
629 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -0700630
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700631 # Make sure the contents are returned from the cache.
632 # (Otherwise it should through an error)
633 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700634
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700635 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700636
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700637 # memcache.get is called twice
638 self.mocked_api.memcache.get.assert_has_calls(
639 [
640 mock.call(url, namespace=namespace),
641 mock.call(url, namespace=namespace),
642 ]
643 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700644
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700645 # memcahce.set is called just once
646 self.mocked_api.memcache.set.assert_called_once_with(
647 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
648 )
Takashi Matsuo30125122015-08-19 11:42:32 -0700649
650
651class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700652 def __init__(self):
653 self.d = {}
654
655 def get(self, url):
656 return self.d.get(url, None)
657
658 def set(self, url, content):
659 self.d[url] = content
660
661 def contains(self, url):
662 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -0700663
664
665class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700666 def test_file_based_cache(self):
667 cache = mock.Mock(wraps=DictCache())
668 with mock.patch(
669 "googleapiclient.discovery_cache.autodetect", return_value=cache
670 ):
671 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700672
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700673 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700674
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700675 # cache.get is called once
676 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
677 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -0700678
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700679 # cache.set is called once
680 with open(datafile("plus.json")) as f:
681 content = f.read()
682 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -0700683
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700684 # Make sure there is a cache entry for the plus v1 discovery doc.
685 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -0700686
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700687 # Make sure the contents are returned from the cache.
688 # (Otherwise it should through an error)
689 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -0700690
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700691 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -0700692
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700693 # cache.get is called twice
694 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -0700695
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700696 # cahce.set is called just once
697 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -0700698
699
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400700class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700701 def test_method_error_checking(self):
702 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
703 plus = build("plus", "v1", http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400704
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700705 # Missing required parameters
706 try:
707 plus.activities().list()
708 self.fail()
709 except TypeError as e:
710 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400711
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700712 # Missing required parameters even if supplied as None.
713 try:
714 plus.activities().list(collection=None, userId=None)
715 self.fail()
716 except TypeError as e:
717 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400718
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700719 # Parameter doesn't match regex
720 try:
721 plus.activities().list(collection="not_a_collection_name", userId="me")
722 self.fail()
723 except TypeError as e:
724 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -0400725
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700726 # Unexpected parameter
727 try:
728 plus.activities().list(flubber=12)
729 self.fail()
730 except TypeError as e:
731 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400732
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700733 def _check_query_types(self, request):
734 parsed = urlparse(request.uri)
735 q = parse_qs(parsed[4])
736 self.assertEqual(q["q"], ["foo"])
737 self.assertEqual(q["i"], ["1"])
738 self.assertEqual(q["n"], ["1.0"])
739 self.assertEqual(q["b"], ["false"])
740 self.assertEqual(q["a"], ["[1, 2, 3]"])
741 self.assertEqual(q["o"], ["{'a': 1}"])
742 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400743
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700744 def test_type_coercion(self):
745 http = HttpMock(datafile("zoo.json"), {"status": "200"})
746 zoo = build("zoo", "v1", http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500747
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700748 request = zoo.query(
749 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
750 )
751 self._check_query_types(request)
752 request = zoo.query(
753 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
754 )
755 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -0500756
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700757 request = zoo.query(
758 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
759 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500760
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700761 request = zoo.query(
762 q="foo",
763 i="1",
764 n="1",
765 b="",
766 a=[1, 2, 3],
767 o={"a": 1},
768 e="bar",
769 er=["one", "three"],
770 rr=["foo", "bar"],
771 )
772 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -0500773
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700774 # Five is right out.
775 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -0500776
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700777 def test_optional_stack_query_parameters(self):
778 http = HttpMock(datafile("zoo.json"), {"status": "200"})
779 zoo = build("zoo", "v1", http=http)
780 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -0800781
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700782 parsed = urlparse(request.uri)
783 q = parse_qs(parsed[4])
784 self.assertEqual(q["trace"], ["html"])
785 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -0500786
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700787 def test_string_params_value_of_none_get_dropped(self):
788 http = HttpMock(datafile("zoo.json"), {"status": "200"})
789 zoo = build("zoo", "v1", http=http)
790 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -0400791
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700792 parsed = urlparse(request.uri)
793 q = parse_qs(parsed[4])
794 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400795
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700796 def test_model_added_query_parameters(self):
797 http = HttpMock(datafile("zoo.json"), {"status": "200"})
798 zoo = build("zoo", "v1", http=http)
799 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400800
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700801 parsed = urlparse(request.uri)
802 q = parse_qs(parsed[4])
803 self.assertEqual(q["alt"], ["json"])
804 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500805
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700806 def test_fallback_to_raw_model(self):
807 http = HttpMock(datafile("zoo.json"), {"status": "200"})
808 zoo = build("zoo", "v1", http=http)
809 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500810
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700811 parsed = urlparse(request.uri)
812 q = parse_qs(parsed[4])
813 self.assertTrue("alt" not in q)
814 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -0500815
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700816 def test_patch(self):
817 http = HttpMock(datafile("zoo.json"), {"status": "200"})
818 zoo = build("zoo", "v1", http=http)
819 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -0500820
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700821 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -0400822
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700823 def test_batch_request_from_discovery(self):
824 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
825 # zoo defines a batchPath
826 zoo = build("zoo", "v1", http=self.http)
827 batch_request = zoo.new_batch_http_request()
828 self.assertEqual(
829 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
830 )
Joe Gregoriof4153422011-03-18 22:45:18 -0400831
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700832 def test_batch_request_from_default(self):
833 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
834 # plus does not define a batchPath
835 plus = build("plus", "v1", http=self.http)
836 batch_request = plus.new_batch_http_request()
837 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -0400838
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700839 def test_tunnel_patch(self):
840 http = HttpMockSequence(
841 [
842 ({"status": "200"}, open(datafile("zoo.json"), "rb").read()),
843 ({"status": "200"}, "echo_request_headers_as_json"),
844 ]
845 )
846 http = tunnel_patch(http)
847 zoo = build("zoo", "v1", http=http, cache_discovery=False)
848 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -0400849
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700850 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -0400851
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700852 def test_plus_resources(self):
853 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
854 plus = build("plus", "v1", http=self.http)
855 self.assertTrue(getattr(plus, "activities"))
856 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -0500857
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700858 def test_oauth2client_credentials(self):
859 credentials = mock.Mock(spec=GoogleCredentials)
860 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400861
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700862 discovery = open(datafile("plus.json")).read()
863 service = build_from_document(discovery, credentials=credentials)
864 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -0700865
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700866 def test_google_auth_credentials(self):
867 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
868 discovery = open(datafile("plus.json")).read()
869 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -0700870
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700871 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
872 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800873
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700874 def test_no_scopes_no_credentials(self):
875 # Zoo doesn't have scopes
876 discovery = open(datafile("zoo.json")).read()
877 service = build_from_document(discovery)
878 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
879 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800880
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700881 def test_full_featured(self):
882 # Zoo should exercise all discovery facets
883 # and should also have no future.json file.
884 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
885 zoo = build("zoo", "v1", http=self.http)
886 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -0700887
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700888 request = zoo.animals().list(name="bat", projection="full")
889 parsed = urlparse(request.uri)
890 q = parse_qs(parsed[4])
891 self.assertEqual(q["name"], ["bat"])
892 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500893
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700894 def test_nested_resources(self):
895 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
896 zoo = build("zoo", "v1", http=self.http)
897 self.assertTrue(getattr(zoo, "animals"))
898 request = zoo.my().favorites().list(max_results="5")
899 parsed = urlparse(request.uri)
900 q = parse_qs(parsed[4])
901 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400902
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700903 @unittest.skipIf(six.PY3, "print is not a reserved name in Python 3")
904 def test_methods_with_reserved_names(self):
905 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
906 zoo = build("zoo", "v1", http=self.http)
907 self.assertTrue(getattr(zoo, "animals"))
908 request = zoo.global_().print_().assert_(max_results="5")
909 parsed = urlparse(request.uri)
910 self.assertEqual(parsed[2], "/zoo/v1/global/print/assert")
Joe Gregorio3fada332011-01-07 17:07:45 -0500911
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700912 def test_top_level_functions(self):
913 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
914 zoo = build("zoo", "v1", http=self.http)
915 self.assertTrue(getattr(zoo, "query"))
916 request = zoo.query(q="foo")
917 parsed = urlparse(request.uri)
918 q = parse_qs(parsed[4])
919 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -0400920
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700921 def test_simple_media_uploads(self):
922 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
923 zoo = build("zoo", "v1", http=self.http)
924 doc = getattr(zoo.animals().insert, "__doc__")
925 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400926
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700927 def test_simple_media_upload_no_max_size_provided(self):
928 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
929 zoo = build("zoo", "v1", http=self.http)
930 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
931 self.assertEquals("image/png", request.headers["content-type"])
932 self.assertEquals(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400933
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700934 def test_simple_media_raise_correct_exceptions(self):
935 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
936 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400937
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700938 try:
939 zoo.animals().insert(media_body=datafile("smiley.png"))
940 self.fail("should throw exception if media is too large.")
941 except MediaUploadSizeError:
942 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400943
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700944 try:
945 zoo.animals().insert(media_body=datafile("small.jpg"))
946 self.fail("should throw exception if mimetype is unacceptable.")
947 except UnacceptableMimeTypeError:
948 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400949
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700950 def test_simple_media_good_upload(self):
951 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
952 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400953
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700954 request = zoo.animals().insert(media_body=datafile("small.png"))
955 self.assertEquals("image/png", request.headers["content-type"])
956 self.assertEquals(b"PNG", request.body[1:4])
957 assertUrisEqual(
958 self,
959 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
960 request.uri,
961 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400962
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700963 def test_simple_media_unknown_mimetype(self):
964 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
965 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400966
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700967 try:
968 zoo.animals().insert(media_body=datafile("small-png"))
969 self.fail("should throw exception if mimetype is unknown.")
970 except UnknownFileType:
971 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -0700972
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700973 request = zoo.animals().insert(
974 media_body=datafile("small-png"), media_mime_type="image/png"
975 )
976 self.assertEquals("image/png", request.headers["content-type"])
977 self.assertEquals(b"PNG", request.body[1:4])
978 assertUrisEqual(
979 self,
980 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
981 request.uri,
982 )
Brian J. Watson38051ac2016-10-25 07:53:08 -0700983
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700984 def test_multipart_media_raise_correct_exceptions(self):
985 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
986 zoo = build("zoo", "v1", http=self.http)
Brian J. Watson38051ac2016-10-25 07:53:08 -0700987
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700988 try:
989 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
990 self.fail("should throw exception if media is too large.")
991 except MediaUploadSizeError:
992 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400993
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700994 try:
995 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
996 self.fail("should throw exception if mimetype is unacceptable.")
997 except UnacceptableMimeTypeError:
998 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400999
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001000 def test_multipart_media_good_upload(self):
1001 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1002 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001003
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001004 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1005 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
1006 with open(datafile("small.png"), "rb") as f:
1007 contents = f.read()
1008 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1009 self.assertEqual(
1010 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1011 b"--==============="
1012 + boundary
1013 + b"==\n"
1014 + b"Content-Type: application/json\n"
1015 + b"MIME-Version: 1.0\n\n"
1016 + b'{"data": {}}\n'
1017 + b"--==============="
1018 + boundary
1019 + b"==\n"
1020 + b"Content-Type: image/png\n"
1021 + b"MIME-Version: 1.0\n"
1022 + b"Content-Transfer-Encoding: binary\n\n"
1023 + contents
1024 + b"\n--==============="
1025 + boundary
1026 + b"==--",
1027 )
1028 assertUrisEqual(
1029 self,
1030 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1031 request.uri,
1032 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001033
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001034 def test_media_capable_method_without_media(self):
1035 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1036 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001037
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001038 request = zoo.animals().insert(body={})
1039 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001040
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001041 def test_resumable_multipart_media_good_upload(self):
1042 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1043 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001044
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001045 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1046 request = zoo.animals().insert(media_body=media_upload, body={})
1047 self.assertTrue(request.headers["content-type"].startswith("application/json"))
1048 self.assertEquals('{"data": {}}', request.body)
1049 self.assertEquals(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001050
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001051 self.assertEquals("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001052
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001053 self.assertNotEquals(request.body, None)
1054 self.assertEquals(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001055
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001056 http = HttpMockSequence(
1057 [
1058 ({"status": "200", "location": "http://upload.example.com"}, ""),
1059 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1060 (
1061 {
1062 "status": "308",
1063 "location": "http://upload.example.com/3",
1064 "range": "0-12",
1065 },
1066 "",
1067 ),
1068 (
1069 {
1070 "status": "308",
1071 "location": "http://upload.example.com/4",
1072 "range": "0-%d" % (media_upload.size() - 2),
1073 },
1074 "",
1075 ),
1076 ({"status": "200"}, '{"foo": "bar"}'),
1077 ]
1078 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001079
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001080 status, body = request.next_chunk(http=http)
1081 self.assertEquals(None, body)
1082 self.assertTrue(isinstance(status, MediaUploadProgress))
1083 self.assertEquals(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001084
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001085 # Two requests should have been made and the resumable_uri should have been
1086 # updated for each one.
1087 self.assertEquals(request.resumable_uri, "http://upload.example.com/2")
1088 self.assertEquals(media_upload, request.resumable)
1089 self.assertEquals(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001090
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001091 # This next chuck call should upload the first chunk
1092 status, body = request.next_chunk(http=http)
1093 self.assertEquals(request.resumable_uri, "http://upload.example.com/3")
1094 self.assertEquals(media_upload, request.resumable)
1095 self.assertEquals(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001096
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001097 # This call will upload the next chunk
1098 status, body = request.next_chunk(http=http)
1099 self.assertEquals(request.resumable_uri, "http://upload.example.com/4")
1100 self.assertEquals(media_upload.size() - 1, request.resumable_progress)
1101 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001102
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001103 # Final call to next_chunk should complete the upload.
1104 status, body = request.next_chunk(http=http)
1105 self.assertEquals(body, {"foo": "bar"})
1106 self.assertEquals(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001107
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001108 def test_resumable_media_good_upload(self):
1109 """Not a multipart upload."""
1110 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1111 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001112
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001113 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1114 request = zoo.animals().insert(media_body=media_upload, body=None)
1115 self.assertEquals(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001116
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001117 self.assertEquals("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001118
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001119 self.assertEquals(request.body, None)
1120 self.assertEquals(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 (
1126 {
1127 "status": "308",
1128 "location": "http://upload.example.com/2",
1129 "range": "0-12",
1130 },
1131 "",
1132 ),
1133 (
1134 {
1135 "status": "308",
1136 "location": "http://upload.example.com/3",
1137 "range": "0-%d" % (media_upload.size() - 2),
1138 },
1139 "",
1140 ),
1141 ({"status": "200"}, '{"foo": "bar"}'),
1142 ]
1143 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001144
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001145 status, body = request.next_chunk(http=http)
1146 self.assertEquals(None, body)
1147 self.assertTrue(isinstance(status, MediaUploadProgress))
1148 self.assertEquals(13, status.resumable_progress)
1149
1150 # Two requests should have been made and the resumable_uri should have been
1151 # updated for each one.
1152 self.assertEquals(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001153
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001154 self.assertEquals(media_upload, request.resumable)
1155 self.assertEquals(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001156
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001157 status, body = request.next_chunk(http=http)
1158 self.assertEquals(request.resumable_uri, "http://upload.example.com/3")
1159 self.assertEquals(media_upload.size() - 1, request.resumable_progress)
1160 self.assertEquals(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001161
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001162 # Final call to next_chunk should complete the upload.
1163 status, body = request.next_chunk(http=http)
1164 self.assertEquals(body, {"foo": "bar"})
1165 self.assertEquals(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001166
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001167 def test_resumable_media_good_upload_from_execute(self):
1168 """Not a multipart upload."""
1169 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1170 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001171
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001172 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1173 request = zoo.animals().insert(media_body=media_upload, body=None)
1174 assertUrisEqual(
1175 self,
1176 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1177 request.uri,
1178 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001179
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001180 http = HttpMockSequence(
1181 [
1182 ({"status": "200", "location": "http://upload.example.com"}, ""),
1183 (
1184 {
1185 "status": "308",
1186 "location": "http://upload.example.com/2",
1187 "range": "0-12",
1188 },
1189 "",
1190 ),
1191 (
1192 {
1193 "status": "308",
1194 "location": "http://upload.example.com/3",
1195 "range": "0-%d" % media_upload.size(),
1196 },
1197 "",
1198 ),
1199 ({"status": "200"}, '{"foo": "bar"}'),
1200 ]
1201 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001202
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001203 body = request.execute(http=http)
1204 self.assertEquals(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001205
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001206 def test_resumable_media_fail_unknown_response_code_first_request(self):
1207 """Not a multipart upload."""
1208 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1209 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001210
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001211 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1212 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001213
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001214 http = HttpMockSequence(
1215 [({"status": "400", "location": "http://upload.example.com"}, "")]
1216 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001217
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001218 try:
1219 request.execute(http=http)
1220 self.fail("Should have raised ResumableUploadError.")
1221 except ResumableUploadError as e:
1222 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001223
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001224 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1225 """Not a multipart upload."""
1226 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1227 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001228
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001229 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1230 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001231
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001232 http = HttpMockSequence(
1233 [
1234 ({"status": "200", "location": "http://upload.example.com"}, ""),
1235 ({"status": "400"}, ""),
1236 ]
1237 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001238
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001239 self.assertRaises(HttpError, request.execute, http=http)
1240 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001241
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001242 http = HttpMockSequence(
1243 [
1244 ({"status": "308", "range": "0-5"}, ""),
1245 ({"status": "308", "range": "0-6"}, ""),
1246 ]
1247 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001248
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001249 status, body = request.next_chunk(http=http)
1250 self.assertEquals(
1251 status.resumable_progress,
1252 7,
1253 "Should have first checked length and then tried to PUT more.",
1254 )
1255 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001256
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001257 # Put it back in an error state.
1258 http = HttpMockSequence([({"status": "400"}, "")])
1259 self.assertRaises(HttpError, request.execute, http=http)
1260 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001261
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001262 # Pretend the last request that 400'd actually succeeded.
1263 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1264 status, body = request.next_chunk(http=http)
1265 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001266
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001267 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1268 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1269 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001270
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001271 # Set up a seekable stream and try to upload in single chunk.
1272 fd = BytesIO(b'01234"56789"')
1273 media_upload = MediaIoBaseUpload(
1274 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1275 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001276
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001277 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001278
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001279 # The single chunk fails, restart at the right point.
1280 http = HttpMockSequence(
1281 [
1282 ({"status": "200", "location": "http://upload.example.com"}, ""),
1283 (
1284 {
1285 "status": "308",
1286 "location": "http://upload.example.com/2",
1287 "range": "0-4",
1288 },
1289 "",
1290 ),
1291 ({"status": "200"}, "echo_request_body"),
1292 ]
1293 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001294
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001295 body = request.execute(http=http)
1296 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001297
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001298 def test_media_io_base_stream_chunksize_resume(self):
1299 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1300 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001301
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001302 # Set up a seekable stream and try to upload in chunks.
1303 fd = BytesIO(b"0123456789")
1304 media_upload = MediaIoBaseUpload(
1305 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1306 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001307
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001308 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001309
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001310 # The single chunk fails, pull the content sent out of the exception.
1311 http = HttpMockSequence(
1312 [
1313 ({"status": "200", "location": "http://upload.example.com"}, ""),
1314 ({"status": "400"}, "echo_request_body"),
1315 ]
1316 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001317
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001318 try:
1319 body = request.execute(http=http)
1320 except HttpError as e:
1321 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001322
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001323 def test_resumable_media_handle_uploads_of_unknown_size(self):
1324 http = HttpMockSequence(
1325 [
1326 ({"status": "200", "location": "http://upload.example.com"}, ""),
1327 ({"status": "200"}, "echo_request_headers_as_json"),
1328 ]
1329 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001330
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001331 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1332 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001333
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001334 # Create an upload that doesn't know the full size of the media.
1335 class IoBaseUnknownLength(MediaUpload):
1336 def chunksize(self):
1337 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001338
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001339 def mimetype(self):
1340 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001341
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001342 def size(self):
1343 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001344
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001345 def resumable(self):
1346 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001347
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001348 def getbytes(self, begin, length):
1349 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001350
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001351 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001352
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001353 request = zoo.animals().insert(media_body=upload, body=None)
1354 status, body = request.next_chunk(http=http)
1355 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001356
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001357 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1358 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1359 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001360
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001361 class IoBaseHasStream(MediaUpload):
1362 def chunksize(self):
1363 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001364
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001365 def mimetype(self):
1366 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001367
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001368 def size(self):
1369 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001370
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001371 def resumable(self):
1372 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001373
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001374 def getbytes(self, begin, length):
1375 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001376
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001377 def has_stream(self):
1378 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001379
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001380 def stream(self):
1381 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001382
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001383 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001384
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001385 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001386
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001387 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001388
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001389 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001390
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001391 # This should raise an exception because stream() will be called.
1392 http = HttpMockSequence(
1393 [
1394 ({"status": "200", "location": "http://upload.example.com"}, ""),
1395 ({"status": "200"}, "echo_request_headers_as_json"),
1396 ]
1397 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001398
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001399 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001400
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001401 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001402
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001403 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1404 http = HttpMockSequence(
1405 [
1406 ({"status": "200", "location": "http://upload.example.com"}, ""),
1407 ({"status": "200"}, "echo_request_headers_as_json"),
1408 ]
1409 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001410
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001411 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1412 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001413
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001414 fd = BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001415
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001416 # Create an upload that doesn't know the full size of the media.
1417 upload = MediaIoBaseUpload(
1418 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1419 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001420
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001421 request = zoo.animals().insert(media_body=upload, body=None)
1422 status, body = request.next_chunk(http=http)
1423 self.assertEqual(
1424 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1425 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001426
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001427 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1428 http = HttpMockSequence(
1429 [
1430 ({"status": "200", "location": "http://upload.example.com"}, ""),
1431 ({"status": "400"}, ""),
1432 ]
1433 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001434
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001435 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1436 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001437
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001438 # Create an upload that doesn't know the full size of the media.
1439 fd = BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001440
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001441 upload = MediaIoBaseUpload(
1442 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1443 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001444
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001445 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001446
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001447 # Put it in an error state.
1448 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001449
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001450 http = HttpMockSequence(
1451 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1452 )
1453 try:
1454 # Should resume the upload by first querying the status of the upload.
1455 request.next_chunk(http=http)
1456 except HttpError as e:
1457 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1458 self.assertEqual(
1459 expected,
1460 json.loads(e.content.decode("utf-8")),
1461 "Should send an empty body when requesting the current upload status.",
1462 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001463
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001464 def test_pickle(self):
1465 sorted_resource_keys = [
1466 "_baseUrl",
1467 "_developerKey",
1468 "_dynamic_attrs",
1469 "_http",
1470 "_model",
1471 "_requestBuilder",
1472 "_resourceDesc",
1473 "_rootDesc",
1474 "_schema",
1475 "animals",
1476 "global_",
1477 "load",
1478 "loadNoTemplate",
1479 "my",
1480 "new_batch_http_request",
1481 "query",
1482 "scopedAnimals",
1483 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04001484
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001485 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1486 zoo = build("zoo", "v1", http=http)
1487 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001488
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001489 pickled_zoo = pickle.dumps(zoo)
1490 new_zoo = pickle.loads(pickled_zoo)
1491 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
1492 self.assertTrue(hasattr(new_zoo, "animals"))
1493 self.assertTrue(callable(new_zoo.animals))
1494 self.assertTrue(hasattr(new_zoo, "global_"))
1495 self.assertTrue(callable(new_zoo.global_))
1496 self.assertTrue(hasattr(new_zoo, "load"))
1497 self.assertTrue(callable(new_zoo.load))
1498 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
1499 self.assertTrue(callable(new_zoo.loadNoTemplate))
1500 self.assertTrue(hasattr(new_zoo, "my"))
1501 self.assertTrue(callable(new_zoo.my))
1502 self.assertTrue(hasattr(new_zoo, "query"))
1503 self.assertTrue(callable(new_zoo.query))
1504 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
1505 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001506
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001507 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
1508 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
1509 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
1510 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
1511 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
1512 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
1513 # _http, _model and _schema won't be equal since we will get new
1514 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001515
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001516 def _dummy_zoo_request(self):
1517 with open(os.path.join(DATA_DIR, "zoo.json"), "rU") as fh:
1518 zoo_contents = fh.read()
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001519
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001520 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
1521 if "REMOTE_ADDR" in os.environ:
1522 zoo_uri = util._add_query_parameter(
1523 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
1524 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001525
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001526 http = build_http()
1527 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001528
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001529 def wrapped_request(uri, method="GET", *args, **kwargs):
1530 if uri == zoo_uri:
1531 return httplib2.Response({"status": "200"}), zoo_contents
1532 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001533
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001534 http.request = wrapped_request
1535 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001536
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001537 def _dummy_token(self):
1538 access_token = "foo"
1539 client_id = "some_client_id"
1540 client_secret = "cOuDdkfjxxnv+"
1541 refresh_token = "1/0/a.df219fjls0"
1542 token_expiry = datetime.datetime.utcnow()
1543 user_agent = "refresh_checker/1.0"
1544 return OAuth2Credentials(
1545 access_token,
1546 client_id,
1547 client_secret,
1548 refresh_token,
1549 token_expiry,
1550 GOOGLE_TOKEN_URI,
1551 user_agent,
1552 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001553
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001554 def test_pickle_with_credentials(self):
1555 credentials = self._dummy_token()
1556 http = self._dummy_zoo_request()
1557 http = credentials.authorize(http)
1558 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001559
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001560 zoo = build("zoo", "v1", http=http)
1561 pickled_zoo = pickle.dumps(zoo)
1562 new_zoo = pickle.loads(pickled_zoo)
1563 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
1564 new_http = new_zoo._http
1565 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05001566
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001567 def test_resumable_media_upload_no_content(self):
1568 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1569 zoo = build("zoo", "v1", http=self.http)
andrewnestera4a44cf2017-03-31 16:09:31 +03001570
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001571 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
1572 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03001573
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001574 self.assertEquals(media_upload, request.resumable)
1575 self.assertEquals(request.body, None)
1576 self.assertEquals(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03001577
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001578 http = HttpMockSequence(
1579 [
1580 ({"status": "200", "location": "http://upload.example.com"}, ""),
1581 (
1582 {
1583 "status": "308",
1584 "location": "http://upload.example.com/2",
1585 "range": "0-0",
1586 },
1587 "",
1588 ),
1589 ]
1590 )
andrewnestera4a44cf2017-03-31 16:09:31 +03001591
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001592 status, body = request.next_chunk(http=http)
1593 self.assertEquals(None, body)
1594 self.assertTrue(isinstance(status, MediaUploadProgress))
1595 self.assertEquals(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03001596
Joe Gregorio708388c2012-06-15 13:43:04 -04001597
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001598class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001599 def test_next_successful_none_on_no_next_page_token(self):
1600 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1601 tasks = build("tasks", "v1", http=self.http)
1602 request = tasks.tasklists().list()
1603 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001604
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001605 def test_next_successful_none_on_empty_page_token(self):
1606 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1607 tasks = build("tasks", "v1", http=self.http)
1608 request = tasks.tasklists().list()
1609 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
1610 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04001611
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001612 def test_next_successful_with_next_page_token(self):
1613 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
1614 tasks = build("tasks", "v1", http=self.http)
1615 request = tasks.tasklists().list()
1616 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
1617 parsed = list(urlparse(next_request.uri))
1618 q = parse_qs(parsed[4])
1619 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00001620
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001621 def test_next_successful_with_next_page_token_alternate_name(self):
1622 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
1623 bigquery = build("bigquery", "v2", http=self.http)
1624 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
1625 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
1626 parsed = list(urlparse(next_request.uri))
1627 q = parse_qs(parsed[4])
1628 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04001629
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001630 def test_next_successful_with_next_page_token_in_body(self):
1631 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
1632 logging = build("logging", "v2", http=self.http)
1633 request = logging.entries().list(body={})
1634 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
1635 body = JsonModel().deserialize(next_request.body)
1636 self.assertEqual(body["pageToken"], "123abc")
Thomas Coffee20af04d2017-02-10 15:24:44 -08001637
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001638 def test_next_with_method_with_no_properties(self):
1639 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
1640 service = build("latitude", "v1", http=self.http)
1641 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08001642
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001643 def test_next_nonexistent_with_no_next_page_token(self):
1644 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
1645 drive = build("drive", "v3", http=self.http)
1646 drive.changes().watch(body={})
1647 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08001648
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001649 def test_next_successful_with_next_page_token_required(self):
1650 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
1651 drive = build("drive", "v3", http=self.http)
1652 request = drive.changes().list(pageToken="startPageToken")
1653 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
1654 parsed = list(urlparse(next_request.uri))
1655 q = parse_qs(parsed[4])
1656 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04001657
Joe Gregorioa98733f2011-09-16 10:12:28 -04001658
Joe Gregorio708388c2012-06-15 13:43:04 -04001659class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001660 def test_get_media(self):
1661 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1662 zoo = build("zoo", "v1", http=http)
1663 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04001664
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001665 parsed = urlparse(request.uri)
1666 q = parse_qs(parsed[4])
1667 self.assertEqual(q["alt"], ["media"])
1668 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04001669
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001670 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
1671 response = request.execute(http=http)
1672 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04001673
1674
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001675if __name__ == "__main__":
1676 unittest.main()