blob: 322a7b4848c0003ad9a46fd14649630757e2b364 [file] [log] [blame]
Craig Citro15744b12015-03-02 13:34:32 -08001#!/usr/bin/env python
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04002#
Craig Citro751b7fb2014-09-23 11:20:38 -07003# Copyright 2014 Google Inc. All Rights Reserved.
Joe Gregorio6d5e94f2010-08-25 23:49:30 -04004#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040016
Joe Gregorio6d5e94f2010-08-25 23:49:30 -040017"""JSON Model tests
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040018
Joe Gregorio6d5e94f2010-08-25 23:49:30 -040019Unit tests for the JSON model.
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040020"""
INADA Naokid898a372015-03-04 03:52:46 +090021from __future__ import absolute_import
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040022
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070023__author__ = "jcgregorio@google.com (Joe Gregorio)"
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040024
Gianluca Paronitti7d630272021-10-21 22:37:37 +020025import io
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040026import httplib2
Craig Citro6ae34d72014-08-18 23:10:09 -070027import json
Bu Sun Kimf706cfd2020-04-20 14:05:05 -070028import pkg_resources
Bu Sun Kim07f647c2019-08-09 14:55:24 -070029import platform
Tres Seaverf6c1b942021-10-12 12:26:30 -040030import unittest
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -040031import urllib
32
John Asmuth864311d2014-04-24 15:46:08 -040033import googleapiclient.model
ade@google.comd69e5e42010-08-31 15:28:20 +010034
John Asmuth864311d2014-04-24 15:46:08 -040035from googleapiclient.errors import HttpError
36from googleapiclient.model import JsonModel
Joe Gregorio34044bc2011-03-07 16:58:33 -050037
Bu Sun Kimf706cfd2020-04-20 14:05:05 -070038_LIBRARY_VERSION = pkg_resources.get_distribution("google-api-python-client").version
Gianluca Paronitti7d630272021-10-21 22:37:37 +020039CSV_TEXT_MOCK = 'column1,column2,column3\nstring1,1.2,string2'
Bu Sun Kimf706cfd2020-04-20 14:05:05 -070040
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040041
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040042class Model(unittest.TestCase):
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070043 def test_json_no_body(self):
44 model = JsonModel(data_wrapper=False)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040045
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070046 headers = {}
47 path_params = {}
48 query_params = {}
49 body = None
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040050
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070051 headers, unused_params, query, body = model.request(
52 headers, path_params, query_params, body
53 )
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040054
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070055 self.assertEqual(headers["accept"], "application/json")
56 self.assertTrue("content-type" not in headers)
57 self.assertNotEqual(query, "")
58 self.assertEqual(body, None)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040059
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070060 def test_json_body(self):
61 model = JsonModel(data_wrapper=False)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040062
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070063 headers = {}
64 path_params = {}
65 query_params = {}
66 body = {}
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040067
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070068 headers, unused_params, query, body = model.request(
69 headers, path_params, query_params, body
70 )
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040071
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070072 self.assertEqual(headers["accept"], "application/json")
73 self.assertEqual(headers["content-type"], "application/json")
74 self.assertNotEqual(query, "")
75 self.assertEqual(body, "{}")
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040076
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070077 def test_json_body_data_wrapper(self):
78 model = JsonModel(data_wrapper=True)
Joe Gregoriod433b2a2011-02-22 10:51:51 -050079
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070080 headers = {}
81 path_params = {}
82 query_params = {}
83 body = {}
Joe Gregoriod433b2a2011-02-22 10:51:51 -050084
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070085 headers, unused_params, query, body = model.request(
86 headers, path_params, query_params, body
87 )
Joe Gregoriod433b2a2011-02-22 10:51:51 -050088
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070089 self.assertEqual(headers["accept"], "application/json")
90 self.assertEqual(headers["content-type"], "application/json")
91 self.assertNotEqual(query, "")
92 self.assertEqual(body, '{"data": {}}')
Joe Gregoriod433b2a2011-02-22 10:51:51 -050093
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070094 def test_json_body_default_data(self):
95 """Test that a 'data' wrapper doesn't get added if one is already present."""
96 model = JsonModel(data_wrapper=True)
Joe Gregorio8963ff92010-10-11 13:14:43 -040097
Bu Sun Kim66bb32c2019-10-30 10:11:58 -070098 headers = {}
99 path_params = {}
100 query_params = {}
101 body = {"data": "foo"}
Joe Gregorio8963ff92010-10-11 13:14:43 -0400102
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700103 headers, unused_params, query, body = model.request(
104 headers, path_params, query_params, body
105 )
Joe Gregorio8963ff92010-10-11 13:14:43 -0400106
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700107 self.assertEqual(headers["accept"], "application/json")
108 self.assertEqual(headers["content-type"], "application/json")
109 self.assertNotEqual(query, "")
110 self.assertEqual(body, '{"data": "foo"}')
Joe Gregorio8963ff92010-10-11 13:14:43 -0400111
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700112 def test_json_build_query(self):
113 model = JsonModel(data_wrapper=False)
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400114
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700115 headers = {}
116 path_params = {}
117 query_params = {
118 "foo": 1,
119 "bar": u"\N{COMET}",
120 "baz": ["fe", "fi", "fo", "fum"], # Repeated parameters
121 "qux": [],
Joe Gregorio34044bc2011-03-07 16:58:33 -0500122 }
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700123 body = {}
Joe Gregorio34044bc2011-03-07 16:58:33 -0500124
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700125 headers, unused_params, query, body = model.request(
126 headers, path_params, query_params, body
127 )
Joe Gregorio34044bc2011-03-07 16:58:33 -0500128
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700129 self.assertEqual(headers["accept"], "application/json")
130 self.assertEqual(headers["content-type"], "application/json")
Ali Afshar81fde8e2012-10-23 11:14:28 -0700131
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -0400132 query_dict = urllib.parse.parse_qs(query[1:])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700133 self.assertEqual(query_dict["foo"], ["1"])
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -0400134 self.assertEqual(query_dict["bar"], [u"\N{COMET}"])
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700135 self.assertEqual(query_dict["baz"], ["fe", "fi", "fo", "fum"])
136 self.assertTrue("qux" not in query_dict)
137 self.assertEqual(body, "{}")
Ali Afshar81fde8e2012-10-23 11:14:28 -0700138
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700139 def test_user_agent(self):
140 model = JsonModel(data_wrapper=False)
141
142 headers = {"user-agent": "my-test-app/1.23.4"}
143 path_params = {}
144 query_params = {}
145 body = {}
146
147 headers, unused_params, unused_query, body = model.request(
148 headers, path_params, query_params, body
149 )
150
151 self.assertEqual(headers["user-agent"], "my-test-app/1.23.4 (gzip)")
152
153 def test_x_goog_api_client(self):
154 model = JsonModel(data_wrapper=False)
155
156 # test header composition for cloud clients that wrap discovery
157 headers = {"x-goog-api-client": "gccl/1.23.4"}
158 path_params = {}
159 query_params = {}
160 body = {}
161
162 headers, unused_params, unused_query, body = model.request(
163 headers, path_params, query_params, body
164 )
165
166 self.assertEqual(
167 headers["x-goog-api-client"],
168 "gccl/1.23.4"
169 + " gdcl/"
Bu Sun Kimf706cfd2020-04-20 14:05:05 -0700170 + _LIBRARY_VERSION
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700171 + " gl-python/"
172 + platform.python_version(),
173 )
174
175 def test_bad_response(self):
176 model = JsonModel(data_wrapper=False)
177 resp = httplib2.Response({"status": "401"})
178 resp.reason = "Unauthorized"
179 content = b'{"error": {"message": "not authorized"}}'
180
181 try:
182 content = model.response(resp, content)
183 self.fail("Should have thrown an exception")
184 except HttpError as e:
185 self.assertTrue("not authorized" in str(e))
186
187 resp["content-type"] = "application/json"
188
189 try:
190 content = model.response(resp, content)
191 self.fail("Should have thrown an exception")
192 except HttpError as e:
193 self.assertTrue("not authorized" in str(e))
194
195 def test_good_response(self):
196 model = JsonModel(data_wrapper=True)
197 resp = httplib2.Response({"status": "200"})
198 resp.reason = "OK"
199 content = '{"data": "is good"}'
200
201 content = model.response(resp, content)
202 self.assertEqual(content, "is good")
203
204 def test_good_response_wo_data(self):
205 model = JsonModel(data_wrapper=False)
206 resp = httplib2.Response({"status": "200"})
207 resp.reason = "OK"
208 content = '{"foo": "is good"}'
209
210 content = model.response(resp, content)
211 self.assertEqual(content, {"foo": "is good"})
212
213 def test_good_response_wo_data_str(self):
214 model = JsonModel(data_wrapper=False)
215 resp = httplib2.Response({"status": "200"})
216 resp.reason = "OK"
217 content = '"data goes here"'
218
219 content = model.response(resp, content)
220 self.assertEqual(content, "data goes here")
221
222 def test_no_content_response(self):
223 model = JsonModel(data_wrapper=False)
224 resp = httplib2.Response({"status": "204"})
225 resp.reason = "No Content"
226 content = ""
227
228 content = model.response(resp, content)
229 self.assertEqual(content, {})
230
231 def test_logging(self):
232 class MockLogging(object):
233 def __init__(self):
234 self.info_record = []
235 self.debug_record = []
236
237 def info(self, message, *args):
238 self.info_record.append(message % args)
239
240 def debug(self, message, *args):
241 self.debug_record.append(message % args)
242
243 class MockResponse(dict):
244 def __init__(self, items):
245 super(MockResponse, self).__init__()
246 self.status = items["status"]
Anthonios Partheniou9f7b4102021-07-23 12:18:25 -0400247 for key, value in items.items():
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700248 self[key] = value
249
250 old_logging = googleapiclient.model.LOGGER
251 googleapiclient.model.LOGGER = MockLogging()
252 googleapiclient.model.dump_request_response = True
253 model = JsonModel()
254 request_body = {"field1": "value1", "field2": "value2"}
255 body_string = model.request({}, {}, {}, request_body)[-1]
256 json_body = json.loads(body_string)
257 self.assertEqual(request_body, json_body)
258
259 response = {
260 "status": 200,
261 "response_field_1": "response_value_1",
262 "response_field_2": "response_value_2",
263 }
264 response_body = model.response(MockResponse(response), body_string)
265 self.assertEqual(request_body, response_body)
266 self.assertEqual(
267 googleapiclient.model.LOGGER.info_record[:2],
268 ["--request-start--", "-headers-start-"],
269 )
270 self.assertTrue(
271 "response_field_1: response_value_1"
272 in googleapiclient.model.LOGGER.info_record
273 )
274 self.assertTrue(
275 "response_field_2: response_value_2"
276 in googleapiclient.model.LOGGER.info_record
277 )
278 self.assertEqual(
279 json.loads(googleapiclient.model.LOGGER.info_record[-2]), request_body
280 )
281 self.assertEqual(
282 googleapiclient.model.LOGGER.info_record[-1], "--response-end--"
283 )
284 googleapiclient.model.LOGGER = old_logging
285
286 def test_no_data_wrapper_deserialize(self):
287 model = JsonModel(data_wrapper=False)
288 resp = httplib2.Response({"status": "200"})
289 resp.reason = "OK"
290 content = '{"data": "is good"}'
291 content = model.response(resp, content)
292 self.assertEqual(content, {"data": "is good"})
293
Gianluca Paronitti7d630272021-10-21 22:37:37 +0200294 def test_no_data_wrapper_deserialize_text_format(self):
295 model = JsonModel(data_wrapper=False)
296 resp = httplib2.Response({"status": "200"})
297 resp.reason = "OK"
298 content = CSV_TEXT_MOCK
299 content = model.response(resp, content)
300 self.assertEqual(content, CSV_TEXT_MOCK)
301
302 def test_no_data_wrapper_deserialize_raise_type_error(self):
303 buffer = io.StringIO()
304 buffer.write('String buffer')
305 model = JsonModel(data_wrapper=False)
306 resp = httplib2.Response({"status": "500"})
307 resp.reason = "The JSON object must be str, bytes or bytearray, not StringIO"
308 content = buffer
309 with self.assertRaises(TypeError):
310 model.response(resp, content)
311
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700312 def test_data_wrapper_deserialize(self):
313 model = JsonModel(data_wrapper=True)
314 resp = httplib2.Response({"status": "200"})
315 resp.reason = "OK"
316 content = '{"data": "is good"}'
317 content = model.response(resp, content)
318 self.assertEqual(content, "is good")
319
320 def test_data_wrapper_deserialize_nodata(self):
321 model = JsonModel(data_wrapper=True)
322 resp = httplib2.Response({"status": "200"})
323 resp.reason = "OK"
324 content = '{"atad": "is good"}'
325 content = model.response(resp, content)
326 self.assertEqual(content, {"atad": "is good"})
Ali Afshar81fde8e2012-10-23 11:14:28 -0700327
328
Bu Sun Kim66bb32c2019-10-30 10:11:58 -0700329if __name__ == "__main__":
330 unittest.main()