blob: 67e8aa6bc50751098ec72000d96fcfda90984c61 [file] [log] [blame]
Joe Gregorio6bcbcea2011-03-10 15:26:05 -05001#!/usr/bin/python2.4
2#
3# Copyright 2010 Google Inc.
4#
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.
16
17"""Http tests
18
19Unit tests for the apiclient.http.
20"""
21
22__author__ = 'jcgregorio@google.com (Joe Gregorio)'
23
Joe Gregorio7cbceab2011-06-27 10:46:54 -040024# Do not remove the httplib2 import
25import httplib2
Joe Gregoriod0bd3882011-11-22 09:49:47 -050026import os
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050027import unittest
28
Joe Gregorio66f57522011-11-30 11:00:00 -050029from apiclient.errors import BatchError
30from apiclient.http import BatchHttpRequest
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050031from apiclient.http import HttpMockSequence
Joe Gregoriod0bd3882011-11-22 09:49:47 -050032from apiclient.http import HttpRequest
Joe Gregoriod0bd3882011-11-22 09:49:47 -050033from apiclient.http import MediaFileUpload
Joe Gregorio66f57522011-11-30 11:00:00 -050034from apiclient.http import MediaUpload
Ali Afshar6f11ea12012-02-07 10:32:14 -050035from apiclient.http import MediaInMemoryUpload
Joe Gregorio66f57522011-11-30 11:00:00 -050036from apiclient.http import set_user_agent
37from apiclient.model import JsonModel
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050038
39
Joe Gregoriod0bd3882011-11-22 09:49:47 -050040DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
41
42
43def datafile(filename):
44 return os.path.join(DATA_DIR, filename)
45
Joe Gregorio6bcbcea2011-03-10 15:26:05 -050046class TestUserAgent(unittest.TestCase):
47
48 def test_set_user_agent(self):
49 http = HttpMockSequence([
50 ({'status': '200'}, 'echo_request_headers'),
51 ])
52
53 http = set_user_agent(http, "my_app/5.5")
54 resp, content = http.request("http://example.com")
55 self.assertEqual(content['user-agent'], 'my_app/5.5')
56
57 def test_set_user_agent_nested(self):
58 http = HttpMockSequence([
59 ({'status': '200'}, 'echo_request_headers'),
60 ])
61
62 http = set_user_agent(http, "my_app/5.5")
63 http = set_user_agent(http, "my_library/0.1")
64 resp, content = http.request("http://example.com")
65 self.assertEqual(content['user-agent'], 'my_app/5.5 my_library/0.1')
66
Joe Gregoriod0bd3882011-11-22 09:49:47 -050067 def test_media_file_upload_to_from_json(self):
68 upload = MediaFileUpload(
69 datafile('small.png'), chunksize=500, resumable=True)
70 self.assertEquals('image/png', upload.mimetype())
71 self.assertEquals(190, upload.size())
72 self.assertEquals(True, upload.resumable())
73 self.assertEquals(500, upload.chunksize())
74 self.assertEquals('PNG', upload.getbytes(1, 3))
75
76 json = upload.to_json()
77 new_upload = MediaUpload.new_from_json(json)
78
79 self.assertEquals('image/png', new_upload.mimetype())
80 self.assertEquals(190, new_upload.size())
81 self.assertEquals(True, new_upload.resumable())
82 self.assertEquals(500, new_upload.chunksize())
83 self.assertEquals('PNG', new_upload.getbytes(1, 3))
84
85 def test_http_request_to_from_json(self):
86
87 def _postproc(*kwargs):
88 pass
89
90 http = httplib2.Http()
91 media_upload = MediaFileUpload(
92 datafile('small.png'), chunksize=500, resumable=True)
93 req = HttpRequest(
94 http,
95 _postproc,
96 'http://example.com',
97 method='POST',
98 body='{}',
99 headers={'content-type': 'multipart/related; boundary="---flubber"'},
100 methodId='foo',
101 resumable=media_upload)
102
103 json = req.to_json()
104 new_req = HttpRequest.from_json(json, http, _postproc)
105
Joe Gregorio66f57522011-11-30 11:00:00 -0500106 self.assertEquals(new_req.headers,
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500107 {'content-type':
108 'multipart/related; boundary="---flubber"'})
109 self.assertEquals(new_req.uri, 'http://example.com')
110 self.assertEquals(new_req.body, '{}')
111 self.assertEquals(new_req.http, http)
112 self.assertEquals(new_req.resumable.to_json(), media_upload.to_json())
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500113
Joe Gregorio66f57522011-11-30 11:00:00 -0500114EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
115Content-Type: application/json
116MIME-Version: 1.0
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500117Host: www.googleapis.com
118content-length: 2\r\n\r\n{}"""
119
120
121NO_BODY_EXPECTED = """POST /someapi/v1/collection/?foo=bar HTTP/1.1
122Content-Type: application/json
123MIME-Version: 1.0
124Host: www.googleapis.com
125content-length: 0\r\n\r\n"""
Joe Gregorio66f57522011-11-30 11:00:00 -0500126
127
128RESPONSE = """HTTP/1.1 200 OK
129Content-Type application/json
130Content-Length: 14
131ETag: "etag/pony"\r\n\r\n{"answer": 42}"""
132
133
134BATCH_RESPONSE = """--batch_foobarbaz
135Content-Type: application/http
136Content-Transfer-Encoding: binary
137Content-ID: <randomness+1>
138
139HTTP/1.1 200 OK
140Content-Type application/json
141Content-Length: 14
142ETag: "etag/pony"\r\n\r\n{"foo": 42}
143
144--batch_foobarbaz
145Content-Type: application/http
146Content-Transfer-Encoding: binary
147Content-ID: <randomness+2>
148
149HTTP/1.1 200 OK
150Content-Type application/json
151Content-Length: 14
152ETag: "etag/sheep"\r\n\r\n{"baz": "qux"}
153--batch_foobarbaz--"""
154
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500155
Joe Gregorio66f57522011-11-30 11:00:00 -0500156class TestBatch(unittest.TestCase):
157
158 def setUp(self):
159 model = JsonModel()
160 self.request1 = HttpRequest(
161 None,
162 model.response,
163 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
164 method='POST',
165 body='{}',
166 headers={'content-type': 'application/json'})
167
168 self.request2 = HttpRequest(
169 None,
170 model.response,
171 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500172 method='GET',
173 body='',
Joe Gregorio66f57522011-11-30 11:00:00 -0500174 headers={'content-type': 'application/json'})
175
176
177 def test_id_to_from_content_id_header(self):
178 batch = BatchHttpRequest()
179 self.assertEquals('12', batch._header_to_id(batch._id_to_header('12')))
180
181 def test_invalid_content_id_header(self):
182 batch = BatchHttpRequest()
183 self.assertRaises(BatchError, batch._header_to_id, '[foo+x]')
184 self.assertRaises(BatchError, batch._header_to_id, 'foo+1')
185 self.assertRaises(BatchError, batch._header_to_id, '<foo>')
186
187 def test_serialize_request(self):
188 batch = BatchHttpRequest()
189 request = HttpRequest(
190 None,
191 None,
192 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
193 method='POST',
194 body='{}',
195 headers={'content-type': 'application/json'},
196 methodId=None,
197 resumable=None)
198 s = batch._serialize_request(request).splitlines()
199 self.assertEquals(s, EXPECTED.splitlines())
200
Joe Gregoriodd813822012-01-25 10:32:47 -0500201 def test_serialize_request_media_body(self):
202 batch = BatchHttpRequest()
203 f = open(datafile('small.png'))
204 body = f.read()
205 f.close()
206
207 request = HttpRequest(
208 None,
209 None,
210 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
211 method='POST',
212 body=body,
213 headers={'content-type': 'application/json'},
214 methodId=None,
215 resumable=None)
216 s = batch._serialize_request(request).splitlines()
217
218
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500219 def test_serialize_request_no_body(self):
220 batch = BatchHttpRequest()
221 request = HttpRequest(
222 None,
223 None,
224 'https://www.googleapis.com/someapi/v1/collection/?foo=bar',
225 method='POST',
226 body='',
227 headers={'content-type': 'application/json'},
228 methodId=None,
229 resumable=None)
230 s = batch._serialize_request(request).splitlines()
231 self.assertEquals(s, NO_BODY_EXPECTED.splitlines())
232
Joe Gregorio66f57522011-11-30 11:00:00 -0500233 def test_deserialize_response(self):
234 batch = BatchHttpRequest()
235 resp, content = batch._deserialize_response(RESPONSE)
236
237 self.assertEquals(resp.status, 200)
238 self.assertEquals(resp.reason, 'OK')
239 self.assertEquals(resp.version, 11)
240 self.assertEquals(content, '{"answer": 42}')
241
242 def test_new_id(self):
243 batch = BatchHttpRequest()
244
245 id_ = batch._new_id()
246 self.assertEquals(id_, '1')
247
248 id_ = batch._new_id()
249 self.assertEquals(id_, '2')
250
251 batch.add(self.request1, request_id='3')
252
253 id_ = batch._new_id()
254 self.assertEquals(id_, '4')
255
256 def test_add(self):
257 batch = BatchHttpRequest()
258 batch.add(self.request1, request_id='1')
259 self.assertRaises(KeyError, batch.add, self.request1, request_id='1')
260
261 def test_add_fail_for_resumable(self):
262 batch = BatchHttpRequest()
263
264 upload = MediaFileUpload(
265 datafile('small.png'), chunksize=500, resumable=True)
266 self.request1.resumable = upload
267 self.assertRaises(BatchError, batch.add, self.request1, request_id='1')
268
269 def test_execute(self):
270 class Callbacks(object):
271 def __init__(self):
272 self.responses = {}
273
274 def f(self, request_id, response):
275 self.responses[request_id] = response
276
277 batch = BatchHttpRequest()
278 callbacks = Callbacks()
279
280 batch.add(self.request1, callback=callbacks.f)
281 batch.add(self.request2, callback=callbacks.f)
282 http = HttpMockSequence([
283 ({'status': '200',
284 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
285 BATCH_RESPONSE),
286 ])
287 batch.execute(http)
288 self.assertEqual(callbacks.responses['1'], {'foo': 42})
289 self.assertEqual(callbacks.responses['2'], {'baz': 'qux'})
290
Joe Gregorio5d1171b2012-01-05 10:48:24 -0500291 def test_execute_request_body(self):
292 batch = BatchHttpRequest()
293
294 batch.add(self.request1)
295 batch.add(self.request2)
296 http = HttpMockSequence([
297 ({'status': '200',
298 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
299 'echo_request_body'),
300 ])
301 try:
302 batch.execute(http)
303 self.fail('Should raise exception')
304 except BatchError, e:
305 boundary, _ = e.content.split(None, 1)
306 self.assertEqual('--', boundary[:2])
307 parts = e.content.split(boundary)
308 self.assertEqual(4, len(parts))
309 self.assertEqual('', parts[0])
310 self.assertEqual('--', parts[3])
311 header = parts[1].splitlines()[1]
312 self.assertEqual('Content-Type: application/http', header)
313
Joe Gregorio66f57522011-11-30 11:00:00 -0500314 def test_execute_global_callback(self):
315 class Callbacks(object):
316 def __init__(self):
317 self.responses = {}
318
319 def f(self, request_id, response):
320 self.responses[request_id] = response
321
322 callbacks = Callbacks()
323 batch = BatchHttpRequest(callback=callbacks.f)
324
325 batch.add(self.request1)
326 batch.add(self.request2)
327 http = HttpMockSequence([
328 ({'status': '200',
329 'content-type': 'multipart/mixed; boundary="batch_foobarbaz"'},
330 BATCH_RESPONSE),
331 ])
332 batch.execute(http)
333 self.assertEqual(callbacks.responses['1'], {'foo': 42})
334 self.assertEqual(callbacks.responses['2'], {'baz': 'qux'})
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500335
Ali Afshar6f11ea12012-02-07 10:32:14 -0500336 def test_media_inmemory_upload(self):
337 media = MediaInMemoryUpload('abcdef', 'text/plain', chunksize=10,
338 resumable=True)
339 self.assertEqual('text/plain', media.mimetype())
340 self.assertEqual(10, media.chunksize())
341 self.assertTrue(media.resumable())
342 self.assertEqual('bc', media.getbytes(1, 2))
343
344 def test_media_inmemory_upload_json_roundtrip(self):
345 media = MediaInMemoryUpload(os.urandom(64), 'text/plain', chunksize=10,
346 resumable=True)
347 data = media.to_json()
348 newmedia = MediaInMemoryUpload.new_from_json(data)
349 self.assertEqual(media._body, newmedia._body)
350 self.assertEqual(media._chunksize, newmedia._chunksize)
351 self.assertEqual(media._resumable, newmedia._resumable)
352 self.assertEqual(media._mimetype, newmedia._mimetype)
353
354
Joe Gregorio6bcbcea2011-03-10 15:26:05 -0500355if __name__ == '__main__':
356 unittest.main()