1 # Copyright 2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
34 from unittest import TestCase
35 from mock import patch, call
36 from tempfile import NamedTemporaryFile
37 from os import urandom
38 from itertools import product
39 from random import randint
42 from collections import OrderedDict
44 from kamaki.clients.utils.ordereddict import OrderedDict
46 from kamaki.clients import pithos, ClientError
49 rest_pkg = 'kamaki.clients.pithos.rest_api.PithosRestClient'
50 pithos_pkg = 'kamaki.clients.pithos.PithosClient'
52 user_id = 'ac0un7-1d-5tr1ng'
56 'content-language': 'en-us',
57 'content-type': 'text/html; charset=utf-8',
58 'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
59 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
60 'server': 'gunicorn/0.14.5',
61 'vary': 'Accept-Language',
62 'x-account-bytes-used': '751615526',
63 'x-account-container-count': 7,
64 'x-account-policy-quota': 53687091200,
65 'x-account-policy-versioning': 'auto'}
67 'content-language': 'en-us',
68 'content-type': 'text/html; charset=utf-8',
69 'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
70 'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
71 'server': 'gunicorn/0.14.5',
72 'vary': 'Accept-Language',
73 'x-container-block-hash': 'sha256',
74 'x-container-block-size': 4194304,
75 'x-container-bytes-used': 309528938,
76 'x-container-object-count': 14,
77 'x-container-object-meta': '',
78 'x-container-policy-quota': 53687091200,
79 'x-container-policy-versioning': 'auto'}
81 'content-language': 'en-us',
82 'content-length': 254965,
83 'content-type': 'application/octet-stream',
84 'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
86 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
87 'server': 'gunicorn/0.14.5',
88 'vary': 'Accept-Language',
89 'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
90 'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
91 'x-object-version': '525996',
92 'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
93 'x-object-meta-k1': 'v1',
94 'x-object-meta-k2': 'v2'}
98 last_modified="2013-02-27T11:56:09.893033+00:00",
101 x_container_policy=dict(quota="21474836480", versioning="auto")),
104 last_modified="2012-10-23T12:25:17.229187+00:00",
107 x_container_policy=dict(quota="21474836480", versioning="auto"))]
110 name="The_Secret_Garden.zip",
111 x_object_public="/public/wdp9p",
113 x_object_version_timestamp="1360237915.7027509",
114 x_object_uuid="s0m3uu1df0r0bj0n3",
115 last_modified="2013-02-07T11:51:55.702751+00:00",
116 content_type="application/octet-stream",
117 x_object_hash="0afdf29f71cd53126225c3f54ca",
118 x_object_version=17737,
119 x_object_modified_by=user_id),
121 name="The_Revealed_Garden.zip",
122 x_object_public="/public/wpd7p",
124 x_object_version_timestamp="13602915.7027509",
125 x_object_uuid="s0m3uu1df0r0bj70w",
126 last_modified="2013-02-07T11:51:55.702751+00:00",
127 content_type="application/octet-stream",
128 x_object_hash="0afdf29f71cd53126225c3f54ca",
129 x_object_version=17737,
130 x_object_modified_by=user_id)]
131 object_hashmap = dict(
132 block_hash="sha256", block_size=4194304, bytes=33554432,
134 "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
135 "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
136 "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
137 "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
138 "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
139 "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
140 "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
141 "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
143 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
144 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
145 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
149 """FR stands for Fake Response"""
157 class PithosRestClient(TestCase):
160 self.url = 'https://www.example.com/pithos'
161 self.token = 'p17h0570k3n'
162 self.client = pithos.PithosRestClient(self.url, self.token)
163 self.client.account = user_id
164 self.client.container = 'c0nt@1n3r_i'
171 @patch('%s.set_param' % rest_pkg)
172 @patch('%s.set_header' % rest_pkg)
173 @patch('%s.head' % rest_pkg, return_value=FR())
174 def test_account_head(self, head, SH, SP):
175 for params in product(
177 (None, '50m3-07h3r-d473'),
178 (None, 'y37-4n7h3r-d473'),
179 ((), ('someval',), ('v1', 'v2',)),
180 (dict(), dict(success=200), dict(k='v', v='k'))):
181 args, kwargs = params[-2], params[-1]
183 self.client.account_head(*(params + args), **kwargs)
185 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
186 IMS, IUS = params[1], params[2]
187 self.assertEqual(SH.mock_calls[-2:], [
188 call('If-Modified-Since', IMS),
189 call('If-Unmodified-Since', IUS)])
190 self.assertEqual(head.mock_calls[-1], call(
191 '/%s' % self.client.account,
193 success=kwargs.pop('success', 204),
196 @patch('%s.set_param' % rest_pkg)
197 @patch('%s.set_header' % rest_pkg)
198 @patch('%s.get' % rest_pkg, return_value=FR())
199 def test_account_get(self, get, SH, SP):
200 keys = ('limit', 'marker', 'format', 'shared', 'until')
201 for params in product(
207 (None, '50m3-07h3r-d473'),
208 (None, 'y37-4n7h3r-d473'),
209 ((), ('someval',), ('v1', 'v2',)),
210 (dict(), dict(success=200), dict(k='v', v='k'))):
211 args, kwargs = params[-2], params[-1]
213 self.client.account_get(*(params + args), **kwargs)
214 self.assertEqual(SP.mock_calls[-5:],
215 [call(keys[i], iff=X) if (
217 keys[i], X, iff=X) for i, X in enumerate(params[:5])])
218 IMS, IUS = params[5], params[6]
219 self.assertEqual(SH.mock_calls[-2:], [
220 call('If-Modified-Since', IMS),
221 call('If-Unmodified-Since', IUS)])
222 self.assertEqual(get.mock_calls[-1], call(
223 '/%s' % self.client.account,
225 success=kwargs.pop('success', (200, 204)),
228 @patch('%s.set_param' % rest_pkg)
229 @patch('%s.set_header' % rest_pkg)
230 @patch('%s.post' % rest_pkg, return_value=FR())
231 def test_account_post(self, post, SH, SP):
232 #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
235 ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
236 (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
238 (None, 'v3r510n1ng'),
239 ((), ('someval',), ('v1', 'v2',)),
240 (dict(), dict(success=200), dict(k='v', v='k'))):
241 args, kwargs = pm[-2:]
243 self.client.account_post(*(pm + args), **kwargs)
245 self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
249 call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
252 call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
254 call('X-Account-Policy-Quota', pm[3]),
255 call('X-Account-Policy-Versioning', pm[4])]
256 self.assertEqual(SH.mock_calls[- len(expected):], expected)
257 self.assertEqual(post.mock_calls[-1], call(
258 '/%s' % self.client.account,
260 success=kwargs.pop('success', 202),
263 @patch('%s.set_param' % rest_pkg)
264 @patch('%s.set_header' % rest_pkg)
265 @patch('%s.head' % rest_pkg, return_value=FR())
266 def test_container_head(self, head, SH, SP):
269 (None, '47h3r-d473'),
270 (None, 'y37-4n47h3r'),
272 (dict(), dict(success=200), dict(k='v', v='k'))):
273 args, kwargs = pm[-2:]
275 self.client.container_head(*(pm + args), **kwargs)
276 unt, ims, ius = pm[0:3]
277 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
278 self.assertEqual(SH.mock_calls[-2:], [
279 call('If-Modified-Since', ims),
280 call('If-Unmodified-Since', ius)])
281 self.assertEqual(head.mock_calls[-1], call(
282 '/%s/%s' % (self.client.account, self.client.container),
284 success=kwargs.pop('success', 204),
287 @patch('%s.set_param' % rest_pkg)
288 @patch('%s.set_header' % rest_pkg)
289 @patch('%s.get' % rest_pkg, return_value=FR())
290 def test_container_get(self, get, SH, SP):
294 (None, 'some/prefix'),
296 (None, '/some/path'),
297 ('json', 'some-format'),
298 ([], ['k1', 'k2', 'k3']),
300 (None, 'unt1l-d473'),
301 (None, 'y37-4n47h3r'),
302 (None, '4n47h3r-d473'),
304 (dict(), dict(success=400), dict(k='v', v='k'))):
305 args, kwargs = pm[-2:]
307 self.client.container_get(*(pm + args), **kwargs)
308 lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
309 exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
310 exp += [call('path', path)] if path else [
311 call('prefix', prfx, iff=prfx),
312 call('delimiter', dlm, iff=dlm)]
313 exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
315 exp += [call('meta', ','.join(meta))]
316 exp += [call('until', unt, iff=unt)]
317 self.assertEqual(SP.mock_calls[- len(exp):], exp)
319 self.assertEqual(SH.mock_calls[-2:], [
320 call('If-Modified-Since', ims),
321 call('If-Unmodified-Since', ius)])
322 self.assertEqual(get.mock_calls[-1], call(
323 '/%s/%s' % (self.client.account, self.client.container),
325 success=kwargs.pop('success', 200),
328 @patch('%s.set_header' % rest_pkg)
329 @patch('%s.put' % rest_pkg, return_value=FR())
330 def test_container_put(self, put, SH):
333 (None, 'v3r51on1ng'),
334 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
336 (dict(), dict(success=400), dict(k='v', v='k'))):
337 args, kwargs = pm[-2:]
339 self.client.container_put(*(pm + args), **kwargs)
340 quota, versioning, metas = pm[-3:]
342 call('X-Container-Policy-Quota', quota),
343 call('X-Container-Policy-Versioning', versioning)] + [
344 call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
345 self.assertEqual(SH.mock_calls[- len(exp):], exp)
346 self.assertEqual(put.mock_calls[-1], call(
347 '/%s/%s' % (self.client.account, self.client.container),
349 success=kwargs.pop('success', (201, 202)),
352 @patch('%s.set_param' % rest_pkg)
353 @patch('%s.set_header' % rest_pkg)
354 @patch('%s.post' % rest_pkg, return_value=FR())
355 def test_container_post(self, post, SH, SP):
358 ('json', 'some-format'),
360 (None, 'v3r51on1ng'),
361 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
362 (None, 'content-type'),
364 (None, 'transfer-encoding'),
366 (dict(), dict(success=400), dict(k='v', v='k'))):
367 args, kwargs = pm[-2:]
369 self.client.container_post(*(pm + args), **kwargs)
371 self.assertEqual(SP.mock_calls[-2:], [
372 call('update', iff=upd),
373 call('format', frmt, iff=frmt)])
374 qta, vrs, metas, ctype, clen, trenc = pm[2:]
375 prfx = 'X-Container-Meta-'
377 call('X-Container-Policy-Quota', qta),
378 call('X-Container-Policy-Versioning', vrs)] + [
379 call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
380 call('Content-Type', ctype),
381 call('Content-Length', clen),
382 call('Transfer-Encoding', trenc)]
383 self.assertEqual(SH.mock_calls[- len(exp):], exp)
385 self.assertEqual(post.mock_calls[-1], call(
386 '/%s/%s' % (self.client.account, self.client.container),
388 success=kwargs.pop('success', 202),
391 @patch('%s.set_param' % rest_pkg)
392 @patch('%s.delete' % rest_pkg, return_value=FR())
393 def test_container_delete(self, delete, SP):
398 (dict(), dict(success=400), dict(k='v', v='k'))):
399 args, kwargs = pm[-2:]
401 self.client.container_delete(*(pm + args), **kwargs)
403 self.assertEqual(SP.mock_calls[-2:], [
404 call('until', unt, iff=unt),
405 call('delimiter', dlm, iff=dlm)])
406 self.assertEqual(delete.mock_calls[-1], call(
407 '/%s/%s' % (self.client.account, self.client.container),
409 success=kwargs.pop('success', 204),
412 @patch('%s.set_param' % rest_pkg)
413 @patch('%s.set_header' % rest_pkg)
414 @patch('%s.head' % rest_pkg, return_value=FR())
415 def test_object_head(self, head, SH, SP):
420 (None, '1f-m0d-51nc3'),
421 (None, '1f-unm0d-51nc3'),
423 (dict(), dict(success=400), dict(k='v', v='k'))):
424 args, kwargs = pm[-2:]
426 self.client.object_head(obj, *(pm + args), **kwargs)
427 vrs, etag, netag, ims, ius = pm[:5]
430 call('version', vrs, iff=vrs))
431 self.assertEqual(SH.mock_calls[-4:], [
432 call('If-Match', etag),
433 call('If-None-Match', netag),
434 call('If-Modified-Since', ims),
435 call('If-Unmodified-Since', ius)])
436 acc, cont = self.client.account, self.client.container
437 self.assertEqual(head.mock_calls[-1], call(
438 '/%s/%s/%s' % (acc, cont, obj),
440 success=kwargs.pop('success', 200),
443 @patch('%s.set_param' % rest_pkg)
444 @patch('%s.set_header' % rest_pkg)
445 @patch('%s.get' % rest_pkg, return_value=FR())
446 def test_object_get(self, get, SH, SP):
451 (None, 'range=74-63'),
458 (dict(), dict(success=400), dict(k='v', v='k'))):
459 args, kwargs = pm[-2:]
461 self.client.object_get(obj, *(pm + args), **kwargs)
462 format, hashmap, version = pm[:3]
463 self.assertEqual(SP.mock_calls[-3:], [
464 call('format', format, iff=format),
465 call('hashmap', hashmap, iff=hashmap),
466 call('version', version, iff=version)])
467 rng, ifrng, im, inm, ims, ius = pm[-6:]
468 self.assertEqual(SH.mock_calls[-6:], [
470 call('If-Range', '', ifrng and rng),
471 call('If-Match', im),
472 call('If-None-Match', inm),
473 call('If-Modified-Since', ims),
474 call('If-Unmodified-Since', ius)])
475 acc, cont = self.client.account, self.client.container
476 self.assertEqual(get.mock_calls[-1], call(
477 '/%s/%s/%s' % (acc, cont, obj),
479 success=kwargs.pop('success', 200),
482 @patch('%s.set_param' % rest_pkg)
483 @patch('%s.set_header' % rest_pkg)
484 @patch('%s.put' % rest_pkg, return_value=FR())
485 def test_object_put(self, put, SH, SP):
490 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
492 (dict(), dict(k2='v2', k3='v3')),
494 (dict(), dict(success=400), dict(k='v', v='k'))):
495 args, kwargs = pm[-2:]
498 for i in range(len(terms)):
500 terms[i] = 'val_%s' % randint(13, 1024)
501 self.client.object_put(
503 *(pm[:3] + tuple(terms) + pm[3:] + args),
505 format, hashmap, delimiter = pm[:3]
506 self.assertEqual(SP.mock_calls[-3:], [
507 call('format', format, iff=format),
508 call('hashmap', hashmap, iff=hashmap),
509 call('delimiter', delimiter, iff=delimiter)])
511 im, inm, etag, clen, ctype, trenc,
512 cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
513 perms, public, metas = pm[3:]
515 call('If-Match', im),
516 call('If-None-Match', inm),
518 call('Content-Length', clen),
519 call('Content-Type', ctype),
520 call('Transfer-Encoding', trenc),
521 call('X-Copy-From', cp),
522 call('X-Move-From', mv),
523 call('X-Source-Account', srcacc),
524 call('X-Source-Version', srcvrs),
525 call('Content-Encoding', conenc),
526 call('Content-Disposition', condis),
527 call('X-Object-Manifest', mnf)]
530 for ptype, pval in perms.items():
532 perm_str += ';' if perm_str else ''
533 perm_str += '%s=%s' % (ptype, ','.join(pval))
534 exp += [call('X-Object-Sharing', perm_str)]
535 exp += [call('X-Object-Public', public)]
536 for k, v in metas.items():
537 exp += [call('X-Object-Meta-%s' % k, v)]
538 self.assertEqual(SH.mock_calls[- len(exp):], exp)
539 acc, cont = self.client.account, self.client.container
540 self.assertEqual(put.mock_calls[-1], call(
541 '/%s/%s/%s' % (acc, cont, obj),
543 success=kwargs.pop('success', 201),
546 @patch('%s.set_param' % rest_pkg)
547 @patch('%s.set_header' % rest_pkg)
548 @patch('%s.copy' % rest_pkg, return_value=FR())
549 def test_object_copy(self, copy, SH, SP):
555 (None, 'ifnonematch'),
556 (None, 'destinationaccount'),
557 (None, 'content-type'),
558 (None, 'content-encoding'),
559 (None, 'content-disp'),
560 (None, 'source-version'),
561 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
563 (dict(), dict(k2='v2', k3='v3')),
565 (dict(), dict(success=400), dict(k='v', v='k'))):
566 args, kwargs = pm[-2:]
568 self.client.object_copy(obj, dest, *(pm + args), **kwargs)
570 self.assertEqual(SP.mock_calls[-2:], [
571 call('format', format, iff=format),
572 call('ignore_content_type', iff=ict)])
573 im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
574 exp = [call('If-Match', im),
575 call('If-None-Match', inm),
576 call('Destination', dest),
577 call('Destination-Account', da),
578 call('Content-Type', ct),
579 call('Content-Encoding', ce),
580 call('Content-Disposition', cd),
581 call('X-Source-Version', sv)]
584 for ptype, pval in perms.items():
586 perm_str += ';' if perm_str else ''
587 perm_str += '%s=%s' % (ptype, ','.join(pval))
588 exp += [call('X-Object-Sharing', perm_str)]
589 exp += [call('X-Object-Public', public)]
590 for k, v in metas.items():
591 exp += [call('X-Object-Meta-%s' % k, v)]
592 self.assertEqual(SH.mock_calls[- len(exp):], exp)
593 acc, cont = self.client.account, self.client.container
594 self.assertEqual(copy.mock_calls[-1], call(
595 '/%s/%s/%s' % (acc, cont, obj),
597 success=kwargs.pop('success', 201),
600 @patch('%s.set_param' % rest_pkg)
601 @patch('%s.set_header' % rest_pkg)
602 @patch('%s.move' % rest_pkg, return_value=FR())
603 def test_object_move(self, move, SH, SP):
608 (None, 'ifnonematch'),
609 (None, 'destination'),
610 (None, 'destinationaccount'),
611 (None, 'content-type'),
612 (None, 'content-encoding'),
613 (None, 'content-disp'),
614 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
616 (dict(), dict(k2='v2', k3='v3')),
618 (dict(), dict(success=400), dict(k='v', v='k'))):
619 args, kwargs = pm[-2:]
621 self.client.object_move(obj, *(pm + args), **kwargs)
623 self.assertEqual(SP.mock_calls[-2:], [
624 call('format', format, iff=format),
625 call('ignore_content_type', iff=ict)])
626 im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
627 exp = [call('If-Match', im),
628 call('If-None-Match', inm),
629 call('Destination', d),
630 call('Destination-Account', da),
631 call('Content-Type', ct),
632 call('Content-Encoding', ce),
633 call('Content-Disposition', cd)]
636 for ptype, pval in perms.items():
638 perm_str += ';' if perm_str else ''
639 perm_str += '%s=%s' % (ptype, ','.join(pval))
640 exp += [call('X-Object-Sharing', perm_str)]
641 exp += [call('X-Object-Public', public)]
642 for k, v in metas.items():
643 exp += [call('X-Object-Meta-%s' % k, v)]
644 self.assertEqual(SH.mock_calls[- len(exp):], exp)
645 acc, cont = self.client.account, self.client.container
646 self.assertEqual(move.mock_calls[-1], call(
647 '/%s/%s/%s' % (acc, cont, obj),
649 success=kwargs.pop('success', 201),
652 @patch('%s.set_param' % rest_pkg)
653 @patch('%s.set_header' % rest_pkg)
654 @patch('%s.post' % rest_pkg, return_value=FR())
655 def test_object_post(self, post, SH, SP):
659 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
661 (dict(), dict(k2='v2', k3='v3')),
663 (dict(), dict(success=400), dict(k='v', v='k'))):
664 args, kwargs = pm[-2:]
667 for i in range(len(terms)):
669 terms[i] = 'val_%s' % randint(13, 1024)
670 self.client.object_post(
672 *(pm[:2] + tuple(terms) + pm[2:] + args),
674 format, update = pm[:2]
675 self.assertEqual(SP.mock_calls[-2:], [
676 call('format', format, iff=format),
677 call('update', iff=update)])
679 im, inm, clen, ctype, crng, trenc, cenc,
680 condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
682 call('If-Match', im),
683 call('If-None-Match', inm),
684 call('Content-Length', clen, iff=not trenc),
685 call('Content-Type', ctype),
686 call('Content-Range', crng),
687 call('Transfer-Encoding', trenc),
688 call('Content-Encoding', cenc),
689 call('Content-Disposition', condis),
690 call('X-Source-Object', srcobj),
691 call('X-Source-Account', srcacc),
692 call('X-Source-Version', srcvrs),
693 call('X-Object-Bytes', obytes),
694 call('X-Object-Manifest', mnfs)]
695 perms, public, metas = pm[2:]
698 for ptype, pval in perms.items():
700 perm_str += ';' if perm_str else ''
701 perm_str += '%s=%s' % (ptype, ','.join(pval))
702 exp += [call('X-Object-Sharing', perm_str)]
703 exp += [call('X-Object-Public', public)]
704 for k, v in metas.items():
705 exp += [call('X-Object-Meta-%s' % k, v)]
706 self.assertEqual(SH.mock_calls[- len(exp):], exp)
707 acc, cont = self.client.account, self.client.container
708 self.assertEqual(post.mock_calls[-1], call(
709 '/%s/%s/%s' % (acc, cont, obj),
711 success=kwargs.pop('success', (202, 204)),
714 @patch('%s.set_param' % rest_pkg)
715 @patch('%s.delete' % rest_pkg, return_value=FR())
716 def test_object_delete(self, delete, SP):
721 (dict(), dict(success=400), dict(k='v', v='k'))):
722 args, kwargs = pm[-2:]
724 self.client.object_delete(
729 self.assertEqual(SP.mock_calls[-2:], [
730 call('until', until, iff=until),
731 call('delimiter', dlm, iff=dlm)])
732 acc, cont = self.client.account, self.client.container
733 self.assertEqual(delete.mock_calls[-1], call(
734 '/%s/%s/%s' % (acc, cont, obj),
736 success=kwargs.pop('success', 204),
740 class PithosClient(TestCase):
744 def _create_temp_file(self, num_of_blocks):
745 self.files.append(NamedTemporaryFile())
746 tmpFile = self.files[-1]
747 file_size = num_of_blocks * 4 * 1024 * 1024
748 print('\n\tCreate tmp file')
749 tmpFile.write(urandom(file_size))
755 def assert_dicts_are_equal(self, d1, d2):
756 for k, v in d1.items():
757 self.assertTrue(k in d2)
758 if isinstance(v, dict):
759 self.assert_dicts_are_equal(v, d2[k])
761 self.assertEqual(unicode(v), unicode(d2[k]))
764 self.url = 'https://www.example.com/pithos'
765 self.token = 'p17h0570k3n'
766 self.client = pithos.PithosClient(self.url, self.token)
767 self.client.account = user_id
768 self.client.container = 'c0nt@1n3r_i'
778 # Pithos+ methods that extend storage API
780 @patch('%s.account_head' % pithos_pkg, return_value=FR())
781 def test_get_account_info(self, AH):
782 FR.headers = account_info
783 for until in (None, 'un71L-d473'):
784 r = self.client.get_account_info(until=until)
785 self.assert_dicts_are_equal(r, account_info)
786 self.assertEqual(AH.mock_calls[-1], call(until=until))
788 self.assertRaises(ClientError, self.client.get_account_info)
790 @patch('%s.account_post' % pithos_pkg, return_value=FR())
791 def test_del_account_meta(self, AP):
792 keys = ['k1', 'k2', 'k3']
794 self.client.del_account_meta(key)
797 call(update=True, metadata={key: ''}))
799 @patch('%s.container_head' % pithos_pkg, return_value=FR())
800 def test_get_container_info(self, CH):
801 FR.headers = container_info
802 r = self.client.get_container_info()
803 self.assert_dicts_are_equal(r, container_info)
805 r = self.client.get_container_info(until=u)
806 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
808 @patch('%s.account_get' % pithos_pkg, return_value=FR())
809 def test_list_containers(self, get):
810 FR.json = container_list
811 r = self.client.list_containers()
812 get.assert_called_once_with()
813 for i in range(len(r)):
814 self.assert_dicts_are_equal(r[i], container_list[i])
816 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
817 @patch('%s.container_post' % pithos_pkg, return_value=FR())
818 @patch('%s.object_put' % pithos_pkg, return_value=FR())
819 def test_upload_object(self, OP, CP, GCI):
821 tmpFile = self._create_temp_file(num_of_blocks)
824 exp_headers = dict(id='container id', name='container name')
825 FR.headers = dict(exp_headers)
826 r = self.client.upload_object(obj, tmpFile)
827 self.assert_dicts_are_equal(r, exp_headers)
828 self.assertEqual(GCI.mock_calls[-1], call())
830 [call1, call2] = OP.mock_calls
831 (args1, kwargs1) = call1[1:3]
832 (args2, kwargs2) = call2[1:3]
833 self.assertEqual(args1, (obj,))
839 hashes=['s0m3h@5h'] * num_of_blocks,
840 bytes=num_of_blocks * 4 * 1024 * 1024),
841 content_encoding=None,
842 content_type='application/octet-stream',
843 content_disposition=None,
846 for k, v in expected1.items():
848 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
849 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
851 self.assertEqual(v, kwargs1[k])
853 (args2, kwargs2) = call2[1:3]
854 self.assertEqual(args2, (obj,))
857 hashes=['s0m3h@5h'] * num_of_blocks,
858 bytes=num_of_blocks * 4 * 1024 * 1024),
859 content_type='application/octet-stream',
863 for k, v in expected2.items():
865 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
866 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
868 self.assertEqual(v, kwargs2[k])
874 from progress.bar import ShadyBar
875 blck_bar = ShadyBar('Mock blck calc.')
876 upld_bar = ShadyBar('Mock uplds')
881 if blck_bar and upld_bar:
884 for i in blck_bar.iter(range(n)):
889 for i in upld_bar.iter(range(n)):
894 r = self.client.upload_object(
896 hash_cb=blck_gen, upload_cb=upld_gen)
897 self.assert_dicts_are_equal(r, exp_headers)
899 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
900 self.assertEqual(OP.mock_calls[i], c)
905 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
906 r = self.client.upload_object(obj, tmpFile,
907 content_type=ctype, sharing=sharing)
908 self.assert_dicts_are_equal(r, exp_headers)
909 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
910 self.assert_dicts_are_equal(
911 OP.mock_calls[-2][2]['permissions'],
918 if_etag_match='if etag match',
921 content_disposition=ctype + 'd15p051710n',
923 content_encoding='802.11',
924 container_info_cache={})
925 r = self.client.upload_object(obj, tmpFile, **kwargs)
926 self.assert_dicts_are_equal(r, exp_headers)
928 kwargs.pop('if_not_exist')
929 ematch = kwargs.pop('if_etag_match')
930 etag = kwargs.pop('etag')
931 self.assert_dicts_are_equal(
932 kwargs.pop('container_info_cache'),
933 {self.client.container: container_info})
934 for arg, val in kwargs.items():
935 self.assertEqual(OP.mock_calls[-2][2][arg], val)
936 self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
937 self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
938 self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
940 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
941 @patch('%s.container_post' % pithos_pkg, return_value=FR())
942 @patch('%s.object_put' % pithos_pkg, return_value=FR())
943 def test_upload_from_string(self, OP, CP, GCI):
945 tmpFile = self._create_temp_file(num_of_blocks)
947 src_str = tmpFile.read()
949 exp_headers = dict(id='container id', name='container name')
950 FR.headers = dict(exp_headers)
951 r = self.client.upload_from_string(obj, src_str)
952 self.assert_dicts_are_equal(r, exp_headers)
953 self.assertEqual(GCI.mock_calls[-1], call())
955 [call1, call2] = OP.mock_calls
956 (args1, kwargs1) = call1[1:3]
957 (args2, kwargs2) = call2[1:3]
958 self.assertEqual(args1, (obj,))
964 hashes=['s0m3h@5h'] * num_of_blocks,
965 bytes=num_of_blocks * 4 * 1024 * 1024),
966 content_encoding=None,
967 content_type='application/octet-stream',
968 content_disposition=None,
971 for k, v in expected1.items():
973 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
974 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
976 self.assertEqual(v, kwargs1[k])
978 (args2, kwargs2) = call2[1:3]
979 self.assertEqual(args2, (obj,))
982 hashes=['s0m3h@5h'] * num_of_blocks,
983 bytes=num_of_blocks * 4 * 1024 * 1024),
984 content_type='application/octet-stream',
988 for k, v in expected2.items():
990 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
991 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
993 self.assertEqual(v, kwargs2[k])
999 from progress.bar import ShadyBar
1000 blck_bar = ShadyBar('Mock blck calc.')
1001 upld_bar = ShadyBar('Mock uplds')
1006 if blck_bar and upld_bar:
1009 for i in blck_bar.iter(range(n)):
1014 for i in upld_bar.iter(range(n)):
1019 r = self.client.upload_object(
1021 hash_cb=blck_gen, upload_cb=upld_gen)
1022 self.assert_dicts_are_equal(r, exp_headers)
1024 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
1025 self.assertEqual(OP.mock_calls[i], c)
1029 ctype = 'video/mpeg'
1030 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
1031 r = self.client.upload_object(obj, tmpFile,
1032 content_type=ctype, sharing=sharing)
1033 self.assert_dicts_are_equal(r, exp_headers)
1034 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
1035 self.assert_dicts_are_equal(
1036 OP.mock_calls[-2][2]['permissions'],
1043 if_etag_match='if etag match',
1046 content_disposition=ctype + 'd15p051710n',
1048 content_encoding='802.11',
1049 container_info_cache={})
1050 r = self.client.upload_object(obj, tmpFile, **kwargs)
1051 self.assert_dicts_are_equal(r, exp_headers)
1053 kwargs.pop('if_not_exist')
1054 ematch = kwargs.pop('if_etag_match')
1055 etag = kwargs.pop('etag')
1056 self.assert_dicts_are_equal(
1057 kwargs.pop('container_info_cache'),
1058 {self.client.container: container_info})
1059 for arg, val in kwargs.items():
1060 self.assertEqual(OP.mock_calls[-2][2][arg], val)
1061 self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
1062 self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
1063 self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
1065 def test_get_object_info(self):
1066 FR.headers = object_info
1069 pithos.PithosClient, 'object_head',
1070 return_value=FR()) as head:
1071 r = self.client.get_object_info(obj)
1072 self.assertEqual(r, object_info)
1073 r = self.client.get_object_info(obj, version=version)
1074 self.assertEqual(head.mock_calls, [
1075 call(obj, version=None),
1076 call(obj, version=version)])
1078 pithos.PithosClient, 'object_head',
1079 side_effect=ClientError('Obj not found', 404)):
1082 self.client.get_object_info,
1083 obj, version=version)
1085 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
1086 def test_get_object_meta(self, GOI):
1087 for version in (None, 'v3r510n'):
1088 r = self.client.get_object_meta(obj, version)
1089 for k in [k for k in object_info if k.startswith('x-object-meta')]:
1090 self.assertEqual(r.pop(k), object_info[k])
1091 self.assertFalse(len(r))
1092 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
1094 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1095 def test_del_object_meta(self, post):
1096 metakey = '50m3m3t4k3y'
1097 self.client.del_object_meta(obj, metakey)
1098 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
1100 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1101 def test_copy_object(self, put):
1102 src_cont = 'src-c0nt41n3r'
1104 dst_cont = 'dst-c0nt41n3r'
1109 source_account=None,
1111 copy_from='/%s/%s' % (src_cont, src_obj),
1114 source_version=None,
1116 self.client.copy_object(src_cont, src_obj, dst_cont)
1117 self.assertEqual(put.mock_calls[-1], expected)
1118 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
1119 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1121 source_version='src-v3r510n',
1122 source_account='src-4cc0un7',
1124 content_type='c0n73n7Typ3',
1126 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
1127 for k, v in kwargs.items():
1128 self.assertEqual(v, put.mock_calls[-1][2][k])
1130 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1131 def test_move_object(self, put):
1132 src_cont = 'src-c0nt41n3r'
1134 dst_cont = 'dst-c0nt41n3r'
1139 source_account=None,
1141 move_from='/%s/%s' % (src_cont, src_obj),
1144 source_version=None,
1146 self.client.move_object(src_cont, src_obj, dst_cont)
1147 self.assertEqual(put.mock_calls[-1], expected)
1148 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1149 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1151 source_version='src-v3r510n',
1152 source_account='src-4cc0un7',
1154 content_type='c0n73n7Typ3',
1156 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1157 for k, v in kwargs.items():
1158 self.assertEqual(v, put.mock_calls[-1][2][k])
1160 # Pithos+ only methods
1162 @patch('%s.container_put' % pithos_pkg, return_value=FR())
1163 def test_create_container(self, CP):
1164 FR.headers = container_info
1165 cont = 'an0th3r_c0n741n3r'
1167 r = self.client.create_container()
1168 self.assert_dicts_are_equal(r, container_info)
1169 CP.assert_called_once_with(quota=None, versioning=None, metadata=None)
1171 bu_cont = self.client.container
1172 r = self.client.create_container(cont)
1173 self.assertEqual(self.client.container, bu_cont)
1174 self.assert_dicts_are_equal(r, container_info)
1177 call(quota=None, versioning=None, metadata=None))
1179 meta = dict(k1='v1', k2='v2')
1180 r = self.client.create_container(cont, 42, 'auto', meta)
1181 self.assertEqual(self.client.container, bu_cont)
1182 self.assert_dicts_are_equal(r, container_info)
1185 call(quota=42, versioning='auto', metadata=meta))
1187 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1188 def test_purge_container(self, CD):
1189 self.client.purge_container()
1190 self.assertTrue('until' in CD.mock_calls[-1][2])
1191 cont = self.client.container
1192 self.client.purge_container('another-container')
1193 self.assertEqual(self.client.container, cont)
1195 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1196 def test_upload_object_unchunked(self, put):
1198 tmpFile = self._create_temp_file(num_of_blocks)
1201 data=num_of_blocks * 4 * 1024 * 1024,
1203 content_encoding='some content_encoding',
1204 content_type='some content-type',
1205 content_disposition='some content_disposition',
1207 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1208 r = self.client.upload_object_unchunked(obj, tmpFile)
1209 self.assert_dicts_are_equal(r, FR.headers)
1210 self.assertEqual(put.mock_calls[-1][1], (obj,))
1212 sorted(put.mock_calls[-1][2].keys()),
1213 sorted(expected.keys()))
1214 kwargs = dict(expected)
1215 kwargs.pop('success')
1216 kwargs['size'] = kwargs.pop('data')
1217 kwargs['sharing'] = kwargs.pop('permissions')
1219 r = self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1220 self.assert_dicts_are_equal(r, FR.headers)
1221 pmc = put.mock_calls[-1][2]
1222 for k, v in expected.items():
1224 self.assertEqual(len(pmc[k]), v)
1226 self.assertEqual(pmc[k], v)
1229 self.client.upload_object_unchunked,
1230 obj, tmpFile, withHashFile=True)
1232 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1233 def test_create_object_by_manifestation(self, put):
1234 manifest = '%s/%s' % (self.client.container, obj)
1237 content_encoding='some content_encoding',
1238 content_type='some content-type',
1239 content_disposition='some content_disposition',
1241 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1242 r = self.client.create_object_by_manifestation(obj)
1243 self.assert_dicts_are_equal(r, FR.headers)
1244 expected = dict(content_length=0, manifest=manifest)
1246 expected['permissions' if k == 'sharing' else k] = None
1247 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1248 r = self.client.create_object_by_manifestation(obj, **kwargs)
1249 self.assert_dicts_are_equal(r, FR.headers)
1250 expected.update(kwargs)
1251 expected['permissions'] = expected.pop('sharing')
1252 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1254 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1255 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1256 def test_download_to_string(self, GET, GOH):
1257 FR.content = 'some sample content'
1258 num_of_blocks = len(object_hashmap['hashes'])
1259 r = self.client.download_to_string(obj)
1260 expected_content = FR.content * num_of_blocks
1261 self.assertEqual(expected_content, r)
1262 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1263 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1268 if_match='if and only if',
1269 if_none_match='if and only not',
1270 if_modified_since='what if not?',
1271 if_unmodified_since='this happens if not!')
1272 expargs = dict(kwargs)
1273 expargs.pop('range_str')
1276 GOH.assert_called_once_with(obj, **expargs)
1278 r = self.client.download_to_string(obj, **kwargs)
1279 expargs['data_range'] = 'bytes=%s' % kwargs['range_str']
1280 for k, v in expargs.items():
1282 GET.mock_calls[-1][2][k],
1285 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1286 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1287 def test_download_object(self, GET, GOH):
1289 tmpFile = self._create_temp_file(num_of_blocks)
1290 FR.content = tmpFile.read(4 * 1024 * 1024)
1291 tmpFile = self._create_temp_file(num_of_blocks)
1292 num_of_blocks = len(object_hashmap['hashes'])
1297 if_match='if and only if',
1298 if_none_match='if and only not',
1299 if_modified_since='what if not?',
1300 if_unmodified_since='this happens if not!',
1301 async_headers=dict(Range='bytes=0-88888888'))
1303 self.client.download_object(obj, tmpFile)
1304 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1305 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1306 for k, v in kwargs.items():
1307 if k == 'async_headers':
1308 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1309 elif k in ('resume', 'range_str'):
1312 self.assertEqual(GET.mock_calls[-1][2][k], None)
1314 # Check ranges are consecutive
1317 for c in GET.mock_calls:
1318 rng_str = c[2]['async_headers']['Range']
1319 (start, rng_str) = rng_str.split('=')
1320 (start, end) = rng_str.split('-')
1321 starts.append(start)
1324 for i, start in enumerate(sorted(starts)):
1326 int(ends[i - 1]) == int(start) - 1
1328 # With progress bars
1330 from progress.bar import ShadyBar
1331 dl_bar = ShadyBar('Mock dl')
1338 for i in dl_bar.iter(range(n)):
1343 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1344 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1347 kwargs.pop('async_headers')
1348 kwargs.pop('resume')
1349 self.client.download_object(obj, tmpFile, **kwargs)
1350 for k, v in kwargs.items():
1351 if k == 'range_str':
1353 GET.mock_calls[-1][2]['data_range'],
1356 self.assertEqual(GET.mock_calls[-1][2][k], v)
1358 # ALl options on no tty
1363 tmpFile.isatty = foo
1364 self.client.download_object(obj, tmpFile, **kwargs)
1365 for k, v in kwargs.items():
1366 if k == 'range_str':
1367 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1369 self.assertEqual(GET.mock_calls[-1][2][k], v)
1371 def test_get_object_hashmap(self):
1372 FR.json = object_hashmap
1373 for empty in (304, 412):
1375 pithos.PithosClient, 'object_get',
1376 side_effect=ClientError('Empty', status=empty)):
1377 r = self.client.get_object_hashmap(obj)
1378 self.assertEqual(r, {})
1384 if_etag_not_match=None,
1385 if_modified_since=None,
1386 if_unmodified_since=None)
1388 version='s0m3v3r51on',
1389 if_match='if match',
1390 if_none_match='if non match',
1391 if_modified_since='some date here',
1392 if_unmodified_since='some date here',
1395 pithos.PithosClient, 'object_get',
1396 return_value=FR()) as get:
1397 r = self.client.get_object_hashmap(obj)
1398 self.assertEqual(r, object_hashmap)
1399 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1400 r = self.client.get_object_hashmap(obj, **kwargs)
1401 exp_args['if_etag_match'] = kwargs.pop('if_match')
1402 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1403 exp_args.update(kwargs)
1404 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1406 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1407 def test_set_account_group(self, post):
1408 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1409 self.client.set_account_group(group, usernames)
1410 post.assert_called_once_with(update=True, groups={group: usernames})
1412 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1413 def test_del_account_group(self, post):
1414 group = 'aU53rGr0up'
1415 self.client.del_account_group(group)
1416 post.assert_called_once_with(update=True, groups={group: []})
1418 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1419 def test_get_account_quota(self, GAI):
1420 key = 'x-account-policy-quota'
1421 r = self.client.get_account_quota()
1422 GAI.assert_called_once_with()
1423 self.assertEqual(r[key], account_info[key])
1425 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1426 def test_get_account_versioning(self, GAI):
1427 key = 'x-account-policy-versioning'
1428 r = self.client.get_account_versioning()
1429 GAI.assert_called_once_with()
1430 self.assertEqual(r[key], account_info[key])
1432 def test_get_account_meta(self):
1433 key = 'x-account-meta-'
1435 pithos.PithosClient, 'get_account_info',
1436 return_value=account_info):
1437 r = self.client.get_account_meta()
1438 keys = [k for k in r if k.startswith(key)]
1439 self.assertFalse(keys)
1440 acc_info = dict(account_info)
1441 acc_info['%sk1' % key] = 'v1'
1442 acc_info['%sk2' % key] = 'v2'
1443 acc_info['%sk3' % key] = 'v3'
1445 pithos.PithosClient, 'get_account_info',
1446 return_value=acc_info):
1447 r = self.client.get_account_meta()
1448 for k in [k for k in acc_info if k.startswith(key)]:
1449 self.assertEqual(r[k], acc_info[k])
1451 def test_get_account_group(self):
1452 key = 'x-account-group-'
1454 pithos.PithosClient, 'get_account_info',
1455 return_value=account_info):
1456 r = self.client.get_account_group()
1457 keys = [k for k in r if k.startswith(key)]
1458 self.assertFalse(keys)
1459 acc_info = dict(account_info)
1460 acc_info['%sk1' % key] = 'g1'
1461 acc_info['%sk2' % key] = 'g2'
1462 acc_info['%sk3' % key] = 'g3'
1464 pithos.PithosClient, 'get_account_info',
1465 return_value=acc_info):
1466 r = self.client.get_account_group()
1467 for k in [k for k in acc_info if k.startswith(key)]:
1468 self.assertEqual(r[k], acc_info[k])
1470 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1471 def test_set_account_meta(self, post):
1472 metas = dict(k1='v1', k2='v2', k3='v3')
1473 self.client.set_account_meta(metas)
1474 post.assert_called_once_with(update=True, metadata=metas)
1477 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1478 def test_set_account_quota(self, post):
1480 self.client.set_account_quota(qu)
1481 post.assert_called_once_with(update=True, quota=qu)
1484 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1485 def test_set_account_versioning(self, post):
1486 vrs = 'n3wV3r51on1ngTyp3'
1487 self.client.set_account_versioning(vrs)
1488 post.assert_called_once_with(update=True, versioning=vrs)
1490 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1491 def test_del_container(self, delete):
1493 dict(delimiter=None, until=None),
1494 dict(delimiter='X', until='50m3d473')):
1495 self.client.del_container(**kwarg)
1496 expected = dict(kwarg)
1497 expected['success'] = (204, 404, 409)
1498 self.assertEqual(delete.mock_calls[-1], call(**expected))
1499 for status_code in (404, 409):
1500 FR.status_code = status_code
1501 self.assertRaises(ClientError, self.client.del_container)
1503 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1504 def test_get_container_versioning(self, GCI):
1505 key = 'x-container-policy-versioning'
1507 bu_cnt = self.client.container
1508 for container in (None, cont):
1509 r = self.client.get_container_versioning(container=container)
1510 self.assertEqual(r[key], container_info[key])
1511 self.assertEqual(GCI.mock_calls[-1], call())
1512 self.assertEqual(bu_cnt, self.client.container)
1514 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1515 def test_get_container_limit(self, GCI):
1516 key = 'x-container-policy-quota'
1518 bu_cnt = self.client.container
1519 for container in (None, cont):
1520 r = self.client.get_container_limit(container=container)
1521 self.assertEqual(r[key], container_info[key])
1522 self.assertEqual(GCI.mock_calls[-1], call())
1523 self.assertEqual(bu_cnt, self.client.container)
1525 def test_get_container_meta(self):
1526 somedate = '50m3d473'
1527 key = 'x-container-meta'
1528 metaval = '50m3m374v41'
1529 container_plus = dict(container_info)
1530 container_plus[key] = metaval
1531 for ret in ((container_info, {}), (container_plus, {key: metaval})):
1533 pithos.PithosClient,
1534 'get_container_info',
1535 return_value=ret[0]) as GCI:
1536 for until in (None, somedate):
1537 r = self.client.get_container_meta(until=until)
1538 self.assertEqual(r, ret[1])
1539 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1541 def test_get_container_object_meta(self):
1542 somedate = '50m3d473'
1543 key = 'x-container-object-meta'
1544 metaval = '50m3m374v41'
1545 container_plus = dict(container_info)
1546 container_plus[key] = metaval
1548 (container_info, {key: ''}),
1549 (container_plus, {key: metaval})):
1551 pithos.PithosClient,
1552 'get_container_info',
1553 return_value=ret[0]) as GCI:
1554 for until in (None, somedate):
1555 r = self.client.get_container_object_meta(until=until)
1556 self.assertEqual(r, ret[1])
1557 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1559 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1560 def test_set_container_meta(self, post):
1561 metas = dict(k1='v1', k2='v2', k3='v3')
1562 self.client.set_container_meta(metas)
1563 post.assert_called_once_with(update=True, metadata=metas)
1565 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1566 def test_del_container_meta(self, AP):
1567 self.client.del_container_meta('somekey')
1568 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1570 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1571 def test_set_container_limit(self, post):
1573 self.client.set_container_limit(qu)
1574 post.assert_called_once_with(update=True, quota=qu)
1576 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1577 def test_set_container_versioning(self, post):
1578 vrs = 'n3wV3r51on1ngTyp3'
1579 self.client.set_container_versioning(vrs)
1580 post.assert_called_once_with(update=True, versioning=vrs)
1582 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1583 def test_del_object(self, delete):
1585 dict(delimiter=None, until=None),
1586 dict(delimiter='X', until='50m3d473')):
1587 self.client.del_object(obj, **kwarg)
1588 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1590 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1591 def test_set_object_meta(self, post):
1592 metas = dict(k1='v1', k2='v2', k3='v3')
1595 self.client.set_object_meta,
1596 obj, 'Non dict arg')
1597 self.client.set_object_meta(obj, metas)
1598 post.assert_called_once_with(obj, update=True, metadata=metas)
1600 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1601 def test_publish_object(self, post):
1602 oinfo = dict(object_info)
1604 oinfo['x-object-public'] = val
1606 pithos.PithosClient, 'get_object_info',
1607 return_value=oinfo) as GOF:
1608 r = self.client.publish_object(obj)
1610 post.mock_calls[-1],
1611 call(obj, public=True, update=True))
1612 self.assertEqual(GOF.mock_calls[-1], call(obj))
1613 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1615 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1616 def test_unpublish_object(self, post):
1617 self.client.unpublish_object(obj)
1618 post.assert_called_once_with(obj, public=False, update=True)
1620 def test_get_object_sharing(self):
1621 info = dict(object_info)
1622 expected = dict(read='u1,g1,u2', write='u1')
1623 info['x-object-sharing'] = '; '.join(
1624 ['%s=%s' % (k, v) for k, v in expected.items()])
1626 pithos.PithosClient, 'get_object_info',
1627 return_value=info) as GOF:
1628 r = self.client.get_object_sharing(obj)
1629 self.assertEqual(GOF.mock_calls[-1], call(obj))
1630 self.assert_dicts_are_equal(r, expected)
1631 info['x-object-sharing'] = '//'.join(
1632 ['%s=%s' % (k, v) for k, v in expected.items()])
1635 self.client.get_object_sharing,
1637 info['x-object-sharing'] = '; '.join(
1638 ['%s:%s' % (k, v) for k, v in expected.items()])
1641 self.client.get_object_sharing,
1643 info['x-object-sharing'] = 'read=%s' % expected['read']
1644 r = self.client.get_object_sharing(obj)
1645 expected.pop('write')
1646 self.assert_dicts_are_equal(r, expected)
1648 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1649 def test_set_object_sharing(self, OP):
1650 read_perms = ['u1', 'g1', 'u2', 'g2']
1651 write_perms = ['u1', 'g1']
1653 dict(read_permission=read_perms, write_permission=write_perms),
1654 dict(read_permission=read_perms),
1655 dict(write_permission=write_perms),
1657 self.client.set_object_sharing(obj, **kwargs)
1658 kwargs['read'] = kwargs.pop('read_permission', '')
1659 kwargs['write'] = kwargs.pop('write_permission', '')
1662 call(obj, update=True, permissions=kwargs))
1664 @patch('%s.set_object_sharing' % pithos_pkg)
1665 def test_del_object_sharing(self, SOS):
1666 self.client.del_object_sharing(obj)
1667 SOS.assert_called_once_with(obj)
1669 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1670 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1671 def test_append_object(self, post, GCI):
1673 tmpFile = self._create_temp_file(num_of_blocks)
1675 file_size = tmpFile.tell()
1676 for turn in range(2):
1680 from progress.bar import ShadyBar
1681 apn_bar = ShadyBar('Mock append')
1688 for i in apn_bar.iter(range(n)):
1695 self.client.append_object(
1697 upload_cb=append_gen if turn else None)
1698 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1699 (args, kwargs) = post.mock_calls[-1][1:3]
1700 self.assertEqual(kwargs['obj'], obj)
1701 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1702 fsize = num_of_blocks * int(kwargs['content_length'])
1703 self.assertEqual(fsize, file_size)
1704 self.assertEqual(kwargs['content_range'], 'bytes */*')
1705 exp = 'application/octet-stream'
1706 self.assertEqual(kwargs['content_type'], exp)
1707 self.assertEqual(kwargs['update'], True)
1709 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1710 def test_truncate_object(self, post):
1712 self.client.truncate_object(obj, upto_bytes)
1713 post.assert_called_once_with(
1716 object_bytes=upto_bytes,
1717 content_range='bytes 0-%s/*' % upto_bytes,
1718 content_type='application/octet-stream',
1719 source_object='/%s/%s' % (self.client.container, obj))
1721 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1722 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1723 def test_overwrite_object(self, post, GCI):
1725 tmpFile = self._create_temp_file(num_of_blocks)
1727 file_size = tmpFile.tell()
1728 info = dict(object_info)
1729 info['content-length'] = file_size
1730 block_size = container_info['x-container-block-size']
1732 pithos.PithosClient, 'get_object_info',
1733 return_value=info) as GOI:
1736 (file_size + 1, file_size + 2)):
1740 self.client.overwrite_object,
1741 obj, start, end, tmpFile)
1742 for start, end in ((0, 144), (144, 233), (233, file_size)):
1745 exp_size = end - start + 1
1746 if not start or exp_size > block_size:
1748 from progress.bar import ShadyBar
1749 owr_bar = ShadyBar('Mock append')
1756 for i in owr_bar.iter(range(n)):
1760 if exp_size > block_size:
1761 exp_size = exp_size % block_size or block_size
1763 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1764 self.assertEqual(GOI.mock_calls[-1], call(obj))
1765 self.assertEqual(GCI.mock_calls[-1], call())
1766 (args, kwargs) = post.mock_calls[-1][1:3]
1767 self.assertEqual(args, (obj,))
1768 self.assertEqual(len(kwargs['data']), exp_size)
1769 self.assertEqual(kwargs['content_length'], exp_size)
1770 self.assertEqual(kwargs['update'], True)
1771 exp = 'application/octet-stream'
1772 self.assertEqual(kwargs['content_type'], exp)
1774 @patch('%s.set_param' % pithos_pkg)
1775 @patch('%s.get' % pithos_pkg, return_value=FR())
1776 def test_get_sharing_accounts(self, get, SP):
1780 dict(limit='50m3-11m17'),
1782 dict(limit='50m3-11m17', marker='X')):
1783 r = self.client.get_sharing_accounts(**kws)
1784 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1785 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1786 limit, marker = kws.get('limit', None), kws.get('marker', None)
1787 self.assertEqual(SP.mock_calls[-2], call(
1789 iff=limit is not None))
1790 self.assertEqual(SP.mock_calls[-1], call(
1792 iff=marker is not None))
1793 for i in range(len(r)):
1794 self.assert_dicts_are_equal(r[i], sharers[i])
1796 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1797 def test_get_object_versionlist(self, get):
1798 info = dict(object_info)
1799 info['versions'] = ['v1', 'v2']
1801 r = self.client.get_object_versionlist(obj)
1802 get.assert_called_once_with(obj, format='json', version='list')
1803 self.assertEqual(r, info['versions'])
1805 if __name__ == '__main__':
1806 from sys import argv
1807 from kamaki.clients.test import runTestCase
1809 if not argv[1:] or argv[1] == 'PithosClient':
1811 runTestCase(PithosClient, 'Pithos Client', argv[2:])
1812 if not argv[1:] or argv[1] == 'PithosRestClient':
1814 runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1816 print('TestCase %s not found' % argv[1])