blob: 1a57ad3a2b8111bcac0f618ca3b9a66f2c8d224d [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:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500450 plus = build("plus", "v1", None, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700451 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:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500458 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700459 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):
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500476 build("plus", "v1", http=http, credentials=mock.sentinel.credentials, static_discovery=False)
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 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500488 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600489 )
490
491 def test_credentials_and_credentials_file_mutually_exclusive(self):
492 with self.assertRaises(google.api_core.exceptions.DuplicateCredentialArgs):
493 build(
494 "plus",
495 "v1",
496 credentials=mock.sentinel.credentials,
497 client_options=google.api_core.client_options.ClientOptions(
498 credentials_file="credentials.json"
499 ),
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500500 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -0600501 )
502
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500503
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100504class DiscoveryFromDocument(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700505 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400506
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700507 def test_can_build_from_local_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700508 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700509 plus = build_from_document(
510 discovery,
511 base="https://www.googleapis.com/",
512 credentials=self.MOCK_CREDENTIALS,
513 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700514 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700515 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500516
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700517 def test_can_build_from_local_deserialized_document(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700518 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700519 discovery = json.loads(discovery)
520 plus = build_from_document(
521 discovery,
522 base="https://www.googleapis.com/",
523 credentials=self.MOCK_CREDENTIALS,
524 )
arithmetic1728981eadf2020-06-02 10:20:10 -0700525 self.assertIsNotNone(plus)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700526 self.assertTrue(hasattr(plus, "activities"))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400527
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700528 def test_building_with_base_remembers_base(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700529 discovery = read_datafile("plus.json")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400530
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700531 base = "https://www.example.com/"
532 plus = build_from_document(
533 discovery, base=base, credentials=self.MOCK_CREDENTIALS
534 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700535 self.assertEqual("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100536
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700537 def test_building_with_optional_http_with_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700538 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700539 plus = build_from_document(
540 discovery,
541 base="https://www.googleapis.com/",
542 credentials=self.MOCK_CREDENTIALS,
543 )
Igor Maravić22435292017-01-19 22:28:22 +0100544
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700545 # plus service requires Authorization, hence we expect to see AuthorizedHttp object here
546 self.assertIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
547 self.assertIsInstance(plus._http.http, httplib2.Http)
548 self.assertIsInstance(plus._http.http.timeout, int)
549 self.assertGreater(plus._http.http.timeout, 0)
Igor Maravić22435292017-01-19 22:28:22 +0100550
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700551 def test_building_with_optional_http_with_no_authorization(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700552 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700553 # Cleanup auth field, so we would use plain http client
554 discovery = json.loads(discovery)
555 discovery["auth"] = {}
556 discovery = json.dumps(discovery)
Igor Maravić22435292017-01-19 22:28:22 +0100557
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700558 plus = build_from_document(
559 discovery, base="https://www.googleapis.com/", credentials=None
560 )
561 # plus service requires Authorization
562 self.assertIsInstance(plus._http, httplib2.Http)
563 self.assertIsInstance(plus._http.timeout, int)
564 self.assertGreater(plus._http.timeout, 0)
Jonathan Wayne Parrotta6e6fbd2015-07-16 15:33:57 -0700565
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700566 def test_building_with_explicit_http(self):
567 http = HttpMock()
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700568 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700569 plus = build_from_document(
570 discovery, base="https://www.googleapis.com/", http=http
571 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700572 self.assertEqual(plus._http, http)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100573
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700574 def test_building_with_developer_key_skips_adc(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700575 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700576 plus = build_from_document(
577 discovery, base="https://www.googleapis.com/", developerKey="123"
578 )
579 self.assertIsInstance(plus._http, httplib2.Http)
580 # It should not be an AuthorizedHttp, because that would indicate that
581 # application default credentials were used.
582 self.assertNotIsInstance(plus._http, google_auth_httplib2.AuthorizedHttp)
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800583
Bu Sun Kim98888da2020-09-23 11:10:39 -0600584 def test_building_with_context_manager(self):
585 discovery = read_datafile("plus.json")
586 with mock.patch("httplib2.Http") as http:
587 with build_from_document(discovery, base="https://www.googleapis.com/", credentials=self.MOCK_CREDENTIALS) as plus:
588 self.assertIsNotNone(plus)
589 self.assertTrue(hasattr(plus, "activities"))
590 plus._http.http.close.assert_called_once()
591
592 def test_resource_close(self):
593 discovery = read_datafile("plus.json")
594 with mock.patch("httplib2.Http") as http:
595 plus = build_from_document(
596 discovery,
597 base="https://www.googleapis.com/",
598 credentials=self.MOCK_CREDENTIALS,
599 )
600 plus.close()
601 plus._http.http.close.assert_called_once()
602
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700603 def test_api_endpoint_override_from_client_options(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700604 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700605 api_endpoint = "https://foo.googleapis.com/"
606 options = google.api_core.client_options.ClientOptions(
607 api_endpoint=api_endpoint
608 )
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700609 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700610 discovery, client_options=options, credentials=self.MOCK_CREDENTIALS
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700611 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700612
613 self.assertEqual(plus._baseUrl, api_endpoint)
614
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300615 def test_api_endpoint_override_from_client_options_mapping_object(self):
616
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700617 discovery = read_datafile("plus.json")
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300618 api_endpoint = "https://foo.googleapis.com/"
619 mapping_object = defaultdict(str)
Bu Sun Kim790e7022020-09-11 20:18:06 -0600620 mapping_object["api_endpoint"] = api_endpoint
621 plus = build_from_document(discovery, client_options=mapping_object)
Pavel Kiselev21af37b2020-06-18 19:50:03 +0300622
623 self.assertEqual(plus._baseUrl, api_endpoint)
624
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700625 def test_api_endpoint_override_from_client_options_dict(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700626 discovery = read_datafile("plus.json")
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700627 api_endpoint = "https://foo.googleapis.com/"
628 plus = build_from_document(
arithmetic1728981eadf2020-06-02 10:20:10 -0700629 discovery,
Bu Sun Kim8ed1dcd2020-05-14 00:42:22 -0700630 client_options={"api_endpoint": api_endpoint},
arithmetic1728981eadf2020-06-02 10:20:10 -0700631 credentials=self.MOCK_CREDENTIALS,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700632 )
633
634 self.assertEqual(plus._baseUrl, api_endpoint)
635
Bu Sun Kim790e7022020-09-11 20:18:06 -0600636 def test_scopes_from_client_options(self):
637 discovery = read_datafile("plus.json")
638
639 with mock.patch("googleapiclient._auth.default_credentials") as default:
640 plus = build_from_document(
641 discovery, client_options={"scopes": ["1", "2"]},
642 )
643
644 default.assert_called_once_with(scopes=["1", "2"], quota_project_id=None)
645
646 def test_quota_project_from_client_options(self):
647 discovery = read_datafile("plus.json")
648
649 with mock.patch("googleapiclient._auth.default_credentials") as default:
650 plus = build_from_document(
651 discovery,
652 client_options=google.api_core.client_options.ClientOptions(
653 quota_project_id="my-project"
654 ),
655 )
656
657 default.assert_called_once_with(scopes=None, quota_project_id="my-project")
658
659 def test_credentials_file_from_client_options(self):
660 discovery = read_datafile("plus.json")
661
662 with mock.patch("googleapiclient._auth.credentials_from_file") as default:
663 plus = build_from_document(
664 discovery,
665 client_options=google.api_core.client_options.ClientOptions(
666 credentials_file="credentials.json"
667 ),
668 )
669
670 default.assert_called_once_with(
671 "credentials.json", scopes=None, quota_project_id=None
672 )
673
Jon Wayne Parrott068eb352017-02-08 10:13:06 -0800674
arithmetic1728981eadf2020-06-02 10:20:10 -0700675REGULAR_ENDPOINT = "https://www.googleapis.com/plus/v1/"
676MTLS_ENDPOINT = "https://www.mtls.googleapis.com/plus/v1/"
677
678
679class DiscoveryFromDocumentMutualTLS(unittest.TestCase):
680 MOCK_CREDENTIALS = mock.Mock(spec=google.auth.credentials.Credentials)
681 ADC_CERT_PATH = "adc_cert_path"
682 ADC_KEY_PATH = "adc_key_path"
683 ADC_PASSPHRASE = "adc_passphrase"
684
arithmetic17282fc5ca12020-08-27 14:08:12 -0700685 def check_http_client_cert(self, resource, has_client_cert="false"):
arithmetic1728981eadf2020-06-02 10:20:10 -0700686 if isinstance(resource._http, google_auth_httplib2.AuthorizedHttp):
687 certs = list(resource._http.http.certificates.iter(""))
688 else:
689 certs = list(resource._http.certificates.iter(""))
arithmetic17282fc5ca12020-08-27 14:08:12 -0700690 if has_client_cert == "true":
arithmetic1728981eadf2020-06-02 10:20:10 -0700691 self.assertEqual(len(certs), 1)
692 self.assertEqual(
693 certs[0], (self.ADC_KEY_PATH, self.ADC_CERT_PATH, self.ADC_PASSPHRASE)
694 )
695 else:
696 self.assertEqual(len(certs), 0)
697
698 def client_encrypted_cert_source(self):
699 return self.ADC_CERT_PATH, self.ADC_KEY_PATH, self.ADC_PASSPHRASE
700
arithmetic17282fc5ca12020-08-27 14:08:12 -0700701 @parameterized.expand(
702 [
703 ("never", "true"),
704 ("auto", "true"),
705 ("always", "true"),
706 ("never", "false"),
707 ("auto", "false"),
708 ("always", "false"),
709 ]
710 )
711 def test_mtls_not_trigger_if_http_provided(self, use_mtls_env, use_client_cert):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700712 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700713
arithmetic17282fc5ca12020-08-27 14:08:12 -0700714 with mock.patch.dict(
715 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
716 ):
717 with mock.patch.dict(
718 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
719 ):
720 plus = build_from_document(discovery, http=httplib2.Http())
721 self.assertIsNotNone(plus)
722 self.assertEqual(plus._baseUrl, REGULAR_ENDPOINT)
723 self.check_http_client_cert(plus, has_client_cert="false")
arithmetic1728981eadf2020-06-02 10:20:10 -0700724
725 @parameterized.expand(
726 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700727 ("never", "true"),
728 ("auto", "true"),
729 ("always", "true"),
730 ("never", "false"),
731 ("auto", "false"),
732 ("always", "false"),
arithmetic1728981eadf2020-06-02 10:20:10 -0700733 ]
734 )
arithmetic17282fc5ca12020-08-27 14:08:12 -0700735 def test_exception_with_client_cert_source(self, use_mtls_env, use_client_cert):
736 discovery = read_datafile("plus.json")
737 with mock.patch.dict(
738 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
739 ):
740 with mock.patch.dict(
741 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
742 ):
743 with self.assertRaises(MutualTLSChannelError):
744 build_from_document(
745 discovery,
746 credentials=self.MOCK_CREDENTIALS,
747 client_options={"client_cert_source": mock.Mock()},
748 )
749
750 @parameterized.expand(
751 [
752 ("never", "true", REGULAR_ENDPOINT),
753 ("auto", "true", MTLS_ENDPOINT),
754 ("always", "true", MTLS_ENDPOINT),
755 ("never", "false", REGULAR_ENDPOINT),
756 ("auto", "false", REGULAR_ENDPOINT),
757 ("always", "false", MTLS_ENDPOINT),
758 ]
759 )
760 def test_mtls_with_provided_client_cert(
761 self, use_mtls_env, use_client_cert, base_url
762 ):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700763 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700764
arithmetic17282fc5ca12020-08-27 14:08:12 -0700765 with mock.patch.dict(
766 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
767 ):
768 with mock.patch.dict(
769 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
770 ):
771 plus = build_from_document(
772 discovery,
773 credentials=self.MOCK_CREDENTIALS,
774 client_options={
775 "client_encrypted_cert_source": self.client_encrypted_cert_source
776 },
777 )
778 self.assertIsNotNone(plus)
779 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
780 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700781
arithmetic17282fc5ca12020-08-27 14:08:12 -0700782 @parameterized.expand(
783 [
784 ("never", "true"),
785 ("auto", "true"),
786 ("always", "true"),
787 ("never", "false"),
788 ("auto", "false"),
789 ("always", "false"),
790 ]
791 )
792 def test_endpoint_not_switch(self, use_mtls_env, use_client_cert):
arithmetic1728981eadf2020-06-02 10:20:10 -0700793 # Test endpoint is not switched if user provided api endpoint
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700794 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700795
arithmetic17282fc5ca12020-08-27 14:08:12 -0700796 with mock.patch.dict(
797 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
798 ):
799 with mock.patch.dict(
800 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
801 ):
802 plus = build_from_document(
803 discovery,
804 credentials=self.MOCK_CREDENTIALS,
805 client_options={
806 "api_endpoint": "https://foo.googleapis.com",
807 "client_encrypted_cert_source": self.client_encrypted_cert_source,
808 },
809 )
810 self.assertIsNotNone(plus)
811 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
812 self.assertEqual(plus._baseUrl, "https://foo.googleapis.com")
arithmetic1728981eadf2020-06-02 10:20:10 -0700813
814 @parameterized.expand(
815 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700816 ("never", "true", REGULAR_ENDPOINT),
817 ("auto", "true", MTLS_ENDPOINT),
818 ("always", "true", MTLS_ENDPOINT),
819 ("never", "false", REGULAR_ENDPOINT),
820 ("auto", "false", REGULAR_ENDPOINT),
821 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700822 ]
823 )
824 @mock.patch(
825 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
826 )
827 @mock.patch(
828 "google.auth.transport.mtls.default_client_encrypted_cert_source", autospec=True
829 )
830 def test_mtls_with_default_client_cert(
831 self,
832 use_mtls_env,
arithmetic17282fc5ca12020-08-27 14:08:12 -0700833 use_client_cert,
arithmetic1728981eadf2020-06-02 10:20:10 -0700834 base_url,
835 default_client_encrypted_cert_source,
836 has_default_client_cert_source,
837 ):
838 has_default_client_cert_source.return_value = True
839 default_client_encrypted_cert_source.return_value = (
840 self.client_encrypted_cert_source
841 )
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700842 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700843
arithmetic17282fc5ca12020-08-27 14:08:12 -0700844 with mock.patch.dict(
845 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
846 ):
847 with mock.patch.dict(
848 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
849 ):
850 plus = build_from_document(
851 discovery,
852 credentials=self.MOCK_CREDENTIALS,
853 adc_cert_path=self.ADC_CERT_PATH,
854 adc_key_path=self.ADC_KEY_PATH,
855 )
856 self.assertIsNotNone(plus)
857 self.check_http_client_cert(plus, has_client_cert=use_client_cert)
858 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700859
860 @parameterized.expand(
861 [
arithmetic17282fc5ca12020-08-27 14:08:12 -0700862 ("never", "true", REGULAR_ENDPOINT),
863 ("auto", "true", REGULAR_ENDPOINT),
864 ("always", "true", MTLS_ENDPOINT),
865 ("never", "false", REGULAR_ENDPOINT),
866 ("auto", "false", REGULAR_ENDPOINT),
867 ("always", "false", MTLS_ENDPOINT),
arithmetic1728981eadf2020-06-02 10:20:10 -0700868 ]
869 )
870 @mock.patch(
871 "google.auth.transport.mtls.has_default_client_cert_source", autospec=True
872 )
873 def test_mtls_with_no_client_cert(
arithmetic17282fc5ca12020-08-27 14:08:12 -0700874 self, use_mtls_env, use_client_cert, base_url, has_default_client_cert_source
arithmetic1728981eadf2020-06-02 10:20:10 -0700875 ):
876 has_default_client_cert_source.return_value = False
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700877 discovery = read_datafile("plus.json")
arithmetic1728981eadf2020-06-02 10:20:10 -0700878
arithmetic17282fc5ca12020-08-27 14:08:12 -0700879 with mock.patch.dict(
880 "os.environ", {"GOOGLE_API_USE_MTLS_ENDPOINT": use_mtls_env}
881 ):
882 with mock.patch.dict(
883 "os.environ", {"GOOGLE_API_USE_CLIENT_CERTIFICATE": use_client_cert}
884 ):
885 plus = build_from_document(
886 discovery,
887 credentials=self.MOCK_CREDENTIALS,
888 adc_cert_path=self.ADC_CERT_PATH,
889 adc_key_path=self.ADC_KEY_PATH,
890 )
891 self.assertIsNotNone(plus)
892 self.check_http_client_cert(plus, has_client_cert="false")
893 self.assertEqual(plus._baseUrl, base_url)
arithmetic1728981eadf2020-06-02 10:20:10 -0700894
895
Joe Gregorioa98733f2011-09-16 10:12:28 -0400896class DiscoveryFromHttp(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700897 def setUp(self):
898 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400899
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700900 def tearDown(self):
901 os.environ = self.old_environ
Joe Gregorio583d9e42011-09-16 15:54:15 -0400902
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700903 def test_userip_is_added_to_discovery_uri(self):
904 # build() will raise an HttpError on a 400, use this to pick the request uri
905 # out of the raised exception.
906 os.environ["REMOTE_ADDR"] = "10.0.0.1"
907 try:
908 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700909 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700910 )
911 zoo = build(
912 "zoo",
913 "v1",
914 http=http,
915 developerKey=None,
916 discoveryServiceUrl="http://example.com",
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500917 static_discovery=False,
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700918 )
919 self.fail("Should have raised an exception.")
920 except HttpError as e:
921 self.assertEqual(e.uri, "http://example.com?userIp=10.0.0.1")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400922
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700923 def test_userip_missing_is_not_added_to_discovery_uri(self):
924 # build() will raise an HttpError on a 400, use this to pick the request uri
925 # out of the raised exception.
926 try:
927 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700928 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700929 )
930 zoo = build(
931 "zoo",
932 "v1",
933 http=http,
934 developerKey=None,
935 discoveryServiceUrl="http://example.com",
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500936 static_discovery=False,
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700937 )
938 self.fail("Should have raised an exception.")
939 except HttpError as e:
940 self.assertEqual(e.uri, "http://example.com")
Joe Gregorioa98733f2011-09-16 10:12:28 -0400941
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700942 def test_key_is_added_to_discovery_uri(self):
943 # build() will raise an HttpError on a 400, use this to pick the request uri
944 # out of the raised exception.
945 try:
946 http = HttpMockSequence(
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700947 [({"status": "400"}, read_datafile("zoo.json", "rb"))]
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700948 )
949 zoo = build(
950 "zoo",
951 "v1",
952 http=http,
953 developerKey="foo",
954 discoveryServiceUrl="http://example.com",
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500955 static_discovery=False,
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700956 )
957 self.fail("Should have raised an exception.")
958 except HttpError as e:
959 self.assertEqual(e.uri, "http://example.com?key=foo")
Arunpn9d779cc2018-11-30 10:25:01 -0800960
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700961 def test_discovery_loading_from_v2_discovery_uri(self):
962 http = HttpMockSequence(
963 [
964 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700965 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700966 ]
967 )
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500968 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700969 self.assertTrue(hasattr(zoo, "animals"))
970
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700971 def test_api_endpoint_override_from_client_options(self):
972 http = HttpMockSequence(
973 [
974 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700975 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700976 ]
977 )
978 api_endpoint = "https://foo.googleapis.com/"
979 options = google.api_core.client_options.ClientOptions(
980 api_endpoint=api_endpoint
981 )
982 zoo = build(
Anthonios Partheniou32d1c592021-01-14 18:48:59 -0500983 "zoo", "v1", http=http, cache_discovery=False, client_options=options, static_discovery=False
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700984 )
985 self.assertEqual(zoo._baseUrl, api_endpoint)
986
987 def test_api_endpoint_override_from_client_options_dict(self):
988 http = HttpMockSequence(
989 [
990 ({"status": "404"}, "Not found"),
Dmitry Frenkelf3348f92020-07-15 13:05:58 -0700991 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -0700992 ]
993 )
994 api_endpoint = "https://foo.googleapis.com/"
995 zoo = build(
996 "zoo",
997 "v1",
998 http=http,
999 cache_discovery=False,
1000 client_options={"api_endpoint": api_endpoint},
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001001 static_discovery=False,
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001002 )
1003 self.assertEqual(zoo._baseUrl, api_endpoint)
1004
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001005 def test_discovery_with_empty_version_uses_v2(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001006 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001007 build("zoo", version=None, http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001008 validate_discovery_requests(self, http, "zoo", None, V2_DISCOVERY_URI)
1009
1010 def test_discovery_with_empty_version_preserves_custom_uri(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001011 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001012 custom_discovery_uri = "https://foo.bar/$discovery"
1013 build(
Bu Sun Kim790e7022020-09-11 20:18:06 -06001014 "zoo",
1015 version=None,
1016 http=http,
1017 cache_discovery=False,
1018 discoveryServiceUrl=custom_discovery_uri,
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001019 static_discovery=False,
Bu Sun Kim790e7022020-09-11 20:18:06 -06001020 )
1021 validate_discovery_requests(self, http, "zoo", None, custom_discovery_uri)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001022
1023 def test_discovery_with_valid_version_uses_v1(self):
Bu Sun Kim790e7022020-09-11 20:18:06 -06001024 http = HttpMockSequence([({"status": "200"}, read_datafile("zoo.json", "rb")),])
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001025 build("zoo", version="v123", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelcd4e8f42020-07-30 12:34:03 -07001026 validate_discovery_requests(self, http, "zoo", "v123", V1_DISCOVERY_URI)
1027
Joe Gregorioa98733f2011-09-16 10:12:28 -04001028
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001029class DiscoveryRetryFromHttp(unittest.TestCase):
1030 def test_repeated_500_retries_and_fails(self):
1031 http = HttpMockSequence(
1032 [
1033 ({"status": "500"}, read_datafile("500.json", "rb")),
1034 ({"status": "503"}, read_datafile("503.json", "rb")),
1035 ]
1036 )
1037 with self.assertRaises(HttpError):
1038 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001039 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001040
1041 mocked_sleep.assert_called_once()
1042 # We also want to verify that we stayed with v1 discovery
1043 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1044
1045 def test_v2_repeated_500_retries_and_fails(self):
1046 http = HttpMockSequence(
1047 [
1048 ({"status": "404"}, "Not found"), # last v1 discovery call
1049 ({"status": "500"}, read_datafile("500.json", "rb")),
1050 ({"status": "503"}, read_datafile("503.json", "rb")),
1051 ]
1052 )
1053 with self.assertRaises(HttpError):
1054 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001055 build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001056
1057 mocked_sleep.assert_called_once()
1058 # We also want to verify that we switched to v2 discovery
1059 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1060
1061 def test_single_500_retries_and_succeeds(self):
1062 http = HttpMockSequence(
1063 [
1064 ({"status": "500"}, read_datafile("500.json", "rb")),
1065 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1066 ]
1067 )
1068 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001069 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001070
1071 self.assertTrue(hasattr(zoo, "animals"))
1072 mocked_sleep.assert_called_once()
1073 # We also want to verify that we stayed with v1 discovery
1074 validate_discovery_requests(self, http, "zoo", "v1", V1_DISCOVERY_URI)
1075
1076 def test_single_500_then_404_retries_and_succeeds(self):
1077 http = HttpMockSequence(
1078 [
1079 ({"status": "500"}, read_datafile("500.json", "rb")),
1080 ({"status": "404"}, "Not found"), # last v1 discovery call
1081 ({"status": "200"}, read_datafile("zoo.json", "rb")),
1082 ]
1083 )
1084 with mock.patch("time.sleep") as mocked_sleep:
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001085 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001086
1087 self.assertTrue(hasattr(zoo, "animals"))
1088 mocked_sleep.assert_called_once()
1089 # We also want to verify that we switched to v2 discovery
1090 validate_discovery_requests(self, http, "zoo", "v1", V2_DISCOVERY_URI)
1091
1092
Takashi Matsuo30125122015-08-19 11:42:32 -07001093class DiscoveryFromAppEngineCache(unittest.TestCase):
Zev Goldstein09e64472020-05-14 16:29:20 -04001094 def setUp(self):
1095 self.old_environ = os.environ.copy()
1096 os.environ["APPENGINE_RUNTIME"] = "python27"
1097
1098 def tearDown(self):
1099 os.environ = self.old_environ
1100
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001101 def test_appengine_memcache(self):
1102 # Hack module import
1103 self.orig_import = __import__
1104 self.mocked_api = mock.MagicMock()
Takashi Matsuo30125122015-08-19 11:42:32 -07001105
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001106 def import_mock(name, *args, **kwargs):
1107 if name == "google.appengine.api":
1108 return self.mocked_api
1109 return self.orig_import(name, *args, **kwargs)
Takashi Matsuo30125122015-08-19 11:42:32 -07001110
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001111 import_fullname = "__builtin__.__import__"
1112 if sys.version_info[0] >= 3:
1113 import_fullname = "builtins.__import__"
Takashi Matsuo30125122015-08-19 11:42:32 -07001114
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001115 with mock.patch(import_fullname, side_effect=import_mock):
1116 namespace = "google-api-client"
1117 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001118
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001119 self.mocked_api.memcache.get.return_value = None
Takashi Matsuo30125122015-08-19 11:42:32 -07001120
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001121 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001122
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001123 # memcache.get is called once
1124 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1125 self.mocked_api.memcache.get.assert_called_once_with(
1126 url, namespace=namespace
1127 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001128
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001129 # memcache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001130 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001131 self.mocked_api.memcache.set.assert_called_once_with(
1132 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1133 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001134
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001135 # Returns the cached content this time.
1136 self.mocked_api.memcache.get.return_value = content
Takashi Matsuo30125122015-08-19 11:42:32 -07001137
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001138 # Make sure the contents are returned from the cache.
1139 # (Otherwise it should through an error)
1140 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001141
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001142 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001143
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001144 # memcache.get is called twice
1145 self.mocked_api.memcache.get.assert_has_calls(
1146 [
1147 mock.call(url, namespace=namespace),
1148 mock.call(url, namespace=namespace),
1149 ]
1150 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001151
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001152 # memcahce.set is called just once
1153 self.mocked_api.memcache.set.assert_called_once_with(
1154 url, content, time=DISCOVERY_DOC_MAX_AGE, namespace=namespace
1155 )
Takashi Matsuo30125122015-08-19 11:42:32 -07001156
1157
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001158class DiscoveryFromStaticDocument(unittest.TestCase):
1159 def test_retrieve_from_local_when_static_discovery_true(self):
1160 http = HttpMockSequence([({"status": "400"}, "")])
1161 drive = build("drive", "v3", http=http, cache_discovery=False,
1162 static_discovery=True)
1163 self.assertIsNotNone(drive)
1164 self.assertTrue(hasattr(drive, "files"))
1165
1166 def test_retrieve_from_internet_when_static_discovery_false(self):
1167 http = HttpMockSequence([({"status": "400"}, "")])
1168 with self.assertRaises(HttpError):
1169 build("drive", "v3", http=http, cache_discovery=False,
1170 static_discovery=False)
1171
1172 def test_unknown_api_when_static_discovery_true(self):
1173 with self.assertRaises(UnknownApiNameOrVersion):
1174 build("doesnotexist", "v3", cache_discovery=False,
1175 static_discovery=True)
1176
1177
Takashi Matsuo30125122015-08-19 11:42:32 -07001178class DictCache(Cache):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001179 def __init__(self):
1180 self.d = {}
1181
1182 def get(self, url):
1183 return self.d.get(url, None)
1184
1185 def set(self, url, content):
1186 self.d[url] = content
1187
1188 def contains(self, url):
1189 return url in self.d
Takashi Matsuo30125122015-08-19 11:42:32 -07001190
1191
1192class DiscoveryFromFileCache(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001193 def test_file_based_cache(self):
1194 cache = mock.Mock(wraps=DictCache())
1195 with mock.patch(
1196 "googleapiclient.discovery_cache.autodetect", return_value=cache
1197 ):
1198 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001199
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001200 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001201
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001202 # cache.get is called once
1203 url = "https://www.googleapis.com/discovery/v1/apis/plus/v1/rest"
1204 cache.get.assert_called_once_with(url)
Takashi Matsuo30125122015-08-19 11:42:32 -07001205
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001206 # cache.set is called once
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001207 content = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001208 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001209
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001210 # Make sure there is a cache entry for the plus v1 discovery doc.
1211 self.assertTrue(cache.contains(url))
Takashi Matsuo30125122015-08-19 11:42:32 -07001212
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001213 # Make sure the contents are returned from the cache.
1214 # (Otherwise it should through an error)
1215 self.http = HttpMock(None, {"status": "200"})
Takashi Matsuo30125122015-08-19 11:42:32 -07001216
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001217 plus = build("plus", "v1", http=self.http, static_discovery=False)
Takashi Matsuo30125122015-08-19 11:42:32 -07001218
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001219 # cache.get is called twice
1220 cache.get.assert_has_calls([mock.call(url), mock.call(url)])
Takashi Matsuo30125122015-08-19 11:42:32 -07001221
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001222 # cahce.set is called just once
1223 cache.set.assert_called_once_with(url, content)
Takashi Matsuo30125122015-08-19 11:42:32 -07001224
1225
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001226class Discovery(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001227 def test_method_error_checking(self):
1228 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001229 plus = build("plus", "v1", http=self.http, static_discovery=False)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001230
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001231 # Missing required parameters
1232 try:
1233 plus.activities().list()
1234 self.fail()
1235 except TypeError as e:
1236 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001237
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001238 # Missing required parameters even if supplied as None.
1239 try:
1240 plus.activities().list(collection=None, userId=None)
1241 self.fail()
1242 except TypeError as e:
1243 self.assertTrue("Missing" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001244
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001245 # Parameter doesn't match regex
1246 try:
1247 plus.activities().list(collection="not_a_collection_name", userId="me")
1248 self.fail()
1249 except TypeError as e:
1250 self.assertTrue("not an allowed value" in str(e))
Joe Gregorio2467afa2012-06-20 12:21:25 -04001251
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001252 # Unexpected parameter
1253 try:
1254 plus.activities().list(flubber=12)
1255 self.fail()
1256 except TypeError as e:
1257 self.assertTrue("unexpected" in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001258
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001259 def _check_query_types(self, request):
1260 parsed = urlparse(request.uri)
1261 q = parse_qs(parsed[4])
1262 self.assertEqual(q["q"], ["foo"])
1263 self.assertEqual(q["i"], ["1"])
1264 self.assertEqual(q["n"], ["1.0"])
1265 self.assertEqual(q["b"], ["false"])
1266 self.assertEqual(q["a"], ["[1, 2, 3]"])
1267 self.assertEqual(q["o"], ["{'a': 1}"])
1268 self.assertEqual(q["e"], ["bar"])
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001269
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001270 def test_type_coercion(self):
1271 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001272 zoo = build("zoo", "v1", http=http, static_discovery=False)
Joe Gregoriobee86832011-02-22 10:00:19 -05001273
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001274 request = zoo.query(
1275 q="foo", i=1.0, n=1.0, b=0, a=[1, 2, 3], o={"a": 1}, e="bar"
1276 )
1277 self._check_query_types(request)
1278 request = zoo.query(
1279 q="foo", i=1, n=1, b=False, a=[1, 2, 3], o={"a": 1}, e="bar"
1280 )
1281 self._check_query_types(request)
Joe Gregoriobee86832011-02-22 10:00:19 -05001282
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001283 request = zoo.query(
1284 q="foo", i="1", n="1", b="", a=[1, 2, 3], o={"a": 1}, e="bar", er="two"
1285 )
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001286
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001287 request = zoo.query(
1288 q="foo",
1289 i="1",
1290 n="1",
1291 b="",
1292 a=[1, 2, 3],
1293 o={"a": 1},
1294 e="bar",
1295 er=["one", "three"],
1296 rr=["foo", "bar"],
1297 )
1298 self._check_query_types(request)
Joe Gregorio6804c7a2011-11-18 14:30:32 -05001299
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001300 # Five is right out.
1301 self.assertRaises(TypeError, zoo.query, er=["one", "five"])
Joe Gregoriobee86832011-02-22 10:00:19 -05001302
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001303 def test_optional_stack_query_parameters(self):
1304 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001305 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001306 request = zoo.query(trace="html", fields="description")
Craig Citro1e742822012-03-01 12:59:22 -08001307
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001308 parsed = urlparse(request.uri)
1309 q = parse_qs(parsed[4])
1310 self.assertEqual(q["trace"], ["html"])
1311 self.assertEqual(q["fields"], ["description"])
Joe Gregorio13217952011-02-22 15:37:38 -05001312
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001313 def test_string_params_value_of_none_get_dropped(self):
1314 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001315 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001316 request = zoo.query(trace=None, fields="description")
Joe Gregoriof4153422011-03-18 22:45:18 -04001317
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001318 parsed = urlparse(request.uri)
1319 q = parse_qs(parsed[4])
1320 self.assertFalse("trace" in q)
Joe Gregorio2467afa2012-06-20 12:21:25 -04001321
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001322 def test_model_added_query_parameters(self):
1323 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001324 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001325 request = zoo.animals().get(name="Lion")
Joe Gregorio4b4002f2012-06-14 15:41:01 -04001326
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001327 parsed = urlparse(request.uri)
1328 q = parse_qs(parsed[4])
1329 self.assertEqual(q["alt"], ["json"])
1330 self.assertEqual(request.headers["accept"], "application/json")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001331
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001332 def test_fallback_to_raw_model(self):
1333 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001334 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001335 request = zoo.animals().getmedia(name="Lion")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001336
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001337 parsed = urlparse(request.uri)
1338 q = parse_qs(parsed[4])
1339 self.assertTrue("alt" not in q)
1340 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorioe08a1662011-12-07 09:48:22 -05001341
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001342 def test_patch(self):
1343 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001344 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001345 request = zoo.animals().patch(name="lion", body='{"description": "foo"}')
Joe Gregorioe08a1662011-12-07 09:48:22 -05001346
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001347 self.assertEqual(request.method, "PATCH")
Joe Gregoriof4153422011-03-18 22:45:18 -04001348
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001349 def test_batch_request_from_discovery(self):
1350 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1351 # zoo defines a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001352 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001353 batch_request = zoo.new_batch_http_request()
1354 self.assertEqual(
1355 batch_request._batch_uri, "https://www.googleapis.com/batchZoo"
1356 )
Joe Gregoriof4153422011-03-18 22:45:18 -04001357
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001358 def test_batch_request_from_default(self):
1359 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
1360 # plus does not define a batchPath
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001361 plus = build("plus", "v1", http=self.http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001362 batch_request = plus.new_batch_http_request()
1363 self.assertEqual(batch_request._batch_uri, "https://www.googleapis.com/batch")
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001364
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001365 def test_tunnel_patch(self):
1366 http = HttpMockSequence(
1367 [
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001368 ({"status": "200"}, read_datafile("zoo.json", "rb")),
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001369 ({"status": "200"}, "echo_request_headers_as_json"),
1370 ]
1371 )
1372 http = tunnel_patch(http)
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001373 zoo = build("zoo", "v1", http=http, cache_discovery=False, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001374 resp = zoo.animals().patch(name="lion", body='{"description": "foo"}').execute()
Pepper Lebeck-Jobe860836f2015-06-12 20:42:23 -04001375
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001376 self.assertTrue("x-http-method-override" in resp)
Joe Gregoriof4153422011-03-18 22:45:18 -04001377
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001378 def test_plus_resources(self):
1379 self.http = HttpMock(datafile("plus.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001380 plus = build("plus", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001381 self.assertTrue(getattr(plus, "activities"))
1382 self.assertTrue(getattr(plus, "people"))
Joe Gregorioca876e42011-02-22 19:39:42 -05001383
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001384 def test_oauth2client_credentials(self):
1385 credentials = mock.Mock(spec=GoogleCredentials)
1386 credentials.create_scoped_required.return_value = False
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001387
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001388 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001389 service = build_from_document(discovery, credentials=credentials)
1390 self.assertEqual(service._http, credentials.authorize.return_value)
Orest Bolohane92c9002014-05-30 11:15:43 -07001391
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001392 def test_google_auth_credentials(self):
1393 credentials = mock.Mock(spec=google.auth.credentials.Credentials)
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001394 discovery = read_datafile("plus.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001395 service = build_from_document(discovery, credentials=credentials)
Orest Bolohane92c9002014-05-30 11:15:43 -07001396
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001397 self.assertIsInstance(service._http, google_auth_httplib2.AuthorizedHttp)
1398 self.assertEqual(service._http.credentials, credentials)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001399
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001400 def test_no_scopes_no_credentials(self):
1401 # Zoo doesn't have scopes
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001402 discovery = read_datafile("zoo.json")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001403 service = build_from_document(discovery)
1404 # Should be an ordinary httplib2.Http instance and not AuthorizedHttp.
1405 self.assertIsInstance(service._http, httplib2.Http)
Jon Wayne Parrott85c2c6d2017-01-05 12:34:49 -08001406
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001407 def test_full_featured(self):
1408 # Zoo should exercise all discovery facets
1409 # and should also have no future.json file.
1410 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001411 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001412 self.assertTrue(getattr(zoo, "animals"))
Orest Bolohane92c9002014-05-30 11:15:43 -07001413
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001414 request = zoo.animals().list(name="bat", projection="full")
1415 parsed = urlparse(request.uri)
1416 q = parse_qs(parsed[4])
1417 self.assertEqual(q["name"], ["bat"])
1418 self.assertEqual(q["projection"], ["full"])
Joe Gregoriof863f7a2011-02-24 03:24:44 -05001419
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001420 def test_nested_resources(self):
1421 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001422 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001423 self.assertTrue(getattr(zoo, "animals"))
1424 request = zoo.my().favorites().list(max_results="5")
1425 parsed = urlparse(request.uri)
1426 q = parse_qs(parsed[4])
1427 self.assertEqual(q["max-results"], ["5"])
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001428
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001429 @unittest.skipIf(six.PY3, "print is not a reserved name in Python 3")
1430 def test_methods_with_reserved_names(self):
1431 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
1432 zoo = build("zoo", "v1", http=self.http)
1433 self.assertTrue(getattr(zoo, "animals"))
1434 request = zoo.global_().print_().assert_(max_results="5")
1435 parsed = urlparse(request.uri)
1436 self.assertEqual(parsed[2], "/zoo/v1/global/print/assert")
Joe Gregorio3fada332011-01-07 17:07:45 -05001437
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001438 def test_top_level_functions(self):
1439 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001440 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001441 self.assertTrue(getattr(zoo, "query"))
1442 request = zoo.query(q="foo")
1443 parsed = urlparse(request.uri)
1444 q = parse_qs(parsed[4])
1445 self.assertEqual(q["q"], ["foo"])
Joe Gregoriod92897c2011-07-07 11:44:56 -04001446
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001447 def test_simple_media_uploads(self):
1448 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001449 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001450 doc = getattr(zoo.animals().insert, "__doc__")
1451 self.assertTrue("media_body" in doc)
Joe Gregorio2379ecc2010-10-26 10:51:28 -04001452
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001453 def test_simple_media_upload_no_max_size_provided(self):
1454 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001455 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001456 request = zoo.animals().crossbreed(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001457 self.assertEqual("image/png", request.headers["content-type"])
1458 self.assertEqual(b"PNG", request.body[1:4])
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001459
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001460 def test_simple_media_raise_correct_exceptions(self):
1461 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001462 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -04001463
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001464 try:
1465 zoo.animals().insert(media_body=datafile("smiley.png"))
1466 self.fail("should throw exception if media is too large.")
1467 except MediaUploadSizeError:
1468 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001469
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001470 try:
1471 zoo.animals().insert(media_body=datafile("small.jpg"))
1472 self.fail("should throw exception if mimetype is unacceptable.")
1473 except UnacceptableMimeTypeError:
1474 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001475
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001476 def test_simple_media_good_upload(self):
1477 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001478 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001479
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001480 request = zoo.animals().insert(media_body=datafile("small.png"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001481 self.assertEqual("image/png", request.headers["content-type"])
1482 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001483 assertUrisEqual(
1484 self,
1485 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1486 request.uri,
1487 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001488
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001489 def test_simple_media_unknown_mimetype(self):
1490 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001491 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
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-png"))
1495 self.fail("should throw exception if mimetype is unknown.")
1496 except UnknownFileType:
1497 pass
Brian J. Watson38051ac2016-10-25 07:53:08 -07001498
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001499 request = zoo.animals().insert(
1500 media_body=datafile("small-png"), media_mime_type="image/png"
1501 )
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001502 self.assertEqual("image/png", request.headers["content-type"])
1503 self.assertEqual(b"PNG", request.body[1:4])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001504 assertUrisEqual(
1505 self,
1506 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json",
1507 request.uri,
1508 )
Brian J. Watson38051ac2016-10-25 07:53:08 -07001509
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001510 def test_multipart_media_raise_correct_exceptions(self):
1511 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001512 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Brian J. Watson38051ac2016-10-25 07:53:08 -07001513
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001514 try:
1515 zoo.animals().insert(media_body=datafile("smiley.png"), body={})
1516 self.fail("should throw exception if media is too large.")
1517 except MediaUploadSizeError:
1518 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001519
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001520 try:
1521 zoo.animals().insert(media_body=datafile("small.jpg"), body={})
1522 self.fail("should throw exception if mimetype is unacceptable.")
1523 except UnacceptableMimeTypeError:
1524 pass
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001525
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001526 def test_multipart_media_good_upload(self, static_discovery=False):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001527 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001528 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001529
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001530 request = zoo.animals().insert(media_body=datafile("small.png"), body={})
1531 self.assertTrue(request.headers["content-type"].startswith("multipart/related"))
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001532 contents = read_datafile("small.png", "rb")
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001533 boundary = re.match(b"--=+([^=]+)", request.body).group(1)
1534 self.assertEqual(
1535 request.body.rstrip(b"\n"), # Python 2.6 does not add a trailing \n
1536 b"--==============="
1537 + boundary
1538 + b"==\n"
1539 + b"Content-Type: application/json\n"
1540 + b"MIME-Version: 1.0\n\n"
1541 + b'{"data": {}}\n'
1542 + b"--==============="
1543 + boundary
1544 + b"==\n"
1545 + b"Content-Type: image/png\n"
1546 + b"MIME-Version: 1.0\n"
1547 + b"Content-Transfer-Encoding: binary\n\n"
1548 + contents
1549 + b"\n--==============="
1550 + boundary
1551 + b"==--",
1552 )
1553 assertUrisEqual(
1554 self,
1555 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json",
1556 request.uri,
1557 )
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001558
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001559 def test_media_capable_method_without_media(self):
1560 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001561 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001562
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001563 request = zoo.animals().insert(body={})
1564 self.assertTrue(request.headers["content-type"], "application/json")
Joe Gregoriofdf7c802011-06-30 12:33:38 -04001565
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001566 def test_resumable_multipart_media_good_upload(self):
1567 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001568 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc5c5a372010-09-22 11:42:32 -04001569
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001570 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1571 request = zoo.animals().insert(media_body=media_upload, body={})
1572 self.assertTrue(request.headers["content-type"].startswith("application/json"))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001573 self.assertEqual('{"data": {}}', request.body)
1574 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001575
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001576 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001577
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07001578 self.assertNotEqual(request.body, None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001579 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001580
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001581 http = HttpMockSequence(
1582 [
1583 ({"status": "200", "location": "http://upload.example.com"}, ""),
1584 ({"status": "308", "location": "http://upload.example.com/2"}, ""),
1585 (
1586 {
1587 "status": "308",
1588 "location": "http://upload.example.com/3",
1589 "range": "0-12",
1590 },
1591 "",
1592 ),
1593 (
1594 {
1595 "status": "308",
1596 "location": "http://upload.example.com/4",
1597 "range": "0-%d" % (media_upload.size() - 2),
1598 },
1599 "",
1600 ),
1601 ({"status": "200"}, '{"foo": "bar"}'),
1602 ]
1603 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001604
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001605 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001606 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001607 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001608 self.assertEqual(0, status.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001609
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001610 # Two requests should have been made and the resumable_uri should have been
1611 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001612 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
1613 self.assertEqual(media_upload, request.resumable)
1614 self.assertEqual(0, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001615
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001616 # This next chuck call should upload the first chunk
1617 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001618 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1619 self.assertEqual(media_upload, request.resumable)
1620 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001621
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001622 # This call will upload the next chunk
1623 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001624 self.assertEqual(request.resumable_uri, "http://upload.example.com/4")
1625 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1626 self.assertEqual('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001627
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001628 # Final call to next_chunk should complete the upload.
1629 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001630 self.assertEqual(body, {"foo": "bar"})
1631 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001632
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001633 def test_resumable_media_good_upload(self):
1634 """Not a multipart upload."""
1635 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001636 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001637
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001638 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1639 request = zoo.animals().insert(media_body=media_upload, body=None)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001640 self.assertEqual(media_upload, request.resumable)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001641
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001642 self.assertEqual("image/png", request.resumable.mimetype())
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001643
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001644 self.assertEqual(request.body, None)
1645 self.assertEqual(request.resumable_uri, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001646
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001647 http = HttpMockSequence(
1648 [
1649 ({"status": "200", "location": "http://upload.example.com"}, ""),
1650 (
1651 {
1652 "status": "308",
1653 "location": "http://upload.example.com/2",
1654 "range": "0-12",
1655 },
1656 "",
1657 ),
1658 (
1659 {
1660 "status": "308",
1661 "location": "http://upload.example.com/3",
1662 "range": "0-%d" % (media_upload.size() - 2),
1663 },
1664 "",
1665 ),
1666 ({"status": "200"}, '{"foo": "bar"}'),
1667 ]
1668 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001669
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001670 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001671 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001672 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001673 self.assertEqual(13, status.resumable_progress)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001674
1675 # Two requests should have been made and the resumable_uri should have been
1676 # updated for each one.
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001677 self.assertEqual(request.resumable_uri, "http://upload.example.com/2")
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001678
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001679 self.assertEqual(media_upload, request.resumable)
1680 self.assertEqual(13, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001681
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001682 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001683 self.assertEqual(request.resumable_uri, "http://upload.example.com/3")
1684 self.assertEqual(media_upload.size() - 1, request.resumable_progress)
1685 self.assertEqual(request.body, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001686
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001687 # Final call to next_chunk should complete the upload.
1688 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001689 self.assertEqual(body, {"foo": "bar"})
1690 self.assertEqual(status, None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001691
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001692 def test_resumable_media_good_upload_from_execute(self):
1693 """Not a multipart upload."""
1694 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001695 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001696
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001697 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1698 request = zoo.animals().insert(media_body=media_upload, body=None)
1699 assertUrisEqual(
1700 self,
1701 "https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json",
1702 request.uri,
1703 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001704
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001705 http = HttpMockSequence(
1706 [
1707 ({"status": "200", "location": "http://upload.example.com"}, ""),
1708 (
1709 {
1710 "status": "308",
1711 "location": "http://upload.example.com/2",
1712 "range": "0-12",
1713 },
1714 "",
1715 ),
1716 (
1717 {
1718 "status": "308",
1719 "location": "http://upload.example.com/3",
1720 "range": "0-%d" % media_upload.size(),
1721 },
1722 "",
1723 ),
1724 ({"status": "200"}, '{"foo": "bar"}'),
1725 ]
1726 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001727
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001728 body = request.execute(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001729 self.assertEqual(body, {"foo": "bar"})
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001730
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001731 def test_resumable_media_fail_unknown_response_code_first_request(self):
1732 """Not a multipart upload."""
1733 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001734 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001735
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001736 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1737 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001738
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001739 http = HttpMockSequence(
1740 [({"status": "400", "location": "http://upload.example.com"}, "")]
1741 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001742
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001743 try:
1744 request.execute(http=http)
1745 self.fail("Should have raised ResumableUploadError.")
1746 except ResumableUploadError as e:
1747 self.assertEqual(400, e.resp.status)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001748
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001749 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
1750 """Not a multipart upload."""
1751 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001752 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001753
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001754 media_upload = MediaFileUpload(datafile("small.png"), resumable=True)
1755 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001756
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001757 http = HttpMockSequence(
1758 [
1759 ({"status": "200", "location": "http://upload.example.com"}, ""),
1760 ({"status": "400"}, ""),
1761 ]
1762 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001763
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001764 self.assertRaises(HttpError, request.execute, http=http)
1765 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001766
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001767 http = HttpMockSequence(
1768 [
1769 ({"status": "308", "range": "0-5"}, ""),
1770 ({"status": "308", "range": "0-6"}, ""),
1771 ]
1772 )
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001773
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001774 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07001775 self.assertEqual(
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001776 status.resumable_progress,
1777 7,
1778 "Should have first checked length and then tried to PUT more.",
1779 )
1780 self.assertFalse(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05001781
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001782 # Put it back in an error state.
1783 http = HttpMockSequence([({"status": "400"}, "")])
1784 self.assertRaises(HttpError, request.execute, http=http)
1785 self.assertTrue(request._in_error_state)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001786
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001787 # Pretend the last request that 400'd actually succeeded.
1788 http = HttpMockSequence([({"status": "200"}, '{"foo": "bar"}')])
1789 status, body = request.next_chunk(http=http)
1790 self.assertEqual(body, {"foo": "bar"})
Joe Gregorio910b9b12012-06-12 09:36:30 -04001791
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001792 def test_media_io_base_stream_unlimited_chunksize_resume(self):
1793 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001794 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001795
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001796 # Set up a seekable stream and try to upload in single chunk.
1797 fd = BytesIO(b'01234"56789"')
1798 media_upload = MediaIoBaseUpload(
1799 fd=fd, mimetype="text/plain", chunksize=-1, resumable=True
1800 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001801
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001802 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001803
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001804 # The single chunk fails, restart at the right point.
1805 http = HttpMockSequence(
1806 [
1807 ({"status": "200", "location": "http://upload.example.com"}, ""),
1808 (
1809 {
1810 "status": "308",
1811 "location": "http://upload.example.com/2",
1812 "range": "0-4",
1813 },
1814 "",
1815 ),
1816 ({"status": "200"}, "echo_request_body"),
1817 ]
1818 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001819
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001820 body = request.execute(http=http)
1821 self.assertEqual("56789", body)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001822
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001823 def test_media_io_base_stream_chunksize_resume(self):
1824 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001825 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001826
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001827 # Set up a seekable stream and try to upload in chunks.
1828 fd = BytesIO(b"0123456789")
1829 media_upload = MediaIoBaseUpload(
1830 fd=fd, mimetype="text/plain", chunksize=5, resumable=True
1831 )
Joe Gregorio5c120db2012-08-23 09:13:55 -04001832
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001833 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001834
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001835 # The single chunk fails, pull the content sent out of the exception.
1836 http = HttpMockSequence(
1837 [
1838 ({"status": "200", "location": "http://upload.example.com"}, ""),
1839 ({"status": "400"}, "echo_request_body"),
1840 ]
1841 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001842
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001843 try:
1844 body = request.execute(http=http)
1845 except HttpError as e:
1846 self.assertEqual(b"01234", e.content)
Pat Ferateed9affd2015-03-03 16:03:15 -08001847
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001848 def test_resumable_media_handle_uploads_of_unknown_size(self):
1849 http = HttpMockSequence(
1850 [
1851 ({"status": "200", "location": "http://upload.example.com"}, ""),
1852 ({"status": "200"}, "echo_request_headers_as_json"),
1853 ]
1854 )
Pat Ferateed9affd2015-03-03 16:03:15 -08001855
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001856 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001857 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio5c120db2012-08-23 09:13:55 -04001858
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001859 # Create an upload that doesn't know the full size of the media.
1860 class IoBaseUnknownLength(MediaUpload):
1861 def chunksize(self):
1862 return 10
Joe Gregorio910b9b12012-06-12 09:36:30 -04001863
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001864 def mimetype(self):
1865 return "image/png"
Joe Gregorio910b9b12012-06-12 09:36:30 -04001866
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001867 def size(self):
1868 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001869
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001870 def resumable(self):
1871 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001872
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001873 def getbytes(self, begin, length):
1874 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001875
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001876 upload = IoBaseUnknownLength()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001877
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001878 request = zoo.animals().insert(media_body=upload, body=None)
1879 status, body = request.next_chunk(http=http)
1880 self.assertEqual(body, {"Content-Range": "bytes 0-9/*", "Content-Length": "10"})
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001881
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001882 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
1883 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001884 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001885
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001886 class IoBaseHasStream(MediaUpload):
1887 def chunksize(self):
1888 return 10
Joe Gregorio44454e42012-06-15 08:38:53 -04001889
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001890 def mimetype(self):
1891 return "image/png"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001892
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001893 def size(self):
1894 return None
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001895
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001896 def resumable(self):
1897 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001898
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001899 def getbytes(self, begin, length):
1900 return "0123456789"
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001901
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001902 def has_stream(self):
1903 return True
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001904
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001905 def stream(self):
1906 raise NotImplementedError()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001907
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001908 upload = IoBaseHasStream()
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001909
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001910 orig_version = sys.version_info
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001911
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001912 sys.version_info = (2, 6, 5, "final", 0)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001913
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001914 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001915
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001916 # This should raise an exception because stream() will be called.
1917 http = HttpMockSequence(
1918 [
1919 ({"status": "200", "location": "http://upload.example.com"}, ""),
1920 ({"status": "200"}, "echo_request_headers_as_json"),
1921 ]
1922 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001923
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001924 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001925
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001926 sys.version_info = orig_version
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001927
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001928 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
1929 http = HttpMockSequence(
1930 [
1931 ({"status": "200", "location": "http://upload.example.com"}, ""),
1932 ({"status": "200"}, "echo_request_headers_as_json"),
1933 ]
1934 )
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001935
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001936 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001937 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorioc80ac9d2012-08-21 14:09:09 -04001938
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001939 fd = BytesIO(b"data goes here")
Joe Gregorio44454e42012-06-15 08:38:53 -04001940
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001941 # Create an upload that doesn't know the full size of the media.
1942 upload = MediaIoBaseUpload(
1943 fd=fd, mimetype="image/png", chunksize=15, resumable=True
1944 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001945
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001946 request = zoo.animals().insert(media_body=upload, body=None)
1947 status, body = request.next_chunk(http=http)
1948 self.assertEqual(
1949 body, {"Content-Range": "bytes 0-13/14", "Content-Length": "14"}
1950 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001951
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001952 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
1953 http = HttpMockSequence(
1954 [
1955 ({"status": "200", "location": "http://upload.example.com"}, ""),
1956 ({"status": "400"}, ""),
1957 ]
1958 )
Joe Gregorio44454e42012-06-15 08:38:53 -04001959
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001960 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05001961 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001962
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001963 # Create an upload that doesn't know the full size of the media.
1964 fd = BytesIO(b"data goes here")
Joe Gregorio910b9b12012-06-12 09:36:30 -04001965
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001966 upload = MediaIoBaseUpload(
1967 fd=fd, mimetype="image/png", chunksize=500, resumable=True
1968 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001969
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001970 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001971
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001972 # Put it in an error state.
1973 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -04001974
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001975 http = HttpMockSequence(
1976 [({"status": "400", "range": "0-5"}, "echo_request_headers_as_json")]
1977 )
1978 try:
1979 # Should resume the upload by first querying the status of the upload.
1980 request.next_chunk(http=http)
1981 except HttpError as e:
1982 expected = {"Content-Range": "bytes */14", "content-length": "0"}
1983 self.assertEqual(
1984 expected,
1985 json.loads(e.content.decode("utf-8")),
1986 "Should send an empty body when requesting the current upload status.",
1987 )
Joe Gregorio910b9b12012-06-12 09:36:30 -04001988
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07001989 def test_pickle(self):
1990 sorted_resource_keys = [
1991 "_baseUrl",
1992 "_developerKey",
1993 "_dynamic_attrs",
1994 "_http",
1995 "_model",
1996 "_requestBuilder",
1997 "_resourceDesc",
1998 "_rootDesc",
1999 "_schema",
2000 "animals",
2001 "global_",
2002 "load",
2003 "loadNoTemplate",
2004 "my",
2005 "new_batch_http_request",
2006 "query",
2007 "scopedAnimals",
2008 ]
Joe Gregorio910b9b12012-06-12 09:36:30 -04002009
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002010 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002011 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002012 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
Joe Gregoriod0bd3882011-11-22 09:49:47 -05002013
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002014 pickled_zoo = pickle.dumps(zoo)
2015 new_zoo = pickle.loads(pickled_zoo)
2016 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
2017 self.assertTrue(hasattr(new_zoo, "animals"))
2018 self.assertTrue(callable(new_zoo.animals))
2019 self.assertTrue(hasattr(new_zoo, "global_"))
2020 self.assertTrue(callable(new_zoo.global_))
2021 self.assertTrue(hasattr(new_zoo, "load"))
2022 self.assertTrue(callable(new_zoo.load))
2023 self.assertTrue(hasattr(new_zoo, "loadNoTemplate"))
2024 self.assertTrue(callable(new_zoo.loadNoTemplate))
2025 self.assertTrue(hasattr(new_zoo, "my"))
2026 self.assertTrue(callable(new_zoo.my))
2027 self.assertTrue(hasattr(new_zoo, "query"))
2028 self.assertTrue(callable(new_zoo.query))
2029 self.assertTrue(hasattr(new_zoo, "scopedAnimals"))
2030 self.assertTrue(callable(new_zoo.scopedAnimals))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002031
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002032 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
2033 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
2034 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
2035 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
2036 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
2037 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
2038 # _http, _model and _schema won't be equal since we will get new
2039 # instances upon un-pickling
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002040
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002041 def _dummy_zoo_request(self):
Dmitry Frenkelf3348f92020-07-15 13:05:58 -07002042 zoo_contents = read_datafile("zoo.json")
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002043
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002044 zoo_uri = uritemplate.expand(DISCOVERY_URI, {"api": "zoo", "apiVersion": "v1"})
2045 if "REMOTE_ADDR" in os.environ:
2046 zoo_uri = util._add_query_parameter(
2047 zoo_uri, "userIp", os.environ["REMOTE_ADDR"]
2048 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002049
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002050 http = build_http()
2051 original_request = http.request
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002052
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002053 def wrapped_request(uri, method="GET", *args, **kwargs):
2054 if uri == zoo_uri:
2055 return httplib2.Response({"status": "200"}), zoo_contents
2056 return original_request(uri, method=method, *args, **kwargs)
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002057
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002058 http.request = wrapped_request
2059 return http
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002060
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002061 def _dummy_token(self):
2062 access_token = "foo"
2063 client_id = "some_client_id"
2064 client_secret = "cOuDdkfjxxnv+"
2065 refresh_token = "1/0/a.df219fjls0"
2066 token_expiry = datetime.datetime.utcnow()
2067 user_agent = "refresh_checker/1.0"
2068 return OAuth2Credentials(
2069 access_token,
2070 client_id,
2071 client_secret,
2072 refresh_token,
2073 token_expiry,
2074 GOOGLE_TOKEN_URI,
2075 user_agent,
2076 )
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002077
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002078 def test_pickle_with_credentials(self):
2079 credentials = self._dummy_token()
2080 http = self._dummy_zoo_request()
2081 http = credentials.authorize(http)
2082 self.assertTrue(hasattr(http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002083
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002084 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002085 pickled_zoo = pickle.dumps(zoo)
2086 new_zoo = pickle.loads(pickled_zoo)
2087 self.assertEqual(sorted(zoo.__dict__.keys()), sorted(new_zoo.__dict__.keys()))
2088 new_http = new_zoo._http
2089 self.assertFalse(hasattr(new_http.request, "credentials"))
Joe Gregoriodc106fc2012-11-20 14:30:14 -05002090
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002091 def test_resumable_media_upload_no_content(self):
2092 self.http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002093 zoo = build("zoo", "v1", http=self.http, static_discovery=False)
andrewnestera4a44cf2017-03-31 16:09:31 +03002094
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002095 media_upload = MediaFileUpload(datafile("empty"), resumable=True)
2096 request = zoo.animals().insert(media_body=media_upload, body=None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002097
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002098 self.assertEqual(media_upload, request.resumable)
2099 self.assertEqual(request.body, None)
2100 self.assertEqual(request.resumable_uri, None)
andrewnestera4a44cf2017-03-31 16:09:31 +03002101
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002102 http = HttpMockSequence(
2103 [
2104 ({"status": "200", "location": "http://upload.example.com"}, ""),
2105 (
2106 {
2107 "status": "308",
2108 "location": "http://upload.example.com/2",
2109 "range": "0-0",
2110 },
2111 "",
2112 ),
2113 ]
2114 )
andrewnestera4a44cf2017-03-31 16:09:31 +03002115
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002116 status, body = request.next_chunk(http=http)
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002117 self.assertEqual(None, body)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002118 self.assertTrue(isinstance(status, MediaUploadProgress))
Bu Sun Kim1cf3cbc2020-03-12 12:38:23 -07002119 self.assertEqual(0, status.progress())
andrewnestera4a44cf2017-03-31 16:09:31 +03002120
Joe Gregorio708388c2012-06-15 13:43:04 -04002121
Joe Gregorioc5c5a372010-09-22 11:42:32 -04002122class Next(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002123 def test_next_successful_none_on_no_next_page_token(self):
2124 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2125 tasks = build("tasks", "v1", http=self.http)
2126 request = tasks.tasklists().list()
2127 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002128
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002129 def test_next_successful_none_on_empty_page_token(self):
2130 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2131 tasks = build("tasks", "v1", http=self.http)
2132 request = tasks.tasklists().list()
2133 next_request = tasks.tasklists().list_next(request, {"nextPageToken": ""})
2134 self.assertEqual(None, next_request)
Joe Gregorio3c676f92011-07-25 10:38:14 -04002135
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002136 def test_next_successful_with_next_page_token(self):
2137 self.http = HttpMock(datafile("tasks.json"), {"status": "200"})
2138 tasks = build("tasks", "v1", http=self.http)
2139 request = tasks.tasklists().list()
2140 next_request = tasks.tasklists().list_next(request, {"nextPageToken": "123abc"})
2141 parsed = list(urlparse(next_request.uri))
2142 q = parse_qs(parsed[4])
2143 self.assertEqual(q["pageToken"][0], "123abc")
Son Dinh2a9a2132015-07-23 16:30:56 +00002144
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002145 def test_next_successful_with_next_page_token_alternate_name(self):
2146 self.http = HttpMock(datafile("bigquery.json"), {"status": "200"})
2147 bigquery = build("bigquery", "v2", http=self.http)
2148 request = bigquery.tabledata().list(datasetId="", projectId="", tableId="")
2149 next_request = bigquery.tabledata().list_next(request, {"pageToken": "123abc"})
2150 parsed = list(urlparse(next_request.uri))
2151 q = parse_qs(parsed[4])
2152 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio3c676f92011-07-25 10:38:14 -04002153
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002154 def test_next_successful_with_next_page_token_in_body(self):
2155 self.http = HttpMock(datafile("logging.json"), {"status": "200"})
2156 logging = build("logging", "v2", http=self.http)
2157 request = logging.entries().list(body={})
2158 next_request = logging.entries().list_next(request, {"nextPageToken": "123abc"})
2159 body = JsonModel().deserialize(next_request.body)
2160 self.assertEqual(body["pageToken"], "123abc")
Thomas Coffee20af04d2017-02-10 15:24:44 -08002161
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002162 def test_next_with_method_with_no_properties(self):
2163 self.http = HttpMock(datafile("latitude.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002164 service = build("latitude", "v1", http=self.http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002165 service.currentLocation().get()
Thomas Coffee20af04d2017-02-10 15:24:44 -08002166
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002167 def test_next_nonexistent_with_no_next_page_token(self):
2168 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2169 drive = build("drive", "v3", http=self.http)
2170 drive.changes().watch(body={})
2171 self.assertFalse(callable(getattr(drive.changes(), "watch_next", None)))
Thomas Coffee20af04d2017-02-10 15:24:44 -08002172
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002173 def test_next_successful_with_next_page_token_required(self):
2174 self.http = HttpMock(datafile("drive.json"), {"status": "200"})
2175 drive = build("drive", "v3", http=self.http)
2176 request = drive.changes().list(pageToken="startPageToken")
2177 next_request = drive.changes().list_next(request, {"nextPageToken": "123abc"})
2178 parsed = list(urlparse(next_request.uri))
2179 q = parse_qs(parsed[4])
2180 self.assertEqual(q["pageToken"][0], "123abc")
Joe Gregorio00cf1d92010-09-27 09:22:03 -04002181
Joe Gregorioa98733f2011-09-16 10:12:28 -04002182
Joe Gregorio708388c2012-06-15 13:43:04 -04002183class MediaGet(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002184 def test_get_media(self):
2185 http = HttpMock(datafile("zoo.json"), {"status": "200"})
Anthonios Partheniou32d1c592021-01-14 18:48:59 -05002186 zoo = build("zoo", "v1", http=http, static_discovery=False)
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002187 request = zoo.animals().get_media(name="Lion")
Joe Gregorio708388c2012-06-15 13:43:04 -04002188
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002189 parsed = urlparse(request.uri)
2190 q = parse_qs(parsed[4])
2191 self.assertEqual(q["alt"], ["media"])
2192 self.assertEqual(request.headers["accept"], "*/*")
Joe Gregorio708388c2012-06-15 13:43:04 -04002193
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002194 http = HttpMockSequence([({"status": "200"}, "standing in for media")])
2195 response = request.execute(http=http)
2196 self.assertEqual(b"standing in for media", response)
Joe Gregorio708388c2012-06-15 13:43:04 -04002197
2198
Bu Sun Kim66bb32c2019-10-30 10:11:58 -07002199if __name__ == "__main__":
2200 unittest.main()