blob: 2c28cd2c168f02622d32f2ac15e3a91be1f173b9 [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
Joe Gregorio910b9b12012-06-12 09:36:30 -040060from oauth2client.anyjson import simplejson
Joe Gregoriodc106fc2012-11-20 14:30:14 -050061from oauth2client.client import OAuth2Credentials
62import uritemplate
63
Joe Gregoriocb8103d2011-02-11 23:20:52 -050064
65DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
66
Joe Gregorio32f048f2012-08-27 16:31:27 -040067FLAGS = gflags.FLAGS
68FLAGS.positional_parameters_enforcement = 'EXCEPTION'
69
Joe Gregorioa98733f2011-09-16 10:12:28 -040070
Joe Gregoriof1ba7f12012-11-16 15:10:47 -050071def assertUrisEqual(testcase, expected, actual):
72 """Test that URIs are the same, up to reordering of query parameters."""
73 expected = urlparse.urlparse(expected)
74 actual = urlparse.urlparse(actual)
75 testcase.assertEqual(expected.scheme, actual.scheme)
76 testcase.assertEqual(expected.netloc, actual.netloc)
77 testcase.assertEqual(expected.path, actual.path)
78 testcase.assertEqual(expected.params, actual.params)
79 testcase.assertEqual(expected.fragment, actual.fragment)
80 expected_query = parse_qs(expected.query)
81 actual_query = parse_qs(actual.query)
82 for name in expected_query.keys():
83 testcase.assertEqual(expected_query[name], actual_query[name])
84 for name in actual_query.keys():
85 testcase.assertEqual(expected_query[name], actual_query[name])
86
87
Joe Gregoriocb8103d2011-02-11 23:20:52 -050088def datafile(filename):
89 return os.path.join(DATA_DIR, filename)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -040090
91
Joe Gregorio504a17f2012-12-07 14:14:26 -050092class SetupHttplib2(unittest.TestCase):
93 def test_retries(self):
94 # Merely loading apiclient.discovery should set the RETRIES to 1.
95 self.assertEqual(1, httplib2.RETRIES)
96
97
Joe Gregorioc5c5a372010-09-22 11:42:32 -040098class Utilities(unittest.TestCase):
99 def test_key2param(self):
100 self.assertEqual('max_results', key2param('max-results'))
101 self.assertEqual('x007_bond', key2param('007-bond'))
102
103
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500104class DiscoveryErrors(unittest.TestCase):
105
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400106 def test_tests_should_be_run_with_strict_positional_enforcement(self):
107 try:
108 plus = build('plus', 'v1', None)
109 self.fail("should have raised a TypeError exception over missing http=.")
110 except TypeError:
111 pass
112
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500113 def test_failed_to_parse_discovery_json(self):
114 self.http = HttpMock(datafile('malformed.json'), {'status': '200'})
115 try:
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400116 plus = build('plus', 'v1', http=self.http)
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500117 self.fail("should have raised an exception over malformed JSON.")
Joe Gregorio49396552011-03-08 10:39:00 -0500118 except InvalidJsonError:
119 pass
Joe Gregorioc0e0fe92011-03-04 16:16:55 -0500120
121
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100122class DiscoveryFromDocument(unittest.TestCase):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400123
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100124 def test_can_build_from_local_document(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500125 discovery = file(datafile('plus.json')).read()
126 plus = build_from_document(discovery, base="https://www.googleapis.com/")
127 self.assertTrue(plus is not None)
Joe Gregorio4772f3d2012-12-10 10:22:37 -0500128 self.assertTrue(hasattr(plus, 'activities'))
129
130 def test_can_build_from_local_deserialized_document(self):
131 discovery = file(datafile('plus.json')).read()
132 discovery = simplejson.loads(discovery)
133 plus = build_from_document(discovery, base="https://www.googleapis.com/")
134 self.assertTrue(plus is not None)
135 self.assertTrue(hasattr(plus, 'activities'))
Joe Gregorioa98733f2011-09-16 10:12:28 -0400136
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100137 def test_building_with_base_remembers_base(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500138 discovery = file(datafile('plus.json')).read()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400139
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100140 base = "https://www.example.com/"
Joe Gregorio7b70f432011-11-09 10:18:51 -0500141 plus = build_from_document(discovery, base=base)
Joe Gregorioa2838152012-07-16 11:52:17 -0400142 self.assertEquals("https://www.googleapis.com/plus/v1/", plus._baseUrl)
ade@google.com6a8c1cb2011-09-06 17:40:00 +0100143
144
Joe Gregorioa98733f2011-09-16 10:12:28 -0400145class DiscoveryFromHttp(unittest.TestCase):
Joe Gregorio583d9e42011-09-16 15:54:15 -0400146 def setUp(self):
Joe Bedafb463cb2011-09-19 17:39:49 -0700147 self.old_environ = os.environ.copy()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400148
Joe Gregorio583d9e42011-09-16 15:54:15 -0400149 def tearDown(self):
150 os.environ = self.old_environ
151
152 def test_userip_is_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400153 # build() will raise an HttpError on a 400, use this to pick the request uri
154 # out of the raised exception.
Joe Gregorio583d9e42011-09-16 15:54:15 -0400155 os.environ['REMOTE_ADDR'] = '10.0.0.1'
Joe Gregorioa98733f2011-09-16 10:12:28 -0400156 try:
157 http = HttpMockSequence([
158 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
159 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400160 zoo = build('zoo', 'v1', http=http, developerKey='foo',
Joe Gregorioa98733f2011-09-16 10:12:28 -0400161 discoveryServiceUrl='http://example.com')
162 self.fail('Should have raised an exception.')
163 except HttpError, e:
Joe Gregorio583d9e42011-09-16 15:54:15 -0400164 self.assertEqual(e.uri, 'http://example.com?userIp=10.0.0.1')
Joe Gregorioa98733f2011-09-16 10:12:28 -0400165
Joe Gregorio583d9e42011-09-16 15:54:15 -0400166 def test_userip_missing_is_not_added_to_discovery_uri(self):
Joe Gregorioa98733f2011-09-16 10:12:28 -0400167 # build() will raise an HttpError on a 400, use this to pick the request uri
168 # out of the raised exception.
169 try:
170 http = HttpMockSequence([
171 ({'status': '400'}, file(datafile('zoo.json'), 'r').read()),
172 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400173 zoo = build('zoo', 'v1', http=http, developerKey=None,
Joe Gregorioa98733f2011-09-16 10:12:28 -0400174 discoveryServiceUrl='http://example.com')
175 self.fail('Should have raised an exception.')
176 except HttpError, e:
177 self.assertEqual(e.uri, 'http://example.com')
178
179
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400180class Discovery(unittest.TestCase):
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400181
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400182 def test_method_error_checking(self):
Joe Gregorio7b70f432011-11-09 10:18:51 -0500183 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400184 plus = build('plus', 'v1', http=self.http)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400185
186 # Missing required parameters
187 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500188 plus.activities().list()
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400189 self.fail()
190 except TypeError, e:
191 self.assertTrue('Missing' in str(e))
192
Joe Gregorio2467afa2012-06-20 12:21:25 -0400193 # Missing required parameters even if supplied as None.
194 try:
195 plus.activities().list(collection=None, userId=None)
196 self.fail()
197 except TypeError, e:
198 self.assertTrue('Missing' in str(e))
199
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400200 # Parameter doesn't match regex
201 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500202 plus.activities().list(collection='not_a_collection_name', userId='me')
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400203 self.fail()
204 except TypeError, e:
Joe Gregorioca876e42011-02-22 19:39:42 -0500205 self.assertTrue('not an allowed value' in str(e))
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400206
207 # Unexpected parameter
208 try:
Joe Gregorio7b70f432011-11-09 10:18:51 -0500209 plus.activities().list(flubber=12)
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400210 self.fail()
211 except TypeError, e:
212 self.assertTrue('unexpected' in str(e))
213
Joe Gregoriobee86832011-02-22 10:00:19 -0500214 def _check_query_types(self, request):
215 parsed = urlparse.urlparse(request.uri)
216 q = parse_qs(parsed[4])
217 self.assertEqual(q['q'], ['foo'])
218 self.assertEqual(q['i'], ['1'])
219 self.assertEqual(q['n'], ['1.0'])
220 self.assertEqual(q['b'], ['false'])
221 self.assertEqual(q['a'], ['[1, 2, 3]'])
222 self.assertEqual(q['o'], ['{\'a\': 1}'])
223 self.assertEqual(q['e'], ['bar'])
224
225 def test_type_coercion(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400226 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400227 zoo = build('zoo', 'v1', http=http)
Joe Gregoriobee86832011-02-22 10:00:19 -0500228
Joe Gregorioa98733f2011-09-16 10:12:28 -0400229 request = zoo.query(
230 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 -0500231 self._check_query_types(request)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400232 request = zoo.query(
233 q="foo", i=1, n=1, b=False, a=[1,2,3], o={'a':1}, e='bar')
Joe Gregoriobee86832011-02-22 10:00:19 -0500234 self._check_query_types(request)
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500235
Joe Gregorioa98733f2011-09-16 10:12:28 -0400236 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800237 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 -0500238
239 request = zoo.query(
Craig Citro1e742822012-03-01 12:59:22 -0800240 q="foo", i="1", n="1", b="", a=[1,2,3], o={'a':1}, e='bar',
241 er=['one', 'three'], rr=['foo', 'bar'])
Joe Gregoriobee86832011-02-22 10:00:19 -0500242 self._check_query_types(request)
243
Craig Citro1e742822012-03-01 12:59:22 -0800244 # Five is right out.
Joe Gregorio20c26e52012-03-02 15:58:31 -0500245 self.assertRaises(TypeError, zoo.query, er=['one', 'five'])
Craig Citro1e742822012-03-01 12:59:22 -0800246
Joe Gregorio13217952011-02-22 15:37:38 -0500247 def test_optional_stack_query_parameters(self):
Joe Gregoriof4153422011-03-18 22:45:18 -0400248 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400249 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400250 request = zoo.query(trace='html', fields='description')
Joe Gregorio13217952011-02-22 15:37:38 -0500251
Joe Gregorioca876e42011-02-22 19:39:42 -0500252 parsed = urlparse.urlparse(request.uri)
253 q = parse_qs(parsed[4])
254 self.assertEqual(q['trace'], ['html'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400255 self.assertEqual(q['fields'], ['description'])
256
Joe Gregorio2467afa2012-06-20 12:21:25 -0400257 def test_string_params_value_of_none_get_dropped(self):
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400258 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400259 zoo = build('zoo', 'v1', http=http)
Joe Gregorio2467afa2012-06-20 12:21:25 -0400260 request = zoo.query(trace=None, fields='description')
261
262 parsed = urlparse.urlparse(request.uri)
263 q = parse_qs(parsed[4])
264 self.assertFalse('trace' in q)
Joe Gregorio4b4002f2012-06-14 15:41:01 -0400265
Joe Gregorioe08a1662011-12-07 09:48:22 -0500266 def test_model_added_query_parameters(self):
267 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400268 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500269 request = zoo.animals().get(name='Lion')
270
271 parsed = urlparse.urlparse(request.uri)
272 q = parse_qs(parsed[4])
273 self.assertEqual(q['alt'], ['json'])
274 self.assertEqual(request.headers['accept'], 'application/json')
275
276 def test_fallback_to_raw_model(self):
277 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400278 zoo = build('zoo', 'v1', http=http)
Joe Gregorioe08a1662011-12-07 09:48:22 -0500279 request = zoo.animals().getmedia(name='Lion')
280
281 parsed = urlparse.urlparse(request.uri)
282 q = parse_qs(parsed[4])
283 self.assertTrue('alt' not in q)
284 self.assertEqual(request.headers['accept'], '*/*')
285
Joe Gregoriof4153422011-03-18 22:45:18 -0400286 def test_patch(self):
287 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400288 zoo = build('zoo', 'v1', http=http)
Joe Gregoriof4153422011-03-18 22:45:18 -0400289 request = zoo.animals().patch(name='lion', body='{"description": "foo"}')
290
291 self.assertEqual(request.method, 'PATCH')
292
293 def test_tunnel_patch(self):
294 http = HttpMockSequence([
295 ({'status': '200'}, file(datafile('zoo.json'), 'r').read()),
296 ({'status': '200'}, 'echo_request_headers_as_json'),
297 ])
298 http = tunnel_patch(http)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400299 zoo = build('zoo', 'v1', http=http)
Joe Gregorioa98733f2011-09-16 10:12:28 -0400300 resp = zoo.animals().patch(
301 name='lion', body='{"description": "foo"}').execute()
Joe Gregoriof4153422011-03-18 22:45:18 -0400302
303 self.assertTrue('x-http-method-override' in resp)
Joe Gregorioca876e42011-02-22 19:39:42 -0500304
Joe Gregorio7b70f432011-11-09 10:18:51 -0500305 def test_plus_resources(self):
306 self.http = HttpMock(datafile('plus.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400307 plus = build('plus', 'v1', http=self.http)
Joe Gregorio7b70f432011-11-09 10:18:51 -0500308 self.assertTrue(getattr(plus, 'activities'))
309 self.assertTrue(getattr(plus, 'people'))
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400310
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400311 def test_full_featured(self):
312 # Zoo should exercise all discovery facets
313 # and should also have no future.json file.
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500314 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400315 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400316 self.assertTrue(getattr(zoo, 'animals'))
Joe Gregoriof863f7a2011-02-24 03:24:44 -0500317
Joe Gregoriof4153422011-03-18 22:45:18 -0400318 request = zoo.animals().list(name='bat', projection="full")
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400319 parsed = urlparse.urlparse(request.uri)
320 q = parse_qs(parsed[4])
321 self.assertEqual(q['name'], ['bat'])
Joe Gregoriof4153422011-03-18 22:45:18 -0400322 self.assertEqual(q['projection'], ['full'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400323
Joe Gregorio3fada332011-01-07 17:07:45 -0500324 def test_nested_resources(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500325 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400326 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio3fada332011-01-07 17:07:45 -0500327 self.assertTrue(getattr(zoo, 'animals'))
328 request = zoo.my().favorites().list(max_results="5")
329 parsed = urlparse.urlparse(request.uri)
330 q = parse_qs(parsed[4])
331 self.assertEqual(q['max-results'], ['5'])
332
Joe Gregoriod92897c2011-07-07 11:44:56 -0400333 def test_methods_with_reserved_names(self):
334 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400335 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod92897c2011-07-07 11:44:56 -0400336 self.assertTrue(getattr(zoo, 'animals'))
337 request = zoo.global_().print_().assert_(max_results="5")
338 parsed = urlparse.urlparse(request.uri)
Joe Gregorioa2838152012-07-16 11:52:17 -0400339 self.assertEqual(parsed[2], '/zoo/v1/global/print/assert')
Joe Gregoriod92897c2011-07-07 11:44:56 -0400340
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500341 def test_top_level_functions(self):
Joe Gregoriocb8103d2011-02-11 23:20:52 -0500342 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400343 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio7a6df3a2011-01-31 21:55:21 -0500344 self.assertTrue(getattr(zoo, 'query'))
345 request = zoo.query(q="foo")
346 parsed = urlparse.urlparse(request.uri)
347 q = parse_qs(parsed[4])
348 self.assertEqual(q['q'], ['foo'])
Joe Gregorio2379ecc2010-10-26 10:51:28 -0400349
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400350 def test_simple_media_uploads(self):
351 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400352 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400353 doc = getattr(zoo.animals().insert, '__doc__')
354 self.assertTrue('media_body' in doc)
355
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400356 def test_simple_media_upload_no_max_size_provided(self):
357 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400358 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio84d3c1f2011-07-25 10:39:45 -0400359 request = zoo.animals().crossbreed(media_body=datafile('small.png'))
360 self.assertEquals('image/png', request.headers['content-type'])
361 self.assertEquals('PNG', request.body[1:4])
362
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400363 def test_simple_media_raise_correct_exceptions(self):
364 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400365 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400366
367 try:
368 zoo.animals().insert(media_body=datafile('smiley.png'))
369 self.fail("should throw exception if media is too large.")
370 except MediaUploadSizeError:
371 pass
372
373 try:
374 zoo.animals().insert(media_body=datafile('small.jpg'))
375 self.fail("should throw exception if mimetype is unacceptable.")
376 except UnacceptableMimeTypeError:
377 pass
378
379 def test_simple_media_good_upload(self):
380 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400381 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400382
383 request = zoo.animals().insert(media_body=datafile('small.png'))
384 self.assertEquals('image/png', request.headers['content-type'])
385 self.assertEquals('PNG', request.body[1:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500386 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400387 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=media&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500388 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400389
390 def test_multipart_media_raise_correct_exceptions(self):
391 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400392 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400393
394 try:
395 zoo.animals().insert(media_body=datafile('smiley.png'), body={})
396 self.fail("should throw exception if media is too large.")
397 except MediaUploadSizeError:
398 pass
399
400 try:
401 zoo.animals().insert(media_body=datafile('small.jpg'), body={})
402 self.fail("should throw exception if mimetype is unacceptable.")
403 except UnacceptableMimeTypeError:
404 pass
405
406 def test_multipart_media_good_upload(self):
407 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400408 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400409
410 request = zoo.animals().insert(media_body=datafile('small.png'), body={})
Joe Gregorioa98733f2011-09-16 10:12:28 -0400411 self.assertTrue(request.headers['content-type'].startswith(
412 'multipart/related'))
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400413 self.assertEquals('--==', request.body[0:4])
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500414 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400415 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=multipart&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500416 request.uri)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400417
418 def test_media_capable_method_without_media(self):
419 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400420 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriofdf7c802011-06-30 12:33:38 -0400421
422 request = zoo.animals().insert(body={})
423 self.assertTrue(request.headers['content-type'], 'application/json')
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400424
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500425 def test_resumable_multipart_media_good_upload(self):
426 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400427 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500428
429 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
430 request = zoo.animals().insert(media_body=media_upload, body={})
431 self.assertTrue(request.headers['content-type'].startswith(
Joe Gregorio945be3e2012-01-27 17:01:06 -0500432 'application/json'))
433 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500434 self.assertEquals(media_upload, request.resumable)
435
436 self.assertEquals('image/png', request.resumable.mimetype())
437
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500438 self.assertNotEquals(request.body, None)
439 self.assertEquals(request.resumable_uri, None)
440
441 http = HttpMockSequence([
442 ({'status': '200',
443 'location': 'http://upload.example.com'}, ''),
444 ({'status': '308',
445 'location': 'http://upload.example.com/2',
446 'range': '0-12'}, ''),
447 ({'status': '308',
448 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500449 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500450 ({'status': '200'}, '{"foo": "bar"}'),
451 ])
452
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400453 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500454 self.assertEquals(None, body)
455 self.assertTrue(isinstance(status, MediaUploadProgress))
456 self.assertEquals(13, status.resumable_progress)
457
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500458 # Two requests should have been made and the resumable_uri should have been
459 # updated for each one.
460 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
461
462 self.assertEquals(media_upload, request.resumable)
463 self.assertEquals(13, request.resumable_progress)
464
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400465 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500466 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500467 self.assertEquals(media_upload.size()-1, request.resumable_progress)
468 self.assertEquals('{"data": {}}', request.body)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500469
470 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400471 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500472 self.assertEquals(body, {"foo": "bar"})
473 self.assertEquals(status, None)
474
475
476 def test_resumable_media_good_upload(self):
477 """Not a multipart upload."""
478 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400479 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500480
481 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
482 request = zoo.animals().insert(media_body=media_upload, body=None)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500483 self.assertEquals(media_upload, request.resumable)
484
485 self.assertEquals('image/png', request.resumable.mimetype())
486
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500487 self.assertEquals(request.body, None)
488 self.assertEquals(request.resumable_uri, None)
489
490 http = HttpMockSequence([
491 ({'status': '200',
492 'location': 'http://upload.example.com'}, ''),
493 ({'status': '308',
494 'location': 'http://upload.example.com/2',
495 'range': '0-12'}, ''),
496 ({'status': '308',
497 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500498 'range': '0-%d' % (media_upload.size() - 2)}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500499 ({'status': '200'}, '{"foo": "bar"}'),
500 ])
501
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400502 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500503 self.assertEquals(None, body)
504 self.assertTrue(isinstance(status, MediaUploadProgress))
505 self.assertEquals(13, status.resumable_progress)
506
507 # Two requests should have been made and the resumable_uri should have been
508 # updated for each one.
509 self.assertEquals(request.resumable_uri, 'http://upload.example.com/2')
510
511 self.assertEquals(media_upload, request.resumable)
512 self.assertEquals(13, request.resumable_progress)
513
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400514 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500515 self.assertEquals(request.resumable_uri, 'http://upload.example.com/3')
Joe Gregorio945be3e2012-01-27 17:01:06 -0500516 self.assertEquals(media_upload.size()-1, request.resumable_progress)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500517 self.assertEquals(request.body, None)
518
519 # Final call to next_chunk should complete the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400520 status, body = request.next_chunk(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500521 self.assertEquals(body, {"foo": "bar"})
522 self.assertEquals(status, None)
523
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500524 def test_resumable_media_good_upload_from_execute(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)
Joe Gregoriof1ba7f12012-11-16 15:10:47 -0500531 assertUrisEqual(self,
Joe Gregorioa2838152012-07-16 11:52:17 -0400532 'https://www.googleapis.com/upload/zoo/v1/animals?uploadType=resumable&alt=json',
Joe Gregoriode860442012-03-02 15:55:52 -0500533 request.uri)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500534
535 http = HttpMockSequence([
536 ({'status': '200',
537 'location': 'http://upload.example.com'}, ''),
538 ({'status': '308',
539 'location': 'http://upload.example.com/2',
540 'range': '0-12'}, ''),
541 ({'status': '308',
542 'location': 'http://upload.example.com/3',
Joe Gregorio945be3e2012-01-27 17:01:06 -0500543 'range': '0-%d' % media_upload.size()}, ''),
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500544 ({'status': '200'}, '{"foo": "bar"}'),
545 ])
546
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400547 body = request.execute(http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500548 self.assertEquals(body, {"foo": "bar"})
549
550 def test_resumable_media_fail_unknown_response_code_first_request(self):
551 """Not a multipart upload."""
552 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400553 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500554
555 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
556 request = zoo.animals().insert(media_body=media_upload, body=None)
557
558 http = HttpMockSequence([
559 ({'status': '400',
560 'location': 'http://upload.example.com'}, ''),
561 ])
562
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400563 self.assertRaises(ResumableUploadError, request.execute, http=http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500564
565 def test_resumable_media_fail_unknown_response_code_subsequent_request(self):
566 """Not a multipart upload."""
567 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400568 zoo = build('zoo', 'v1', http=self.http)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500569
570 media_upload = MediaFileUpload(datafile('small.png'), resumable=True)
571 request = zoo.animals().insert(media_body=media_upload, body=None)
572
573 http = HttpMockSequence([
574 ({'status': '200',
575 'location': 'http://upload.example.com'}, ''),
576 ({'status': '400'}, ''),
577 ])
578
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400579 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400580 self.assertTrue(request._in_error_state)
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500581
Joe Gregorio910b9b12012-06-12 09:36:30 -0400582 http = HttpMockSequence([
583 ({'status': '308',
584 'range': '0-5'}, ''),
585 ({'status': '308',
586 'range': '0-6'}, ''),
587 ])
588
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400589 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400590 self.assertEquals(status.resumable_progress, 7,
591 'Should have first checked length and then tried to PUT more.')
592 self.assertFalse(request._in_error_state)
593
594 # Put it back in an error state.
595 http = HttpMockSequence([
596 ({'status': '400'}, ''),
597 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400598 self.assertRaises(HttpError, request.execute, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400599 self.assertTrue(request._in_error_state)
600
601 # Pretend the last request that 400'd actually succeeded.
602 http = HttpMockSequence([
603 ({'status': '200'}, '{"foo": "bar"}'),
604 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400605 status, body = request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400606 self.assertEqual(body, {'foo': 'bar'})
607
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400608 def test_media_io_base_stream_unlimited_chunksize_resume(self):
609 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
610 zoo = build('zoo', 'v1', http=self.http)
611
612 try:
613 import io
614
615 # Set up a seekable stream and try to upload in single chunk.
616 fd = io.BytesIO('01234"56789"')
617 media_upload = MediaIoBaseUpload(
618 fd=fd, mimetype='text/plain', chunksize=-1, resumable=True)
619
620 request = zoo.animals().insert(media_body=media_upload, body=None)
621
622 # The single chunk fails, restart at the right point.
623 http = HttpMockSequence([
624 ({'status': '200',
625 'location': 'http://upload.example.com'}, ''),
626 ({'status': '308',
627 'location': 'http://upload.example.com/2',
628 'range': '0-4'}, ''),
629 ({'status': '200'}, 'echo_request_body'),
630 ])
631
632 body = request.execute(http=http)
633 self.assertEqual('56789', body)
634
635 except ImportError:
636 pass
637
Joe Gregorio5c120db2012-08-23 09:13:55 -0400638
639 def test_media_io_base_stream_chunksize_resume(self):
640 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
641 zoo = build('zoo', 'v1', http=self.http)
642
643 try:
644 import io
645
646 # Set up a seekable stream and try to upload in chunks.
647 fd = io.BytesIO('0123456789')
648 media_upload = MediaIoBaseUpload(
649 fd=fd, mimetype='text/plain', chunksize=5, resumable=True)
650
651 request = zoo.animals().insert(media_body=media_upload, body=None)
652
653 # The single chunk fails, pull the content sent out of the exception.
654 http = HttpMockSequence([
655 ({'status': '200',
656 'location': 'http://upload.example.com'}, ''),
657 ({'status': '400'}, 'echo_request_body'),
658 ])
659
660 try:
661 body = request.execute(http=http)
662 except HttpError, e:
663 self.assertEqual('01234', e.content)
664
665 except ImportError:
666 pass
667
668
Joe Gregorio910b9b12012-06-12 09:36:30 -0400669 def test_resumable_media_handle_uploads_of_unknown_size(self):
670 http = HttpMockSequence([
671 ({'status': '200',
672 'location': 'http://upload.example.com'}, ''),
673 ({'status': '200'}, 'echo_request_headers_as_json'),
674 ])
675
676 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400677 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400678
Joe Gregorio910b9b12012-06-12 09:36:30 -0400679 # Create an upload that doesn't know the full size of the media.
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400680 class IoBaseUnknownLength(MediaUpload):
681 def chunksize(self):
682 return 10
683
684 def mimetype(self):
685 return 'image/png'
686
687 def size(self):
688 return None
689
690 def resumable(self):
691 return True
692
693 def getbytes(self, begin, length):
694 return '0123456789'
695
696 upload = IoBaseUnknownLength()
Joe Gregorio910b9b12012-06-12 09:36:30 -0400697
698 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400699 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400700 self.assertEqual(body, {
701 'Content-Range': 'bytes 0-9/*',
702 'Content-Length': '10',
703 })
Joe Gregorio44454e42012-06-15 08:38:53 -0400704
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400705 def test_resumable_media_no_streaming_on_unsupported_platforms(self):
706 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
707 zoo = build('zoo', 'v1', http=self.http)
708
709 class IoBaseHasStream(MediaUpload):
710 def chunksize(self):
711 return 10
712
713 def mimetype(self):
714 return 'image/png'
715
716 def size(self):
717 return None
718
719 def resumable(self):
720 return True
721
722 def getbytes(self, begin, length):
723 return '0123456789'
724
725 def has_stream(self):
726 return True
727
728 def stream(self):
729 raise NotImplementedError()
730
731 upload = IoBaseHasStream()
732
733 orig_version = sys.version_info
734 sys.version_info = (2, 5, 5, 'final', 0)
735
736 request = zoo.animals().insert(media_body=upload, body=None)
737
738 http = HttpMockSequence([
739 ({'status': '200',
740 'location': 'http://upload.example.com'}, ''),
741 ({'status': '200'}, 'echo_request_headers_as_json'),
742 ])
743
744 # This should not raise an exception because stream() shouldn't be called.
745 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400746 self.assertEqual(body, {
747 'Content-Range': 'bytes 0-9/*',
748 'Content-Length': '10'
749 })
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400750
751 sys.version_info = (2, 6, 5, 'final', 0)
752
753 request = zoo.animals().insert(media_body=upload, body=None)
754
755 # This should raise an exception because stream() will be called.
756 http = HttpMockSequence([
757 ({'status': '200',
758 'location': 'http://upload.example.com'}, ''),
759 ({'status': '200'}, 'echo_request_headers_as_json'),
760 ])
761
762 self.assertRaises(NotImplementedError, request.next_chunk, http=http)
763
764 sys.version_info = orig_version
765
Joe Gregorio44454e42012-06-15 08:38:53 -0400766 def test_resumable_media_handle_uploads_of_unknown_size_eof(self):
767 http = HttpMockSequence([
768 ({'status': '200',
769 'location': 'http://upload.example.com'}, ''),
770 ({'status': '200'}, 'echo_request_headers_as_json'),
771 ])
772
773 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400774 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio44454e42012-06-15 08:38:53 -0400775
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400776 fd = StringIO.StringIO('data goes here')
Joe Gregorio44454e42012-06-15 08:38:53 -0400777
778 # Create an upload that doesn't know the full size of the media.
779 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400780 fd=fd, mimetype='image/png', chunksize=15, resumable=True)
Joe Gregorio44454e42012-06-15 08:38:53 -0400781
782 request = zoo.animals().insert(media_body=upload, body=None)
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400783 status, body = request.next_chunk(http=http)
Joe Gregorio5c120db2012-08-23 09:13:55 -0400784 self.assertEqual(body, {
785 'Content-Range': 'bytes 0-13/14',
786 'Content-Length': '14',
787 })
Joe Gregorio910b9b12012-06-12 09:36:30 -0400788
789 def test_resumable_media_handle_resume_of_upload_of_unknown_size(self):
790 http = HttpMockSequence([
791 ({'status': '200',
792 'location': 'http://upload.example.com'}, ''),
793 ({'status': '400'}, ''),
794 ])
795
796 self.http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400797 zoo = build('zoo', 'v1', http=self.http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400798
799 # Create an upload that doesn't know the full size of the media.
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400800 fd = StringIO.StringIO('data goes here')
Joe Gregorio910b9b12012-06-12 09:36:30 -0400801
802 upload = MediaIoBaseUpload(
Joe Gregorio4a2c29f2012-07-12 12:52:47 -0400803 fd=fd, mimetype='image/png', chunksize=500, resumable=True)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400804
805 request = zoo.animals().insert(media_body=upload, body=None)
806
807 # Put it in an error state.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400808 self.assertRaises(HttpError, request.next_chunk, http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400809
810 http = HttpMockSequence([
811 ({'status': '400',
812 'range': '0-5'}, 'echo_request_headers_as_json'),
813 ])
814 try:
815 # Should resume the upload by first querying the status of the upload.
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400816 request.next_chunk(http=http)
Joe Gregorio910b9b12012-06-12 09:36:30 -0400817 except HttpError, e:
818 expected = {
Joe Gregorioc80ac9d2012-08-21 14:09:09 -0400819 'Content-Range': 'bytes */14',
Joe Gregorio910b9b12012-06-12 09:36:30 -0400820 'content-length': '0'
821 }
822 self.assertEqual(expected, simplejson.loads(e.content),
823 'Should send an empty body when requesting the current upload status.')
Joe Gregoriod0bd3882011-11-22 09:49:47 -0500824
Joe Gregoriodc106fc2012-11-20 14:30:14 -0500825 def test_pickle(self):
826 sorted_resource_keys = ['_baseUrl',
827 '_developerKey',
828 '_dynamic_attrs',
829 '_http',
830 '_model',
831 '_requestBuilder',
832 '_resourceDesc',
833 '_rootDesc',
834 '_schema',
835 'animals',
836 'global_',
837 'load',
838 'loadNoTemplate',
839 'my',
840 'query',
841 'scopedAnimals']
842
843 http = HttpMock(datafile('zoo.json'), {'status': '200'})
844 zoo = build('zoo', 'v1', http=http)
845 self.assertEqual(sorted(zoo.__dict__.keys()), sorted_resource_keys)
846
847 pickled_zoo = pickle.dumps(zoo)
848 new_zoo = pickle.loads(pickled_zoo)
849 self.assertEqual(sorted(new_zoo.__dict__.keys()), sorted_resource_keys)
850 self.assertTrue(hasattr(new_zoo, 'animals'))
851 self.assertTrue(callable(new_zoo.animals))
852 self.assertTrue(hasattr(new_zoo, 'global_'))
853 self.assertTrue(callable(new_zoo.global_))
854 self.assertTrue(hasattr(new_zoo, 'load'))
855 self.assertTrue(callable(new_zoo.load))
856 self.assertTrue(hasattr(new_zoo, 'loadNoTemplate'))
857 self.assertTrue(callable(new_zoo.loadNoTemplate))
858 self.assertTrue(hasattr(new_zoo, 'my'))
859 self.assertTrue(callable(new_zoo.my))
860 self.assertTrue(hasattr(new_zoo, 'query'))
861 self.assertTrue(callable(new_zoo.query))
862 self.assertTrue(hasattr(new_zoo, 'scopedAnimals'))
863 self.assertTrue(callable(new_zoo.scopedAnimals))
864
865 self.assertEqual(zoo._dynamic_attrs, new_zoo._dynamic_attrs)
866 self.assertEqual(zoo._baseUrl, new_zoo._baseUrl)
867 self.assertEqual(zoo._developerKey, new_zoo._developerKey)
868 self.assertEqual(zoo._requestBuilder, new_zoo._requestBuilder)
869 self.assertEqual(zoo._resourceDesc, new_zoo._resourceDesc)
870 self.assertEqual(zoo._rootDesc, new_zoo._rootDesc)
871 # _http, _model and _schema won't be equal since we will get new
872 # instances upon un-pickling
873
874 def _dummy_zoo_request(self):
875 with open(os.path.join(DATA_DIR, 'zoo.json'), 'rU') as fh:
876 zoo_contents = fh.read()
877
878 zoo_uri = uritemplate.expand(DISCOVERY_URI,
879 {'api': 'zoo', 'apiVersion': 'v1'})
880 if 'REMOTE_ADDR' in os.environ:
881 zoo_uri = _add_query_parameter(zoo_uri, 'userIp',
882 os.environ['REMOTE_ADDR'])
883
884 http = httplib2.Http()
885 original_request = http.request
886 def wrapped_request(uri, method='GET', *args, **kwargs):
887 if uri == zoo_uri:
888 return httplib2.Response({'status': '200'}), zoo_contents
889 return original_request(uri, method=method, *args, **kwargs)
890 http.request = wrapped_request
891 return http
892
893 def _dummy_token(self):
894 access_token = 'foo'
895 client_id = 'some_client_id'
896 client_secret = 'cOuDdkfjxxnv+'
897 refresh_token = '1/0/a.df219fjls0'
898 token_expiry = datetime.datetime.utcnow()
899 token_uri = 'https://www.google.com/accounts/o8/oauth2/token'
900 user_agent = 'refresh_checker/1.0'
901 return OAuth2Credentials(
902 access_token, client_id, client_secret,
903 refresh_token, token_expiry, token_uri,
904 user_agent)
905
906
907 def test_pickle_with_credentials(self):
908 credentials = self._dummy_token()
909 http = self._dummy_zoo_request()
910 http = credentials.authorize(http)
911 self.assertTrue(hasattr(http.request, 'credentials'))
912
913 zoo = build('zoo', 'v1', http=http)
914 pickled_zoo = pickle.dumps(zoo)
915 new_zoo = pickle.loads(pickled_zoo)
916 self.assertEqual(sorted(zoo.__dict__.keys()),
917 sorted(new_zoo.__dict__.keys()))
918 new_http = new_zoo._http
919 self.assertFalse(hasattr(new_http.request, 'credentials'))
920
Joe Gregorio708388c2012-06-15 13:43:04 -0400921
Joe Gregorioc5c5a372010-09-22 11:42:32 -0400922class Next(unittest.TestCase):
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400923
Joe Gregorio3c676f92011-07-25 10:38:14 -0400924 def test_next_successful_none_on_no_next_page_token(self):
925 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400926 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400927 request = tasks.tasklists().list()
928 self.assertEqual(None, tasks.tasklists().list_next(request, {}))
929
930 def test_next_successful_with_next_page_token(self):
931 self.http = HttpMock(datafile('tasks.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400932 tasks = build('tasks', 'v1', http=self.http)
Joe Gregorio3c676f92011-07-25 10:38:14 -0400933 request = tasks.tasklists().list()
Joe Gregorioa98733f2011-09-16 10:12:28 -0400934 next_request = tasks.tasklists().list_next(
935 request, {'nextPageToken': '123abc'})
Joe Gregorio3c676f92011-07-25 10:38:14 -0400936 parsed = list(urlparse.urlparse(next_request.uri))
937 q = parse_qs(parsed[4])
938 self.assertEqual(q['pageToken'][0], '123abc')
939
Joe Gregorio555f33c2011-08-19 14:56:07 -0400940 def test_next_with_method_with_no_properties(self):
941 self.http = HttpMock(datafile('latitude.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400942 service = build('latitude', 'v1', http=self.http)
Joe Gregorio555f33c2011-08-19 14:56:07 -0400943 request = service.currentLocation().get()
Joe Gregorio00cf1d92010-09-27 09:22:03 -0400944
Joe Gregorioa98733f2011-09-16 10:12:28 -0400945
Joe Gregorio708388c2012-06-15 13:43:04 -0400946class MediaGet(unittest.TestCase):
947
948 def test_get_media(self):
949 http = HttpMock(datafile('zoo.json'), {'status': '200'})
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400950 zoo = build('zoo', 'v1', http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400951 request = zoo.animals().get_media(name='Lion')
952
953 parsed = urlparse.urlparse(request.uri)
954 q = parse_qs(parsed[4])
955 self.assertEqual(q['alt'], ['media'])
956 self.assertEqual(request.headers['accept'], '*/*')
957
958 http = HttpMockSequence([
959 ({'status': '200'}, 'standing in for media'),
960 ])
Joe Gregorio68a8cfe2012-08-03 16:17:40 -0400961 response = request.execute(http=http)
Joe Gregorio708388c2012-06-15 13:43:04 -0400962 self.assertEqual('standing in for media', response)
963
964
Joe Gregorioba9ea7f2010-08-19 15:49:04 -0400965if __name__ == '__main__':
966 unittest.main()