blob: 09971f7409bff378ed38c4f5cc4fba6582445e52 [file] [log] [blame]
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04001#!/usr/bin/python2.4
Joe Gregoriof863f7a2011-02-24 03:24:44 -05002# -*- coding: utf-8 -*-
Joe Gregorioba9ea7f2010-08-19 15:49:04 -04003#
Joe Gregorio6d5e94f2010-08-25 23:49:30 -04004# Copyright 2010 Google Inc.
5#
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"""
23
24__author__ = 'jcgregorio@google.com (Joe Gregorio)'
25
Joe Gregorio32f048f2012-08-27 16:31:27 -040026import gflags
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040027import httplib2
28import os
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040029import sys
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040030import unittest
Joe Gregorio00cf1d92010-09-27 09:22:03 -040031import urlparse
Joe Gregorio910b9b12012-06-12 09:36:30 -040032import StringIO
33
Joe Gregorioa98733f2011-09-16 10:12:28 -040034
ade@google.comc5eb46f2010-09-27 23:35:39 +010035try:
36 from urlparse import parse_qs
37except ImportError:
38 from cgi import parse_qs
Joe Gregorio00cf1d92010-09-27 09:22:03 -040039
ade@google.com6a8c1cb2011-09-06 17:40:00 +010040from apiclient.discovery import build, build_from_document, key2param
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050041from apiclient.errors import HttpError
Joe Gregorio49396552011-03-08 10:39:00 -050042from apiclient.errors import InvalidJsonError
Joe Gregoriofdf7c802011-06-30 12:33:38 -040043from apiclient.errors import MediaUploadSizeError
Joe Gregoriod0bd3882011-11-22 09:49:47 -050044from apiclient.errors import ResumableUploadError
Joe Gregoriofdf7c802011-06-30 12:33:38 -040045from apiclient.errors import UnacceptableMimeTypeError
Joe Gregoriod0bd3882011-11-22 09:49:47 -050046from apiclient.http import HttpMock
47from apiclient.http import HttpMockSequence
48from apiclient.http import MediaFileUpload
Joe Gregorio910b9b12012-06-12 09:36:30 -040049from apiclient.http import MediaIoBaseUpload
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040050from apiclient.http import MediaUpload
Joe Gregoriod0bd3882011-11-22 09:49:47 -050051from apiclient.http import MediaUploadProgress
52from apiclient.http import tunnel_patch
Joe Gregorio910b9b12012-06-12 09:36:30 -040053from oauth2client.anyjson import simplejson
Joe Gregoriocb8103d2011-02-11 23:20:52 -050054
55DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
56
Joe Gregorio32f048f2012-08-27 16:31:27 -040057FLAGS = gflags.FLAGS
58FLAGS.positional_parameters_enforcement = 'EXCEPTION'
59
Joe Gregorioa98733f2011-09-16 10:12:28 -040060
Joe Gregoriocb8103d2011-02-11 23:20:52 -050061def datafile(filename):
62 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040063
64
Joe Gregorioc5c5a372010-09-22 11:42:32 -040065class Utilities(unittest.TestCase):
66 def test_key2param(self):
67 self.assertEqual('max_results', key2param('max-results'))
68 self.assertEqual('x007_bond', key2param('007-bond'))
69
70
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050071class DiscoveryErrors(unittest.TestCase):
72
Joe Gregorio68a8cfe2012-08-03 16:17:40 -040073 def test_tests_should_be_run_with_strict_positional_enforcement(self):
74 try:
75 plus = build('plus', 'v1', None)
76 self.fail("should have raised a TypeError exception over missing http=.")
77 except TypeError:
78 pass
79
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050080 def test_failed_to_parse_discovery_json(self):
81 self.http = HttpMock(datafile('malformed.json'), {'status': '200'})
82 try:
Joe Gregorio68a8cfe2012-08-03 16:17:40 -040083 plus = build('plus', 'v1', http=self.http)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050084 self.fail("should have raised an exception over malformed JSON.")
Joe Gregorio49396552011-03-08 10:39:00 -050085 except InvalidJsonError:
86 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050087
88
ade@google.com6a8c1cb2011-09-06 17:40:00 +010089class DiscoveryFromDocument(unittest.TestCase):
Joe Gregorioa98733f2011-09-16 10:12:28 -040090
ade@google.com6a8c1cb2011-09-06 17:40:00 +010091 def test_can_build_from_local_document(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -050092 discovery = file(datafile('plus.json')).read()
93 plus = build_from_document(discovery, base="https://www.googleapis.com/")
94 self.assertTrue(plus is not None)
Joe Gregorioa98733f2011-09-16 10:12:28 -040095
ade@google.com6a8c1cb2011-09-06 17:40:00 +010096 def test_building_with_base_remembers_base(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -050097 discovery = file(datafile('plus.json')).read()
Joe Gregorioa98733f2011-09-16 10:12:28 -040098
ade@google.com6a8c1cb2011-09-06 17:40:00 +010099 base = "https://www.example.com/"
Joe Gregorio7b70f432011-11-09 10:18:51 -0500100 plus = build_from_document(discovery, base=base)
Joe Gregorioa2838152012-07-16 11:52:17 -0400101 self.assertEquals("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100102
103
Joe Gregorioa98733f2011-09-16 10:12:28 -0400104class DiscoveryFromHttp(unittest.TestCase):
Joe Gregorio583d9e42011-09-16 15:54:15 -0400105 def setUp(self):
Joe Bedafb463cb2011-09-19 17:39:49 -0700106 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400107
Joe Gregorio583d9e42011-09-16 15:54:15 -0400108 def tearDown(self):
109 os.environ = self.old_environ
110
111 def test_userip_is_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400112 # build() will raise an HttpError on a 400, use this to pick the request uri
113 # out of the raised exception.
Joe Gregorio583d9e42011-09-16 15:54:15 -0400114 os.environ['REMOTE_ADDR'] = '10.0.0.1'
Joe Gregorioa98733f2011-09-16 10:12:28 -0400115 try:
116 http = HttpMockSequence([
117 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
118 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400119 zoo = build('zoo', 'v1', http=http, developerKey='foo',
Joe Gregorioa98733f2011-09-16 10:12:28 -0400120 discoveryServiceUrl='http://example.com')
121 self.fail('Should have raised an exception.')
122 except HttpError, e:
Joe Gregorio583d9e42011-09-16 15:54:15 -0400123 self.assertEqual(e.uri, 'http://example.com?userIp=10.0.0.1')
Joe Gregorioa98733f2011-09-16 10:12:28 -0400124
Joe Gregorio583d9e42011-09-16 15:54:15 -0400125 def test_userip_missing_is_not_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400126 # build() will raise an HttpError on a 400, use this to pick the request uri
127 # out of the raised exception.
128 try:
129 http = HttpMockSequence([
130 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
131 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400132 zoo = build('zoo', 'v1', http=http, developerKey=None,
Joe Gregorioa98733f2011-09-16 10:12:28 -0400133 discoveryServiceUrl='http://example.com')
134 self.fail('Should have raised an exception.')
135 except HttpError, e:
136 self.assertEqual(e.uri, 'http://example.com')
137
138
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400139class Discovery(unittest.TestCase):
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400140
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400141 def test_method_error_checking(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500142 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400143 plus = build('plus', 'v1', http=self.http)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400144
145 # Missing required parameters
146 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500147 plus.activities().list()
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400148 self.fail()
149 except TypeError, e:
150 self.assertTrue('Missing' in str(e))
151
Joe Gregorio2467afa2012-06-20 12:21:25 -0400152 # Missing required parameters even if supplied as None.
153 try:
154 plus.activities().list(collection=None, userId=None)
155 self.fail()
156 except TypeError, e:
157 self.assertTrue('Missing' in str(e))
158
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400159 # Parameter doesn't match regex
160 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500161 plus.activities().list(collection='not_a_collection_name', userId='me')
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400162 self.fail()
163 except TypeError, e:
Joe Gregorioca876e42011-02-22 19:39:42 -0500164 self.assertTrue('not an allowed value' in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400165
166 # Unexpected parameter
167 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500168 plus.activities().list(flubber=12)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400169 self.fail()
170 except TypeError, e:
171 self.assertTrue('unexpected' in str(e))
172
Joe Gregoriobee86832011-02-22 10:00:19 -0500173 def _check_query_types(self, request):
174 parsed = urlparse.urlparse(request.uri)
175 q = parse_qs(parsed[4])
176 self.assertEqual(q['q'], ['foo'])
177 self.assertEqual(q['i'], ['1'])
178 self.assertEqual(q['n'], ['1.0'])
179 self.assertEqual(q['b'], ['false'])
180 self.assertEqual(q['a'], ['[1, 2, 3]'])
181 self.assertEqual(q['o'], ['{\'a\': 1}'])
182 self.assertEqual(q['e'], ['bar'])
183
184 def test_type_coercion(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400185 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400186 zoo = build('zoo', 'v1', http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500187
Joe Gregorioa98733f2011-09-16 10:12:28 -0400188 request = zoo.query(
189 q="foo", i=1.0, n=1.0, b=0, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500190 self._check_query_types(request)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400191 request = zoo.query(
192 q="foo", i=1, n=1, b=False, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500193 self._check_query_types(request)
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500194
Joe Gregorioa98733f2011-09-16 10:12:28 -0400195 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800196 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar', er='two')
Joe Gregorio6804c7a2011-11-18 14:30:32 -0500197
198 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800199 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar',
200 er=['one', 'three'], rr=['foo', 'bar'])
Joe Gregoriobee86832011-02-22 10:00:19 -0500201 self._check_query_types(request)
202
Craig Citro1e742822012-03-01 12:59:22 -0800203 # Five is right out.
Joe Gregorio20c26e52012-03-02 15:58:31 -0500204 self.assertRaises(TypeError, zoo.query, er=['one', 'five'])
Craig Citro1e742822012-03-01 12:59:22 -0800205
Joe Gregorio13217952011-02-22 15:37:38 -0500206 def test_optional_stack_query_parameters(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400207 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400208 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400209 request = zoo.query(trace='html', fields='description')
Joe Gregorio13217952011-02-22 15:37:38 -0500210
Joe Gregorioca876e42011-02-22 19:39:42 -0500211 parsed = urlparse.urlparse(request.uri)
212 q = parse_qs(parsed[4])
213 self.assertEqual(q['trace'], ['html'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400214 self.assertEqual(q['fields'], ['description'])
215
Joe Gregorio2467afa2012-06-20 12:21:25 -0400216 def test_string_params_value_of_none_get_dropped(self):
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400217 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400218 zoo = build('zoo', 'v1', http=http)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400219 request = zoo.query(trace=None, fields='description')
220
221 parsed = urlparse.urlparse(request.uri)
222 q = parse_qs(parsed[4])
223 self.assertFalse('trace' in q)
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400224
Joe Gregorioe08a1662011-12-07 09:48:22 -0500225 def test_model_added_query_parameters(self):
226 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400227 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500228 request = zoo.animals().get(name='Lion')
229
230 parsed = urlparse.urlparse(request.uri)
231 q = parse_qs(parsed[4])
232 self.assertEqual(q['alt'], ['json'])
233 self.assertEqual(request.headers['accept'], 'application/json')
234
235 def test_fallback_to_raw_model(self):
236 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400237 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500238 request = zoo.animals().getmedia(name='Lion')
239
240 parsed = urlparse.urlparse(request.uri)
241 q = parse_qs(parsed[4])
242 self.assertTrue('alt' not in q)
243 self.assertEqual(request.headers['accept'], '*/*')
244
Joe Gregoriof4153422011-03-18 22:45:18 -0400245 def test_patch(self):
246 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400247 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400248 request = zoo.animals().patch(name='lion', body='{"description": "foo"}')
249
250 self.assertEqual(request.method, 'PATCH')
251
252 def test_tunnel_patch(self):
253 http = HttpMockSequence([
254 ({'status': '200'}, file(datafile('zoo.json'), 'r').read()),
255 ({'status': '200'}, 'echo_request_headers_as_json'),
256 ])
257 http = tunnel_patch(http)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400258 zoo = build('zoo', 'v1', http=http)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400259 resp = zoo.animals().patch(
260 name='lion', body='{"description": "foo"}').execute()
Joe Gregoriof4153422011-03-18 22:45:18 -0400261
262 self.assertTrue('x-http-method-override' in resp)
Joe Gregorioca876e42011-02-22 19:39:42 -0500263
Joe Gregorio7b70f432011-11-09 10:18:51 -0500264 def test_plus_resources(self):
265 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400266 plus = build('plus', 'v1', http=self.http)
Joe Gregorio7b70f432011-11-09 10:18:51 -0500267 self.assertTrue(getattr(plus, 'activities'))
268 self.assertTrue(getattr(plus, 'people'))
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400269
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400270 def test_full_featured(self):
271 # Zoo should exercise all discovery facets
272 # and should also have no future.json file.
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500273 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400274 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400275 self.assertTrue(getattr(zoo, 'animals'))
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500276
Joe Gregoriof4153422011-03-18 22:45:18 -0400277 request = zoo.animals().list(name='bat', projection="full")
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400278 parsed = urlparse.urlparse(request.uri)
279 q = parse_qs(parsed[4])
280 self.assertEqual(q['name'], ['bat'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400281 self.assertEqual(q['projection'], ['full'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400282
Joe Gregorio3fada332011-01-07 17:07:45 -0500283 def test_nested_resources(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500284 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400285 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio3fada332011-01-07 17:07:45 -0500286 self.assertTrue(getattr(zoo, 'animals'))
287 request = zoo.my().favorites().list(max_results="5")
288 parsed = urlparse.urlparse(request.uri)
289 q = parse_qs(parsed[4])
290 self.assertEqual(q['max-results'], ['5'])
291
Joe Gregoriod92897c2011-07-07 11:44:56 -0400292 def test_methods_with_reserved_names(self):
293 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400294 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod92897c2011-07-07 11:44:56 -0400295 self.assertTrue(getattr(zoo, 'animals'))
296 request = zoo.global_().print_().assert_(max_results="5")
297 parsed = urlparse.urlparse(request.uri)
Joe Gregorioa2838152012-07-16 11:52:17 -0400298 self.assertEqual(parsed[2], '/zoo/v1/global/print/assert')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400299
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500300 def test_top_level_functions(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500301 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400302 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500303 self.assertTrue(getattr(zoo, 'query'))
304 request = zoo.query(q="foo")
305 parsed = urlparse.urlparse(request.uri)
306 q = parse_qs(parsed[4])
307 self.assertEqual(q['q'], ['foo'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400308
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400309 def test_simple_media_uploads(self):
310 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400311 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400312 doc = getattr(zoo.animals().insert, '__doc__')
313 self.assertTrue('media_body' in doc)
314
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400315 def test_simple_media_upload_no_max_size_provided(self):
316 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400317 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400318 request = zoo.animals().crossbreed(media_body=datafile('small.png'))
319 self.assertEquals('image/png', request.headers['content-type'])
320 self.assertEquals('PNG', request.body[1:4])
321
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400322 def test_simple_media_raise_correct_exceptions(self):
323 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400324 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400325
326 try:
327 zoo.animals().insert(media_body=datafile('smiley.png'))
328 self.fail("should throw exception if media is too large.")
329 except MediaUploadSizeError:
330 pass
331
332 try:
333 zoo.animals().insert(media_body=datafile('small.jpg'))
334 self.fail("should throw exception if mimetype is unacceptable.")
335 except UnacceptableMimeTypeError:
336 pass
337
338 def test_simple_media_good_upload(self):
339 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400340 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400341
342 request = zoo.animals().insert(media_body=datafile('small.png'))
343 self.assertEquals('image/png', request.headers['content-type'])
344 self.assertEquals('PNG', request.body[1:4])
Joe Gregoriode860442012-03-02 15:55:52 -0500345 self.assertEqual(
Joe Gregorioa2838152012-07-16 11:52:17 -0400346 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500347 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400348
349 def test_multipart_media_raise_correct_exceptions(self):
350 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400351 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400352
353 try:
354 zoo.animals().insert(media_body=datafile('smiley.png'), body={})
355 self.fail("should throw exception if media is too large.")
356 except MediaUploadSizeError:
357 pass
358
359 try:
360 zoo.animals().insert(media_body=datafile('small.jpg'), body={})
361 self.fail("should throw exception if mimetype is unacceptable.")
362 except UnacceptableMimeTypeError:
363 pass
364
365 def test_multipart_media_good_upload(self):
366 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400367 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400368
369 request = zoo.animals().insert(media_body=datafile('small.png'), body={})
Joe Gregorioa98733f2011-09-16 10:12:28 -0400370 self.assertTrue(request.headers['content-type'].startswith(
371 'multipart/related'))
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400372 self.assertEquals('--==', request.body[0:4])
Joe Gregoriode860442012-03-02 15:55:52 -0500373 self.assertEqual(
Joe Gregorioa2838152012-07-16 11:52:17 -0400374 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500375 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400376
377 def test_media_capable_method_without_media(self):
378 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400379 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400380
381 request = zoo.animals().insert(body={})
382 self.assertTrue(request.headers['content-type'], 'application/json')
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400383
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500384 def test_resumable_multipart_media_good_upload(self):
385 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400386 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500387
388 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
389 request = zoo.animals().insert(media_body=media_upload, body={})
390 self.assertTrue(request.headers['content-type'].startswith(
Joe Gregorio945be3e2012-01-27 17:01:06 -0500391 'application/json'))
392 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500393 self.assertEquals(media_upload, request.resumable)
394
395 self.assertEquals('image/png', request.resumable.mimetype())
396
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500397 self.assertNotEquals(request.body, None)
398 self.assertEquals(request.resumable_uri, None)
399
400 http = HttpMockSequence([
401 ({'status': '200',
402 'location': 'http://upload.example.com'}, ''),
403 ({'status': '308',
404 'location': 'http://upload.example.com/2',
405 'range': '0-12'}, ''),
406 ({'status': '308',
407 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500408 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500409 ({'status': '200'}, '{"foo": "bar"}'),
410 ])
411
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400412 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500413 self.assertEquals(None, body)
414 self.assertTrue(isinstance(status, MediaUploadProgress))
415 self.assertEquals(13, status.resumable_progress)
416
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500417 # Two requests should have been made and the resumable_uri should have been
418 # updated for each one.
419 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
420
421 self.assertEquals(media_upload, request.resumable)
422 self.assertEquals(13, request.resumable_progress)
423
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400424 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500425 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500426 self.assertEquals(media_upload.size()-1, request.resumable_progress)
427 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500428
429 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400430 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500431 self.assertEquals(body, {"foo": "bar"})
432 self.assertEquals(status, None)
433
434
435 def test_resumable_media_good_upload(self):
436 """Not a multipart upload."""
437 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400438 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500439
440 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
441 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500442 self.assertEquals(media_upload, request.resumable)
443
444 self.assertEquals('image/png', request.resumable.mimetype())
445
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500446 self.assertEquals(request.body, None)
447 self.assertEquals(request.resumable_uri, None)
448
449 http = HttpMockSequence([
450 ({'status': '200',
451 'location': 'http://upload.example.com'}, ''),
452 ({'status': '308',
453 'location': 'http://upload.example.com/2',
454 'range': '0-12'}, ''),
455 ({'status': '308',
456 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500457 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500458 ({'status': '200'}, '{"foo": "bar"}'),
459 ])
460
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400461 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500462 self.assertEquals(None, body)
463 self.assertTrue(isinstance(status, MediaUploadProgress))
464 self.assertEquals(13, status.resumable_progress)
465
466 # Two requests should have been made and the resumable_uri should have been
467 # updated for each one.
468 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
469
470 self.assertEquals(media_upload, request.resumable)
471 self.assertEquals(13, request.resumable_progress)
472
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400473 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500474 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500475 self.assertEquals(media_upload.size()-1, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500476 self.assertEquals(request.body, None)
477
478 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400479 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500480 self.assertEquals(body, {"foo": "bar"})
481 self.assertEquals(status, None)
482
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500483 def test_resumable_media_good_upload_from_execute(self):
484 """Not a multipart upload."""
485 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400486 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500487
488 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
489 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriode860442012-03-02 15:55:52 -0500490 self.assertEqual(
Joe Gregorioa2838152012-07-16 11:52:17 -0400491 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500492 request.uri)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500493
494 http = HttpMockSequence([
495 ({'status': '200',
496 'location': 'http://upload.example.com'}, ''),
497 ({'status': '308',
498 'location': 'http://upload.example.com/2',
499 'range': '0-12'}, ''),
500 ({'status': '308',
501 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500502 'range': '0-%d' % media_upload.size()}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500503 ({'status': '200'}, '{"foo": "bar"}'),
504 ])
505
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400506 body = request.execute(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500507 self.assertEquals(body, {"foo": "bar"})
508
509 def test_resumable_media_fail_unknown_response_code_first_request(self):
510 """Not a multipart upload."""
511 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400512 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500513
514 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
515 request = zoo.animals().insert(media_body=media_upload, body=None)
516
517 http = HttpMockSequence([
518 ({'status': '400',
519 'location': 'http://upload.example.com'}, ''),
520 ])
521
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400522 self.assertRaises(ResumableUploadError, request.execute, http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500523
524 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
525 """Not a multipart upload."""
526 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400527 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500528
529 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
530 request = zoo.animals().insert(media_body=media_upload, body=None)
531
532 http = HttpMockSequence([
533 ({'status': '200',
534 'location': 'http://upload.example.com'}, ''),
535 ({'status': '400'}, ''),
536 ])
537
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400538 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400539 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500540
Joe Gregorio910b9b12012-06-12 09:36:30 -0400541 http = HttpMockSequence([
542 ({'status': '308',
543 'range': '0-5'}, ''),
544 ({'status': '308',
545 'range': '0-6'}, ''),
546 ])
547
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400548 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400549 self.assertEquals(status.resumable_progress, 7,
550 'Should have first checked length and then tried to PUT more.')
551 self.assertFalse(request._in_error_state)
552
553 # Put it back in an error state.
554 http = HttpMockSequence([
555 ({'status': '400'}, ''),
556 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400557 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400558 self.assertTrue(request._in_error_state)
559
560 # Pretend the last request that 400'd actually succeeded.
561 http = HttpMockSequence([
562 ({'status': '200'}, '{"foo": "bar"}'),
563 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400564 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400565 self.assertEqual(body, {'foo': 'bar'})
566
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400567 def test_media_io_base_stream_unlimited_chunksize_resume(self):
568 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
569 zoo = build('zoo', 'v1', http=self.http)
570
571 try:
572 import io
573
574 # Set up a seekable stream and try to upload in single chunk.
575 fd = io.BytesIO('01234"56789"')
576 media_upload = MediaIoBaseUpload(
577 fd=fd, mimetype='text/plain', chunksize=-1, resumable=True)
578
579 request = zoo.animals().insert(media_body=media_upload, body=None)
580
581 # The single chunk fails, restart at the right point.
582 http = HttpMockSequence([
583 ({'status': '200',
584 'location': 'http://upload.example.com'}, ''),
585 ({'status': '308',
586 'location': 'http://upload.example.com/2',
587 'range': '0-4'}, ''),
588 ({'status': '200'}, 'echo_request_body'),
589 ])
590
591 body = request.execute(http=http)
592 self.assertEqual('56789', body)
593
594 except ImportError:
595 pass
596
Joe Gregorio5c120db2012-08-23 09:13:55 -0400597
598 def test_media_io_base_stream_chunksize_resume(self):
599 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
600 zoo = build('zoo', 'v1', http=self.http)
601
602 try:
603 import io
604
605 # Set up a seekable stream and try to upload in chunks.
606 fd = io.BytesIO('0123456789')
607 media_upload = MediaIoBaseUpload(
608 fd=fd, mimetype='text/plain', chunksize=5, resumable=True)
609
610 request = zoo.animals().insert(media_body=media_upload, body=None)
611
612 # The single chunk fails, pull the content sent out of the exception.
613 http = HttpMockSequence([
614 ({'status': '200',
615 'location': 'http://upload.example.com'}, ''),
616 ({'status': '400'}, 'echo_request_body'),
617 ])
618
619 try:
620 body = request.execute(http=http)
621 except HttpError, e:
622 self.assertEqual('01234', e.content)
623
624 except ImportError:
625 pass
626
627
Joe Gregorio910b9b12012-06-12 09:36:30 -0400628 def test_resumable_media_handle_uploads_of_unknown_size(self):
629 http = HttpMockSequence([
630 ({'status': '200',
631 'location': 'http://upload.example.com'}, ''),
632 ({'status': '200'}, 'echo_request_headers_as_json'),
633 ])
634
635 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400636 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400637
Joe Gregorio910b9b12012-06-12 09:36:30 -0400638 # Create an upload that doesn't know the full size of the media.
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400639 class IoBaseUnknownLength(MediaUpload):
640 def chunksize(self):
641 return 10
642
643 def mimetype(self):
644 return 'image/png'
645
646 def size(self):
647 return None
648
649 def resumable(self):
650 return True
651
652 def getbytes(self, begin, length):
653 return '0123456789'
654
655 upload = IoBaseUnknownLength()
Joe Gregorio910b9b12012-06-12 09:36:30 -0400656
657 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400658 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400659 self.assertEqual(body, {
660 'Content-Range': 'bytes 0-9/*',
661 'Content-Length': '10',
662 })
Joe Gregorio44454e42012-06-15 08:38:53 -0400663
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400664 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
665 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
666 zoo = build('zoo', 'v1', http=self.http)
667
668 class IoBaseHasStream(MediaUpload):
669 def chunksize(self):
670 return 10
671
672 def mimetype(self):
673 return 'image/png'
674
675 def size(self):
676 return None
677
678 def resumable(self):
679 return True
680
681 def getbytes(self, begin, length):
682 return '0123456789'
683
684 def has_stream(self):
685 return True
686
687 def stream(self):
688 raise NotImplementedError()
689
690 upload = IoBaseHasStream()
691
692 orig_version = sys.version_info
693 sys.version_info = (2, 5, 5, 'final', 0)
694
695 request = zoo.animals().insert(media_body=upload, body=None)
696
697 http = HttpMockSequence([
698 ({'status': '200',
699 'location': 'http://upload.example.com'}, ''),
700 ({'status': '200'}, 'echo_request_headers_as_json'),
701 ])
702
703 # This should not raise an exception because stream() shouldn't be called.
704 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400705 self.assertEqual(body, {
706 'Content-Range': 'bytes 0-9/*',
707 'Content-Length': '10'
708 })
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400709
710 sys.version_info = (2, 6, 5, 'final', 0)
711
712 request = zoo.animals().insert(media_body=upload, body=None)
713
714 # This should raise an exception because stream() will be called.
715 http = HttpMockSequence([
716 ({'status': '200',
717 'location': 'http://upload.example.com'}, ''),
718 ({'status': '200'}, 'echo_request_headers_as_json'),
719 ])
720
721 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
722
723 sys.version_info = orig_version
724
Joe Gregorio44454e42012-06-15 08:38:53 -0400725 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
726 http = HttpMockSequence([
727 ({'status': '200',
728 'location': 'http://upload.example.com'}, ''),
729 ({'status': '200'}, 'echo_request_headers_as_json'),
730 ])
731
732 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400733 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio44454e42012-06-15 08:38:53 -0400734
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400735 fd = StringIO.StringIO('data goes here')
Joe Gregorio44454e42012-06-15 08:38:53 -0400736
737 # Create an upload that doesn't know the full size of the media.
738 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400739 fd=fd, mimetype='image/png', chunksize=15, resumable=True)
Joe Gregorio44454e42012-06-15 08:38:53 -0400740
741 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400742 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400743 self.assertEqual(body, {
744 'Content-Range': 'bytes 0-13/14',
745 'Content-Length': '14',
746 })
Joe Gregorio910b9b12012-06-12 09:36:30 -0400747
748 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
749 http = HttpMockSequence([
750 ({'status': '200',
751 'location': 'http://upload.example.com'}, ''),
752 ({'status': '400'}, ''),
753 ])
754
755 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400756 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400757
758 # Create an upload that doesn't know the full size of the media.
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400759 fd = StringIO.StringIO('data goes here')
Joe Gregorio910b9b12012-06-12 09:36:30 -0400760
761 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400762 fd=fd, mimetype='image/png', chunksize=500, resumable=True)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400763
764 request = zoo.animals().insert(media_body=upload, body=None)
765
766 # Put it in an error state.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400767 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400768
769 http = HttpMockSequence([
770 ({'status': '400',
771 'range': '0-5'}, 'echo_request_headers_as_json'),
772 ])
773 try:
774 # Should resume the upload by first querying the status of the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400775 request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400776 except HttpError, e:
777 expected = {
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400778 'Content-Range': 'bytes */14',
Joe Gregorio910b9b12012-06-12 09:36:30 -0400779 'content-length': '0'
780 }
781 self.assertEqual(expected, simplejson.loads(e.content),
782 'Should send an empty body when requesting the current upload status.')
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500783
Joe Gregorio708388c2012-06-15 13:43:04 -0400784
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400785class Next(unittest.TestCase):
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400786
Joe Gregorio3c676f92011-07-25 10:38:14 -0400787 def test_next_successful_none_on_no_next_page_token(self):
788 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400789 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400790 request = tasks.tasklists().list()
791 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
792
793 def test_next_successful_with_next_page_token(self):
794 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400795 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400796 request = tasks.tasklists().list()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400797 next_request = tasks.tasklists().list_next(
798 request, {'nextPageToken': '123abc'})
Joe Gregorio3c676f92011-07-25 10:38:14 -0400799 parsed = list(urlparse.urlparse(next_request.uri))
800 q = parse_qs(parsed[4])
801 self.assertEqual(q['pageToken'][0], '123abc')
802
Joe Gregorio555f33c2011-08-19 14:56:07 -0400803 def test_next_with_method_with_no_properties(self):
804 self.http = HttpMock(datafile('latitude.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400805 service = build('latitude', 'v1', http=self.http)
Joe Gregorio555f33c2011-08-19 14:56:07 -0400806 request = service.currentLocation().get()
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400807
Joe Gregorioa98733f2011-09-16 10:12:28 -0400808
Joe Gregorio708388c2012-06-15 13:43:04 -0400809class MediaGet(unittest.TestCase):
810
811 def test_get_media(self):
812 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400813 zoo = build('zoo', 'v1', http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400814 request = zoo.animals().get_media(name='Lion')
815
816 parsed = urlparse.urlparse(request.uri)
817 q = parse_qs(parsed[4])
818 self.assertEqual(q['alt'], ['media'])
819 self.assertEqual(request.headers['accept'], '*/*')
820
821 http = HttpMockSequence([
822 ({'status': '200'}, 'standing in for media'),
823 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400824 response = request.execute(http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400825 self.assertEqual('standing in for media', response)
826
827
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400828if __name__ == '__main__':
829 unittest.main()