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)),
718 @patch('%s.set_param' % rest_pkg)
719 @patch('%s.delete' % rest_pkg, return_value=FR())
720 def test_object_delete(self, delete, SP):
725 (dict(), dict(success=400), dict(k='v', v='k'))):
726 args, kwargs = pm[-2:]
728 self.client.object_delete(
733 self.assertEqual(SP.mock_calls[-2:], [
734 call('until', until, iff=until),
735 call('delimiter', dlm, iff=dlm)])
736 acc, cont = self.client.account, self.client.container
737 self.assertEqual(delete.mock_calls[-1], call(
738 '/%s/%s/%s' % (acc, cont, obj),
740 success=kwargs.pop('success', 204),
744 class Pithos(TestCase):
748 def _create_temp_file(self, num_of_blocks):
749 self.files.append(NamedTemporaryFile())
750 tmpFile = self.files[-1]
751 file_size = num_of_blocks * 4 * 1024 * 1024
752 print('\n\tCreate tmp file')
753 tmpFile.write(urandom(file_size))
759 def assert_dicts_are_equal(self, d1, d2):
760 for k, v in d1.items():
761 self.assertTrue(k in d2)
762 if isinstance(v, dict):
763 self.assert_dicts_are_equal(v, d2[k])
765 self.assertEqual(unicode(v), unicode(d2[k]))
768 self.url = 'https://www.example.com/pithos'
769 self.token = 'p17h0570k3n'
770 self.client = PithosClient(self.url, self.token)
771 self.client.account = user_id
772 self.client.container = 'c0nt@1n3r_i'
782 # Pithos+ methods that extend storage API
784 @patch('%s.account_head' % pithos_pkg, return_value=FR())
785 def test_get_account_info(self, AH):
786 FR.headers = account_info
787 for until in (None, 'un71L-d473'):
788 r = self.client.get_account_info(until=until)
789 self.assert_dicts_are_equal(r, account_info)
790 self.assertEqual(AH.mock_calls[-1], call(until=until))
792 self.assertRaises(ClientError, self.client.get_account_info)
794 @patch('%s.account_post' % pithos_pkg, return_value=FR())
795 def test_del_account_meta(self, AP):
796 keys = ['k1', 'k2', 'k3']
798 self.client.del_account_meta(key)
801 call(update=True, metadata={key: ''}))
803 @patch('%s.container_head' % pithos_pkg, return_value=FR())
804 def test_get_container_info(self, CH):
805 FR.headers = container_info
806 r = self.client.get_container_info()
807 self.assert_dicts_are_equal(r, container_info)
809 r = self.client.get_container_info(until=u)
810 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
812 @patch('%s.account_get' % pithos_pkg, return_value=FR())
813 def test_list_containers(self, get):
814 FR.json = container_list
815 r = self.client.list_containers()
816 get.assert_called_once_with()
817 for i in range(len(r)):
818 self.assert_dicts_are_equal(r[i], container_list[i])
820 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
821 @patch('%s.container_post' % pithos_pkg, return_value=FR())
822 @patch('%s.object_put' % pithos_pkg, return_value=FR())
823 def test_upload_object(self, OP, CP, GCI):
825 tmpFile = self._create_temp_file(num_of_blocks)
828 self.client.upload_object(obj, tmpFile)
829 self.assertEqual(GCI.mock_calls[-1], call())
830 [call1, call2] = OP.mock_calls
832 (args1, kwargs1) = call1[1:3]
833 (args2, kwargs2) = call2[1:3]
834 self.assertEqual(args1, (obj,))
840 hashes=['s0m3h@5h'] * num_of_blocks,
841 bytes=num_of_blocks * 4 * 1024 * 1024),
843 content_encoding=None,
844 content_type='application/octet-stream',
845 content_disposition=None,
848 for k, v in expected1.items():
850 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
851 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
853 self.assertEqual(v, kwargs1[k])
855 (args2, kwargs2) = call2[1:3]
856 self.assertEqual(args2, (obj,))
859 hashes=['s0m3h@5h'] * num_of_blocks,
860 bytes=num_of_blocks * 4 * 1024 * 1024),
861 content_type='application/octet-stream',
865 for k, v in expected2.items():
867 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
868 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
870 self.assertEqual(v, kwargs2[k])
876 from progress.bar import ShadyBar
877 blck_bar = ShadyBar('Mock blck calc.')
878 upld_bar = ShadyBar('Mock uplds')
883 if blck_bar and upld_bar:
886 for i in blck_bar.iter(range(n)):
891 for i in upld_bar.iter(range(n)):
896 self.client.upload_object(
898 hash_cb=blck_gen, upload_cb=upld_gen)
900 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
901 self.assertEqual(OP.mock_calls[i], c)
906 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
907 self.client.upload_object(obj, tmpFile,
908 content_type=ctype, sharing=sharing)
909 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
910 self.assert_dicts_are_equal(
911 OP.mock_calls[-2][2]['permissions'],
919 content_disposition=ctype + 'd15p051710n',
921 content_encoding='802.11')
922 self.client.upload_object(obj, tmpFile, **kwargs)
923 for arg, val in kwargs.items():
924 self.assertEqual(OP.mock_calls[-2][2][arg], val)
926 def test_get_object_info(self):
927 FR.headers = object_info
930 PithosClient, 'object_head',
931 return_value=FR()) as head:
932 r = self.client.get_object_info(obj)
933 self.assertEqual(r, object_info)
934 r = self.client.get_object_info(obj, version=version)
935 self.assertEqual(head.mock_calls, [
936 call(obj, version=None),
937 call(obj, version=version)])
939 PithosClient, 'object_head',
940 side_effect=ClientError('Obj not found', 404)):
943 self.client.get_object_info,
944 obj, version=version)
946 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
947 def test_get_object_meta(self, GOI):
948 for version in (None, 'v3r510n'):
949 r = self.client.get_object_meta(obj, version)
950 for k in [k for k in object_info if k.startswith('x-object-meta')]:
951 self.assertEqual(r.pop(k), object_info[k])
952 self.assertFalse(len(r))
953 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
955 @patch('%s.object_post' % pithos_pkg, return_value=FR())
956 def test_del_object_meta(self, post):
957 metakey = '50m3m3t4k3y'
958 self.client.del_object_meta(obj, metakey)
959 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
961 @patch('%s.object_put' % pithos_pkg, return_value=FR())
962 def test_copy_object(self, put):
963 src_cont = 'src-c0nt41n3r'
965 dst_cont = 'dst-c0nt41n3r'
972 copy_from='/%s/%s' % (src_cont, src_obj),
977 self.client.copy_object(src_cont, src_obj, dst_cont)
978 self.assertEqual(put.mock_calls[-1], expected)
979 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
980 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
982 source_version='src-v3r510n',
983 source_account='src-4cc0un7',
985 content_type='c0n73n7Typ3',
987 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
988 for k, v in kwargs.items():
989 self.assertEqual(v, put.mock_calls[-1][2][k])
991 @patch('%s.object_put' % pithos_pkg, return_value=FR())
992 def test_move_object(self, put):
993 src_cont = 'src-c0nt41n3r'
995 dst_cont = 'dst-c0nt41n3r'
1000 source_account=None,
1002 move_from='/%s/%s' % (src_cont, src_obj),
1005 source_version=None,
1007 self.client.move_object(src_cont, src_obj, dst_cont)
1008 self.assertEqual(put.mock_calls[-1], expected)
1009 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1010 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1012 source_version='src-v3r510n',
1013 source_account='src-4cc0un7',
1015 content_type='c0n73n7Typ3',
1017 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1018 for k, v in kwargs.items():
1019 self.assertEqual(v, put.mock_calls[-1][2][k])
1021 # Pithos+ only methods
1023 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1024 def test_purge_container(self, CD):
1025 self.client.purge_container()
1026 self.assertTrue('until' in CD.mock_calls[-1][2])
1027 cont = self.client.container
1028 self.client.purge_container('another-container')
1029 self.assertEqual(self.client.container, cont)
1031 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1032 def test_upload_object_unchunked(self, put):
1034 tmpFile = self._create_temp_file(num_of_blocks)
1037 data=num_of_blocks * 4 * 1024 * 1024,
1039 content_encoding='some content_encoding',
1040 content_type='some content-type',
1041 content_disposition='some content_disposition',
1043 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1044 self.client.upload_object_unchunked(obj, tmpFile)
1045 self.assertEqual(put.mock_calls[-1][1], (obj,))
1047 sorted(put.mock_calls[-1][2].keys()),
1048 sorted(expected.keys()))
1049 kwargs = dict(expected)
1050 kwargs.pop('success')
1051 kwargs['size'] = kwargs.pop('data')
1052 kwargs['sharing'] = kwargs.pop('permissions')
1054 self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1055 pmc = put.mock_calls[-1][2]
1056 for k, v in expected.items():
1058 self.assertEqual(len(pmc[k]), v)
1060 self.assertEqual(pmc[k], v)
1063 self.client.upload_object_unchunked,
1064 obj, tmpFile, withHashFile=True)
1066 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1067 def test_create_object_by_manifestation(self, put):
1068 manifest = '%s/%s' % (self.client.container, obj)
1071 content_encoding='some content_encoding',
1072 content_type='some content-type',
1073 content_disposition='some content_disposition',
1075 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1076 self.client.create_object_by_manifestation(obj)
1077 expected = dict(content_length=0, manifest=manifest)
1079 expected['permissions' if k == 'sharing' else k] = None
1080 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1081 self.client.create_object_by_manifestation(obj, **kwargs)
1082 expected.update(kwargs)
1083 expected['permissions'] = expected.pop('sharing')
1084 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1086 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1087 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1088 def test_download_object(self, GET, GOH):
1090 tmpFile = self._create_temp_file(num_of_blocks)
1091 FR.content = tmpFile.read(4 * 1024 * 1024)
1092 tmpFile = self._create_temp_file(num_of_blocks)
1093 num_of_blocks = len(object_hashmap['hashes'])
1098 if_match='if and only if',
1099 if_none_match='if and only not',
1100 if_modified_since='what if not?',
1101 if_unmodified_since='this happens if not!',
1102 async_headers=dict(Range='bytes=0-88888888'))
1104 self.client.download_object(obj, tmpFile)
1105 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1106 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1107 for k, v in kwargs.items():
1108 if k == 'async_headers':
1109 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1110 elif k in ('resume', 'range_str'):
1113 self.assertEqual(GET.mock_calls[-1][2][k], None)
1115 # Check ranges are consecutive
1118 for c in GET.mock_calls:
1119 rng_str = c[2]['async_headers']['Range']
1120 (start, rng_str) = rng_str.split('=')
1121 (start, end) = rng_str.split('-')
1122 starts.append(start)
1125 for i, start in enumerate(sorted(starts)):
1127 int(ends[i - 1]) == int(start) - 1
1129 # With progress bars
1131 from progress.bar import ShadyBar
1132 dl_bar = ShadyBar('Mock dl')
1139 for i in dl_bar.iter(range(n)):
1144 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1145 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1148 kwargs.pop('async_headers')
1149 kwargs.pop('resume')
1150 self.client.download_object(obj, tmpFile, **kwargs)
1151 for k, v in kwargs.items():
1152 if k == 'range_str':
1154 GET.mock_calls[-1][2]['data_range'],
1157 self.assertEqual(GET.mock_calls[-1][2][k], v)
1159 # ALl options on no tty
1165 tmpFile.isatty = foo
1166 self.client.download_object(obj, tmpFile, **kwargs)
1167 for k, v in kwargs.items():
1168 if k == 'range_str':
1169 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1171 self.assertEqual(GET.mock_calls[-1][2][k], v)
1173 def test_get_object_hashmap(self):
1174 FR.json = object_hashmap
1175 for empty in (304, 412):
1177 PithosClient, 'object_get',
1178 side_effect=ClientError('Empty', status=empty)):
1179 r = self.client.get_object_hashmap(obj)
1180 self.assertEqual(r, {})
1186 if_etag_not_match=None,
1187 if_modified_since=None,
1188 if_unmodified_since=None)
1190 version='s0m3v3r51on',
1191 if_match='if match',
1192 if_none_match='if non match',
1193 if_modified_since='some date here',
1194 if_unmodified_since='some date here',
1197 PithosClient, 'object_get',
1198 return_value=FR()) as get:
1199 r = self.client.get_object_hashmap(obj)
1200 self.assertEqual(r, object_hashmap)
1201 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1202 r = self.client.get_object_hashmap(obj, **kwargs)
1203 exp_args['if_etag_match'] = kwargs.pop('if_match')
1204 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1205 exp_args.update(kwargs)
1206 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1208 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1209 def test_set_account_group(self, post):
1210 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1211 self.client.set_account_group(group, usernames)
1212 post.assert_called_once_with(update=True, groups={group: usernames})
1214 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1215 def test_del_account_group(self, post):
1216 group = 'aU53rGr0up'
1217 self.client.del_account_group(group)
1218 post.assert_called_once_with(update=True, groups={group: []})
1220 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1221 def test_get_account_quota(self, GAI):
1222 key = 'x-account-policy-quota'
1223 r = self.client.get_account_quota()
1224 GAI.assert_called_once_with()
1225 self.assertEqual(r[key], account_info[key])
1227 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1228 def test_get_account_versioning(self, GAI):
1229 key = 'x-account-policy-versioning'
1230 r = self.client.get_account_versioning()
1231 GAI.assert_called_once_with()
1232 self.assertEqual(r[key], account_info[key])
1234 def test_get_account_meta(self):
1235 key = 'x-account-meta-'
1237 PithosClient, 'get_account_info',
1238 return_value=account_info):
1239 r = self.client.get_account_meta()
1240 keys = [k for k in r if k.startswith(key)]
1241 self.assertFalse(keys)
1242 acc_info = dict(account_info)
1243 acc_info['%sk1' % key] = 'v1'
1244 acc_info['%sk2' % key] = 'v2'
1245 acc_info['%sk3' % key] = 'v3'
1247 PithosClient, 'get_account_info',
1248 return_value=acc_info):
1249 r = self.client.get_account_meta()
1250 for k in [k for k in acc_info if k.startswith(key)]:
1251 self.assertEqual(r[k], acc_info[k])
1253 def test_get_account_group(self):
1254 key = 'x-account-group-'
1256 PithosClient, 'get_account_info',
1257 return_value=account_info):
1258 r = self.client.get_account_group()
1259 keys = [k for k in r if k.startswith(key)]
1260 self.assertFalse(keys)
1261 acc_info = dict(account_info)
1262 acc_info['%sk1' % key] = 'g1'
1263 acc_info['%sk2' % key] = 'g2'
1264 acc_info['%sk3' % key] = 'g3'
1266 PithosClient, 'get_account_info',
1267 return_value=acc_info):
1268 r = self.client.get_account_group()
1269 for k in [k for k in acc_info if k.startswith(key)]:
1270 self.assertEqual(r[k], acc_info[k])
1272 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1273 def test_set_account_meta(self, post):
1274 metas = dict(k1='v1', k2='v2', k3='v3')
1275 self.client.set_account_meta(metas)
1276 post.assert_called_once_with(update=True, metadata=metas)
1278 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1279 def test_set_account_quota(self, post):
1281 self.client.set_account_quota(qu)
1282 post.assert_called_once_with(update=True, quota=qu)
1284 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1285 def test_set_account_versioning(self, post):
1286 vrs = 'n3wV3r51on1ngTyp3'
1287 self.client.set_account_versioning(vrs)
1288 post.assert_called_once_with(update=True, versioning=vrs)
1290 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1291 def test_del_container(self, delete):
1293 dict(delimiter=None, until=None),
1294 dict(delimiter='X', until='50m3d473')):
1295 self.client.del_container(**kwarg)
1296 expected = dict(kwarg)
1297 expected['success'] = (204, 404, 409)
1298 self.assertEqual(delete.mock_calls[-1], call(**expected))
1299 for status_code in (404, 409):
1300 FR.status_code = status_code
1301 self.assertRaises(ClientError, self.client.del_container)
1303 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1304 def test_get_container_versioning(self, GCI):
1305 key = 'x-container-policy-versioning'
1307 bu_cnt = self.client.container
1308 for container in (None, cont):
1309 r = self.client.get_container_versioning(container=container)
1310 self.assertEqual(r[key], container_info[key])
1311 self.assertEqual(GCI.mock_calls[-1], call())
1312 self.assertEqual(bu_cnt, self.client.container)
1314 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1315 def test_get_container_quota(self, GCI):
1316 key = 'x-container-policy-quota'
1318 bu_cnt = self.client.container
1319 for container in (None, cont):
1320 r = self.client.get_container_quota(container=container)
1321 self.assertEqual(r[key], container_info[key])
1322 self.assertEqual(GCI.mock_calls[-1], call())
1323 self.assertEqual(bu_cnt, self.client.container)
1325 def test_get_container_meta(self):
1326 somedate = '50m3d473'
1327 key = 'x-container-meta'
1328 metaval = '50m3m374v41'
1329 container_plus = dict(container_info)
1330 container_plus[key] = metaval
1331 for ret in ((container_info, {}), (container_plus, {key: metaval})):
1334 'get_container_info',
1335 return_value=ret[0]) as GCI:
1336 for until in (None, somedate):
1337 r = self.client.get_container_meta(until=until)
1338 self.assertEqual(r, ret[1])
1339 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1341 def test_get_container_object_meta(self):
1342 somedate = '50m3d473'
1343 key = 'x-container-object-meta'
1344 metaval = '50m3m374v41'
1345 container_plus = dict(container_info)
1346 container_plus[key] = metaval
1348 (container_info, {key: ''}),
1349 (container_plus, {key: metaval})):
1352 'get_container_info',
1353 return_value=ret[0]) as GCI:
1354 for until in (None, somedate):
1355 r = self.client.get_container_object_meta(until=until)
1356 self.assertEqual(r, ret[1])
1357 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1359 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1360 def test_set_container_meta(self, post):
1361 metas = dict(k1='v1', k2='v2', k3='v3')
1362 self.client.set_container_meta(metas)
1363 post.assert_called_once_with(update=True, metadata=metas)
1365 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1366 def test_del_container_meta(self, AP):
1367 self.client.del_container_meta('somekey')
1368 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1370 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1371 def test_set_container_quota(self, post):
1373 self.client.set_container_quota(qu)
1374 post.assert_called_once_with(update=True, quota=qu)
1376 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1377 def test_set_container_versioning(self, post):
1378 vrs = 'n3wV3r51on1ngTyp3'
1379 self.client.set_container_versioning(vrs)
1380 post.assert_called_once_with(update=True, versioning=vrs)
1382 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1383 def test_del_object(self, delete):
1385 dict(delimiter=None, until=None),
1386 dict(delimiter='X', until='50m3d473')):
1387 self.client.del_object(obj, **kwarg)
1388 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1390 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1391 def test_set_object_meta(self, post):
1392 metas = dict(k1='v1', k2='v2', k3='v3')
1395 self.client.set_object_meta,
1396 obj, 'Non dict arg')
1397 self.client.set_object_meta(obj, metas)
1398 post.assert_called_once_with(obj, update=True, metadata=metas)
1400 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1401 def test_publish_object(self, post):
1402 oinfo = dict(object_info)
1404 oinfo['x-object-public'] = val
1406 PithosClient, 'get_object_info',
1407 return_value=oinfo) as GOF:
1408 r = self.client.publish_object(obj)
1410 post.mock_calls[-1],
1411 call(obj, public=True, update=True))
1412 self.assertEqual(GOF.mock_calls[-1], call(obj))
1413 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1415 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1416 def test_unpublish_object(self, post):
1417 self.client.unpublish_object(obj)
1418 post.assert_called_once_with(obj, public=False, update=True)
1420 def test_get_object_sharing(self):
1421 info = dict(object_info)
1422 expected = dict(read='u1,g1,u2', write='u1')
1423 info['x-object-sharing'] = '; '.join(
1424 ['%s=%s' % (k, v) for k, v in expected.items()])
1426 PithosClient, 'get_object_info',
1427 return_value=info) as GOF:
1428 r = self.client.get_object_sharing(obj)
1429 self.assertEqual(GOF.mock_calls[-1], call(obj))
1430 self.assert_dicts_are_equal(r, expected)
1431 info['x-object-sharing'] = '//'.join(
1432 ['%s=%s' % (k, v) for k, v in expected.items()])
1435 self.client.get_object_sharing,
1437 info['x-object-sharing'] = '; '.join(
1438 ['%s:%s' % (k, v) for k, v in expected.items()])
1441 self.client.get_object_sharing,
1443 info['x-object-sharing'] = 'read=%s' % expected['read']
1444 r = self.client.get_object_sharing(obj)
1445 expected.pop('write')
1446 self.assert_dicts_are_equal(r, expected)
1448 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1449 def test_set_object_sharing(self, OP):
1450 read_perms = ['u1', 'g1', 'u2', 'g2']
1451 write_perms = ['u1', 'g1']
1453 dict(read_permition=read_perms, write_permition=write_perms),
1454 dict(read_permition=read_perms),
1455 dict(write_permition=write_perms),
1457 self.client.set_object_sharing(obj, **kwargs)
1458 kwargs['read'] = kwargs.pop('read_permition', '')
1459 kwargs['write'] = kwargs.pop('write_permition', '')
1462 call(obj, update=True, permissions=kwargs))
1464 @patch('%s.set_object_sharing' % pithos_pkg)
1465 def test_del_object_sharing(self, SOS):
1466 self.client.del_object_sharing(obj)
1467 SOS.assert_called_once_with(obj)
1469 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1470 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1471 def test_append_object(self, post, GCI):
1473 tmpFile = self._create_temp_file(num_of_blocks)
1475 file_size = tmpFile.tell()
1476 for turn in range(2):
1480 from progress.bar import ShadyBar
1481 apn_bar = ShadyBar('Mock append')
1488 for i in apn_bar.iter(range(n)):
1495 self.client.append_object(
1497 upload_cb=append_gen if turn else None)
1498 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1499 (args, kwargs) = post.mock_calls[-1][1:3]
1500 self.assertEqual(args, (obj,))
1501 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1502 fsize = num_of_blocks * int(kwargs['content_length'])
1503 self.assertEqual(fsize, file_size)
1504 self.assertEqual(kwargs['content_range'], 'bytes */*')
1505 exp = 'application/octet-stream'
1506 self.assertEqual(kwargs['content_type'], exp)
1507 self.assertEqual(kwargs['update'], True)
1509 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1510 def test_truncate_object(self, post):
1512 self.client.truncate_object(obj, upto_bytes)
1513 post.assert_called_once_with(
1516 object_bytes=upto_bytes,
1517 content_range='bytes 0-%s/*' % upto_bytes,
1518 content_type='application/octet-stream',
1519 source_object='/%s/%s' % (self.client.container, obj))
1521 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1522 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1523 def test_overwrite_object(self, post, GCI):
1525 tmpFile = self._create_temp_file(num_of_blocks)
1527 file_size = tmpFile.tell()
1528 info = dict(object_info)
1529 info['content-length'] = file_size
1530 block_size = container_info['x-container-block-size']
1532 PithosClient, 'get_object_info',
1533 return_value=info) as GOI:
1536 (file_size + 1, file_size + 2)):
1540 self.client.overwrite_object,
1541 obj, start, end, tmpFile)
1542 for start, end in ((0, 144), (144, 233), (233, file_size)):
1545 exp_size = end - start + 1
1546 if not start or exp_size > block_size:
1548 from progress.bar import ShadyBar
1549 owr_bar = ShadyBar('Mock append')
1556 for i in owr_bar.iter(range(n)):
1560 if exp_size > block_size:
1561 exp_size = exp_size % block_size or block_size
1563 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1564 self.assertEqual(GOI.mock_calls[-1], call(obj))
1565 self.assertEqual(GCI.mock_calls[-1], call())
1566 (args, kwargs) = post.mock_calls[-1][1:3]
1567 self.assertEqual(args, (obj,))
1568 self.assertEqual(len(kwargs['data']), exp_size)
1569 self.assertEqual(kwargs['content_length'], exp_size)
1570 self.assertEqual(kwargs['update'], True)
1571 exp = 'application/octet-stream'
1572 self.assertEqual(kwargs['content_type'], exp)
1574 @patch('%s.set_param' % pithos_pkg)
1575 @patch('%s.get' % pithos_pkg, return_value=FR())
1576 def test_get_sharing_accounts(self, get, SP):
1580 dict(limit='50m3-11m17'),
1582 dict(limit='50m3-11m17', marker='X')):
1583 r = self.client.get_sharing_accounts(**kws)
1584 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1585 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1586 limit, marker = kws.get('limit', None), kws.get('marker', None)
1587 self.assertEqual(SP.mock_calls[-2], call(
1589 iff=limit is not None))
1590 self.assertEqual(SP.mock_calls[-1], call(
1592 iff=marker is not None))
1593 for i in range(len(r)):
1594 self.assert_dicts_are_equal(r[i], sharers[i])
1596 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1597 def test_get_object_versionlist(self, get):
1598 info = dict(object_info)
1599 info['versions'] = ['v1', 'v2']
1601 r = self.client.get_object_versionlist(obj)
1602 get.assert_called_once_with(obj, format='json', version='list')
1603 self.assertEqual(r, info['versions'])
1605 if __name__ == '__main__':
1606 from sys import argv
1607 from kamaki.clients.test import runTestCase
1609 if not argv[1:] or argv[1] == 'Pithos':
1611 runTestCase(Pithos, 'Pithos Client', argv[2:])
1612 if not argv[1:] or argv[1] == 'PithosRest':
1614 runTestCase(PithosRest, 'PithosRest Client', argv[2:])
1616 print('TestCase %s not found' % argv[1])