blob: c6bc59937b88ae8dbe6df4f80d496e0e43aa7c04 [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
Pavel Kiselev21af37b2020-06-18 19:50:03 +030041from collections import defaultdict
Joe Gregoriodc106fc2012-11-20 14:30:14 -050042
arithmetic1728981eadf2020-06-02 10:20:10 -070043from parameterized import parameterized
Takashi Matsuo30125122015-08-19 11:42:32 -070044import mock
45
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080046import google.auth.credentials
arithmetic1728981eadf2020-06-02 10:20:10 -070047from google.auth.transport import mtls
48from google.auth.exceptions import MutualTLSChannelError
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080049import google_auth_httplib2
Bu Sun Kim790e7022020-09-11 20:18:06 -060050import google.api_core.exceptions
51
John Asmuth864311d2014-04-24 15:46:08 -040052from googleapiclient.discovery import _fix_up_media_upload
53from googleapiclient.discovery import _fix_up_method_description
54from googleapiclient.discovery import _fix_up_parameters
Craig Citro7ee535d2015-02-23 10:11:14 -080055from googleapiclient.discovery import _urljoin
John Asmuth864311d2014-04-24 15:46:08 -040056from googleapiclient.discovery import build
57from googleapiclient.discovery import build_from_document
58from googleapiclient.discovery import DISCOVERY_URI
59from googleapiclient.discovery import key2param
60from googleapiclient.discovery import MEDIA_BODY_PARAMETER_DEFAULT_VALUE
Brian J. Watson38051ac2016-10-25 07:53:08 -070061from googleapiclient.discovery import MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE
John Asmuth864311d2014-04-24 15:46:08 -040062from googleapiclient.discovery import ResourceMethodParameters
63from googleapiclient.discovery import STACK_QUERY_PARAMETERS
64from googleapiclient.discovery import STACK_QUERY_PARAMETER_DEFAULT_VALUE
Dmitry Frenkelf3348f92020-07-15 13:05:58 -070065from googleapiclient.discovery import V1_DISCOVERY_URI
66from googleapiclient.discovery import V2_DISCOVERY_URI
Takashi Matsuo30125122015-08-19 11:42:32 -070067from googleapiclient.discovery_cache import DISCOVERY_DOC_MAX_AGE
68from googleapiclient.discovery_cache.base import Cache
John Asmuth864311d2014-04-24 15:46:08 -040069from googleapiclient.errors import HttpError
70from googleapiclient.errors import InvalidJsonError
71from googleapiclient.errors import MediaUploadSizeError
72from googleapiclient.errors import ResumableUploadError
73from googleapiclient.errors import UnacceptableMimeTypeError
Takashi Matsuo3772f9d2015-09-04 12:25:55 -070074from googleapiclient.errors import UnknownApiNameOrVersion
Brian J. Watson38051ac2016-10-25 07:53:08 -070075from googleapiclient.errors import UnknownFileType
Igor Maravić22435292017-01-19 22:28:22 +010076from googleapiclient.http import build_http
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -040077from googleapiclient.http import BatchHttpRequest
John Asmuth864311d2014-04-24 15:46:08 -040078from googleapiclient.http import HttpMock
79from googleapiclient.http import HttpMockSequence
80from googleapiclient.http import MediaFileUpload
81from googleapiclient.http import MediaIoBaseUpload
82from googleapiclient.http import MediaUpload
83from googleapiclient.http import MediaUploadProgress
84from googleapiclient.http import tunnel_patch
Thomas Coffee20af04d2017-02-10 15:24:44 -080085from googleapiclient.model import JsonModel
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +090086from googleapiclient.schema import Schemas
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080087from oauth2client import GOOGLE_TOKEN_URI
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -080088from oauth2client.client import OAuth2Credentials, GoogleCredentials
Joe Gregorio79daca02013-03-29 16:25:52 -040089
Helen Koikede13e3b2018-04-26 16:05:16 -030090from googleapiclient import _helpers as util
Jon Wayne Parrott36d4e1b2016-10-17 13:31:33 -070091
Joe Gregoriodc106fc2012-11-20 14:30:14 -050092import uritemplate
93
Joe Gregoriocb8103d2011-02-11 23:20:52 -050094
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070095DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
Joe Gregoriocb8103d2011-02-11 23:20:52 -050096
Joe Gregorioa98733f2011-09-16 10:12:28 -040097
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050098def assertUrisEqual(testcase, expected, actual):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070099 """Test that URIs are the same, up to reordering of query parameters."""
100 expected = urlparse(expected)
101 actual = urlparse(actual)
102 testcase.assertEqual(expected.scheme, actual.scheme)
103 testcase.assertEqual(expected.netloc, actual.netloc)
104 testcase.assertEqual(expected.path, actual.path)
105 testcase.assertEqual(expected.params, actual.params)
106 testcase.assertEqual(expected.fragment, actual.fragment)
107 expected_query = parse_qs(expected.query)
108 actual_query = parse_qs(actual.query)
109 for name in list(expected_query.keys()):
110 testcase.assertEqual(expected_query[name], actual_query[name])
111 for name in list(actual_query.keys()):
112 testcase.assertEqual(expected_query[name], actual_query[name])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500113
114
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700115def assert_discovery_uri(testcase, actual, service_name, version, discovery):
116 """Assert that discovery URI used was the one that was expected
117 for a given service and version."""
118 params = {"api": service_name, "apiVersion": version}
119 expanded_requested_uri = uritemplate.expand(discovery, params)
120 assertUrisEqual(testcase, expanded_requested_uri, actual)
121
122
Bu Sun Kim790e7022020-09-11 20:18:06 -0600123def validate_discovery_requests(testcase, http_mock, service_name, version, discovery):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700124 """Validates that there have > 0 calls to Http Discovery
125 and that LAST discovery URI used was the one that was expected
126 for a given service and version."""
127 testcase.assertTrue(len(http_mock.request_sequence) > 0)
128 if len(http_mock.request_sequence) > 0:
129 actual_uri = http_mock.request_sequence[-1][0]
Bu Sun Kim790e7022020-09-11 20:18:06 -0600130 assert_discovery_uri(testcase, actual_uri, service_name, version, discovery)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700131
132
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500133def datafile(filename):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700134 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400135
136
Bu Sun Kim790e7022020-09-11 20:18:06 -0600137def read_datafile(filename, mode="r"):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700138 with open(datafile(filename), mode=mode) as f:
139 return f.read()
140
141
Joe Gregorio504a17f2012-12-07 14:14:26 -0500142class SetupHttplib2(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700143 def test_retries(self):
144 # Merely loading googleapiclient.discovery should set the RETRIES to 1.
145 self.assertEqual(1, httplib2.RETRIES)
Joe Gregorio504a17f2012-12-07 14:14:26 -0500146
147
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400148class Utilities(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700149 def setUp(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700150 self.zoo_root_desc = json.loads(read_datafile("zoo.json", "r"))
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700151 self.zoo_get_method_desc = self.zoo_root_desc["methods"]["query"]
152 self.zoo_animals_resource = self.zoo_root_desc["resources"]["animals"]
153 self.zoo_insert_method_desc = self.zoo_animals_resource["methods"]["insert"]
154 self.zoo_schema = Schemas(self.zoo_root_desc)
Daniel Hermesc2113242013-02-27 10:16:13 -0800155
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700156 def test_key2param(self):
157 self.assertEqual("max_results", key2param("max-results"))
158 self.assertEqual("x007_bond", key2param("007-bond"))
Daniel Hermesc2113242013-02-27 10:16:13 -0800159
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700160 def _base_fix_up_parameters_test(self, method_desc, http_method, root_desc, schema):
161 self.assertEqual(method_desc["httpMethod"], http_method)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400162
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700163 method_desc_copy = copy.deepcopy(method_desc)
164 self.assertEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800165
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700166 parameters = _fix_up_parameters(
167 method_desc_copy, root_desc, http_method, schema
168 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800169
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700170 self.assertNotEqual(method_desc, method_desc_copy)
Daniel Hermesc2113242013-02-27 10:16:13 -0800171
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700172 for param_name in STACK_QUERY_PARAMETERS:
173 self.assertEqual(
174 STACK_QUERY_PARAMETER_DEFAULT_VALUE, parameters[param_name]
175 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800176
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700177 for param_name, value in six.iteritems(root_desc.get("parameters", {})):
178 self.assertEqual(value, parameters[param_name])
Daniel Hermesc2113242013-02-27 10:16:13 -0800179
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700180 return parameters
Daniel Hermesc2113242013-02-27 10:16:13 -0800181
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700182 def test_fix_up_parameters_get(self):
183 parameters = self._base_fix_up_parameters_test(
184 self.zoo_get_method_desc, "GET", self.zoo_root_desc, self.zoo_schema
185 )
186 # Since http_method is 'GET'
187 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800188
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700189 def test_fix_up_parameters_insert(self):
190 parameters = self._base_fix_up_parameters_test(
191 self.zoo_insert_method_desc, "POST", self.zoo_root_desc, self.zoo_schema
192 )
193 body = {"description": "The request body.", "type": "object", "$ref": "Animal"}
194 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800195
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700196 def test_fix_up_parameters_check_body(self):
197 dummy_root_desc = {}
198 dummy_schema = {
199 "Request": {
200 "properties": {
201 "description": "Required. Dummy parameter.",
202 "type": "string",
203 }
204 }
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900205 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700206 no_payload_http_method = "DELETE"
207 with_payload_http_method = "PUT"
Daniel Hermesc2113242013-02-27 10:16:13 -0800208
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700209 invalid_method_desc = {"response": "Who cares"}
210 valid_method_desc = {
211 "request": {"key1": "value1", "key2": "value2", "$ref": "Request"}
212 }
Daniel Hermesc2113242013-02-27 10:16:13 -0800213
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700214 parameters = _fix_up_parameters(
215 invalid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
216 )
217 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800218
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700219 parameters = _fix_up_parameters(
220 valid_method_desc, dummy_root_desc, no_payload_http_method, dummy_schema
221 )
222 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800223
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700224 parameters = _fix_up_parameters(
225 invalid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
226 )
227 self.assertFalse("body" in parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800228
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700229 parameters = _fix_up_parameters(
230 valid_method_desc, dummy_root_desc, with_payload_http_method, dummy_schema
231 )
232 body = {
233 "description": "The request body.",
234 "type": "object",
235 "$ref": "Request",
236 "key1": "value1",
237 "key2": "value2",
238 }
239 self.assertEqual(parameters["body"], body)
Daniel Hermesc2113242013-02-27 10:16:13 -0800240
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700241 def test_fix_up_parameters_optional_body(self):
242 # Request with no parameters
243 dummy_schema = {"Request": {"properties": {}}}
244 method_desc = {"request": {"$ref": "Request"}}
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900245
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700246 parameters = _fix_up_parameters(method_desc, {}, "POST", dummy_schema)
Jean-Loup Roussel-Clouet0c0c8972018-04-28 05:42:43 +0900247
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700248 def _base_fix_up_method_description_test(
249 self,
250 method_desc,
251 initial_parameters,
252 final_parameters,
253 final_accept,
254 final_max_size,
255 final_media_path_url,
256 ):
arithmetic1728981eadf2020-06-02 10:20:10 -0700257 fake_root_desc = {
258 "rootUrl": "http://root/",
259 "servicePath": "fake/",
260 "mtlsRootUrl": "http://root/",
261 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700262 fake_path_url = "fake-path/"
Daniel Hermesc2113242013-02-27 10:16:13 -0800263
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700264 accept, max_size, media_path_url = _fix_up_media_upload(
265 method_desc, fake_root_desc, fake_path_url, initial_parameters
266 )
267 self.assertEqual(accept, final_accept)
268 self.assertEqual(max_size, final_max_size)
269 self.assertEqual(media_path_url, final_media_path_url)
270 self.assertEqual(initial_parameters, final_parameters)
Daniel Hermesc2113242013-02-27 10:16:13 -0800271
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700272 def test_fix_up_media_upload_no_initial_invalid(self):
273 invalid_method_desc = {"response": "Who cares"}
274 self._base_fix_up_method_description_test(
275 invalid_method_desc, {}, {}, [], 0, None
276 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800277
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700278 def test_fix_up_media_upload_no_initial_valid_minimal(self):
279 valid_method_desc = {"mediaUpload": {"accept": []}}
280 final_parameters = {
281 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
282 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
283 }
284 self._base_fix_up_method_description_test(
285 valid_method_desc,
286 {},
287 final_parameters,
288 [],
289 0,
290 "http://root/upload/fake/fake-path/",
291 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800292
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700293 def test_fix_up_media_upload_no_initial_valid_full(self):
294 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
295 final_parameters = {
296 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
297 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
298 }
299 ten_gb = 10 * 2 ** 30
300 self._base_fix_up_method_description_test(
301 valid_method_desc,
302 {},
303 final_parameters,
304 ["*/*"],
305 ten_gb,
306 "http://root/upload/fake/fake-path/",
307 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800308
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700309 def test_fix_up_media_upload_with_initial_invalid(self):
310 invalid_method_desc = {"response": "Who cares"}
311 initial_parameters = {"body": {}}
312 self._base_fix_up_method_description_test(
313 invalid_method_desc, initial_parameters, initial_parameters, [], 0, None
314 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800315
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700316 def test_fix_up_media_upload_with_initial_valid_minimal(self):
317 valid_method_desc = {"mediaUpload": {"accept": []}}
318 initial_parameters = {"body": {}}
319 final_parameters = {
320 "body": {},
321 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
322 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
323 }
324 self._base_fix_up_method_description_test(
325 valid_method_desc,
326 initial_parameters,
327 final_parameters,
328 [],
329 0,
330 "http://root/upload/fake/fake-path/",
331 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800332
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700333 def test_fix_up_media_upload_with_initial_valid_full(self):
334 valid_method_desc = {"mediaUpload": {"accept": ["*/*"], "maxSize": "10GB"}}
335 initial_parameters = {"body": {}}
336 final_parameters = {
337 "body": {},
338 "media_body": MEDIA_BODY_PARAMETER_DEFAULT_VALUE,
339 "media_mime_type": MEDIA_MIME_TYPE_PARAMETER_DEFAULT_VALUE,
340 }
341 ten_gb = 10 * 2 ** 30
342 self._base_fix_up_method_description_test(
343 valid_method_desc,
344 initial_parameters,
345 final_parameters,
346 ["*/*"],
347 ten_gb,
348 "http://root/upload/fake/fake-path/",
349 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800350
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700351 def test_fix_up_method_description_get(self):
352 result = _fix_up_method_description(
353 self.zoo_get_method_desc, self.zoo_root_desc, self.zoo_schema
354 )
355 path_url = "query"
356 http_method = "GET"
357 method_id = "bigquery.query"
358 accept = []
359 max_size = 0
360 media_path_url = None
361 self.assertEqual(
362 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
363 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800364
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700365 def test_fix_up_method_description_insert(self):
366 result = _fix_up_method_description(
367 self.zoo_insert_method_desc, self.zoo_root_desc, self.zoo_schema
368 )
369 path_url = "animals"
370 http_method = "POST"
371 method_id = "zoo.animals.insert"
372 accept = ["image/png"]
373 max_size = 1024
374 media_path_url = "https://www.googleapis.com/upload/zoo/v1/animals"
375 self.assertEqual(
376 result, (path_url, http_method, method_id, accept, max_size, media_path_url)
377 )
Daniel Hermesc2113242013-02-27 10:16:13 -0800378
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700379 def test_urljoin(self):
380 # We want to exhaustively test various URL combinations.
381 simple_bases = ["https://www.googleapis.com", "https://www.googleapis.com/"]
382 long_urls = ["foo/v1/bar:custom?alt=json", "/foo/v1/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800383
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700384 long_bases = [
385 "https://www.googleapis.com/foo/v1",
386 "https://www.googleapis.com/foo/v1/",
387 ]
388 simple_urls = ["bar:custom?alt=json", "/bar:custom?alt=json"]
Craig Citro7ee535d2015-02-23 10:11:14 -0800389
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700390 final_url = "https://www.googleapis.com/foo/v1/bar:custom?alt=json"
391 for base, url in itertools.product(simple_bases, long_urls):
392 self.assertEqual(final_url, _urljoin(base, url))
393 for base, url in itertools.product(long_bases, simple_urls):
394 self.assertEqual(final_url, _urljoin(base, url))
Craig Citro7ee535d2015-02-23 10:11:14 -0800395
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700396 def test_ResourceMethodParameters_zoo_get(self):
397 parameters = ResourceMethodParameters(self.zoo_get_method_desc)
Craig Citro7ee535d2015-02-23 10:11:14 -0800398
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700399 param_types = {
400 "a": "any",
401 "b": "boolean",
402 "e": "string",
403 "er": "string",
404 "i": "integer",
405 "n": "number",
406 "o": "object",
407 "q": "string",
408 "rr": "string",
409 }
410 keys = list(param_types.keys())
411 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
412 self.assertEqual(parameters.required_params, [])
413 self.assertEqual(sorted(parameters.repeated_params), ["er", "rr"])
414 self.assertEqual(parameters.pattern_params, {"rr": "[a-z]+"})
415 self.assertEqual(
416 sorted(parameters.query_params),
417 ["a", "b", "e", "er", "i", "n", "o", "q", "rr"],
418 )
419 self.assertEqual(parameters.path_params, set())
420 self.assertEqual(parameters.param_types, param_types)
421 enum_params = {"e": ["foo", "bar"], "er": ["one", "two", "three"]}
422 self.assertEqual(parameters.enum_params, enum_params)
Daniel Hermes954e1242013-02-28 09:28:37 -0800423
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700424 def test_ResourceMethodParameters_zoo_animals_patch(self):
425 method_desc = self.zoo_animals_resource["methods"]["patch"]
426 parameters = ResourceMethodParameters(method_desc)
Daniel Hermes954e1242013-02-28 09:28:37 -0800427
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700428 param_types = {"name": "string"}
429 keys = list(param_types.keys())
430 self.assertEqual(parameters.argmap, dict((key, key) for key in keys))
431 self.assertEqual(parameters.required_params, ["name"])
432 self.assertEqual(parameters.repeated_params, [])
433 self.assertEqual(parameters.pattern_params, {})
434 self.assertEqual(parameters.query_params, [])
435 self.assertEqual(parameters.path_params, set(["name"]))
436 self.assertEqual(parameters.param_types, param_types)
437 self.assertEqual(parameters.enum_params, {})
Daniel Hermes954e1242013-02-28 09:28:37 -0800438
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400439
Bu Sun Kim98888da2020-09-23 11:10:39 -0600440class Discovery(unittest.TestCase):
441 def test_discovery_http_is_closed(self):
442 http = HttpMock(datafile("malformed.json"), {"status": "200"})
443 service = build("plus", "v1", credentials=mock.sentinel.credentials)
444 http.close.assert_called_once()
445
446
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500447class DiscoveryErrors(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700448 def test_tests_should_be_run_with_strict_positional_enforcement(self):
449 try:
450 plus = build("plus", "v1", None)
451 self.fail("should have raised a TypeError exception over missing http=.")
452 except TypeError:
453 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500454
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700455 def test_failed_to_parse_discovery_json(self):
456 self.http = HttpMock(datafile("malformed.json"), {"status": "200"})
457 try:
458 plus = build("plus", "v1", http=self.http, cache_discovery=False)
459 self.fail("should have raised an exception over malformed JSON.")
460 except InvalidJsonError:
461 pass
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400462
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700463 def test_unknown_api_name_or_version(self):
464 http = HttpMockSequence(
465 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700466 ({"status": "404"}, read_datafile("zoo.json", "rb")),
467 ({"status": "404"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700468 ]
469 )
470 with self.assertRaises(UnknownApiNameOrVersion):
471 plus = build("plus", "v1", http=http, cache_discovery=False)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500472
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700473 def test_credentials_and_http_mutually_exclusive(self):
474 http = HttpMock(datafile("plus.json"), {"status": "200"})
475 with self.assertRaises(ValueError):
476 build("plus", "v1", http=http, credentials=mock.sentinel.credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -0800477
Bu Sun Kim790e7022020-09-11 20:18:06 -0600478 def test_credentials_file_and_http_mutually_exclusive(self):
479 http = HttpMock(datafile("plus.json"), {"status": "200"})
480 with self.assertRaises(ValueError):
481 build(
482 "plus",
483 "v1",
484 http=http,
485 client_options=google.api_core.client_options.ClientOptions(
486 credentials_file="credentials.json"
487 ),
488 )
489
490 def test_credentials_and_credentials_file_mutually_exclusive(self):
491 with self.assertRaises(google.api_core.exceptions.DuplicateCredentialArgs):
492 build(
493 "plus",
494 "v1",
495 credentials=mock.sentinel.credentials,
496 client_options=google.api_core.client_options.ClientOptions(
497 credentials_file="credentials.json"
498 ),
499 )
500
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500501
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100502class DiscoveryFromDocument(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700503 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400504
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700505 def test_can_build_from_local_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700506 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700507 plus = build_from_document(
508 discovery,
509 base="https://www.googleapis.com/",
510 credentials=self.MOCK_CREDENTIALS,
511 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700512 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700513 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500514
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700515 def test_can_build_from_local_deserialized_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700516 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700517 discovery = json.loads(discovery)
518 plus = build_from_document(
519 discovery,
520 base="https://www.googleapis.com/",
521 credentials=self.MOCK_CREDENTIALS,
522 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700523 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700524 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400525
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700526 def test_building_with_base_remembers_base(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700527 discovery = read_datafile("plus.json")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400528
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700529 base = "https://www.example.com/"
530 plus = build_from_document(
531 discovery, base=base, credentials=self.MOCK_CREDENTIALS
532 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700533 self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100534
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700535 def test_building_with_optional_http_with_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700536 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700537 plus = build_from_document(
538 discovery,
539 base="https://www.googleapis.com/",
540 credentials=self.MOCK_CREDENTIALS,
541 )
Igor Maravić22435292017-01-19 22:28:22 +0100542
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700543 # plus service requires Authorization, hence we expect to see AuthorizedHttp object here
544 self.assertIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
545 self.assertIsInstance(plus._http.http, httplib2.Http)
546 self.assertIsInstance(plus._http.http.timeout, int)
547 self.assertGreater(plus._http.http.timeout, 0)
Igor Maravić22435292017-01-19 22:28:22 +0100548
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700549 def test_building_with_optional_http_with_no_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700550 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700551 # Cleanup auth field, so we would use plain http client
552 discovery = json.loads(discovery)
553 discovery["auth"] = {}
554 discovery = json.dumps(discovery)
Igor Maravić22435292017-01-19 22:28:22 +0100555
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700556 plus = build_from_document(
557 discovery, base="https://www.googleapis.com/", credentials=None
558 )
559 # plus service requires Authorization
560 self.assertIsInstance(plus._http, httplib2.Http)
561 self.assertIsInstance(plus._http.timeout, int)
562 self.assertGreater(plus._http.timeout, 0)
Jonathan Wayne Parrotta6e6fbd2015-07-16 15:33:57 -0700563
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700564 def test_building_with_explicit_http(self):
565 http = HttpMock()
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700566 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700567 plus = build_from_document(
568 discovery, base="https://www.googleapis.com/", http=http
569 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700570 self.assertEqual(plus._http, http)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100571
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700572 def test_building_with_developer_key_skips_adc(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700573 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700574 plus = build_from_document(
575 discovery, base="https://www.googleapis.com/", developerKey="123"
576 )
577 self.assertIsInstance(plus._http, httplib2.Http)
578 # It should not be an AuthorizedHttp, because that would indicate that
579 # application default credentials were used.
580 self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800581
Bu Sun Kim98888da2020-09-23 11:10:39 -0600582 def test_building_with_context_manager(self):
583 discovery = read_datafile("plus.json")
584 with mock.patch("httplib2.Http") as http:
585 with build_from_document(discovery, base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus:
586 self.assertIsNotNone(plus)
587 self.assertTrue(hasattr(plus, "activities"))
588 plus._http.http.close.assert_called_once()
589
590 def test_resource_close(self):
591 discovery = read_datafile("plus.json")
592 with mock.patch("httplib2.Http") as http:
593 plus = build_from_document(
594 discovery,
595 base="https://www.googleapis.com/",
596 credentials=self.MOCK_CREDENTIALS,
597 )
598 plus.close()
599 plus._http.http.close.assert_called_once()
600
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700601 def test_api_endpoint_override_from_client_options(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700602 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700603 api_endpoint = "https://foo.googleapis.com/"
604 options = google.api_core.client_options.ClientOptions(
605 api_endpoint=api_endpoint
606 )
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700607 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700608 discovery, client_options=options, credentials=self.MOCK_CREDENTIALS
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700609 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700610
611 self.assertEqual(plus._baseUrl, api_endpoint)
612
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300613 def test_api_endpoint_override_from_client_options_mapping_object(self):
614
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700615 discovery = read_datafile("plus.json")
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300616 api_endpoint = "https://foo.googleapis.com/"
617 mapping_object = defaultdict(str)
Bu Sun Kim790e7022020-09-11 20:18:06 -0600618 mapping_object["api_endpoint"] = api_endpoint
619 plus = build_from_document(discovery, client_options=mapping_object)
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300620
621 self.assertEqual(plus._baseUrl, api_endpoint)
622
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700623 def test_api_endpoint_override_from_client_options_dict(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700624 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700625 api_endpoint = "https://foo.googleapis.com/"
626 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700627 discovery,
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700628 client_options={"api_endpoint": api_endpoint},
arithmetic1728981eadf2020-06-02 10:20:10 -0700629 credentials=self.MOCK_CREDENTIALS,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700630 )
631
632 self.assertEqual(plus._baseUrl, api_endpoint)
633
Bu Sun Kim790e7022020-09-11 20:18:06 -0600634 def test_scopes_from_client_options(self):
635 discovery = read_datafile("plus.json")
636
637 with mock.patch("googleapiclient._auth.default_credentials") as default:
638 plus = build_from_document(
639 discovery, client_options={"scopes": ["1", "2"]},
640 )
641
642 default.assert_called_once_with(scopes=["1", "2"], quota_project_id=None)
643
644 def test_quota_project_from_client_options(self):
645 discovery = read_datafile("plus.json")
646
647 with mock.patch("googleapiclient._auth.default_credentials") as default:
648 plus = build_from_document(
649 discovery,
650 client_options=google.api_core.client_options.ClientOptions(
651 quota_project_id="my-project"
652 ),
653 )
654
655 default.assert_called_once_with(scopes=None, quota_project_id="my-project")
656
657 def test_credentials_file_from_client_options(self):
658 discovery = read_datafile("plus.json")
659
660 with mock.patch("googleapiclient._auth.credentials_from_file") as default:
661 plus = build_from_document(
662 discovery,
663 client_options=google.api_core.client_options.ClientOptions(
664 credentials_file="credentials.json"
665 ),
666 )
667
668 default.assert_called_once_with(
669 "credentials.json", scopes=None, quota_project_id=None
670 )
671
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800672
arithmetic1728981eadf2020-06-02 10:20:10 -0700673REGULAR_ENDPOINT = "https://www.googleapis.com/plus/v1/"
674MTLS_ENDPOINT = "https://www.mtls.googleapis.com/plus/v1/"
675
676
677class DiscoveryFromDocumentMutualTLS(unittest.TestCase):
678 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
679 ADC_CERT_PATH = "adc_cert_path"
680 ADC_KEY_PATH = "adc_key_path"
681 ADC_PASSPHRASE = "adc_passphrase"
682
arithmetic17282fc5ca12020-08-27 14:08:12 -0700683 def check_http_client_cert(self, resource, has_client_cert="false"):
arithmetic1728981eadf2020-06-02 10:20:10 -0700684 if isinstance(resource._http, google_auth_httplib2.AuthorizedHttp):
685 certs = list(resource._http.http.certificates.iter(""))
686 else:
687 certs = list(resource._http.certificates.iter(""))
arithmetic17282fc5ca12020-08-27 14:08:12 -0700688 if has_client_cert == "true":
arithmetic1728981eadf2020-06-02 10:20:10 -0700689 self.assertEqual(len(certs), 1)
690 self.assertEqual(
691 certs[0], (self.ADC_KEY_PATH, self.ADC_CERT_PATH, self.ADC_PASSPHRASE)
692 )
693 else:
694 self.assertEqual(len(certs), 0)
695
696 def client_encrypted_cert_source(self):
697 return self.ADC_CERT_PATH, self.ADC_KEY_PATH, self.ADC_PASSPHRASE
698
arithmetic17282fc5ca12020-08-27 14:08:12 -0700699 @parameterized.expand(
700 [
701 ("never", "true"),
702 ("auto", "true"),
703 ("always", "true"),
704 ("never", "false"),
705 ("auto", "false"),
706 ("always", "false"),
707 ]
708 )
709 def test_mtls_not_trigger_if_http_provided(self, use_mtls_env, use_client_cert):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700710 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700711
arithmetic17282fc5ca12020-08-27 14:08:12 -0700712 with mock.patch.dict(
713 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
714 ):
715 with mock.patch.dict(
716 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
717 ):
718 plus = build_from_document(discovery, http=httplib2.Http())
719 self.assertIsNotNone(plus)
720 self.assertEqual(plus._baseUrl, REGULAR_ENDPOINT)
721 self.check_http_client_cert(plus, has_client_cert="false")
arithmetic1728981eadf2020-06-02 10:20:10 -0700722
723 @parameterized.expand(
724 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700725 ("never", "true"),
726 ("auto", "true"),
727 ("always", "true"),
728 ("never", "false"),
729 ("auto", "false"),
730 ("always", "false"),
arithmetic1728981eadf2020-06-02 10:20:10 -0700731 ]
732 )
arithmetic17282fc5ca12020-08-27 14:08:12 -0700733 def test_exception_with_client_cert_source(self, use_mtls_env, use_client_cert):
734 discovery = read_datafile("plus.json")
735 with mock.patch.dict(
736 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
737 ):
738 with mock.patch.dict(
739 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
740 ):
741 with self.assertRaises(MutualTLSChannelError):
742 build_from_document(
743 discovery,
744 credentials=self.MOCK_CREDENTIALS,
745 client_options={"client_cert_source": mock.Mock()},
746 )
747
748 @parameterized.expand(
749 [
750 ("never", "true", REGULAR_ENDPOINT),
751 ("auto", "true", MTLS_ENDPOINT),
752 ("always", "true", MTLS_ENDPOINT),
753 ("never", "false", REGULAR_ENDPOINT),
754 ("auto", "false", REGULAR_ENDPOINT),
755 ("always", "false", MTLS_ENDPOINT),
756 ]
757 )
758 def test_mtls_with_provided_client_cert(
759 self, use_mtls_env, use_client_cert, base_url
760 ):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700761 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700762
arithmetic17282fc5ca12020-08-27 14:08:12 -0700763 with mock.patch.dict(
764 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
765 ):
766 with mock.patch.dict(
767 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
768 ):
769 plus = build_from_document(
770 discovery,
771 credentials=self.MOCK_CREDENTIALS,
772 client_options={
773 "client_encrypted_cert_source": self.client_encrypted_cert_source
774 },
775 )
776 self.assertIsNotNone(plus)
777 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
778 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700779
arithmetic17282fc5ca12020-08-27 14:08:12 -0700780 @parameterized.expand(
781 [
782 ("never", "true"),
783 ("auto", "true"),
784 ("always", "true"),
785 ("never", "false"),
786 ("auto", "false"),
787 ("always", "false"),
788 ]
789 )
790 def test_endpoint_not_switch(self, use_mtls_env, use_client_cert):
arithmetic1728981eadf2020-06-02 10:20:10 -0700791 # Test endpoint is not switched if user provided api endpoint
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700792 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700793
arithmetic17282fc5ca12020-08-27 14:08:12 -0700794 with mock.patch.dict(
795 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
796 ):
797 with mock.patch.dict(
798 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
799 ):
800 plus = build_from_document(
801 discovery,
802 credentials=self.MOCK_CREDENTIALS,
803 client_options={
804 "api_endpoint": "https://foo.googleapis.com",
805 "client_encrypted_cert_source": self.client_encrypted_cert_source,
806 },
807 )
808 self.assertIsNotNone(plus)
809 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
810 self.assertEqual(plus._baseUrl, "https://foo.googleapis.com")
arithmetic1728981eadf2020-06-02 10:20:10 -0700811
812 @parameterized.expand(
813 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700814 ("never", "true", REGULAR_ENDPOINT),
815 ("auto", "true", MTLS_ENDPOINT),
816 ("always", "true", MTLS_ENDPOINT),
817 ("never", "false", REGULAR_ENDPOINT),
818 ("auto", "false", REGULAR_ENDPOINT),
819 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700820 ]
821 )
822 @mock.patch(
823 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
824 )
825 @mock.patch(
826 "google.auth.transport.mtls.default_client_encrypted_cert_source", autospec=True
827 )
828 def test_mtls_with_default_client_cert(
829 self,
830 use_mtls_env,
arithmetic17282fc5ca12020-08-27 14:08:12 -0700831 use_client_cert,
arithmetic1728981eadf2020-06-02 10:20:10 -0700832 base_url,
833 default_client_encrypted_cert_source,
834 has_default_client_cert_source,
835 ):
836 has_default_client_cert_source.return_value = True
837 default_client_encrypted_cert_source.return_value = (
838 self.client_encrypted_cert_source
839 )
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700840 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700841
arithmetic17282fc5ca12020-08-27 14:08:12 -0700842 with mock.patch.dict(
843 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
844 ):
845 with mock.patch.dict(
846 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
847 ):
848 plus = build_from_document(
849 discovery,
850 credentials=self.MOCK_CREDENTIALS,
851 adc_cert_path=self.ADC_CERT_PATH,
852 adc_key_path=self.ADC_KEY_PATH,
853 )
854 self.assertIsNotNone(plus)
855 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
856 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700857
858 @parameterized.expand(
859 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700860 ("never", "true", REGULAR_ENDPOINT),
861 ("auto", "true", REGULAR_ENDPOINT),
862 ("always", "true", MTLS_ENDPOINT),
863 ("never", "false", REGULAR_ENDPOINT),
864 ("auto", "false", REGULAR_ENDPOINT),
865 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700866 ]
867 )
868 @mock.patch(
869 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
870 )
871 def test_mtls_with_no_client_cert(
arithmetic17282fc5ca12020-08-27 14:08:12 -0700872 self, use_mtls_env, use_client_cert, base_url, has_default_client_cert_source
arithmetic1728981eadf2020-06-02 10:20:10 -0700873 ):
874 has_default_client_cert_source.return_value = False
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700875 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700876
arithmetic17282fc5ca12020-08-27 14:08:12 -0700877 with mock.patch.dict(
878 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
879 ):
880 with mock.patch.dict(
881 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
882 ):
883 plus = build_from_document(
884 discovery,
885 credentials=self.MOCK_CREDENTIALS,
886 adc_cert_path=self.ADC_CERT_PATH,
887 adc_key_path=self.ADC_KEY_PATH,
888 )
889 self.assertIsNotNone(plus)
890 self.check_http_client_cert(plus, has_client_cert="false")
891 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700892
893
Joe Gregorioa98733f2011-09-16 10:12:28 -0400894class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700895 def setUp(self):
896 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400897
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700898 def tearDown(self):
899 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400900
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700901 def test_userip_is_added_to_discovery_uri(self):
902 # build() will raise an HttpError on a 400, use this to pick the request uri
903 # out of the raised exception.
904 os.environ["REMOTE_ADDR"] = "10.0.0.1"
905 try:
906 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700907 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700908 )
909 zoo = build(
910 "zoo",
911 "v1",
912 http=http,
913 developerKey=None,
914 discoveryServiceUrl="http://example.com",
915 )
916 self.fail("Should have raised an exception.")
917 except HttpError as e:
918 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400919
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700920 def test_userip_missing_is_not_added_to_discovery_uri(self):
921 # build() will raise an HttpError on a 400, use this to pick the request uri
922 # out of the raised exception.
923 try:
924 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700925 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700926 )
927 zoo = build(
928 "zoo",
929 "v1",
930 http=http,
931 developerKey=None,
932 discoveryServiceUrl="http://example.com",
933 )
934 self.fail("Should have raised an exception.")
935 except HttpError as e:
936 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400937
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700938 def test_key_is_added_to_discovery_uri(self):
939 # build() will raise an HttpError on a 400, use this to pick the request uri
940 # out of the raised exception.
941 try:
942 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700943 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700944 )
945 zoo = build(
946 "zoo",
947 "v1",
948 http=http,
949 developerKey="foo",
950 discoveryServiceUrl="http://example.com",
951 )
952 self.fail("Should have raised an exception.")
953 except HttpError as e:
954 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800955
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700956 def test_discovery_loading_from_v2_discovery_uri(self):
957 http = HttpMockSequence(
958 [
959 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700960 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700961 ]
962 )
963 zoo = build("zoo", "v1", http=http, cache_discovery=False)
964 self.assertTrue(hasattr(zoo, "animals"))
965
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700966 def test_api_endpoint_override_from_client_options(self):
967 http = HttpMockSequence(
968 [
969 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700970 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700971 ]
972 )
973 api_endpoint = "https://foo.googleapis.com/"
974 options = google.api_core.client_options.ClientOptions(
975 api_endpoint=api_endpoint
976 )
977 zoo = build(
978 "zoo", "v1", http=http, cache_discovery=False, client_options=options
979 )
980 self.assertEqual(zoo._baseUrl, api_endpoint)
981
982 def test_api_endpoint_override_from_client_options_dict(self):
983 http = HttpMockSequence(
984 [
985 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700986 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700987 ]
988 )
989 api_endpoint = "https://foo.googleapis.com/"
990 zoo = build(
991 "zoo",
992 "v1",
993 http=http,
994 cache_discovery=False,
995 client_options={"api_endpoint": api_endpoint},
996 )
997 self.assertEqual(zoo._baseUrl, api_endpoint)
998
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -0700999 def test_discovery_with_empty_version_uses_v2(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001000 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001001 build("zoo", version=None, http=http, cache_discovery=False)
1002 validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI)
1003
1004 def test_discovery_with_empty_version_preserves_custom_uri(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001005 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001006 custom_discovery_uri = "https://foo.bar/$discovery"
1007 build(
Bu Sun Kim790e7022020-09-11 20:18:06 -06001008 "zoo",
1009 version=None,
1010 http=http,
1011 cache_discovery=False,
1012 discoveryServiceUrl=custom_discovery_uri,
1013 )
1014 validate_discovery_requests(self, http, "zoo", None, custom_discovery_uri)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001015
1016 def test_discovery_with_valid_version_uses_v1(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001017 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001018 build("zoo", version="v123", http=http, cache_discovery=False)
1019 validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI)
1020
Joe Gregorioa98733f2011-09-16 10:12:28 -04001021
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001022class DiscoveryRetryFromHttp(unittest.TestCase):
1023 def test_repeated_500_retries_and_fails(self):
1024 http = HttpMockSequence(
1025 [
1026 ({"status": "500"}, read_datafile("500.json", "rb")),
1027 ({"status": "503"}, read_datafile("503.json", "rb")),
1028 ]
1029 )
1030 with self.assertRaises(HttpError):
1031 with mock.patch("time.sleep") as mocked_sleep:
1032 build("zoo", "v1", http=http, cache_discovery=False)
1033
1034 mocked_sleep.assert_called_once()
1035 # We also want to verify that we stayed with v1 discovery
1036 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1037
1038 def test_v2_repeated_500_retries_and_fails(self):
1039 http = HttpMockSequence(
1040 [
1041 ({"status": "404"}, "Not found"), # last v1 discovery call
1042 ({"status": "500"}, read_datafile("500.json", "rb")),
1043 ({"status": "503"}, read_datafile("503.json", "rb")),
1044 ]
1045 )
1046 with self.assertRaises(HttpError):
1047 with mock.patch("time.sleep") as mocked_sleep:
1048 build("zoo", "v1", http=http, cache_discovery=False)
1049
1050 mocked_sleep.assert_called_once()
1051 # We also want to verify that we switched to v2 discovery
1052 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1053
1054 def test_single_500_retries_and_succeeds(self):
1055 http = HttpMockSequence(
1056 [
1057 ({"status": "500"}, read_datafile("500.json", "rb")),
1058 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1059 ]
1060 )
1061 with mock.patch("time.sleep") as mocked_sleep:
1062 zoo = build("zoo", "v1", http=http, cache_discovery=False)
1063
1064 self.assertTrue(hasattr(zoo, "animals"))
1065 mocked_sleep.assert_called_once()
1066 # We also want to verify that we stayed with v1 discovery
1067 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1068
1069 def test_single_500_then_404_retries_and_succeeds(self):
1070 http = HttpMockSequence(
1071 [
1072 ({"status": "500"}, read_datafile("500.json", "rb")),
1073 ({"status": "404"}, "Not found"), # last v1 discovery call
1074 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1075 ]
1076 )
1077 with mock.patch("time.sleep") as mocked_sleep:
1078 zoo = build("zoo", "v1", http=http, cache_discovery=False)
1079
1080 self.assertTrue(hasattr(zoo, "animals"))
1081 mocked_sleep.assert_called_once()
1082 # We also want to verify that we switched to v2 discovery
1083 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1084
1085
Takashi Matsuo30125122015-08-19 11:42:32 -07001086class DiscoveryFromAppEngineCache(unittest.TestCase):
Zev Goldstein09e64472020-05-14 16:29:20 -04001087 def setUp(self):
1088 self.old_environ = os.environ.copy()
1089 os.environ["APPENGINE_RUNTIME"] = "python27"
1090
1091 def tearDown(self):
1092 os.environ = self.old_environ
1093
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001094 def test_appengine_memcache(self):
1095 # Hack module import
1096 self.orig_import = __import__
1097 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -07001098
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001099 def import_mock(name, *args, **kwargs):
1100 if name == "google.appengine.api":
1101 return self.mocked_api
1102 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -07001103
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001104 import_fullname = "__builtin__.__import__"
1105 if sys.version_info[0] >= 3:
1106 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -07001107
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001108 with mock.patch(import_fullname, side_effect=import_mock):
1109 namespace = "google-api-client"
1110 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001111
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001112 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -07001113
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001114 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -07001115
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001116 # memcache.get is called once
1117 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1118 self.mocked_api.memcache.get.assert_called_once_with(
1119 url, namespace=namespace
1120 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001121
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001122 # memcache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001123 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001124 self.mocked_api.memcache.set.assert_called_once_with(
1125 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1126 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001127
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001128 # Returns the cached content this time.
1129 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -07001130
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001131 # Make sure the contents are returned from the cache.
1132 # (Otherwise it should through an error)
1133 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001134
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001135 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -07001136
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001137 # memcache.get is called twice
1138 self.mocked_api.memcache.get.assert_has_calls(
1139 [
1140 mock.call(url, namespace=namespace),
1141 mock.call(url, namespace=namespace),
1142 ]
1143 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001144
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001145 # memcahce.set is called just once
1146 self.mocked_api.memcache.set.assert_called_once_with(
1147 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1148 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001149
1150
1151class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001152 def __init__(self):
1153 self.d = {}
1154
1155 def get(self, url):
1156 return self.d.get(url, None)
1157
1158 def set(self, url, content):
1159 self.d[url] = content
1160
1161 def contains(self, url):
1162 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -07001163
1164
1165class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001166 def test_file_based_cache(self):
1167 cache = mock.Mock(wraps=DictCache())
1168 with mock.patch(
1169 "googleapiclient.discovery_cache.autodetect", return_value=cache
1170 ):
1171 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001172
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001173 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -07001174
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001175 # cache.get is called once
1176 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1177 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -07001178
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001179 # cache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001180 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001181 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001182
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001183 # Make sure there is a cache entry for the plus v1 discovery doc.
1184 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -07001185
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001186 # Make sure the contents are returned from the cache.
1187 # (Otherwise it should through an error)
1188 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001189
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001190 plus = build("plus", "v1", http=self.http)
Takashi Matsuo30125122015-08-19 11:42:32 -07001191
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001192 # cache.get is called twice
1193 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -07001194
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001195 # cahce.set is called just once
1196 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001197
1198
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001199class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001200 def test_method_error_checking(self):
1201 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1202 plus = build("plus", "v1", http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001203
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001204 # Missing required parameters
1205 try:
1206 plus.activities().list()
1207 self.fail()
1208 except TypeError as e:
1209 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001210
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001211 # Missing required parameters even if supplied as None.
1212 try:
1213 plus.activities().list(collection=None, userId=None)
1214 self.fail()
1215 except TypeError as e:
1216 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001217
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001218 # Parameter doesn't match regex
1219 try:
1220 plus.activities().list(collection="not_a_collection_name", userId="me")
1221 self.fail()
1222 except TypeError as e:
1223 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -04001224
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001225 # Unexpected parameter
1226 try:
1227 plus.activities().list(flubber=12)
1228 self.fail()
1229 except TypeError as e:
1230 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001231
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001232 def _check_query_types(self, request):
1233 parsed = urlparse(request.uri)
1234 q = parse_qs(parsed[4])
1235 self.assertEqual(q["q"], ["foo"])
1236 self.assertEqual(q["i"], ["1"])
1237 self.assertEqual(q["n"], ["1.0"])
1238 self.assertEqual(q["b"], ["false"])
1239 self.assertEqual(q["a"], ["[1, 2, 3]"])
1240 self.assertEqual(q["o"], ["{'a': 1}"])
1241 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001242
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001243 def test_type_coercion(self):
1244 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1245 zoo = build("zoo", "v1", http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -05001246
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001247 request = zoo.query(
1248 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
1249 )
1250 self._check_query_types(request)
1251 request = zoo.query(
1252 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
1253 )
1254 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -05001255
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001256 request = zoo.query(
1257 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
1258 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001259
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001260 request = zoo.query(
1261 q="foo",
1262 i="1",
1263 n="1",
1264 b="",
1265 a=[1, 2, 3],
1266 o={"a": 1},
1267 e="bar",
1268 er=["one", "three"],
1269 rr=["foo", "bar"],
1270 )
1271 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -05001272
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001273 # Five is right out.
1274 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -05001275
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001276 def test_optional_stack_query_parameters(self):
1277 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1278 zoo = build("zoo", "v1", http=http)
1279 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -08001280
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001281 parsed = urlparse(request.uri)
1282 q = parse_qs(parsed[4])
1283 self.assertEqual(q["trace"], ["html"])
1284 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -05001285
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001286 def test_string_params_value_of_none_get_dropped(self):
1287 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1288 zoo = build("zoo", "v1", http=http)
1289 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -04001290
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001291 parsed = urlparse(request.uri)
1292 q = parse_qs(parsed[4])
1293 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -04001294
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001295 def test_model_added_query_parameters(self):
1296 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1297 zoo = build("zoo", "v1", http=http)
1298 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -04001299
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001300 parsed = urlparse(request.uri)
1301 q = parse_qs(parsed[4])
1302 self.assertEqual(q["alt"], ["json"])
1303 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001304
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001305 def test_fallback_to_raw_model(self):
1306 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1307 zoo = build("zoo", "v1", http=http)
1308 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001309
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001310 parsed = urlparse(request.uri)
1311 q = parse_qs(parsed[4])
1312 self.assertTrue("alt" not in q)
1313 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001314
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001315 def test_patch(self):
1316 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1317 zoo = build("zoo", "v1", http=http)
1318 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -05001319
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001320 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -04001321
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001322 def test_batch_request_from_discovery(self):
1323 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1324 # zoo defines a batchPath
1325 zoo = build("zoo", "v1", http=self.http)
1326 batch_request = zoo.new_batch_http_request()
1327 self.assertEqual(
1328 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
1329 )
Joe Gregoriof4153422011-03-18 22:45:18 -04001330
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001331 def test_batch_request_from_default(self):
1332 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1333 # plus does not define a batchPath
Bu Sun Kim790e7022020-09-11 20:18:06 -06001334 plus = build("plus", "v1", http=self.http, cache_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001335 batch_request = plus.new_batch_http_request()
1336 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001337
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001338 def test_tunnel_patch(self):
1339 http = HttpMockSequence(
1340 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001341 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001342 ({"status": "200"}, "echo_request_headers_as_json"),
1343 ]
1344 )
1345 http = tunnel_patch(http)
1346 zoo = build("zoo", "v1", http=http, cache_discovery=False)
1347 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001348
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001349 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -04001350
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001351 def test_plus_resources(self):
1352 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1353 plus = build("plus", "v1", http=self.http)
1354 self.assertTrue(getattr(plus, "activities"))
1355 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -05001356
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001357 def test_oauth2client_credentials(self):
1358 credentials = mock.Mock(spec=GoogleCredentials)
1359 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001360
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001361 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001362 service = build_from_document(discovery, credentials=credentials)
1363 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -07001364
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001365 def test_google_auth_credentials(self):
1366 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001367 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001368 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -07001369
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001370 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
1371 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001372
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001373 def test_no_scopes_no_credentials(self):
1374 # Zoo doesn't have scopes
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001375 discovery = read_datafile("zoo.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001376 service = build_from_document(discovery)
1377 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
1378 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001379
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001380 def test_full_featured(self):
1381 # Zoo should exercise all discovery facets
1382 # and should also have no future.json file.
1383 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1384 zoo = build("zoo", "v1", http=self.http)
1385 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -07001386
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001387 request = zoo.animals().list(name="bat", projection="full")
1388 parsed = urlparse(request.uri)
1389 q = parse_qs(parsed[4])
1390 self.assertEqual(q["name"], ["bat"])
1391 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001392
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001393 def test_nested_resources(self):
1394 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1395 zoo = build("zoo", "v1", http=self.http)
1396 self.assertTrue(getattr(zoo, "animals"))
1397 request = zoo.my().favorites().list(max_results="5")
1398 parsed = urlparse(request.uri)
1399 q = parse_qs(parsed[4])
1400 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001401
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001402 @unittest.skipIf(six.PY3, "print is not a reserved name in Python 3")
1403 def test_methods_with_reserved_names(self):
1404 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1405 zoo = build("zoo", "v1", http=self.http)
1406 self.assertTrue(getattr(zoo, "animals"))
1407 request = zoo.global_().print_().assert_(max_results="5")
1408 parsed = urlparse(request.uri)
1409 self.assertEqual(parsed[2], "/zoo/v1/global/print/assert")
Joe Gregorio3fada332011-01-07 17:07:45 -05001410
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001411 def test_top_level_functions(self):
1412 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1413 zoo = build("zoo", "v1", http=self.http)
1414 self.assertTrue(getattr(zoo, "query"))
1415 request = zoo.query(q="foo")
1416 parsed = urlparse(request.uri)
1417 q = parse_qs(parsed[4])
1418 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -04001419
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001420 def test_simple_media_uploads(self):
1421 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1422 zoo = build("zoo", "v1", http=self.http)
1423 doc = getattr(zoo.animals().insert, "__doc__")
1424 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001425
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001426 def test_simple_media_upload_no_max_size_provided(self):
1427 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1428 zoo = build("zoo", "v1", http=self.http)
1429 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001430 self.assertEqual("image/png", request.headers["content-type"])
1431 self.assertEqual(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001432
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001433 def test_simple_media_raise_correct_exceptions(self):
1434 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1435 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -04001436
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001437 try:
1438 zoo.animals().insert(media_body=datafile("smiley.png"))
1439 self.fail("should throw exception if media is too large.")
1440 except MediaUploadSizeError:
1441 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001442
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001443 try:
1444 zoo.animals().insert(media_body=datafile("small.jpg"))
1445 self.fail("should throw exception if mimetype is unacceptable.")
1446 except UnacceptableMimeTypeError:
1447 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001448
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001449 def test_simple_media_good_upload(self):
1450 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1451 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001452
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001453 request = zoo.animals().insert(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001454 self.assertEqual("image/png", request.headers["content-type"])
1455 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001456 assertUrisEqual(
1457 self,
1458 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1459 request.uri,
1460 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001461
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001462 def test_simple_media_unknown_mimetype(self):
1463 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1464 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001465
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001466 try:
1467 zoo.animals().insert(media_body=datafile("small-png"))
1468 self.fail("should throw exception if mimetype is unknown.")
1469 except UnknownFileType:
1470 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -07001471
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001472 request = zoo.animals().insert(
1473 media_body=datafile("small-png"), media_mime_type="image/png"
1474 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001475 self.assertEqual("image/png", request.headers["content-type"])
1476 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001477 assertUrisEqual(
1478 self,
1479 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1480 request.uri,
1481 )
Brian J. Watson38051ac2016-10-25 07:53:08 -07001482
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001483 def test_multipart_media_raise_correct_exceptions(self):
1484 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1485 zoo = build("zoo", "v1", http=self.http)
Brian J. Watson38051ac2016-10-25 07:53:08 -07001486
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001487 try:
1488 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
1489 self.fail("should throw exception if media is too large.")
1490 except MediaUploadSizeError:
1491 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001492
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001493 try:
1494 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
1495 self.fail("should throw exception if mimetype is unacceptable.")
1496 except UnacceptableMimeTypeError:
1497 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001498
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001499 def test_multipart_media_good_upload(self):
1500 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1501 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001502
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001503 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1504 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001505 contents = read_datafile("small.png", "rb")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001506 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1507 self.assertEqual(
1508 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1509 b"--==============="
1510 + boundary
1511 + b"==\n"
1512 + b"Content-Type: application/json\n"
1513 + b"MIME-Version: 1.0\n\n"
1514 + b'{"data": {}}\n'
1515 + b"--==============="
1516 + boundary
1517 + b"==\n"
1518 + b"Content-Type: image/png\n"
1519 + b"MIME-Version: 1.0\n"
1520 + b"Content-Transfer-Encoding: binary\n\n"
1521 + contents
1522 + b"\n--==============="
1523 + boundary
1524 + b"==--",
1525 )
1526 assertUrisEqual(
1527 self,
1528 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1529 request.uri,
1530 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001531
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001532 def test_media_capable_method_without_media(self):
1533 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1534 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001535
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001536 request = zoo.animals().insert(body={})
1537 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001538
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001539 def test_resumable_multipart_media_good_upload(self):
1540 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1541 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001542
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001543 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1544 request = zoo.animals().insert(media_body=media_upload, body={})
1545 self.assertTrue(request.headers["content-type"].startswith("application/json"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001546 self.assertEqual('{"data": {}}', request.body)
1547 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001548
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001549 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001550
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001551 self.assertNotEqual(request.body, None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001552 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001553
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001554 http = HttpMockSequence(
1555 [
1556 ({"status": "200", "location": "http://upload.example.com"}, ""),
1557 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1558 (
1559 {
1560 "status": "308",
1561 "location": "http://upload.example.com/3",
1562 "range": "0-12",
1563 },
1564 "",
1565 ),
1566 (
1567 {
1568 "status": "308",
1569 "location": "http://upload.example.com/4",
1570 "range": "0-%d" % (media_upload.size() - 2),
1571 },
1572 "",
1573 ),
1574 ({"status": "200"}, '{"foo": "bar"}'),
1575 ]
1576 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001577
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001578 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001579 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001580 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001581 self.assertEqual(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001582
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001583 # Two requests should have been made and the resumable_uri should have been
1584 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001585 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
1586 self.assertEqual(media_upload, request.resumable)
1587 self.assertEqual(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001588
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001589 # This next chuck call should upload the first chunk
1590 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001591 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1592 self.assertEqual(media_upload, request.resumable)
1593 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001594
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001595 # This call will upload the next chunk
1596 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001597 self.assertEqual(request.resumable_uri, "http://upload.example.com/4")
1598 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1599 self.assertEqual('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001600
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001601 # Final call to next_chunk should complete the upload.
1602 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001603 self.assertEqual(body, {"foo": "bar"})
1604 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001605
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001606 def test_resumable_media_good_upload(self):
1607 """Not a multipart upload."""
1608 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1609 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001610
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001611 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1612 request = zoo.animals().insert(media_body=media_upload, body=None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001613 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001614
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001615 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001616
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001617 self.assertEqual(request.body, None)
1618 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001619
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001620 http = HttpMockSequence(
1621 [
1622 ({"status": "200", "location": "http://upload.example.com"}, ""),
1623 (
1624 {
1625 "status": "308",
1626 "location": "http://upload.example.com/2",
1627 "range": "0-12",
1628 },
1629 "",
1630 ),
1631 (
1632 {
1633 "status": "308",
1634 "location": "http://upload.example.com/3",
1635 "range": "0-%d" % (media_upload.size() - 2),
1636 },
1637 "",
1638 ),
1639 ({"status": "200"}, '{"foo": "bar"}'),
1640 ]
1641 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001642
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001643 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001644 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001645 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001646 self.assertEqual(13, status.resumable_progress)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001647
1648 # Two requests should have been made and the resumable_uri should have been
1649 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001650 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001651
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001652 self.assertEqual(media_upload, request.resumable)
1653 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001654
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001655 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001656 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1657 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1658 self.assertEqual(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001659
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001660 # Final call to next_chunk should complete the upload.
1661 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001662 self.assertEqual(body, {"foo": "bar"})
1663 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001664
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001665 def test_resumable_media_good_upload_from_execute(self):
1666 """Not a multipart upload."""
1667 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1668 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001669
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001670 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1671 request = zoo.animals().insert(media_body=media_upload, body=None)
1672 assertUrisEqual(
1673 self,
1674 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1675 request.uri,
1676 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001677
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001678 http = HttpMockSequence(
1679 [
1680 ({"status": "200", "location": "http://upload.example.com"}, ""),
1681 (
1682 {
1683 "status": "308",
1684 "location": "http://upload.example.com/2",
1685 "range": "0-12",
1686 },
1687 "",
1688 ),
1689 (
1690 {
1691 "status": "308",
1692 "location": "http://upload.example.com/3",
1693 "range": "0-%d" % media_upload.size(),
1694 },
1695 "",
1696 ),
1697 ({"status": "200"}, '{"foo": "bar"}'),
1698 ]
1699 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001700
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001701 body = request.execute(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001702 self.assertEqual(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001703
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001704 def test_resumable_media_fail_unknown_response_code_first_request(self):
1705 """Not a multipart upload."""
1706 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1707 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001708
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001709 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1710 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001711
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001712 http = HttpMockSequence(
1713 [({"status": "400", "location": "http://upload.example.com"}, "")]
1714 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001715
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001716 try:
1717 request.execute(http=http)
1718 self.fail("Should have raised ResumableUploadError.")
1719 except ResumableUploadError as e:
1720 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001721
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001722 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1723 """Not a multipart upload."""
1724 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1725 zoo = build("zoo", "v1", http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001726
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001727 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1728 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001729
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001730 http = HttpMockSequence(
1731 [
1732 ({"status": "200", "location": "http://upload.example.com"}, ""),
1733 ({"status": "400"}, ""),
1734 ]
1735 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001736
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001737 self.assertRaises(HttpError, request.execute, http=http)
1738 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001739
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001740 http = HttpMockSequence(
1741 [
1742 ({"status": "308", "range": "0-5"}, ""),
1743 ({"status": "308", "range": "0-6"}, ""),
1744 ]
1745 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001746
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001747 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001748 self.assertEqual(
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001749 status.resumable_progress,
1750 7,
1751 "Should have first checked length and then tried to PUT more.",
1752 )
1753 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001754
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001755 # Put it back in an error state.
1756 http = HttpMockSequence([({"status": "400"}, "")])
1757 self.assertRaises(HttpError, request.execute, http=http)
1758 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001759
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001760 # Pretend the last request that 400'd actually succeeded.
1761 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1762 status, body = request.next_chunk(http=http)
1763 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001764
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001765 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1766 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1767 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001768
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001769 # Set up a seekable stream and try to upload in single chunk.
1770 fd = BytesIO(b'01234"56789"')
1771 media_upload = MediaIoBaseUpload(
1772 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1773 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001774
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001775 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001776
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001777 # The single chunk fails, restart at the right point.
1778 http = HttpMockSequence(
1779 [
1780 ({"status": "200", "location": "http://upload.example.com"}, ""),
1781 (
1782 {
1783 "status": "308",
1784 "location": "http://upload.example.com/2",
1785 "range": "0-4",
1786 },
1787 "",
1788 ),
1789 ({"status": "200"}, "echo_request_body"),
1790 ]
1791 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001792
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001793 body = request.execute(http=http)
1794 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001795
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001796 def test_media_io_base_stream_chunksize_resume(self):
1797 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1798 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001799
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001800 # Set up a seekable stream and try to upload in chunks.
1801 fd = BytesIO(b"0123456789")
1802 media_upload = MediaIoBaseUpload(
1803 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1804 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001805
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001806 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001807
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001808 # The single chunk fails, pull the content sent out of the exception.
1809 http = HttpMockSequence(
1810 [
1811 ({"status": "200", "location": "http://upload.example.com"}, ""),
1812 ({"status": "400"}, "echo_request_body"),
1813 ]
1814 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001815
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001816 try:
1817 body = request.execute(http=http)
1818 except HttpError as e:
1819 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001820
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001821 def test_resumable_media_handle_uploads_of_unknown_size(self):
1822 http = HttpMockSequence(
1823 [
1824 ({"status": "200", "location": "http://upload.example.com"}, ""),
1825 ({"status": "200"}, "echo_request_headers_as_json"),
1826 ]
1827 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001828
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001829 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1830 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001831
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001832 # Create an upload that doesn't know the full size of the media.
1833 class IoBaseUnknownLength(MediaUpload):
1834 def chunksize(self):
1835 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001836
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001837 def mimetype(self):
1838 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001839
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001840 def size(self):
1841 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001842
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001843 def resumable(self):
1844 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001845
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001846 def getbytes(self, begin, length):
1847 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001848
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001849 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001850
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001851 request = zoo.animals().insert(media_body=upload, body=None)
1852 status, body = request.next_chunk(http=http)
1853 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001854
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001855 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1856 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1857 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001858
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001859 class IoBaseHasStream(MediaUpload):
1860 def chunksize(self):
1861 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001862
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001863 def mimetype(self):
1864 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001865
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001866 def size(self):
1867 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001868
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001869 def resumable(self):
1870 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001871
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001872 def getbytes(self, begin, length):
1873 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001874
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001875 def has_stream(self):
1876 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001877
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001878 def stream(self):
1879 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001880
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001881 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001882
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001883 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001884
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001885 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001886
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001887 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001888
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001889 # This should raise an exception because stream() will be called.
1890 http = HttpMockSequence(
1891 [
1892 ({"status": "200", "location": "http://upload.example.com"}, ""),
1893 ({"status": "200"}, "echo_request_headers_as_json"),
1894 ]
1895 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001896
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001897 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001898
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001899 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001900
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001901 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1902 http = HttpMockSequence(
1903 [
1904 ({"status": "200", "location": "http://upload.example.com"}, ""),
1905 ({"status": "200"}, "echo_request_headers_as_json"),
1906 ]
1907 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001908
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001909 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1910 zoo = build("zoo", "v1", http=self.http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001911
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001912 fd = BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001913
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001914 # Create an upload that doesn't know the full size of the media.
1915 upload = MediaIoBaseUpload(
1916 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1917 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001918
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001919 request = zoo.animals().insert(media_body=upload, body=None)
1920 status, body = request.next_chunk(http=http)
1921 self.assertEqual(
1922 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1923 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001924
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001925 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1926 http = HttpMockSequence(
1927 [
1928 ({"status": "200", "location": "http://upload.example.com"}, ""),
1929 ({"status": "400"}, ""),
1930 ]
1931 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001932
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001933 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1934 zoo = build("zoo", "v1", http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001935
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001936 # Create an upload that doesn't know the full size of the media.
1937 fd = BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001938
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001939 upload = MediaIoBaseUpload(
1940 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1941 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001942
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001943 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001944
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001945 # Put it in an error state.
1946 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001947
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001948 http = HttpMockSequence(
1949 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1950 )
1951 try:
1952 # Should resume the upload by first querying the status of the upload.
1953 request.next_chunk(http=http)
1954 except HttpError as e:
1955 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1956 self.assertEqual(
1957 expected,
1958 json.loads(e.content.decode("utf-8")),
1959 "Should send an empty body when requesting the current upload status.",
1960 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001961
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001962 def test_pickle(self):
1963 sorted_resource_keys = [
1964 "_baseUrl",
1965 "_developerKey",
1966 "_dynamic_attrs",
1967 "_http",
1968 "_model",
1969 "_requestBuilder",
1970 "_resourceDesc",
1971 "_rootDesc",
1972 "_schema",
1973 "animals",
1974 "global_",
1975 "load",
1976 "loadNoTemplate",
1977 "my",
1978 "new_batch_http_request",
1979 "query",
1980 "scopedAnimals",
1981 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04001982
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001983 http = HttpMock(datafile("zoo.json"), {"status": "200"})
1984 zoo = build("zoo", "v1", http=http)
1985 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001986
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001987 pickled_zoo = pickle.dumps(zoo)
1988 new_zoo = pickle.loads(pickled_zoo)
1989 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
1990 self.assertTrue(hasattr(new_zoo, "animals"))
1991 self.assertTrue(callable(new_zoo.animals))
1992 self.assertTrue(hasattr(new_zoo, "global_"))
1993 self.assertTrue(callable(new_zoo.global_))
1994 self.assertTrue(hasattr(new_zoo, "load"))
1995 self.assertTrue(callable(new_zoo.load))
1996 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
1997 self.assertTrue(callable(new_zoo.loadNoTemplate))
1998 self.assertTrue(hasattr(new_zoo, "my"))
1999 self.assertTrue(callable(new_zoo.my))
2000 self.assertTrue(hasattr(new_zoo, "query"))
2001 self.assertTrue(callable(new_zoo.query))
2002 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
2003 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002004
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002005 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
2006 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
2007 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
2008 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
2009 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
2010 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
2011 # _http, _model and _schema won't be equal since we will get new
2012 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002013
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002014 def _dummy_zoo_request(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07002015 zoo_contents = read_datafile("zoo.json")
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002016
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002017 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
2018 if "REMOTE_ADDR" in os.environ:
2019 zoo_uri = util._add_query_parameter(
2020 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
2021 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002022
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002023 http = build_http()
2024 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002025
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002026 def wrapped_request(uri, method="GET", *args, **kwargs):
2027 if uri == zoo_uri:
2028 return httplib2.Response({"status": "200"}), zoo_contents
2029 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002030
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002031 http.request = wrapped_request
2032 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002033
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002034 def _dummy_token(self):
2035 access_token = "foo"
2036 client_id = "some_client_id"
2037 client_secret = "cOuDdkfjxxnv+"
2038 refresh_token = "1/0/a.df219fjls0"
2039 token_expiry = datetime.datetime.utcnow()
2040 user_agent = "refresh_checker/1.0"
2041 return OAuth2Credentials(
2042 access_token,
2043 client_id,
2044 client_secret,
2045 refresh_token,
2046 token_expiry,
2047 GOOGLE_TOKEN_URI,
2048 user_agent,
2049 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002050
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002051 def test_pickle_with_credentials(self):
2052 credentials = self._dummy_token()
2053 http = self._dummy_zoo_request()
2054 http = credentials.authorize(http)
2055 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002056
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002057 zoo = build("zoo", "v1", http=http)
2058 pickled_zoo = pickle.dumps(zoo)
2059 new_zoo = pickle.loads(pickled_zoo)
2060 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
2061 new_http = new_zoo._http
2062 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002063
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002064 def test_resumable_media_upload_no_content(self):
2065 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
2066 zoo = build("zoo", "v1", http=self.http)
andrewnestera4a44cf2017-03-31 16:09:31 +03002067
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002068 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
2069 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002070
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002071 self.assertEqual(media_upload, request.resumable)
2072 self.assertEqual(request.body, None)
2073 self.assertEqual(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002074
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002075 http = HttpMockSequence(
2076 [
2077 ({"status": "200", "location": "http://upload.example.com"}, ""),
2078 (
2079 {
2080 "status": "308",
2081 "location": "http://upload.example.com/2",
2082 "range": "0-0",
2083 },
2084 "",
2085 ),
2086 ]
2087 )
andrewnestera4a44cf2017-03-31 16:09:31 +03002088
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002089 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002090 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002091 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002092 self.assertEqual(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03002093
Joe Gregorio708388c2012-06-15 13:43:04 -04002094
Joe Gregorioc5c5a372010-09-22 11:42:32 -04002095class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002096 def test_next_successful_none_on_no_next_page_token(self):
2097 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2098 tasks = build("tasks", "v1", http=self.http)
2099 request = tasks.tasklists().list()
2100 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002101
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002102 def test_next_successful_none_on_empty_page_token(self):
2103 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2104 tasks = build("tasks", "v1", http=self.http)
2105 request = tasks.tasklists().list()
2106 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
2107 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04002108
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002109 def test_next_successful_with_next_page_token(self):
2110 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2111 tasks = build("tasks", "v1", http=self.http)
2112 request = tasks.tasklists().list()
2113 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
2114 parsed = list(urlparse(next_request.uri))
2115 q = parse_qs(parsed[4])
2116 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00002117
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002118 def test_next_successful_with_next_page_token_alternate_name(self):
2119 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
2120 bigquery = build("bigquery", "v2", http=self.http)
2121 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
2122 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
2123 parsed = list(urlparse(next_request.uri))
2124 q = parse_qs(parsed[4])
2125 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04002126
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002127 def test_next_successful_with_next_page_token_in_body(self):
2128 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
2129 logging = build("logging", "v2", http=self.http)
2130 request = logging.entries().list(body={})
2131 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
2132 body = JsonModel().deserialize(next_request.body)
2133 self.assertEqual(body["pageToken"], "123abc")
Thomas Coffee20af04d2017-02-10 15:24:44 -08002134
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002135 def test_next_with_method_with_no_properties(self):
2136 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
2137 service = build("latitude", "v1", http=self.http)
2138 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08002139
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002140 def test_next_nonexistent_with_no_next_page_token(self):
2141 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2142 drive = build("drive", "v3", http=self.http)
2143 drive.changes().watch(body={})
2144 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08002145
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002146 def test_next_successful_with_next_page_token_required(self):
2147 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2148 drive = build("drive", "v3", http=self.http)
2149 request = drive.changes().list(pageToken="startPageToken")
2150 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
2151 parsed = list(urlparse(next_request.uri))
2152 q = parse_qs(parsed[4])
2153 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002154
Joe Gregorioa98733f2011-09-16 10:12:28 -04002155
Joe Gregorio708388c2012-06-15 13:43:04 -04002156class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002157 def test_get_media(self):
2158 http = HttpMock(datafile("zoo.json"), {"status": "200"})
2159 zoo = build("zoo", "v1", http=http)
2160 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04002161
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002162 parsed = urlparse(request.uri)
2163 q = parse_qs(parsed[4])
2164 self.assertEqual(q["alt"], ["media"])
2165 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04002166
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002167 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
2168 response = request.execute(http=http)
2169 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04002170
2171
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002172if __name__ == "__main__":
2173 unittest.main()