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.commisioning.utils.ordereddict import OrderedDict
46 from kamaki.clients import ClientError
47 from kamaki.clients.pithos import PithosClient, PithosRestClient
50 rest_pkg = 'kamaki.clients.pithos.rest_api.PithosRestClient'
51 pithos_pkg = 'kamaki.clients.pithos.PithosClient'
53 user_id = 'ac0un7-1d-5tr1ng'
57 'content-language': 'en-us',
58 'content-type': 'text/html; charset=utf-8',
59 'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
60 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
61 'server': 'gunicorn/0.14.5',
62 'vary': 'Accept-Language',
63 'x-account-bytes-used': '751615526',
64 'x-account-container-count': 7,
65 'x-account-policy-quota': 53687091200,
66 'x-account-policy-versioning': 'auto'}
68 'content-language': 'en-us',
69 'content-type': 'text/html; charset=utf-8',
70 'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
71 'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
72 'server': 'gunicorn/0.14.5',
73 'vary': 'Accept-Language',
74 'x-container-block-hash': 'sha256',
75 'x-container-block-size': 4194304,
76 'x-container-bytes-used': 309528938,
77 'x-container-object-count': 14,
78 'x-container-object-meta': '',
79 'x-container-policy-quota': 53687091200,
80 'x-container-policy-versioning': 'auto'}
82 'content-language': 'en-us',
83 'content-length': 254965,
84 'content-type': 'application/octet-stream',
85 'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
87 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
88 'server': 'gunicorn/0.14.5',
89 'vary': 'Accept-Language',
90 'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
91 'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
92 'x-object-version': '525996',
93 'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
94 'x-object-meta-k1': 'v1',
95 'x-object-meta-k2': 'v2'}
99 last_modified="2013-02-27T11:56:09.893033+00:00",
102 x_container_policy=dict(quota="21474836480", versioning="auto")),
105 last_modified="2012-10-23T12:25:17.229187+00:00",
108 x_container_policy=dict(quota="21474836480", versioning="auto"))]
111 name="The_Secret_Garden.zip",
112 x_object_public="/public/wdp9p",
114 x_object_version_timestamp="1360237915.7027509",
115 x_object_uuid="s0m3uu1df0r0bj0n3",
116 last_modified="2013-02-07T11:51:55.702751+00:00",
117 content_type="application/octet-stream",
118 x_object_hash="0afdf29f71cd53126225c3f54ca",
119 x_object_version=17737,
120 x_object_modified_by=user_id),
122 name="The_Revealed_Garden.zip",
123 x_object_public="/public/wpd7p",
125 x_object_version_timestamp="13602915.7027509",
126 x_object_uuid="s0m3uu1df0r0bj70w",
127 last_modified="2013-02-07T11:51:55.702751+00:00",
128 content_type="application/octet-stream",
129 x_object_hash="0afdf29f71cd53126225c3f54ca",
130 x_object_version=17737,
131 x_object_modified_by=user_id)]
132 object_hashmap = dict(
133 block_hash="sha256", block_size=4194304, bytes=33554432,
135 "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
136 "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
137 "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
138 "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
139 "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
140 "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
141 "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
142 "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
144 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
145 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
146 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
150 """FR stands for Fake Response"""
161 class PithosRest(TestCase):
164 self.url = 'https://www.example.com/pithos'
165 self.token = 'p17h0570k3n'
166 self.client = PithosRestClient(self.url, self.token)
167 self.client.account = user_id
168 self.client.container = 'c0nt@1n3r_i'
175 @patch('%s.set_param' % rest_pkg)
176 @patch('%s.set_header' % rest_pkg)
177 @patch('%s.head' % rest_pkg, return_value=FR())
178 def test_account_head(self, head, SH, SP):
179 for params in product(
181 (None, '50m3-07h3r-d473'),
182 (None, 'y37-4n7h3r-d473'),
183 ((), ('someval',), ('v1', 'v2',)),
184 (dict(), dict(success=200), dict(k='v', v='k'))):
185 args, kwargs = params[-2], params[-1]
187 self.client.account_head(*(params + args), **kwargs)
189 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
190 IMS, IUS = params[1], params[2]
191 self.assertEqual(SH.mock_calls[-2:], [
192 call('If-Modified-Since', IMS),
193 call('If-Unmodified-Since', IUS)])
194 self.assertEqual(head.mock_calls[-1], call(
195 '/%s' % self.client.account,
197 success=kwargs.pop('success', 204),
200 @patch('%s.set_param' % rest_pkg)
201 @patch('%s.set_header' % rest_pkg)
202 @patch('%s.get' % rest_pkg, return_value=FR())
203 def test_account_get(self, get, SH, SP):
204 keys = ('limit', 'marker', 'format', 'shared', 'until')
205 for params in product(
211 (None, '50m3-07h3r-d473'),
212 (None, 'y37-4n7h3r-d473'),
213 ((), ('someval',), ('v1', 'v2',)),
214 (dict(), dict(success=200), dict(k='v', v='k'))):
215 args, kwargs = params[-2], params[-1]
217 self.client.account_get(*(params + args), **kwargs)
218 self.assertEqual(SP.mock_calls[-5:],
219 [call(keys[i], iff=X) if (
221 keys[i], X, iff=X) for i, X in enumerate(params[:5])])
222 IMS, IUS = params[5], params[6]
223 self.assertEqual(SH.mock_calls[-2:], [
224 call('If-Modified-Since', IMS),
225 call('If-Unmodified-Since', IUS)])
226 self.assertEqual(get.mock_calls[-1], call(
227 '/%s' % self.client.account,
229 success=kwargs.pop('success', (200, 204)),
232 @patch('%s.set_param' % rest_pkg)
233 @patch('%s.set_header' % rest_pkg)
234 @patch('%s.post' % rest_pkg, return_value=FR())
235 def test_account_post(self, post, SH, SP):
236 #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
239 ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
240 (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
242 (None, 'v3r510n1ng'),
243 ((), ('someval',), ('v1', 'v2',)),
244 (dict(), dict(success=200), dict(k='v', v='k'))):
245 args, kwargs = pm[-2:]
247 self.client.account_post(*(pm + args), **kwargs)
249 self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
253 call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
256 call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
258 call('X-Account-Policy-Quota', pm[3]),
259 call('X-Account-Policy-Versioning', pm[4])]
260 self.assertEqual(SH.mock_calls[- len(expected):], expected)
261 self.assertEqual(post.mock_calls[-1], call(
262 '/%s' % self.client.account,
264 success=kwargs.pop('success', 202),
267 @patch('%s.set_param' % rest_pkg)
268 @patch('%s.set_header' % rest_pkg)
269 @patch('%s.head' % rest_pkg, return_value=FR())
270 def test_container_head(self, head, SH, SP):
273 (None, '47h3r-d473'),
274 (None, 'y37-4n47h3r'),
276 (dict(), dict(success=200), dict(k='v', v='k'))):
277 args, kwargs = pm[-2:]
279 self.client.container_head(*(pm + args), **kwargs)
280 unt, ims, ius = pm[0:3]
281 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
282 self.assertEqual(SH.mock_calls[-2:], [
283 call('If-Modified-Since', ims),
284 call('If-Unmodified-Since', ius)])
285 self.assertEqual(head.mock_calls[-1], call(
286 '/%s/%s' % (self.client.account, self.client.container),
288 success=kwargs.pop('success', 204),
291 @patch('%s.set_param' % rest_pkg)
292 @patch('%s.set_header' % rest_pkg)
293 @patch('%s.get' % rest_pkg, return_value=FR())
294 def test_container_get(self, get, SH, SP):
298 (None, 'some/prefix'),
300 (None, '/some/path'),
301 ('json', 'some-format'),
302 ([], ['k1', 'k2', 'k3']),
304 (None, 'unt1l-d473'),
305 (None, 'y37-4n47h3r'),
306 (None, '4n47h3r-d473'),
308 (dict(), dict(success=400), dict(k='v', v='k'))):
309 args, kwargs = pm[-2:]
311 self.client.container_get(*(pm + args), **kwargs)
312 lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
313 exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
314 exp += [call('path', path)] if path else [
315 call('prefix', prfx, iff=prfx),
316 call('delimiter', dlm, iff=dlm)]
317 exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
319 exp += [call('meta', ','.join(meta))]
320 exp += [call('until', unt, iff=unt)]
321 self.assertEqual(SP.mock_calls[- len(exp):], exp)
323 self.assertEqual(SH.mock_calls[-2:], [
324 call('If-Modified-Since', ims),
325 call('If-Unmodified-Since', ius)])
326 self.assertEqual(get.mock_calls[-1], call(
327 '/%s/%s' % (self.client.account, self.client.container),
329 success=kwargs.pop('success', 200),
332 @patch('%s.set_header' % rest_pkg)
333 @patch('%s.put' % rest_pkg, return_value=FR())
334 def test_container_put(self, put, SH):
337 (None, 'v3r51on1ng'),
338 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
340 (dict(), dict(success=400), dict(k='v', v='k'))):
341 args, kwargs = pm[-2:]
343 self.client.container_put(*(pm + args), **kwargs)
344 quota, versioning, metas = pm[-3:]
346 call('X-Container-Policy-Quota', quota),
347 call('X-Container-Policy-Versioning', versioning)] + [
348 call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
349 self.assertEqual(SH.mock_calls[- len(exp):], exp)
350 self.assertEqual(put.mock_calls[-1], call(
351 '/%s/%s' % (self.client.account, self.client.container),
353 success=kwargs.pop('success', (201, 202)),
356 @patch('%s.set_param' % rest_pkg)
357 @patch('%s.set_header' % rest_pkg)
358 @patch('%s.post' % rest_pkg, return_value=FR())
359 def test_container_post(self, post, SH, SP):
362 ('json', 'some-format'),
364 (None, 'v3r51on1ng'),
365 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
366 (None, 'content-type'),
368 (None, 'transfer-encoding'),
370 (dict(), dict(success=400), dict(k='v', v='k'))):
371 args, kwargs = pm[-2:]
373 self.client.container_post(*(pm + args), **kwargs)
375 self.assertEqual(SP.mock_calls[-2:], [
376 call('update', iff=upd),
377 call('format', frmt, iff=frmt)])
378 qta, vrs, metas, ctype, clen, trenc = pm[2:]
379 prfx = 'X-Container-Meta-'
381 call('X-Container-Policy-Quota', qta),
382 call('X-Container-Policy-Versioning', vrs)] + [
383 call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
384 call('Content-Type', ctype),
385 call('Content-Length', clen),
386 call('Transfer-Encoding', trenc)]
387 self.assertEqual(SH.mock_calls[- len(exp):], exp)
389 self.assertEqual(post.mock_calls[-1], call(
390 '/%s/%s' % (self.client.account, self.client.container),
392 success=kwargs.pop('success', 202),
395 @patch('%s.set_param' % rest_pkg)
396 @patch('%s.delete' % rest_pkg, return_value=FR())
397 def test_container_delete(self, delete, SP):
402 (dict(), dict(success=400), dict(k='v', v='k'))):
403 args, kwargs = pm[-2:]
405 self.client.container_delete(*(pm + args), **kwargs)
407 self.assertEqual(SP.mock_calls[-2:], [
408 call('until', unt, iff=unt),
409 call('delimiter', dlm, iff=dlm)])
410 self.assertEqual(delete.mock_calls[-1], call(
411 '/%s/%s' % (self.client.account, self.client.container),
413 success=kwargs.pop('success', 204),
416 @patch('%s.set_param' % rest_pkg)
417 @patch('%s.set_header' % rest_pkg)
418 @patch('%s.head' % rest_pkg, return_value=FR())
419 def test_object_head(self, head, SH, SP):
424 (None, '1f-m0d-51nc3'),
425 (None, '1f-unm0d-51nc3'),
427 (dict(), dict(success=400), dict(k='v', v='k'))):
428 args, kwargs = pm[-2:]
430 self.client.object_head(obj, *(pm + args), **kwargs)
431 vrs, etag, netag, ims, ius = pm[:5]
434 call('version', vrs, iff=vrs))
435 self.assertEqual(SH.mock_calls[-4:], [
436 call('If-Match', etag),
437 call('If-None-Match', netag),
438 call('If-Modified-Since', ims),
439 call('If-Unmodified-Since', ius)])
440 acc, cont = self.client.account, self.client.container
441 self.assertEqual(head.mock_calls[-1], call(
442 '/%s/%s/%s' % (acc, cont, obj),
444 success=kwargs.pop('success', 200),
447 @patch('%s.set_param' % rest_pkg)
448 @patch('%s.set_header' % rest_pkg)
449 @patch('%s.get' % rest_pkg, return_value=FR())
450 def test_object_get(self, get, SH, SP):
455 (None, 'range=74-63'),
462 (dict(), dict(success=400), dict(k='v', v='k'))):
463 args, kwargs = pm[-2:]
465 self.client.object_get(obj, *(pm + args), **kwargs)
466 format, hashmap, version = pm[:3]
467 self.assertEqual(SP.mock_calls[-3:], [
468 call('format', format, iff=format),
469 call('hashmap', hashmap, iff=hashmap),
470 call('version', version, iff=version)])
471 rng, ifrng, im, inm, ims, ius = pm[-6:]
472 self.assertEqual(SH.mock_calls[-6:], [
474 call('If-Range', '', ifrng and rng),
475 call('If-Match', im),
476 call('If-None-Match', inm),
477 call('If-Modified-Since', ims),
478 call('If-Unmodified-Since', ius)])
479 acc, cont = self.client.account, self.client.container
480 self.assertEqual(get.mock_calls[-1], call(
481 '/%s/%s/%s' % (acc, cont, obj),
483 success=kwargs.pop('success', 200),
486 @patch('%s.set_param' % rest_pkg)
487 @patch('%s.set_header' % rest_pkg)
488 @patch('%s.put' % rest_pkg, return_value=FR())
489 def test_object_put(self, put, SH, SP):
494 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
496 (dict(), dict(k2='v2', k3='v3')),
498 (dict(), dict(success=400), dict(k='v', v='k'))):
499 args, kwargs = pm[-2:]
502 for i in range(len(terms)):
504 terms[i] = 'val_%s' % randint(13, 1024)
505 self.client.object_put(
507 *(pm[:3] + tuple(terms) + pm[3:] + args),
509 format, hashmap, delimiter = pm[:3]
510 self.assertEqual(SP.mock_calls[-3:], [
511 call('format', format, iff=format),
512 call('hashmap', hashmap, iff=hashmap),
513 call('delimiter', delimiter, iff=delimiter)])
515 im, inm, etag, clen, ctype, trenc,
516 cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
517 perms, public, metas = pm[3:]
519 call('If-Match', im),
520 call('If-None-Match', inm),
522 call('Content-Length', clen),
523 call('Content-Type', ctype),
524 call('Transfer-Encoding', trenc),
525 call('X-Copy-From', cp),
526 call('X-Move-From', mv),
527 call('X-Source-Account', srcacc),
528 call('X-Source-Version', srcvrs),
529 call('Content-Encoding', conenc),
530 call('Content-Disposition', condis),
531 call('X-Object-Manifest', mnf)]
534 for ptype, pval in perms.items():
536 perm_str += ';' if perm_str else ''
537 perm_str += '%s=%s' % (ptype, ','.join(pval))
538 exp += [call('X-Object-Sharing', perm_str)]
539 exp += [call('X-Object-Public', public)]
540 for k, v in metas.items():
541 exp += [call('X-Object-Meta-%s' % k, v)]
542 self.assertEqual(SH.mock_calls[- len(exp):], exp)
543 acc, cont = self.client.account, self.client.container
544 self.assertEqual(put.mock_calls[-1], call(
545 '/%s/%s/%s' % (acc, cont, obj),
547 success=kwargs.pop('success', 201),
550 @patch('%s.set_param' % rest_pkg)
551 @patch('%s.set_header' % rest_pkg)
552 @patch('%s.copy' % rest_pkg, return_value=FR())
553 def test_object_copy(self, copy, SH, SP):
559 (None, 'ifnonematch'),
560 (None, 'destinationaccount'),
561 (None, 'content-type'),
562 (None, 'content-encoding'),
563 (None, 'content-disp'),
564 (None, 'source-version'),
565 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
567 (dict(), dict(k2='v2', k3='v3')),
569 (dict(), dict(success=400), dict(k='v', v='k'))):
570 args, kwargs = pm[-2:]
572 self.client.object_copy(obj, dest, *(pm + args), **kwargs)
574 self.assertEqual(SP.mock_calls[-2:], [
575 call('format', format, iff=format),
576 call('ignore_content_type', iff=ict)])
577 im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
578 exp = [call('If-Match', im),
579 call('If-None-Match', inm),
580 call('Destination', dest),
581 call('Destination-Account', da),
582 call('Content-Type', ct),
583 call('Content-Encoding', ce),
584 call('Content-Disposition', cd),
585 call('X-Source-Version', sv)]
588 for ptype, pval in perms.items():
590 perm_str += ';' if perm_str else ''
591 perm_str += '%s=%s' % (ptype, ','.join(pval))
592 exp += [call('X-Object-Sharing', perm_str)]
593 exp += [call('X-Object-Public', public)]
594 for k, v in metas.items():
595 exp += [call('X-Object-Meta-%s' % k, v)]
596 self.assertEqual(SH.mock_calls[- len(exp):], exp)
597 acc, cont = self.client.account, self.client.container
598 self.assertEqual(copy.mock_calls[-1], call(
599 '/%s/%s/%s' % (acc, cont, obj),
601 success=kwargs.pop('success', 201),
604 @patch('%s.set_param' % rest_pkg)
605 @patch('%s.set_header' % rest_pkg)
606 @patch('%s.move' % rest_pkg, return_value=FR())
607 def test_object_move(self, move, SH, SP):
612 (None, 'ifnonematch'),
613 (None, 'destination'),
614 (None, 'destinationaccount'),
615 (None, 'content-type'),
616 (None, 'content-encoding'),
617 (None, 'content-disp'),
618 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
620 (dict(), dict(k2='v2', k3='v3')),
622 (dict(), dict(success=400), dict(k='v', v='k'))):
623 args, kwargs = pm[-2:]
625 self.client.object_move(obj, *(pm + args), **kwargs)
627 self.assertEqual(SP.mock_calls[-2:], [
628 call('format', format, iff=format),
629 call('ignore_content_type', iff=ict)])
630 im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
631 exp = [call('If-Match', im),
632 call('If-None-Match', inm),
633 call('Destination', d),
634 call('Destination-Account', da),
635 call('Content-Type', ct),
636 call('Content-Encoding', ce),
637 call('Content-Disposition', cd)]
640 for ptype, pval in perms.items():
642 perm_str += ';' if perm_str else ''
643 perm_str += '%s=%s' % (ptype, ','.join(pval))
644 exp += [call('X-Object-Sharing', perm_str)]
645 exp += [call('X-Object-Public', public)]
646 for k, v in metas.items():
647 exp += [call('X-Object-Meta-%s' % k, v)]
648 self.assertEqual(SH.mock_calls[- len(exp):], exp)
649 acc, cont = self.client.account, self.client.container
650 self.assertEqual(move.mock_calls[-1], call(
651 '/%s/%s/%s' % (acc, cont, obj),
653 success=kwargs.pop('success', 201),
656 @patch('%s.set_param' % rest_pkg)
657 @patch('%s.set_header' % rest_pkg)
658 @patch('%s.post' % rest_pkg, return_value=FR())
659 def test_object_post(self, post, SH, SP):
663 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
665 (dict(), dict(k2='v2', k3='v3')),
667 (dict(), dict(success=400), dict(k='v', v='k'))):
668 args, kwargs = pm[-2:]
671 for i in range(len(terms)):
673 terms[i] = 'val_%s' % randint(13, 1024)
674 self.client.object_post(
676 *(pm[:2] + tuple(terms) + pm[2:] + args),
678 format, update = pm[:2]
679 self.assertEqual(SP.mock_calls[-2:], [
680 call('format', format, iff=format),
681 call('update', iff=update)])
683 im, inm, clen, ctype, crng, trenc, cenc,
684 condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
686 call('If-Match', im),
687 call('If-None-Match', inm),
688 call('Content-Length', clen, iff=not trenc),
689 call('Content-Type', ctype),
690 call('Content-Range', crng),
691 call('Transfer-Encoding', trenc),
692 call('Content-Encoding', cenc),
693 call('Content-Disposition', condis),
694 call('X-Source-Object', srcobj),
695 call('X-Source-Account', srcacc),
696 call('X-Source-Version', srcvrs),
697 call('X-Object-Bytes', obytes),
698 call('X-Object-Manifest', mnfs)]
699 perms, public, metas = pm[2:]
702 for ptype, pval in perms.items():
704 perm_str += ';' if perm_str else ''
705 perm_str += '%s=%s' % (ptype, ','.join(pval))
706 exp += [call('X-Object-Sharing', perm_str)]
707 exp += [call('X-Object-Public', public)]
708 for k, v in metas.items():
709 exp += [call('X-Object-Meta-%s' % k, v)]
710 self.assertEqual(SH.mock_calls[- len(exp):], exp)
711 acc, cont = self.client.account, self.client.container
712 self.assertEqual(post.mock_calls[-1], call(
713 '/%s/%s/%s' % (acc, cont, obj),
715 success=kwargs.pop('success', (202, 204)),
719 class Pithos(TestCase):
723 def _create_temp_file(self, num_of_blocks):
724 self.files.append(NamedTemporaryFile())
725 tmpFile = self.files[-1]
726 file_size = num_of_blocks * 4 * 1024 * 1024
727 print('\n\tCreate tmp file')
728 tmpFile.write(urandom(file_size))
734 def assert_dicts_are_equal(self, d1, d2):
735 for k, v in d1.items():
736 self.assertTrue(k in d2)
737 if isinstance(v, dict):
738 self.assert_dicts_are_equal(v, d2[k])
740 self.assertEqual(unicode(v), unicode(d2[k]))
743 self.url = 'https://www.example.com/pithos'
744 self.token = 'p17h0570k3n'
745 self.client = PithosClient(self.url, self.token)
746 self.client.account = user_id
747 self.client.container = 'c0nt@1n3r_i'
757 # Pithos+ methods that extend storage API
759 @patch('%s.account_head' % pithos_pkg, return_value=FR())
760 def test_get_account_info(self, AH):
761 FR.headers = account_info
762 for until in (None, 'un71L-d473'):
763 r = self.client.get_account_info(until=until)
764 self.assert_dicts_are_equal(r, account_info)
765 self.assertEqual(AH.mock_calls[-1], call(until=until))
767 self.assertRaises(ClientError, self.client.get_account_info)
769 @patch('%s.account_post' % pithos_pkg, return_value=FR())
770 def test_del_account_meta(self, AP):
771 keys = ['k1', 'k2', 'k3']
773 self.client.del_account_meta(key)
776 call(update=True, metadata={key: ''}))
778 @patch('%s.container_head' % pithos_pkg, return_value=FR())
779 def test_get_container_info(self, CH):
780 FR.headers = container_info
781 r = self.client.get_container_info()
782 self.assert_dicts_are_equal(r, container_info)
784 r = self.client.get_container_info(until=u)
785 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
787 @patch('%s.account_get' % pithos_pkg, return_value=FR())
788 def test_list_containers(self, get):
789 FR.json = container_list
790 r = self.client.list_containers()
791 get.assert_called_once_with()
792 for i in range(len(r)):
793 self.assert_dicts_are_equal(r[i], container_list[i])
795 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
796 @patch('%s.container_post' % pithos_pkg, return_value=FR())
797 @patch('%s.object_put' % pithos_pkg, return_value=FR())
798 def test_upload_object(self, OP, CP, GCI):
800 tmpFile = self._create_temp_file(num_of_blocks)
803 self.client.upload_object(obj, tmpFile)
804 self.assertEqual(GCI.mock_calls[-1], call())
805 [call1, call2] = OP.mock_calls
807 (args1, kwargs1) = call1[1:3]
808 (args2, kwargs2) = call2[1:3]
809 self.assertEqual(args1, (obj,))
815 hashes=['s0m3h@5h'] * num_of_blocks,
816 bytes=num_of_blocks * 4 * 1024 * 1024),
818 content_encoding=None,
819 content_type='application/octet-stream',
820 content_disposition=None,
823 for k, v in expected1.items():
825 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
826 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
828 self.assertEqual(v, kwargs1[k])
830 (args2, kwargs2) = call2[1:3]
831 self.assertEqual(args2, (obj,))
834 hashes=['s0m3h@5h'] * num_of_blocks,
835 bytes=num_of_blocks * 4 * 1024 * 1024),
836 content_type='application/octet-stream',
840 for k, v in expected2.items():
842 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
843 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
845 self.assertEqual(v, kwargs2[k])
851 from progress.bar import ShadyBar
852 blck_bar = ShadyBar('Mock blck calc.')
853 upld_bar = ShadyBar('Mock uplds')
858 if blck_bar and upld_bar:
861 for i in blck_bar.iter(range(n)):
866 for i in upld_bar.iter(range(n)):
871 self.client.upload_object(
873 hash_cb=blck_gen, upload_cb=upld_gen)
875 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
876 self.assertEqual(OP.mock_calls[i], c)
881 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
882 self.client.upload_object(obj, tmpFile,
883 content_type=ctype, sharing=sharing)
884 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
885 self.assert_dicts_are_equal(
886 OP.mock_calls[-2][2]['permissions'],
894 content_disposition=ctype + 'd15p051710n',
896 content_encoding='802.11')
897 self.client.upload_object(obj, tmpFile, **kwargs)
898 for arg, val in kwargs.items():
899 self.assertEqual(OP.mock_calls[-2][2][arg], val)
901 def test_get_object_info(self):
902 FR.headers = object_info
905 PithosClient, 'object_head',
906 return_value=FR()) as head:
907 r = self.client.get_object_info(obj)
908 self.assertEqual(r, object_info)
909 r = self.client.get_object_info(obj, version=version)
910 self.assertEqual(head.mock_calls, [
911 call(obj, version=None),
912 call(obj, version=version)])
914 PithosClient, 'object_head',
915 side_effect=ClientError('Obj not found', 404)):
918 self.client.get_object_info,
919 obj, version=version)
921 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
922 def test_get_object_meta(self, GOI):
923 for version in (None, 'v3r510n'):
924 r = self.client.get_object_meta(obj, version)
925 for k in [k for k in object_info if k.startswith('x-object-meta')]:
926 self.assertEqual(r.pop(k), object_info[k])
927 self.assertFalse(len(r))
928 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
930 @patch('%s.object_post' % pithos_pkg, return_value=FR())
931 def test_del_object_meta(self, post):
932 metakey = '50m3m3t4k3y'
933 self.client.del_object_meta(obj, metakey)
934 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
936 @patch('%s.object_put' % pithos_pkg, return_value=FR())
937 def test_copy_object(self, put):
938 src_cont = 'src-c0nt41n3r'
940 dst_cont = 'dst-c0nt41n3r'
947 copy_from='/%s/%s' % (src_cont, src_obj),
952 self.client.copy_object(src_cont, src_obj, dst_cont)
953 self.assertEqual(put.mock_calls[-1], expected)
954 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
955 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
957 source_version='src-v3r510n',
958 source_account='src-4cc0un7',
960 content_type='c0n73n7Typ3',
962 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
963 for k, v in kwargs.items():
964 self.assertEqual(v, put.mock_calls[-1][2][k])
966 @patch('%s.object_put' % pithos_pkg, return_value=FR())
967 def test_move_object(self, put):
968 src_cont = 'src-c0nt41n3r'
970 dst_cont = 'dst-c0nt41n3r'
977 move_from='/%s/%s' % (src_cont, src_obj),
982 self.client.move_object(src_cont, src_obj, dst_cont)
983 self.assertEqual(put.mock_calls[-1], expected)
984 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
985 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
987 source_version='src-v3r510n',
988 source_account='src-4cc0un7',
990 content_type='c0n73n7Typ3',
992 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
993 for k, v in kwargs.items():
994 self.assertEqual(v, put.mock_calls[-1][2][k])
996 # Pithos+ only methods
998 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
999 def test_purge_container(self, CD):
1000 self.client.purge_container()
1001 self.assertTrue('until' in CD.mock_calls[-1][2])
1002 cont = self.client.container
1003 self.client.purge_container('another-container')
1004 self.assertEqual(self.client.container, cont)
1006 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1007 def test_upload_object_unchunked(self, put):
1009 tmpFile = self._create_temp_file(num_of_blocks)
1012 data=num_of_blocks * 4 * 1024 * 1024,
1014 content_encoding='some content_encoding',
1015 content_type='some content-type',
1016 content_disposition='some content_disposition',
1018 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1019 self.client.upload_object_unchunked(obj, tmpFile)
1020 self.assertEqual(put.mock_calls[-1][1], (obj,))
1022 sorted(put.mock_calls[-1][2].keys()),
1023 sorted(expected.keys()))
1024 kwargs = dict(expected)
1025 kwargs.pop('success')
1026 kwargs['size'] = kwargs.pop('data')
1027 kwargs['sharing'] = kwargs.pop('permissions')
1029 self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1030 pmc = put.mock_calls[-1][2]
1031 for k, v in expected.items():
1033 self.assertEqual(len(pmc[k]), v)
1035 self.assertEqual(pmc[k], v)
1038 self.client.upload_object_unchunked,
1039 obj, tmpFile, withHashFile=True)
1041 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1042 def test_create_object_by_manifestation(self, put):
1043 manifest = '%s/%s' % (self.client.container, obj)
1046 content_encoding='some content_encoding',
1047 content_type='some content-type',
1048 content_disposition='some content_disposition',
1050 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1051 self.client.create_object_by_manifestation(obj)
1052 expected = dict(content_length=0, manifest=manifest)
1054 expected['permissions' if k == 'sharing' else k] = None
1055 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1056 self.client.create_object_by_manifestation(obj, **kwargs)
1057 expected.update(kwargs)
1058 expected['permissions'] = expected.pop('sharing')
1059 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1061 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1062 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1063 def test_download_object(self, GET, GOH):
1065 tmpFile = self._create_temp_file(num_of_blocks)
1066 FR.content = tmpFile.read(4 * 1024 * 1024)
1067 tmpFile = self._create_temp_file(num_of_blocks)
1068 num_of_blocks = len(object_hashmap['hashes'])
1073 if_match='if and only if',
1074 if_none_match='if and only not',
1075 if_modified_since='what if not?',
1076 if_unmodified_since='this happens if not!',
1077 async_headers=dict(Range='bytes=0-88888888'))
1079 self.client.download_object(obj, tmpFile)
1080 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1081 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1082 for k, v in kwargs.items():
1083 if k == 'async_headers':
1084 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1085 elif k in ('resume', 'range_str'):
1088 self.assertEqual(GET.mock_calls[-1][2][k], None)
1090 # Check ranges are consecutive
1093 for c in GET.mock_calls:
1094 rng_str = c[2]['async_headers']['Range']
1095 (start, rng_str) = rng_str.split('=')
1096 (start, end) = rng_str.split('-')
1097 starts.append(start)
1100 for i, start in enumerate(sorted(starts)):
1102 int(ends[i - 1]) == int(start) - 1
1104 # With progress bars
1106 from progress.bar import ShadyBar
1107 dl_bar = ShadyBar('Mock dl')
1114 for i in dl_bar.iter(range(n)):
1119 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1120 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1123 kwargs.pop('async_headers')
1124 kwargs.pop('resume')
1125 self.client.download_object(obj, tmpFile, **kwargs)
1126 for k, v in kwargs.items():
1127 if k == 'range_str':
1129 GET.mock_calls[-1][2]['data_range'],
1132 self.assertEqual(GET.mock_calls[-1][2][k], v)
1134 # ALl options on no tty
1140 tmpFile.isatty = foo
1141 self.client.download_object(obj, tmpFile, **kwargs)
1142 for k, v in kwargs.items():
1143 if k == 'range_str':
1144 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1146 self.assertEqual(GET.mock_calls[-1][2][k], v)
1148 def test_get_object_hashmap(self):
1149 FR.json = object_hashmap
1150 for empty in (304, 412):
1152 PithosClient, 'object_get',
1153 side_effect=ClientError('Empty', status=empty)):
1154 r = self.client.get_object_hashmap(obj)
1155 self.assertEqual(r, {})
1161 if_etag_not_match=None,
1162 if_modified_since=None,
1163 if_unmodified_since=None)
1165 version='s0m3v3r51on',
1166 if_match='if match',
1167 if_none_match='if non match',
1168 if_modified_since='some date here',
1169 if_unmodified_since='some date here',
1172 PithosClient, 'object_get',
1173 return_value=FR()) as get:
1174 r = self.client.get_object_hashmap(obj)
1175 self.assertEqual(r, object_hashmap)
1176 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1177 r = self.client.get_object_hashmap(obj, **kwargs)
1178 exp_args['if_etag_match'] = kwargs.pop('if_match')
1179 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1180 exp_args.update(kwargs)
1181 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1183 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1184 def test_set_account_group(self, post):
1185 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1186 self.client.set_account_group(group, usernames)
1187 post.assert_called_once_with(update=True, groups={group: usernames})
1189 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1190 def test_del_account_group(self, post):
1191 group = 'aU53rGr0up'
1192 self.client.del_account_group(group)
1193 post.assert_called_once_with(update=True, groups={group: []})
1195 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1196 def test_get_account_quota(self, GAI):
1197 key = 'x-account-policy-quota'
1198 r = self.client.get_account_quota()
1199 GAI.assert_called_once_with()
1200 self.assertEqual(r[key], account_info[key])
1202 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1203 def test_get_account_versioning(self, GAI):
1204 key = 'x-account-policy-versioning'
1205 r = self.client.get_account_versioning()
1206 GAI.assert_called_once_with()
1207 self.assertEqual(r[key], account_info[key])
1209 def test_get_account_meta(self):
1210 key = 'x-account-meta-'
1212 PithosClient, 'get_account_info',
1213 return_value=account_info):
1214 r = self.client.get_account_meta()
1215 keys = [k for k in r if k.startswith(key)]
1216 self.assertFalse(keys)
1217 acc_info = dict(account_info)
1218 acc_info['%sk1' % key] = 'v1'
1219 acc_info['%sk2' % key] = 'v2'
1220 acc_info['%sk3' % key] = 'v3'
1222 PithosClient, 'get_account_info',
1223 return_value=acc_info):
1224 r = self.client.get_account_meta()
1225 for k in [k for k in acc_info if k.startswith(key)]:
1226 self.assertEqual(r[k], acc_info[k])
1228 def test_get_account_group(self):
1229 key = 'x-account-group-'
1231 PithosClient, 'get_account_info',
1232 return_value=account_info):
1233 r = self.client.get_account_group()
1234 keys = [k for k in r if k.startswith(key)]
1235 self.assertFalse(keys)
1236 acc_info = dict(account_info)
1237 acc_info['%sk1' % key] = 'g1'
1238 acc_info['%sk2' % key] = 'g2'
1239 acc_info['%sk3' % key] = 'g3'
1241 PithosClient, 'get_account_info',
1242 return_value=acc_info):
1243 r = self.client.get_account_group()
1244 for k in [k for k in acc_info if k.startswith(key)]:
1245 self.assertEqual(r[k], acc_info[k])
1247 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1248 def test_set_account_meta(self, post):
1249 metas = dict(k1='v1', k2='v2', k3='v3')
1250 self.client.set_account_meta(metas)
1251 post.assert_called_once_with(update=True, metadata=metas)
1253 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1254 def test_set_account_quota(self, post):
1256 self.client.set_account_quota(qu)
1257 post.assert_called_once_with(update=True, quota=qu)
1259 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1260 def test_set_account_versioning(self, post):
1261 vrs = 'n3wV3r51on1ngTyp3'
1262 self.client.set_account_versioning(vrs)
1263 post.assert_called_once_with(update=True, versioning=vrs)
1265 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1266 def test_del_container(self, delete):
1268 dict(delimiter=None, until=None),
1269 dict(delimiter='X', until='50m3d473')):
1270 self.client.del_container(**kwarg)
1271 expected = dict(kwarg)
1272 expected['success'] = (204, 404, 409)
1273 self.assertEqual(delete.mock_calls[-1], call(**expected))
1274 for status_code in (404, 409):
1275 FR.status_code = status_code
1276 self.assertRaises(ClientError, self.client.del_container)
1278 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1279 def test_get_container_versioning(self, GCI):
1280 key = 'x-container-policy-versioning'
1282 bu_cnt = self.client.container
1283 for container in (None, cont):
1284 r = self.client.get_container_versioning(container=container)
1285 self.assertEqual(r[key], container_info[key])
1286 self.assertEqual(GCI.mock_calls[-1], call())
1287 self.assertEqual(bu_cnt, self.client.container)
1289 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1290 def test_get_container_quota(self, GCI):
1291 key = 'x-container-policy-quota'
1293 bu_cnt = self.client.container
1294 for container in (None, cont):
1295 r = self.client.get_container_quota(container=container)
1296 self.assertEqual(r[key], container_info[key])
1297 self.assertEqual(GCI.mock_calls[-1], call())
1298 self.assertEqual(bu_cnt, self.client.container)
1300 def test_get_container_meta(self):
1301 somedate = '50m3d473'
1302 key = 'x-container-meta'
1303 metaval = '50m3m374v41'
1304 container_plus = dict(container_info)
1305 container_plus[key] = metaval
1306 for ret in ((container_info, {}), (container_plus, {key: metaval})):
1309 'get_container_info',
1310 return_value=ret[0]) as GCI:
1311 for until in (None, somedate):
1312 r = self.client.get_container_meta(until=until)
1313 self.assertEqual(r, ret[1])
1314 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1316 def test_get_container_object_meta(self):
1317 somedate = '50m3d473'
1318 key = 'x-container-object-meta'
1319 metaval = '50m3m374v41'
1320 container_plus = dict(container_info)
1321 container_plus[key] = metaval
1323 (container_info, {key: ''}),
1324 (container_plus, {key: metaval})):
1327 'get_container_info',
1328 return_value=ret[0]) as GCI:
1329 for until in (None, somedate):
1330 r = self.client.get_container_object_meta(until=until)
1331 self.assertEqual(r, ret[1])
1332 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1334 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1335 def test_set_container_meta(self, post):
1336 metas = dict(k1='v1', k2='v2', k3='v3')
1337 self.client.set_container_meta(metas)
1338 post.assert_called_once_with(update=True, metadata=metas)
1340 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1341 def test_del_container_meta(self, AP):
1342 self.client.del_container_meta('somekey')
1343 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1345 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1346 def test_set_container_quota(self, post):
1348 self.client.set_container_quota(qu)
1349 post.assert_called_once_with(update=True, quota=qu)
1351 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1352 def test_set_container_versioning(self, post):
1353 vrs = 'n3wV3r51on1ngTyp3'
1354 self.client.set_container_versioning(vrs)
1355 post.assert_called_once_with(update=True, versioning=vrs)
1357 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1358 def test_del_object(self, delete):
1360 dict(delimiter=None, until=None),
1361 dict(delimiter='X', until='50m3d473')):
1362 self.client.del_object(obj, **kwarg)
1363 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1365 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1366 def test_set_object_meta(self, post):
1367 metas = dict(k1='v1', k2='v2', k3='v3')
1370 self.client.set_object_meta,
1371 obj, 'Non dict arg')
1372 self.client.set_object_meta(obj, metas)
1373 post.assert_called_once_with(obj, update=True, metadata=metas)
1375 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1376 def test_publish_object(self, post):
1377 oinfo = dict(object_info)
1379 oinfo['x-object-public'] = val
1381 PithosClient, 'get_object_info',
1382 return_value=oinfo) as GOF:
1383 r = self.client.publish_object(obj)
1385 post.mock_calls[-1],
1386 call(obj, public=True, update=True))
1387 self.assertEqual(GOF.mock_calls[-1], call(obj))
1388 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1390 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1391 def test_unpublish_object(self, post):
1392 self.client.unpublish_object(obj)
1393 post.assert_called_once_with(obj, public=False, update=True)
1395 def test_get_object_sharing(self):
1396 info = dict(object_info)
1397 expected = dict(read='u1,g1,u2', write='u1')
1398 info['x-object-sharing'] = '; '.join(
1399 ['%s=%s' % (k, v) for k, v in expected.items()])
1401 PithosClient, 'get_object_info',
1402 return_value=info) as GOF:
1403 r = self.client.get_object_sharing(obj)
1404 self.assertEqual(GOF.mock_calls[-1], call(obj))
1405 self.assert_dicts_are_equal(r, expected)
1406 info['x-object-sharing'] = '//'.join(
1407 ['%s=%s' % (k, v) for k, v in expected.items()])
1410 self.client.get_object_sharing,
1412 info['x-object-sharing'] = '; '.join(
1413 ['%s:%s' % (k, v) for k, v in expected.items()])
1416 self.client.get_object_sharing,
1418 info['x-object-sharing'] = 'read=%s' % expected['read']
1419 r = self.client.get_object_sharing(obj)
1420 expected.pop('write')
1421 self.assert_dicts_are_equal(r, expected)
1423 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1424 def test_set_object_sharing(self, OP):
1425 read_perms = ['u1', 'g1', 'u2', 'g2']
1426 write_perms = ['u1', 'g1']
1428 dict(read_permition=read_perms, write_permition=write_perms),
1429 dict(read_permition=read_perms),
1430 dict(write_permition=write_perms),
1432 self.client.set_object_sharing(obj, **kwargs)
1433 kwargs['read'] = kwargs.pop('read_permition', '')
1434 kwargs['write'] = kwargs.pop('write_permition', '')
1437 call(obj, update=True, permissions=kwargs))
1439 @patch('%s.set_object_sharing' % pithos_pkg)
1440 def test_del_object_sharing(self, SOS):
1441 self.client.del_object_sharing(obj)
1442 SOS.assert_called_once_with(obj)
1444 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1445 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1446 def test_append_object(self, post, GCI):
1448 tmpFile = self._create_temp_file(num_of_blocks)
1450 file_size = tmpFile.tell()
1451 for turn in range(2):
1455 from progress.bar import ShadyBar
1456 apn_bar = ShadyBar('Mock append')
1463 for i in apn_bar.iter(range(n)):
1470 self.client.append_object(
1472 upload_cb=append_gen if turn else None)
1473 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1474 (args, kwargs) = post.mock_calls[-1][1:3]
1475 self.assertEqual(args, (obj,))
1476 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1477 fsize = num_of_blocks * int(kwargs['content_length'])
1478 self.assertEqual(fsize, file_size)
1479 self.assertEqual(kwargs['content_range'], 'bytes */*')
1480 exp = 'application/octet-stream'
1481 self.assertEqual(kwargs['content_type'], exp)
1482 self.assertEqual(kwargs['update'], True)
1484 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1485 def test_truncate_object(self, post):
1487 self.client.truncate_object(obj, upto_bytes)
1488 post.assert_called_once_with(
1491 object_bytes=upto_bytes,
1492 content_range='bytes 0-%s/*' % upto_bytes,
1493 content_type='application/octet-stream',
1494 source_object='/%s/%s' % (self.client.container, obj))
1496 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1497 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1498 def test_overwrite_object(self, post, GCI):
1500 tmpFile = self._create_temp_file(num_of_blocks)
1502 file_size = tmpFile.tell()
1503 info = dict(object_info)
1504 info['content-length'] = file_size
1505 block_size = container_info['x-container-block-size']
1507 PithosClient, 'get_object_info',
1508 return_value=info) as GOI:
1511 (file_size + 1, file_size + 2)):
1515 self.client.overwrite_object,
1516 obj, start, end, tmpFile)
1517 for start, end in ((0, 144), (144, 233), (233, file_size)):
1520 exp_size = end - start + 1
1521 if not start or exp_size > block_size:
1523 from progress.bar import ShadyBar
1524 owr_bar = ShadyBar('Mock append')
1531 for i in owr_bar.iter(range(n)):
1535 if exp_size > block_size:
1536 exp_size = exp_size % block_size or block_size
1538 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1539 self.assertEqual(GOI.mock_calls[-1], call(obj))
1540 self.assertEqual(GCI.mock_calls[-1], call())
1541 (args, kwargs) = post.mock_calls[-1][1:3]
1542 self.assertEqual(args, (obj,))
1543 self.assertEqual(len(kwargs['data']), exp_size)
1544 self.assertEqual(kwargs['content_length'], exp_size)
1545 self.assertEqual(kwargs['update'], True)
1546 exp = 'application/octet-stream'
1547 self.assertEqual(kwargs['content_type'], exp)
1549 @patch('%s.set_param' % pithos_pkg)
1550 @patch('%s.get' % pithos_pkg, return_value=FR())
1551 def test_get_sharing_accounts(self, get, SP):
1555 dict(limit='50m3-11m17'),
1557 dict(limit='50m3-11m17', marker='X')):
1558 r = self.client.get_sharing_accounts(**kws)
1559 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1560 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1561 limit, marker = kws.get('limit', None), kws.get('marker', None)
1562 self.assertEqual(SP.mock_calls[-2], call(
1564 iff=limit is not None))
1565 self.assertEqual(SP.mock_calls[-1], call(
1567 iff=marker is not None))
1568 for i in range(len(r)):
1569 self.assert_dicts_are_equal(r[i], sharers[i])
1571 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1572 def test_get_object_versionlist(self, get):
1573 info = dict(object_info)
1574 info['versions'] = ['v1', 'v2']
1576 r = self.client.get_object_versionlist(obj)
1577 get.assert_called_once_with(obj, format='json', version='list')
1578 self.assertEqual(r, info['versions'])
1580 if __name__ == '__main__':
1581 from sys import argv
1582 from kamaki.clients.test import runTestCase
1584 if not argv[1:] or argv[1] == 'Pithos':
1586 runTestCase(Pithos, 'Pithos Client', argv[2:])
1587 if not argv[1:] or argv[1] == 'PithosRest':
1589 runTestCase(PithosRest, 'PithosRest Client', argv[2:])
1591 print('TestCase %s not found' % argv[1])