blob: 46d8e337506ae46ae475514ffab28e1c2fe6e84a [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 Gregoriodc106fc2012-11-20 14:30:14 -050026import datetime
Joe Gregorio32f048f2012-08-27 16:31:27 -040027import gflags
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040028import httplib2
29import os
Joe Gregoriodc106fc2012-11-20 14:30:14 -050030import pickle
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040031import sys
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040032import unittest
Joe Gregorio00cf1d92010-09-27 09:22:03 -040033import urlparse
Joe Gregorio910b9b12012-06-12 09:36:30 -040034import StringIO
35
Joe Gregorioa98733f2011-09-16 10:12:28 -040036
ade@google.comc5eb46f2010-09-27 23:35:39 +010037try:
38 from urlparse import parse_qs
39except ImportError:
40 from cgi import parse_qs
Joe Gregorio00cf1d92010-09-27 09:22:03 -040041
Joe Gregoriodc106fc2012-11-20 14:30:14 -050042
43from apiclient.discovery import _add_query_parameter
44from apiclient.discovery import build
45from apiclient.discovery import build_from_document
46from apiclient.discovery import DISCOVERY_URI
47from apiclient.discovery import key2param
Joe Gregorioc0e0fe92011-03-04 16:16:55 -050048from apiclient.errors import HttpError
Joe Gregorio49396552011-03-08 10:39:00 -050049from apiclient.errors import InvalidJsonError
Joe Gregoriofdf7c802011-06-30 12:33:38 -040050from apiclient.errors import MediaUploadSizeError
Joe Gregoriod0bd3882011-11-22 09:49:47 -050051from apiclient.errors import ResumableUploadError
Joe Gregoriofdf7c802011-06-30 12:33:38 -040052from apiclient.errors import UnacceptableMimeTypeError
Joe Gregoriod0bd3882011-11-22 09:49:47 -050053from apiclient.http import HttpMock
54from apiclient.http import HttpMockSequence
55from apiclient.http import MediaFileUpload
Joe Gregorio910b9b12012-06-12 09:36:30 -040056from apiclient.http import MediaIoBaseUpload
Joe Gregorioc80ac9d2012-08-21 14:09:09 -040057from apiclient.http import MediaUpload
Joe Gregoriod0bd3882011-11-22 09:49:47 -050058from apiclient.http import MediaUploadProgress
59from apiclient.http import tunnel_patch
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -080060from oauth2client import GOOGLE_TOKEN_URI
Joe Gregorio910b9b12012-06-12 09:36:30 -040061from oauth2client.anyjson import simplejson
Joe Gregoriodc106fc2012-11-20 14:30:14 -050062from oauth2client.client import OAuth2Credentials
63import uritemplate
64
Joe Gregoriocb8103d2011-02-11 23:20:52 -050065
66DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
67
Joe Gregorio32f048f2012-08-27 16:31:27 -040068FLAGS = gflags.FLAGS
69FLAGS.positional_parameters_enforcement = 'EXCEPTION'
70
Joe Gregorioa98733f2011-09-16 10:12:28 -040071
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050072def assertUrisEqual(testcase, expected, actual):
73 """Test that URIs are the same, up to reordering of query parameters."""
74 expected = urlparse.urlparse(expected)
75 actual = urlparse.urlparse(actual)
76 testcase.assertEqual(expected.scheme, actual.scheme)
77 testcase.assertEqual(expected.netloc, actual.netloc)
78 testcase.assertEqual(expected.path, actual.path)
79 testcase.assertEqual(expected.params, actual.params)
80 testcase.assertEqual(expected.fragment, actual.fragment)
81 expected_query = parse_qs(expected.query)
82 actual_query = parse_qs(actual.query)
83 for name in expected_query.keys():
84 testcase.assertEqual(expected_query[name], actual_query[name])
85 for name in actual_query.keys():
86 testcase.assertEqual(expected_query[name], actual_query[name])
87
88
Joe Gregoriocb8103d2011-02-11 23:20:52 -050089def datafile(filename):
90 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040091
92
Joe Gregorio504a17f2012-12-07 14:14:26 -050093class SetupHttplib2(unittest.TestCase):
94 def test_retries(self):
95 # Merely loading apiclient.discovery should set the RETRIES to 1.
96 self.assertEqual(1, httplib2.RETRIES)
97
98
Joe Gregorioc5c5a372010-09-22 11:42:32 -040099class Utilities(unittest.TestCase):
100 def test_key2param(self):
101 self.assertEqual('max_results', key2param('max-results'))
102 self.assertEqual('x007_bond', key2param('007-bond'))
103
104
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500105class DiscoveryErrors(unittest.TestCase):
106
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400107 def test_tests_should_be_run_with_strict_positional_enforcement(self):
108 try:
109 plus = build('plus', 'v1', None)
110 self.fail("should have raised a TypeError exception over missing http=.")
111 except TypeError:
112 pass
113
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500114 def test_failed_to_parse_discovery_json(self):
115 self.http = HttpMock(datafile('malformed.json'), {'status': '200'})
116 try:
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400117 plus = build('plus', 'v1', http=self.http)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500118 self.fail("should have raised an exception over malformed JSON.")
Joe Gregorio49396552011-03-08 10:39:00 -0500119 except InvalidJsonError:
120 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500121
122
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100123class DiscoveryFromDocument(unittest.TestCase):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400124
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100125 def test_can_build_from_local_document(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500126 discovery = file(datafile('plus.json')).read()
127 plus = build_from_document(discovery, base="https://www.googleapis.com/")
128 self.assertTrue(plus is not None)
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500129 self.assertTrue(hasattr(plus, 'activities'))
130
131 def test_can_build_from_local_deserialized_document(self):
132 discovery = file(datafile('plus.json')).read()
133 discovery = simplejson.loads(discovery)
134 plus = build_from_document(discovery, base="https://www.googleapis.com/")
135 self.assertTrue(plus is not None)
136 self.assertTrue(hasattr(plus, 'activities'))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400137
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100138 def test_building_with_base_remembers_base(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500139 discovery = file(datafile('plus.json')).read()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400140
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100141 base = "https://www.example.com/"
Joe Gregorio7b70f432011-11-09 10:18:51 -0500142 plus = build_from_document(discovery, base=base)
Joe Gregorioa2838152012-07-16 11:52:17 -0400143 self.assertEquals("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100144
145
Joe Gregorioa98733f2011-09-16 10:12:28 -0400146class DiscoveryFromHttp(unittest.TestCase):
Joe Gregorio583d9e42011-09-16 15:54:15 -0400147 def setUp(self):
Joe Bedafb463cb2011-09-19 17:39:49 -0700148 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400149
Joe Gregorio583d9e42011-09-16 15:54:15 -0400150 def tearDown(self):
151 os.environ = self.old_environ
152
153 def test_userip_is_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400154 # build() will raise an HttpError on a 400, use this to pick the request uri
155 # out of the raised exception.
Joe Gregorio583d9e42011-09-16 15:54:15 -0400156 os.environ['REMOTE_ADDR'] = '10.0.0.1'
Joe Gregorioa98733f2011-09-16 10:12:28 -0400157 try:
158 http = HttpMockSequence([
159 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
160 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400161 zoo = build('zoo', 'v1', http=http, developerKey='foo',
Joe Gregorioa98733f2011-09-16 10:12:28 -0400162 discoveryServiceUrl='http://example.com')
163 self.fail('Should have raised an exception.')
164 except HttpError, e:
Joe Gregorio583d9e42011-09-16 15:54:15 -0400165 self.assertEqual(e.uri, 'http://example.com?userIp=10.0.0.1')
Joe Gregorioa98733f2011-09-16 10:12:28 -0400166
Joe Gregorio583d9e42011-09-16 15:54:15 -0400167 def test_userip_missing_is_not_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400168 # build() will raise an HttpError on a 400, use this to pick the request uri
169 # out of the raised exception.
170 try:
171 http = HttpMockSequence([
172 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
173 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400174 zoo = build('zoo', 'v1', http=http, developerKey=None,
Joe Gregorioa98733f2011-09-16 10:12:28 -0400175 discoveryServiceUrl='http://example.com')
176 self.fail('Should have raised an exception.')
177 except HttpError, e:
178 self.assertEqual(e.uri, 'http://example.com')
179
180
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400181class Discovery(unittest.TestCase):
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400182
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400183 def test_method_error_checking(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500184 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400185 plus = build('plus', 'v1', http=self.http)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400186
187 # Missing required parameters
188 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500189 plus.activities().list()
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400190 self.fail()
191 except TypeError, e:
192 self.assertTrue('Missing' in str(e))
193
Joe Gregorio2467afa2012-06-20 12:21:25 -0400194 # Missing required parameters even if supplied as None.
195 try:
196 plus.activities().list(collection=None, userId=None)
197 self.fail()
198 except TypeError, e:
199 self.assertTrue('Missing' in str(e))
200
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400201 # Parameter doesn't match regex
202 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500203 plus.activities().list(collection='not_a_collection_name', userId='me')
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400204 self.fail()
205 except TypeError, e:
Joe Gregorioca876e42011-02-22 19:39:42 -0500206 self.assertTrue('not an allowed value' in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400207
208 # Unexpected parameter
209 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500210 plus.activities().list(flubber=12)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400211 self.fail()
212 except TypeError, e:
213 self.assertTrue('unexpected' in str(e))
214
Joe Gregoriobee86832011-02-22 10:00:19 -0500215 def _check_query_types(self, request):
216 parsed = urlparse.urlparse(request.uri)
217 q = parse_qs(parsed[4])
218 self.assertEqual(q['q'], ['foo'])
219 self.assertEqual(q['i'], ['1'])
220 self.assertEqual(q['n'], ['1.0'])
221 self.assertEqual(q['b'], ['false'])
222 self.assertEqual(q['a'], ['[1, 2, 3]'])
223 self.assertEqual(q['o'], ['{\'a\': 1}'])
224 self.assertEqual(q['e'], ['bar'])
225
226 def test_type_coercion(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400227 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400228 zoo = build('zoo', 'v1', http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500229
Joe Gregorioa98733f2011-09-16 10:12:28 -0400230 request = zoo.query(
231 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 -0500232 self._check_query_types(request)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400233 request = zoo.query(
234 q="foo", i=1, n=1, b=False, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500235 self._check_query_types(request)
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500236
Joe Gregorioa98733f2011-09-16 10:12:28 -0400237 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800238 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 -0500239
240 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800241 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar',
242 er=['one', 'three'], rr=['foo', 'bar'])
Joe Gregoriobee86832011-02-22 10:00:19 -0500243 self._check_query_types(request)
244
Craig Citro1e742822012-03-01 12:59:22 -0800245 # Five is right out.
Joe Gregorio20c26e52012-03-02 15:58:31 -0500246 self.assertRaises(TypeError, zoo.query, er=['one', 'five'])
Craig Citro1e742822012-03-01 12:59:22 -0800247
Joe Gregorio13217952011-02-22 15:37:38 -0500248 def test_optional_stack_query_parameters(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400249 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400250 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400251 request = zoo.query(trace='html', fields='description')
Joe Gregorio13217952011-02-22 15:37:38 -0500252
Joe Gregorioca876e42011-02-22 19:39:42 -0500253 parsed = urlparse.urlparse(request.uri)
254 q = parse_qs(parsed[4])
255 self.assertEqual(q['trace'], ['html'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400256 self.assertEqual(q['fields'], ['description'])
257
Joe Gregorio2467afa2012-06-20 12:21:25 -0400258 def test_string_params_value_of_none_get_dropped(self):
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400259 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400260 zoo = build('zoo', 'v1', http=http)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400261 request = zoo.query(trace=None, fields='description')
262
263 parsed = urlparse.urlparse(request.uri)
264 q = parse_qs(parsed[4])
265 self.assertFalse('trace' in q)
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400266
Joe Gregorioe08a1662011-12-07 09:48:22 -0500267 def test_model_added_query_parameters(self):
268 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400269 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500270 request = zoo.animals().get(name='Lion')
271
272 parsed = urlparse.urlparse(request.uri)
273 q = parse_qs(parsed[4])
274 self.assertEqual(q['alt'], ['json'])
275 self.assertEqual(request.headers['accept'], 'application/json')
276
277 def test_fallback_to_raw_model(self):
278 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400279 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500280 request = zoo.animals().getmedia(name='Lion')
281
282 parsed = urlparse.urlparse(request.uri)
283 q = parse_qs(parsed[4])
284 self.assertTrue('alt' not in q)
285 self.assertEqual(request.headers['accept'], '*/*')
286
Joe Gregoriof4153422011-03-18 22:45:18 -0400287 def test_patch(self):
288 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400289 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400290 request = zoo.animals().patch(name='lion', body='{"description": "foo"}')
291
292 self.assertEqual(request.method, 'PATCH')
293
294 def test_tunnel_patch(self):
295 http = HttpMockSequence([
296 ({'status': '200'}, file(datafile('zoo.json'), 'r').read()),
297 ({'status': '200'}, 'echo_request_headers_as_json'),
298 ])
299 http = tunnel_patch(http)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400300 zoo = build('zoo', 'v1', http=http)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400301 resp = zoo.animals().patch(
302 name='lion', body='{"description": "foo"}').execute()
Joe Gregoriof4153422011-03-18 22:45:18 -0400303
304 self.assertTrue('x-http-method-override' in resp)
Joe Gregorioca876e42011-02-22 19:39:42 -0500305
Joe Gregorio7b70f432011-11-09 10:18:51 -0500306 def test_plus_resources(self):
307 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400308 plus = build('plus', 'v1', http=self.http)
Joe Gregorio7b70f432011-11-09 10:18:51 -0500309 self.assertTrue(getattr(plus, 'activities'))
310 self.assertTrue(getattr(plus, 'people'))
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400311
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400312 def test_full_featured(self):
313 # Zoo should exercise all discovery facets
314 # and should also have no future.json file.
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500315 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400316 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400317 self.assertTrue(getattr(zoo, 'animals'))
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500318
Joe Gregoriof4153422011-03-18 22:45:18 -0400319 request = zoo.animals().list(name='bat', projection="full")
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400320 parsed = urlparse.urlparse(request.uri)
321 q = parse_qs(parsed[4])
322 self.assertEqual(q['name'], ['bat'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400323 self.assertEqual(q['projection'], ['full'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400324
Joe Gregorio3fada332011-01-07 17:07:45 -0500325 def test_nested_resources(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500326 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400327 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio3fada332011-01-07 17:07:45 -0500328 self.assertTrue(getattr(zoo, 'animals'))
329 request = zoo.my().favorites().list(max_results="5")
330 parsed = urlparse.urlparse(request.uri)
331 q = parse_qs(parsed[4])
332 self.assertEqual(q['max-results'], ['5'])
333
Joe Gregoriod92897c2011-07-07 11:44:56 -0400334 def test_methods_with_reserved_names(self):
335 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400336 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod92897c2011-07-07 11:44:56 -0400337 self.assertTrue(getattr(zoo, 'animals'))
338 request = zoo.global_().print_().assert_(max_results="5")
339 parsed = urlparse.urlparse(request.uri)
Joe Gregorioa2838152012-07-16 11:52:17 -0400340 self.assertEqual(parsed[2], '/zoo/v1/global/print/assert')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400341
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500342 def test_top_level_functions(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500343 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400344 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500345 self.assertTrue(getattr(zoo, 'query'))
346 request = zoo.query(q="foo")
347 parsed = urlparse.urlparse(request.uri)
348 q = parse_qs(parsed[4])
349 self.assertEqual(q['q'], ['foo'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400350
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400351 def test_simple_media_uploads(self):
352 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400353 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400354 doc = getattr(zoo.animals().insert, '__doc__')
355 self.assertTrue('media_body' in doc)
356
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400357 def test_simple_media_upload_no_max_size_provided(self):
358 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400359 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400360 request = zoo.animals().crossbreed(media_body=datafile('small.png'))
361 self.assertEquals('image/png', request.headers['content-type'])
362 self.assertEquals('PNG', request.body[1:4])
363
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400364 def test_simple_media_raise_correct_exceptions(self):
365 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400366 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400367
368 try:
369 zoo.animals().insert(media_body=datafile('smiley.png'))
370 self.fail("should throw exception if media is too large.")
371 except MediaUploadSizeError:
372 pass
373
374 try:
375 zoo.animals().insert(media_body=datafile('small.jpg'))
376 self.fail("should throw exception if mimetype is unacceptable.")
377 except UnacceptableMimeTypeError:
378 pass
379
380 def test_simple_media_good_upload(self):
381 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400382 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400383
384 request = zoo.animals().insert(media_body=datafile('small.png'))
385 self.assertEquals('image/png', request.headers['content-type'])
386 self.assertEquals('PNG', request.body[1:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500387 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400388 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500389 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400390
391 def test_multipart_media_raise_correct_exceptions(self):
392 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400393 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400394
395 try:
396 zoo.animals().insert(media_body=datafile('smiley.png'), body={})
397 self.fail("should throw exception if media is too large.")
398 except MediaUploadSizeError:
399 pass
400
401 try:
402 zoo.animals().insert(media_body=datafile('small.jpg'), body={})
403 self.fail("should throw exception if mimetype is unacceptable.")
404 except UnacceptableMimeTypeError:
405 pass
406
407 def test_multipart_media_good_upload(self):
408 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400409 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400410
411 request = zoo.animals().insert(media_body=datafile('small.png'), body={})
Joe Gregorioa98733f2011-09-16 10:12:28 -0400412 self.assertTrue(request.headers['content-type'].startswith(
413 'multipart/related'))
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400414 self.assertEquals('--==', request.body[0:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500415 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400416 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500417 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400418
419 def test_media_capable_method_without_media(self):
420 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400421 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400422
423 request = zoo.animals().insert(body={})
424 self.assertTrue(request.headers['content-type'], 'application/json')
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400425
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500426 def test_resumable_multipart_media_good_upload(self):
427 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400428 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500429
430 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
431 request = zoo.animals().insert(media_body=media_upload, body={})
432 self.assertTrue(request.headers['content-type'].startswith(
Joe Gregorio945be3e2012-01-27 17:01:06 -0500433 'application/json'))
434 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500435 self.assertEquals(media_upload, request.resumable)
436
437 self.assertEquals('image/png', request.resumable.mimetype())
438
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500439 self.assertNotEquals(request.body, None)
440 self.assertEquals(request.resumable_uri, None)
441
442 http = HttpMockSequence([
443 ({'status': '200',
444 'location': 'http://upload.example.com'}, ''),
445 ({'status': '308',
446 'location': 'http://upload.example.com/2',
447 'range': '0-12'}, ''),
448 ({'status': '308',
449 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500450 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500451 ({'status': '200'}, '{"foo": "bar"}'),
452 ])
453
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400454 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500455 self.assertEquals(None, body)
456 self.assertTrue(isinstance(status, MediaUploadProgress))
457 self.assertEquals(13, status.resumable_progress)
458
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500459 # Two requests should have been made and the resumable_uri should have been
460 # updated for each one.
461 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
462
463 self.assertEquals(media_upload, request.resumable)
464 self.assertEquals(13, request.resumable_progress)
465
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400466 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500467 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500468 self.assertEquals(media_upload.size()-1, request.resumable_progress)
469 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500470
471 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400472 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500473 self.assertEquals(body, {"foo": "bar"})
474 self.assertEquals(status, None)
475
476
477 def test_resumable_media_good_upload(self):
478 """Not a multipart upload."""
479 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400480 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500481
482 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
483 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500484 self.assertEquals(media_upload, request.resumable)
485
486 self.assertEquals('image/png', request.resumable.mimetype())
487
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500488 self.assertEquals(request.body, None)
489 self.assertEquals(request.resumable_uri, None)
490
491 http = HttpMockSequence([
492 ({'status': '200',
493 'location': 'http://upload.example.com'}, ''),
494 ({'status': '308',
495 'location': 'http://upload.example.com/2',
496 'range': '0-12'}, ''),
497 ({'status': '308',
498 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500499 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500500 ({'status': '200'}, '{"foo": "bar"}'),
501 ])
502
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400503 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500504 self.assertEquals(None, body)
505 self.assertTrue(isinstance(status, MediaUploadProgress))
506 self.assertEquals(13, status.resumable_progress)
507
508 # Two requests should have been made and the resumable_uri should have been
509 # updated for each one.
510 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
511
512 self.assertEquals(media_upload, request.resumable)
513 self.assertEquals(13, request.resumable_progress)
514
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400515 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500516 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500517 self.assertEquals(media_upload.size()-1, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500518 self.assertEquals(request.body, None)
519
520 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400521 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500522 self.assertEquals(body, {"foo": "bar"})
523 self.assertEquals(status, None)
524
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500525 def test_resumable_media_good_upload_from_execute(self):
526 """Not a multipart upload."""
527 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400528 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500529
530 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
531 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500532 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400533 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500534 request.uri)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500535
536 http = HttpMockSequence([
537 ({'status': '200',
538 'location': 'http://upload.example.com'}, ''),
539 ({'status': '308',
540 'location': 'http://upload.example.com/2',
541 'range': '0-12'}, ''),
542 ({'status': '308',
543 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500544 'range': '0-%d' % media_upload.size()}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500545 ({'status': '200'}, '{"foo": "bar"}'),
546 ])
547
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400548 body = request.execute(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500549 self.assertEquals(body, {"foo": "bar"})
550
551 def test_resumable_media_fail_unknown_response_code_first_request(self):
552 """Not a multipart upload."""
553 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400554 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500555
556 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
557 request = zoo.animals().insert(media_body=media_upload, body=None)
558
559 http = HttpMockSequence([
560 ({'status': '400',
561 'location': 'http://upload.example.com'}, ''),
562 ])
563
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400564 self.assertRaises(ResumableUploadError, request.execute, http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500565
566 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
567 """Not a multipart upload."""
568 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400569 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500570
571 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
572 request = zoo.animals().insert(media_body=media_upload, body=None)
573
574 http = HttpMockSequence([
575 ({'status': '200',
576 'location': 'http://upload.example.com'}, ''),
577 ({'status': '400'}, ''),
578 ])
579
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400580 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400581 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500582
Joe Gregorio910b9b12012-06-12 09:36:30 -0400583 http = HttpMockSequence([
584 ({'status': '308',
585 'range': '0-5'}, ''),
586 ({'status': '308',
587 'range': '0-6'}, ''),
588 ])
589
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400590 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400591 self.assertEquals(status.resumable_progress, 7,
592 'Should have first checked length and then tried to PUT more.')
593 self.assertFalse(request._in_error_state)
594
595 # Put it back in an error state.
596 http = HttpMockSequence([
597 ({'status': '400'}, ''),
598 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400599 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400600 self.assertTrue(request._in_error_state)
601
602 # Pretend the last request that 400'd actually succeeded.
603 http = HttpMockSequence([
604 ({'status': '200'}, '{"foo": "bar"}'),
605 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400606 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400607 self.assertEqual(body, {'foo': 'bar'})
608
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400609 def test_media_io_base_stream_unlimited_chunksize_resume(self):
610 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
611 zoo = build('zoo', 'v1', http=self.http)
612
613 try:
614 import io
615
616 # Set up a seekable stream and try to upload in single chunk.
617 fd = io.BytesIO('01234"56789"')
618 media_upload = MediaIoBaseUpload(
619 fd=fd, mimetype='text/plain', chunksize=-1, resumable=True)
620
621 request = zoo.animals().insert(media_body=media_upload, body=None)
622
623 # The single chunk fails, restart at the right point.
624 http = HttpMockSequence([
625 ({'status': '200',
626 'location': 'http://upload.example.com'}, ''),
627 ({'status': '308',
628 'location': 'http://upload.example.com/2',
629 'range': '0-4'}, ''),
630 ({'status': '200'}, 'echo_request_body'),
631 ])
632
633 body = request.execute(http=http)
634 self.assertEqual('56789', body)
635
636 except ImportError:
637 pass
638
Joe Gregorio5c120db2012-08-23 09:13:55 -0400639
640 def test_media_io_base_stream_chunksize_resume(self):
641 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
642 zoo = build('zoo', 'v1', http=self.http)
643
644 try:
645 import io
646
647 # Set up a seekable stream and try to upload in chunks.
648 fd = io.BytesIO('0123456789')
649 media_upload = MediaIoBaseUpload(
650 fd=fd, mimetype='text/plain', chunksize=5, resumable=True)
651
652 request = zoo.animals().insert(media_body=media_upload, body=None)
653
654 # The single chunk fails, pull the content sent out of the exception.
655 http = HttpMockSequence([
656 ({'status': '200',
657 'location': 'http://upload.example.com'}, ''),
658 ({'status': '400'}, 'echo_request_body'),
659 ])
660
661 try:
662 body = request.execute(http=http)
663 except HttpError, e:
664 self.assertEqual('01234', e.content)
665
666 except ImportError:
667 pass
668
669
Joe Gregorio910b9b12012-06-12 09:36:30 -0400670 def test_resumable_media_handle_uploads_of_unknown_size(self):
671 http = HttpMockSequence([
672 ({'status': '200',
673 'location': 'http://upload.example.com'}, ''),
674 ({'status': '200'}, 'echo_request_headers_as_json'),
675 ])
676
677 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400678 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400679
Joe Gregorio910b9b12012-06-12 09:36:30 -0400680 # Create an upload that doesn't know the full size of the media.
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400681 class IoBaseUnknownLength(MediaUpload):
682 def chunksize(self):
683 return 10
684
685 def mimetype(self):
686 return 'image/png'
687
688 def size(self):
689 return None
690
691 def resumable(self):
692 return True
693
694 def getbytes(self, begin, length):
695 return '0123456789'
696
697 upload = IoBaseUnknownLength()
Joe Gregorio910b9b12012-06-12 09:36:30 -0400698
699 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400700 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400701 self.assertEqual(body, {
702 'Content-Range': 'bytes 0-9/*',
703 'Content-Length': '10',
704 })
Joe Gregorio44454e42012-06-15 08:38:53 -0400705
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400706 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
707 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
708 zoo = build('zoo', 'v1', http=self.http)
709
710 class IoBaseHasStream(MediaUpload):
711 def chunksize(self):
712 return 10
713
714 def mimetype(self):
715 return 'image/png'
716
717 def size(self):
718 return None
719
720 def resumable(self):
721 return True
722
723 def getbytes(self, begin, length):
724 return '0123456789'
725
726 def has_stream(self):
727 return True
728
729 def stream(self):
730 raise NotImplementedError()
731
732 upload = IoBaseHasStream()
733
734 orig_version = sys.version_info
735 sys.version_info = (2, 5, 5, 'final', 0)
736
737 request = zoo.animals().insert(media_body=upload, body=None)
738
739 http = HttpMockSequence([
740 ({'status': '200',
741 'location': 'http://upload.example.com'}, ''),
742 ({'status': '200'}, 'echo_request_headers_as_json'),
743 ])
744
745 # This should not raise an exception because stream() shouldn't be called.
746 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400747 self.assertEqual(body, {
748 'Content-Range': 'bytes 0-9/*',
749 'Content-Length': '10'
750 })
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400751
752 sys.version_info = (2, 6, 5, 'final', 0)
753
754 request = zoo.animals().insert(media_body=upload, body=None)
755
756 # This should raise an exception because stream() will be called.
757 http = HttpMockSequence([
758 ({'status': '200',
759 'location': 'http://upload.example.com'}, ''),
760 ({'status': '200'}, 'echo_request_headers_as_json'),
761 ])
762
763 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
764
765 sys.version_info = orig_version
766
Joe Gregorio44454e42012-06-15 08:38:53 -0400767 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
768 http = HttpMockSequence([
769 ({'status': '200',
770 'location': 'http://upload.example.com'}, ''),
771 ({'status': '200'}, 'echo_request_headers_as_json'),
772 ])
773
774 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400775 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio44454e42012-06-15 08:38:53 -0400776
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400777 fd = StringIO.StringIO('data goes here')
Joe Gregorio44454e42012-06-15 08:38:53 -0400778
779 # Create an upload that doesn't know the full size of the media.
780 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400781 fd=fd, mimetype='image/png', chunksize=15, resumable=True)
Joe Gregorio44454e42012-06-15 08:38:53 -0400782
783 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400784 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400785 self.assertEqual(body, {
786 'Content-Range': 'bytes 0-13/14',
787 'Content-Length': '14',
788 })
Joe Gregorio910b9b12012-06-12 09:36:30 -0400789
790 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
791 http = HttpMockSequence([
792 ({'status': '200',
793 'location': 'http://upload.example.com'}, ''),
794 ({'status': '400'}, ''),
795 ])
796
797 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400798 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400799
800 # Create an upload that doesn't know the full size of the media.
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400801 fd = StringIO.StringIO('data goes here')
Joe Gregorio910b9b12012-06-12 09:36:30 -0400802
803 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400804 fd=fd, mimetype='image/png', chunksize=500, resumable=True)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400805
806 request = zoo.animals().insert(media_body=upload, body=None)
807
808 # Put it in an error state.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400809 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400810
811 http = HttpMockSequence([
812 ({'status': '400',
813 'range': '0-5'}, 'echo_request_headers_as_json'),
814 ])
815 try:
816 # Should resume the upload by first querying the status of the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400817 request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400818 except HttpError, e:
819 expected = {
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400820 'Content-Range': 'bytes */14',
Joe Gregorio910b9b12012-06-12 09:36:30 -0400821 'content-length': '0'
822 }
823 self.assertEqual(expected, simplejson.loads(e.content),
824 'Should send an empty body when requesting the current upload status.')
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500825
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500826 def test_pickle(self):
827 sorted_resource_keys = ['_baseUrl',
828 '_developerKey',
829 '_dynamic_attrs',
830 '_http',
831 '_model',
832 '_requestBuilder',
833 '_resourceDesc',
834 '_rootDesc',
835 '_schema',
836 'animals',
837 'global_',
838 'load',
839 'loadNoTemplate',
840 'my',
841 'query',
842 'scopedAnimals']
843
844 http = HttpMock(datafile('zoo.json'), {'status': '200'})
845 zoo = build('zoo', 'v1', http=http)
846 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
847
848 pickled_zoo = pickle.dumps(zoo)
849 new_zoo = pickle.loads(pickled_zoo)
850 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
851 self.assertTrue(hasattr(new_zoo, 'animals'))
852 self.assertTrue(callable(new_zoo.animals))
853 self.assertTrue(hasattr(new_zoo, 'global_'))
854 self.assertTrue(callable(new_zoo.global_))
855 self.assertTrue(hasattr(new_zoo, 'load'))
856 self.assertTrue(callable(new_zoo.load))
857 self.assertTrue(hasattr(new_zoo, 'loadNoTemplate'))
858 self.assertTrue(callable(new_zoo.loadNoTemplate))
859 self.assertTrue(hasattr(new_zoo, 'my'))
860 self.assertTrue(callable(new_zoo.my))
861 self.assertTrue(hasattr(new_zoo, 'query'))
862 self.assertTrue(callable(new_zoo.query))
863 self.assertTrue(hasattr(new_zoo, 'scopedAnimals'))
864 self.assertTrue(callable(new_zoo.scopedAnimals))
865
Joe Gregorio003b6e42013-02-13 15:42:19 -0500866 self.assertEqual(sorted(zoo._dynamic_attrs), sorted(new_zoo._dynamic_attrs))
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500867 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
868 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
869 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
870 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
871 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
872 # _http, _model and _schema won't be equal since we will get new
873 # instances upon un-pickling
874
875 def _dummy_zoo_request(self):
876 with open(os.path.join(DATA_DIR, 'zoo.json'), 'rU') as fh:
877 zoo_contents = fh.read()
878
879 zoo_uri = uritemplate.expand(DISCOVERY_URI,
880 {'api': 'zoo', 'apiVersion': 'v1'})
881 if 'REMOTE_ADDR' in os.environ:
882 zoo_uri = _add_query_parameter(zoo_uri, 'userIp',
883 os.environ['REMOTE_ADDR'])
884
885 http = httplib2.Http()
886 original_request = http.request
887 def wrapped_request(uri, method='GET', *args, **kwargs):
888 if uri == zoo_uri:
889 return httplib2.Response({'status': '200'}), zoo_contents
890 return original_request(uri, method=method, *args, **kwargs)
891 http.request = wrapped_request
892 return http
893
894 def _dummy_token(self):
895 access_token = 'foo'
896 client_id = 'some_client_id'
897 client_secret = 'cOuDdkfjxxnv+'
898 refresh_token = '1/0/a.df219fjls0'
899 token_expiry = datetime.datetime.utcnow()
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500900 user_agent = 'refresh_checker/1.0'
901 return OAuth2Credentials(
902 access_token, client_id, client_secret,
dhermes@google.coma9eb0bb2013-02-06 09:19:01 -0800903 refresh_token, token_expiry, GOOGLE_TOKEN_URI,
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500904 user_agent)
905
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500906 def test_pickle_with_credentials(self):
907 credentials = self._dummy_token()
908 http = self._dummy_zoo_request()
909 http = credentials.authorize(http)
910 self.assertTrue(hasattr(http.request, 'credentials'))
911
912 zoo = build('zoo', 'v1', http=http)
913 pickled_zoo = pickle.dumps(zoo)
914 new_zoo = pickle.loads(pickled_zoo)
915 self.assertEqual(sorted(zoo.__dict__.keys()),
916 sorted(new_zoo.__dict__.keys()))
917 new_http = new_zoo._http
918 self.assertFalse(hasattr(new_http.request, 'credentials'))
919
Joe Gregorio708388c2012-06-15 13:43:04 -0400920
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400921class Next(unittest.TestCase):
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400922
Joe Gregorio3c676f92011-07-25 10:38:14 -0400923 def test_next_successful_none_on_no_next_page_token(self):
924 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400925 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400926 request = tasks.tasklists().list()
927 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
928
929 def test_next_successful_with_next_page_token(self):
930 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400931 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400932 request = tasks.tasklists().list()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400933 next_request = tasks.tasklists().list_next(
934 request, {'nextPageToken': '123abc'})
Joe Gregorio3c676f92011-07-25 10:38:14 -0400935 parsed = list(urlparse.urlparse(next_request.uri))
936 q = parse_qs(parsed[4])
937 self.assertEqual(q['pageToken'][0], '123abc')
938
Joe Gregorio555f33c2011-08-19 14:56:07 -0400939 def test_next_with_method_with_no_properties(self):
940 self.http = HttpMock(datafile('latitude.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400941 service = build('latitude', 'v1', http=self.http)
Joe Gregorio555f33c2011-08-19 14:56:07 -0400942 request = service.currentLocation().get()
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400943
Joe Gregorioa98733f2011-09-16 10:12:28 -0400944
Joe Gregorio708388c2012-06-15 13:43:04 -0400945class MediaGet(unittest.TestCase):
946
947 def test_get_media(self):
948 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400949 zoo = build('zoo', 'v1', http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400950 request = zoo.animals().get_media(name='Lion')
951
952 parsed = urlparse.urlparse(request.uri)
953 q = parse_qs(parsed[4])
954 self.assertEqual(q['alt'], ['media'])
955 self.assertEqual(request.headers['accept'], '*/*')
956
957 http = HttpMockSequence([
958 ({'status': '200'}, 'standing in for media'),
959 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400960 response = request.execute(http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400961 self.assertEqual('standing in for media', response)
962
963
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400964if __name__ == '__main__':
965 unittest.main()