blob: 006eb47dc7b377bd1f6d78b25943a775260712c6 [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
22import six
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040023
24__author__ = 'jcgregorio@google.com (Joe Gregorio)'
25
Joe Gregorio34044bc2011-03-07 16:58:33 -050026import copy
Craig Citro6ae34d72014-08-18 23:10:09 -070027import json
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040028import os
Bu Sun Kim07f647c2019-08-09 14:55:24 -070029import platform
Pat Ferate497a90f2015-03-09 09:52:54 -070030import unittest2 as unittest
Joe Gregorioc5c5a372010-09-22 11:42:32 -040031import httplib2
John Asmuth864311d2014-04-24 15:46:08 -040032import googleapiclient.model
ade@google.comd69e5e42010-08-31 15:28:20 +010033
John Asmuth864311d2014-04-24 15:46:08 -040034from googleapiclient import __version__
35from googleapiclient.errors import HttpError
36from googleapiclient.model import JsonModel
Joe Gregorio34044bc2011-03-07 16:58:33 -050037
Pat Ferated5b61bd2015-03-03 16:04:11 -080038from six.moves.urllib.parse import parse_qs
ade@google.comd69e5e42010-08-31 15:28:20 +010039
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040040
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040041class Model(unittest.TestCase):
42 def test_json_no_body(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -050043 model = JsonModel(data_wrapper=False)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040044
45 headers = {}
ade@google.com850cf552010-08-20 23:24:56 +010046 path_params = {}
47 query_params = {}
48 body = None
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040049
Joe Gregorio5a4c3282013-07-29 13:48:06 -040050 headers, unused_params, query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -040051 headers, path_params, query_params, body)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040052
53 self.assertEqual(headers['accept'], 'application/json')
54 self.assertTrue('content-type' not in headers)
55 self.assertNotEqual(query, '')
56 self.assertEqual(body, None)
57
58 def test_json_body(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -050059 model = JsonModel(data_wrapper=False)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040060
61 headers = {}
ade@google.com850cf552010-08-20 23:24:56 +010062 path_params = {}
63 query_params = {}
64 body = {}
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040065
Joe Gregorio5a4c3282013-07-29 13:48:06 -040066 headers, unused_params, query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -040067 headers, path_params, query_params, body)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040068
69 self.assertEqual(headers['accept'], 'application/json')
70 self.assertEqual(headers['content-type'], 'application/json')
71 self.assertNotEqual(query, '')
Joe Gregorio913e70d2010-11-05 15:38:23 -040072 self.assertEqual(body, '{}')
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040073
Joe Gregoriod433b2a2011-02-22 10:51:51 -050074 def test_json_body_data_wrapper(self):
75 model = JsonModel(data_wrapper=True)
76
77 headers = {}
78 path_params = {}
79 query_params = {}
80 body = {}
81
Joe Gregorio5a4c3282013-07-29 13:48:06 -040082 headers, unused_params, query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -040083 headers, path_params, query_params, body)
Joe Gregoriod433b2a2011-02-22 10:51:51 -050084
85 self.assertEqual(headers['accept'], 'application/json')
86 self.assertEqual(headers['content-type'], 'application/json')
87 self.assertNotEqual(query, '')
88 self.assertEqual(body, '{"data": {}}')
89
Joe Gregorio8963ff92010-10-11 13:14:43 -040090 def test_json_body_default_data(self):
91 """Test that a 'data' wrapper doesn't get added if one is already present."""
Joe Gregoriod433b2a2011-02-22 10:51:51 -050092 model = JsonModel(data_wrapper=True)
Joe Gregorio8963ff92010-10-11 13:14:43 -040093
94 headers = {}
95 path_params = {}
96 query_params = {}
97 body = {'data': 'foo'}
98
Joe Gregorio5a4c3282013-07-29 13:48:06 -040099 headers, unused_params, query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -0400100 headers, path_params, query_params, body)
Joe Gregorio8963ff92010-10-11 13:14:43 -0400101
102 self.assertEqual(headers['accept'], 'application/json')
103 self.assertEqual(headers['content-type'], 'application/json')
104 self.assertNotEqual(query, '')
105 self.assertEqual(body, '{"data": "foo"}')
106
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400107 def test_json_build_query(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500108 model = JsonModel(data_wrapper=False)
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400109
110 headers = {}
111 path_params = {}
Joe Gregorio61d7e962011-02-22 22:52:07 -0500112 query_params = {'foo': 1, 'bar': u'\N{COMET}',
113 'baz': ['fe', 'fi', 'fo', 'fum'], # Repeated parameters
114 'qux': []}
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400115 body = {}
116
Joe Gregorio5a4c3282013-07-29 13:48:06 -0400117 headers, unused_params, query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -0400118 headers, path_params, query_params, body)
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400119
120 self.assertEqual(headers['accept'], 'application/json')
121 self.assertEqual(headers['content-type'], 'application/json')
122
Joe Gregorio61d7e962011-02-22 22:52:07 -0500123 query_dict = parse_qs(query[1:])
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400124 self.assertEqual(query_dict['foo'], ['1'])
Pat Ferateca397a72015-03-03 18:06:15 -0800125 if six.PY3:
126 # Python 3, no need to encode
127 self.assertEqual(query_dict['bar'], [u'\N{COMET}'])
128 else:
129 # Python 2, encode string
130 self.assertEqual(query_dict['bar'], [u'\N{COMET}'.encode('utf-8')])
Joe Gregorio61d7e962011-02-22 22:52:07 -0500131 self.assertEqual(query_dict['baz'], ['fe', 'fi', 'fo', 'fum'])
132 self.assertTrue('qux' not in query_dict)
Joe Gregorio913e70d2010-11-05 15:38:23 -0400133 self.assertEqual(body, '{}')
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400134
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400135 def test_user_agent(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500136 model = JsonModel(data_wrapper=False)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400137
138 headers = {'user-agent': 'my-test-app/1.23.4'}
139 path_params = {}
140 query_params = {}
141 body = {}
142
Joe Gregorio5a4c3282013-07-29 13:48:06 -0400143 headers, unused_params, unused_query, body = model.request(
Joe Gregorioa314b1f2013-04-05 16:25:11 -0400144 headers, path_params, query_params, body)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400145
Joe Gregorioa314b1f2013-04-05 16:25:11 -0400146 self.assertEqual(headers['user-agent'],
Bu Sun Kim07f647c2019-08-09 14:55:24 -0700147 'my-test-app/1.23.4 (gzip)')
148
149 def test_x_goog_api_client(self):
150 model = JsonModel(data_wrapper=False)
151
152 # test header composition for cloud clients that wrap discovery
153 headers = {'x-goog-api-client': 'gccl/1.23.4'}
154 path_params = {}
155 query_params = {}
156 body = {}
157
158 headers, unused_params, unused_query, body = model.request(
159 headers, path_params, query_params, body)
160
161 self.assertEqual(headers['x-goog-api-client'],
162 'gccl/1.23.4' + ' gdcl/' + __version__ + ' gl-python/' + platform.python_version())
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400163
164 def test_bad_response(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500165 model = JsonModel(data_wrapper=False)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400166 resp = httplib2.Response({'status': '401'})
167 resp.reason = 'Unauthorized'
INADA Naoki09157612015-03-25 01:51:03 +0900168 content = b'{"error": {"message": "not authorized"}}'
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400169
170 try:
171 content = model.response(resp, content)
172 self.fail('Should have thrown an exception')
INADA Naokic1505df2014-08-20 15:19:53 +0900173 except HttpError as e:
Joe Gregorio20b54fb2012-07-26 09:59:35 -0400174 self.assertTrue('not authorized' in str(e))
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400175
176 resp['content-type'] = 'application/json'
177
178 try:
179 content = model.response(resp, content)
180 self.fail('Should have thrown an exception')
INADA Naokic1505df2014-08-20 15:19:53 +0900181 except HttpError as e:
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400182 self.assertTrue('not authorized' in str(e))
183
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400184 def test_good_response(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500185 model = JsonModel(data_wrapper=True)
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400186 resp = httplib2.Response({'status': '200'})
187 resp.reason = 'OK'
188 content = '{"data": "is good"}'
189
190 content = model.response(resp, content)
191 self.assertEqual(content, 'is good')
Joe Gregoriofe695fb2010-08-30 12:04:04 -0400192
Joe Gregorio78a508d2010-10-26 16:36:36 -0400193 def test_good_response_wo_data(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500194 model = JsonModel(data_wrapper=False)
Joe Gregorio78a508d2010-10-26 16:36:36 -0400195 resp = httplib2.Response({'status': '200'})
196 resp.reason = 'OK'
197 content = '{"foo": "is good"}'
198
199 content = model.response(resp, content)
200 self.assertEqual(content, {'foo': 'is good'})
201
202 def test_good_response_wo_data_str(self):
Joe Gregoriod433b2a2011-02-22 10:51:51 -0500203 model = JsonModel(data_wrapper=False)
Joe Gregorio78a508d2010-10-26 16:36:36 -0400204 resp = httplib2.Response({'status': '200'})
205 resp.reason = 'OK'
206 content = '"data goes here"'
207
208 content = model.response(resp, content)
209 self.assertEqual(content, 'data goes here')
210
Matt McDonald2a5f4132011-04-29 16:32:27 -0400211 def test_no_content_response(self):
212 model = JsonModel(data_wrapper=False)
213 resp = httplib2.Response({'status': '204'})
214 resp.reason = 'No Content'
215 content = ''
Joe Gregorio34044bc2011-03-07 16:58:33 -0500216
Matt McDonald2a5f4132011-04-29 16:32:27 -0400217 content = model.response(resp, content)
218 self.assertEqual(content, {})
Joe Gregorio34044bc2011-03-07 16:58:33 -0500219
Matt McDonald2a5f4132011-04-29 16:32:27 -0400220 def test_logging(self):
Joe Gregorio34044bc2011-03-07 16:58:33 -0500221 class MockLogging(object):
222 def __init__(self):
223 self.info_record = []
224 self.debug_record = []
225 def info(self, message, *args):
226 self.info_record.append(message % args)
227
228 def debug(self, message, *args):
229 self.debug_record.append(message % args)
230
231 class MockResponse(dict):
232 def __init__(self, items):
233 super(MockResponse, self).__init__()
234 self.status = items['status']
INADA Naokid898a372015-03-04 03:52:46 +0900235 for key, value in six.iteritems(items):
Joe Gregorio34044bc2011-03-07 16:58:33 -0500236 self[key] = value
Emmett Butler09699152016-02-08 14:26:00 -0800237 old_logging = googleapiclient.model.LOGGER
238 googleapiclient.model.LOGGER = MockLogging()
John Asmuth864311d2014-04-24 15:46:08 -0400239 googleapiclient.model.dump_request_response = True
Matt McDonald2a5f4132011-04-29 16:32:27 -0400240 model = JsonModel()
Joe Gregorio34044bc2011-03-07 16:58:33 -0500241 request_body = {
242 'field1': 'value1',
243 'field2': 'value2'
244 }
245 body_string = model.request({}, {}, {}, request_body)[-1]
Craig Citro6ae34d72014-08-18 23:10:09 -0700246 json_body = json.loads(body_string)
Joe Gregorio34044bc2011-03-07 16:58:33 -0500247 self.assertEqual(request_body, json_body)
248
249 response = {'status': 200,
250 'response_field_1': 'response_value_1',
251 'response_field_2': 'response_value_2'}
252 response_body = model.response(MockResponse(response), body_string)
253 self.assertEqual(request_body, response_body)
Emmett Butler09699152016-02-08 14:26:00 -0800254 self.assertEqual(googleapiclient.model.LOGGER.info_record[:2],
Joe Gregorioafdf50b2011-03-08 09:41:52 -0500255 ['--request-start--',
256 '-headers-start-'])
257 self.assertTrue('response_field_1: response_value_1' in
Emmett Butler09699152016-02-08 14:26:00 -0800258 googleapiclient.model.LOGGER.info_record)
Joe Gregorioafdf50b2011-03-08 09:41:52 -0500259 self.assertTrue('response_field_2: response_value_2' in
Emmett Butler09699152016-02-08 14:26:00 -0800260 googleapiclient.model.LOGGER.info_record)
261 self.assertEqual(json.loads(googleapiclient.model.LOGGER.info_record[-2]),
Joe Gregorio34044bc2011-03-07 16:58:33 -0500262 request_body)
Emmett Butler09699152016-02-08 14:26:00 -0800263 self.assertEqual(googleapiclient.model.LOGGER.info_record[-1],
Joe Gregorio34044bc2011-03-07 16:58:33 -0500264 '--response-end--')
Emmett Butler09699152016-02-08 14:26:00 -0800265 googleapiclient.model.LOGGER = old_logging
Joe Gregorio34044bc2011-03-07 16:58:33 -0500266
Ali Afshar81fde8e2012-10-23 11:14:28 -0700267 def test_no_data_wrapper_deserialize(self):
268 model = JsonModel(data_wrapper=False)
269 resp = httplib2.Response({'status': '200'})
270 resp.reason = 'OK'
271 content = '{"data": "is good"}'
272 content = model.response(resp, content)
273 self.assertEqual(content, {'data': 'is good'})
274
275 def test_data_wrapper_deserialize(self):
276 model = JsonModel(data_wrapper=True)
277 resp = httplib2.Response({'status': '200'})
278 resp.reason = 'OK'
279 content = '{"data": "is good"}'
280 content = model.response(resp, content)
281 self.assertEqual(content, 'is good')
282
283 def test_data_wrapper_deserialize_nodata(self):
284 model = JsonModel(data_wrapper=True)
285 resp = httplib2.Response({'status': '200'})
286 resp.reason = 'OK'
287 content = '{"atad": "is good"}'
288 content = model.response(resp, content)
289 self.assertEqual(content, {'atad': 'is good'})
290
291
Joe Gregorio34044bc2011-03-07 16:58:33 -0500292
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400293if __name__ == '__main__':
294 unittest.main()