1 # Copyright 2013 GRNET S.A. All rights reserved.
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
7 # 1. Redistributions of source code must retain the above
8 # copyright notice, this list of conditions and the following
11 # 2. Redistributions in binary form must reproduce the above
12 # copyright notice, this list of conditions and the following
13 # disclaimer in the documentation and/or other materials
14 # provided with the distribution.
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
34 from unittest import TestCase
35 from mock import patch, call
36 from tempfile import NamedTemporaryFile
37 from os import urandom
38 from itertools import product
39 from random import randint
42 from collections import OrderedDict
44 from kamaki.clients.utils.ordereddict import OrderedDict
46 from kamaki.clients import pithos, ClientError
49 rest_pkg = 'kamaki.clients.pithos.rest_api.PithosRestClient'
50 pithos_pkg = 'kamaki.clients.pithos.PithosClient'
52 user_id = 'ac0un7-1d-5tr1ng'
56 'content-language': 'en-us',
57 'content-type': 'text/html; charset=utf-8',
58 'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
59 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
60 'server': 'gunicorn/0.14.5',
61 'vary': 'Accept-Language',
62 'x-account-bytes-used': '751615526',
63 'x-account-container-count': 7,
64 'x-account-policy-quota': 53687091200,
65 'x-account-policy-versioning': 'auto'}
67 'content-language': 'en-us',
68 'content-type': 'text/html; charset=utf-8',
69 'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
70 'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
71 'server': 'gunicorn/0.14.5',
72 'vary': 'Accept-Language',
73 'x-container-block-hash': 'sha256',
74 'x-container-block-size': 4194304,
75 'x-container-bytes-used': 309528938,
76 'x-container-object-count': 14,
77 'x-container-object-meta': '',
78 'x-container-policy-quota': 53687091200,
79 'x-container-policy-versioning': 'auto'}
81 'content-language': 'en-us',
82 'content-length': 254965,
83 'content-type': 'application/octet-stream',
84 'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
86 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
87 'server': 'gunicorn/0.14.5',
88 'vary': 'Accept-Language',
89 'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
90 'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
91 'x-object-version': '525996',
92 'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
93 'x-object-meta-k1': 'v1',
94 'x-object-meta-k2': 'v2'}
98 last_modified="2013-02-27T11:56:09.893033+00:00",
101 x_container_policy=dict(quota="21474836480", versioning="auto")),
104 last_modified="2012-10-23T12:25:17.229187+00:00",
107 x_container_policy=dict(quota="21474836480", versioning="auto"))]
110 name="The_Secret_Garden.zip",
111 x_object_public="/public/wdp9p",
113 x_object_version_timestamp="1360237915.7027509",
114 x_object_uuid="s0m3uu1df0r0bj0n3",
115 last_modified="2013-02-07T11:51:55.702751+00:00",
116 content_type="application/octet-stream",
117 x_object_hash="0afdf29f71cd53126225c3f54ca",
118 x_object_version=17737,
119 x_object_modified_by=user_id),
121 name="The_Revealed_Garden.zip",
122 x_object_public="/public/wpd7p",
124 x_object_version_timestamp="13602915.7027509",
125 x_object_uuid="s0m3uu1df0r0bj70w",
126 last_modified="2013-02-07T11:51:55.702751+00:00",
127 content_type="application/octet-stream",
128 x_object_hash="0afdf29f71cd53126225c3f54ca",
129 x_object_version=17737,
130 x_object_modified_by=user_id)]
131 object_hashmap = dict(
132 block_hash="sha256", block_size=4194304, bytes=33554432,
134 "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
135 "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
136 "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
137 "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
138 "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
139 "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
140 "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
141 "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
143 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
144 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
145 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
149 """FR stands for Fake Response"""
157 class PithosRestClient(TestCase):
160 self.url = 'https://www.example.com/pithos'
161 self.token = 'p17h0570k3n'
162 self.client = pithos.PithosRestClient(self.url, self.token)
163 self.client.account = user_id
164 self.client.container = 'c0nt@1n3r_i'
171 @patch('%s.set_param' % rest_pkg)
172 @patch('%s.set_header' % rest_pkg)
173 @patch('%s.head' % rest_pkg, return_value=FR())
174 def test_account_head(self, head, SH, SP):
175 for params in product(
177 (None, '50m3-07h3r-d473'),
178 (None, 'y37-4n7h3r-d473'),
179 ((), ('someval',), ('v1', 'v2',)),
180 (dict(), dict(success=200), dict(k='v', v='k'))):
181 args, kwargs = params[-2], params[-1]
183 self.client.account_head(*(params + args), **kwargs)
185 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
186 IMS, IUS = params[1], params[2]
187 self.assertEqual(SH.mock_calls[-2:], [
188 call('If-Modified-Since', IMS),
189 call('If-Unmodified-Since', IUS)])
190 self.assertEqual(head.mock_calls[-1], call(
191 '/%s' % self.client.account,
193 success=kwargs.pop('success', 204),
196 @patch('%s.set_param' % rest_pkg)
197 @patch('%s.set_header' % rest_pkg)
198 @patch('%s.get' % rest_pkg, return_value=FR())
199 def test_account_get(self, get, SH, SP):
200 keys = ('limit', 'marker', 'format', 'shared', 'until')
201 for params in product(
207 (None, '50m3-07h3r-d473'),
208 (None, 'y37-4n7h3r-d473'),
209 ((), ('someval',), ('v1', 'v2',)),
210 (dict(), dict(success=200), dict(k='v', v='k'))):
211 args, kwargs = params[-2], params[-1]
213 self.client.account_get(*(params + args), **kwargs)
214 self.assertEqual(SP.mock_calls[-5:],
215 [call(keys[i], iff=X) if (
217 keys[i], X, iff=X) for i, X in enumerate(params[:5])])
218 IMS, IUS = params[5], params[6]
219 self.assertEqual(SH.mock_calls[-2:], [
220 call('If-Modified-Since', IMS),
221 call('If-Unmodified-Since', IUS)])
222 self.assertEqual(get.mock_calls[-1], call(
223 '/%s' % self.client.account,
225 success=kwargs.pop('success', (200, 204)),
228 @patch('%s.set_param' % rest_pkg)
229 @patch('%s.set_header' % rest_pkg)
230 @patch('%s.post' % rest_pkg, return_value=FR())
231 def test_account_post(self, post, SH, SP):
232 #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
235 ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
236 (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
238 (None, 'v3r510n1ng'),
239 ((), ('someval',), ('v1', 'v2',)),
240 (dict(), dict(success=200), dict(k='v', v='k'))):
241 args, kwargs = pm[-2:]
243 self.client.account_post(*(pm + args), **kwargs)
245 self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
249 call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
252 call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
254 call('X-Account-Policy-Quota', pm[3]),
255 call('X-Account-Policy-Versioning', pm[4])]
256 self.assertEqual(SH.mock_calls[- len(expected):], expected)
257 self.assertEqual(post.mock_calls[-1], call(
258 '/%s' % self.client.account,
260 success=kwargs.pop('success', 202),
263 @patch('%s.set_param' % rest_pkg)
264 @patch('%s.set_header' % rest_pkg)
265 @patch('%s.head' % rest_pkg, return_value=FR())
266 def test_container_head(self, head, SH, SP):
269 (None, '47h3r-d473'),
270 (None, 'y37-4n47h3r'),
272 (dict(), dict(success=200), dict(k='v', v='k'))):
273 args, kwargs = pm[-2:]
275 self.client.container_head(*(pm + args), **kwargs)
276 unt, ims, ius = pm[0:3]
277 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
278 self.assertEqual(SH.mock_calls[-2:], [
279 call('If-Modified-Since', ims),
280 call('If-Unmodified-Since', ius)])
281 self.assertEqual(head.mock_calls[-1], call(
282 '/%s/%s' % (self.client.account, self.client.container),
284 success=kwargs.pop('success', 204),
287 @patch('%s.set_param' % rest_pkg)
288 @patch('%s.set_header' % rest_pkg)
289 @patch('%s.get' % rest_pkg, return_value=FR())
290 def test_container_get(self, get, SH, SP):
294 (None, 'some/prefix'),
296 (None, '/some/path'),
297 ('json', 'some-format'),
298 ([], ['k1', 'k2', 'k3']),
300 (None, 'unt1l-d473'),
301 (None, 'y37-4n47h3r'),
302 (None, '4n47h3r-d473'),
304 (dict(), dict(success=400), dict(k='v', v='k'))):
305 args, kwargs = pm[-2:]
307 self.client.container_get(*(pm + args), **kwargs)
308 lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
309 exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
310 exp += [call('path', path)] if path else [
311 call('prefix', prfx, iff=prfx),
312 call('delimiter', dlm, iff=dlm)]
313 exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
315 exp += [call('meta', ','.join(meta))]
316 exp += [call('until', unt, iff=unt)]
317 self.assertEqual(SP.mock_calls[- len(exp):], exp)
319 self.assertEqual(SH.mock_calls[-2:], [
320 call('If-Modified-Since', ims),
321 call('If-Unmodified-Since', ius)])
322 self.assertEqual(get.mock_calls[-1], call(
323 '/%s/%s' % (self.client.account, self.client.container),
325 success=kwargs.pop('success', 200),
328 @patch('%s.set_header' % rest_pkg)
329 @patch('%s.put' % rest_pkg, return_value=FR())
330 def test_container_put(self, put, SH):
333 (None, 'v3r51on1ng'),
334 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
336 (dict(), dict(success=400), dict(k='v', v='k'))):
337 args, kwargs = pm[-2:]
339 self.client.container_put(*(pm + args), **kwargs)
340 quota, versioning, metas = pm[-3:]
342 call('X-Container-Policy-Quota', quota),
343 call('X-Container-Policy-Versioning', versioning)] + [
344 call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
345 self.assertEqual(SH.mock_calls[- len(exp):], exp)
346 self.assertEqual(put.mock_calls[-1], call(
347 '/%s/%s' % (self.client.account, self.client.container),
349 success=kwargs.pop('success', (201, 202)),
352 @patch('%s.set_param' % rest_pkg)
353 @patch('%s.set_header' % rest_pkg)
354 @patch('%s.post' % rest_pkg, return_value=FR())
355 def test_container_post(self, post, SH, SP):
358 ('json', 'some-format'),
360 (None, 'v3r51on1ng'),
361 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
362 (None, 'content-type'),
364 (None, 'transfer-encoding'),
366 (dict(), dict(success=400), dict(k='v', v='k'))):
367 args, kwargs = pm[-2:]
369 self.client.container_post(*(pm + args), **kwargs)
371 self.assertEqual(SP.mock_calls[-2:], [
372 call('update', iff=upd),
373 call('format', frmt, iff=frmt)])
374 qta, vrs, metas, ctype, clen, trenc = pm[2:]
375 prfx = 'X-Container-Meta-'
377 call('X-Container-Policy-Quota', qta),
378 call('X-Container-Policy-Versioning', vrs)] + [
379 call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
380 call('Content-Type', ctype),
381 call('Content-Length', clen),
382 call('Transfer-Encoding', trenc)]
383 self.assertEqual(SH.mock_calls[- len(exp):], exp)
385 self.assertEqual(post.mock_calls[-1], call(
386 '/%s/%s' % (self.client.account, self.client.container),
388 success=kwargs.pop('success', 202),
391 @patch('%s.set_param' % rest_pkg)
392 @patch('%s.delete' % rest_pkg, return_value=FR())
393 def test_container_delete(self, delete, SP):
398 (dict(), dict(success=400), dict(k='v', v='k'))):
399 args, kwargs = pm[-2:]
401 self.client.container_delete(*(pm + args), **kwargs)
403 self.assertEqual(SP.mock_calls[-2:], [
404 call('until', unt, iff=unt),
405 call('delimiter', dlm, iff=dlm)])
406 self.assertEqual(delete.mock_calls[-1], call(
407 '/%s/%s' % (self.client.account, self.client.container),
409 success=kwargs.pop('success', 204),
412 @patch('%s.set_param' % rest_pkg)
413 @patch('%s.set_header' % rest_pkg)
414 @patch('%s.head' % rest_pkg, return_value=FR())
415 def test_object_head(self, head, SH, SP):
420 (None, '1f-m0d-51nc3'),
421 (None, '1f-unm0d-51nc3'),
423 (dict(), dict(success=400), dict(k='v', v='k'))):
424 args, kwargs = pm[-2:]
426 self.client.object_head(obj, *(pm + args), **kwargs)
427 vrs, etag, netag, ims, ius = pm[:5]
430 call('version', vrs, iff=vrs))
431 self.assertEqual(SH.mock_calls[-4:], [
432 call('If-Match', etag),
433 call('If-None-Match', netag),
434 call('If-Modified-Since', ims),
435 call('If-Unmodified-Since', ius)])
436 acc, cont = self.client.account, self.client.container
437 self.assertEqual(head.mock_calls[-1], call(
438 '/%s/%s/%s' % (acc, cont, obj),
440 success=kwargs.pop('success', 200),
443 @patch('%s.set_param' % rest_pkg)
444 @patch('%s.set_header' % rest_pkg)
445 @patch('%s.get' % rest_pkg, return_value=FR())
446 def test_object_get(self, get, SH, SP):
451 (None, 'range=74-63'),
458 (dict(), dict(success=400), dict(k='v', v='k'))):
459 args, kwargs = pm[-2:]
461 self.client.object_get(obj, *(pm + args), **kwargs)
462 format, hashmap, version = pm[:3]
463 self.assertEqual(SP.mock_calls[-3:], [
464 call('format', format, iff=format),
465 call('hashmap', hashmap, iff=hashmap),
466 call('version', version, iff=version)])
467 rng, ifrng, im, inm, ims, ius = pm[-6:]
468 self.assertEqual(SH.mock_calls[-6:], [
470 call('If-Range', '', ifrng and rng),
471 call('If-Match', im),
472 call('If-None-Match', inm),
473 call('If-Modified-Since', ims),
474 call('If-Unmodified-Since', ius)])
475 acc, cont = self.client.account, self.client.container
476 self.assertEqual(get.mock_calls[-1], call(
477 '/%s/%s/%s' % (acc, cont, obj),
479 success=kwargs.pop('success', 200),
482 @patch('%s.set_param' % rest_pkg)
483 @patch('%s.set_header' % rest_pkg)
484 @patch('%s.put' % rest_pkg, return_value=FR())
485 def test_object_put(self, put, SH, SP):
490 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
492 (dict(), dict(k2='v2', k3='v3')),
494 (dict(), dict(success=400), dict(k='v', v='k'))):
495 args, kwargs = pm[-2:]
498 for i in range(len(terms)):
500 terms[i] = 'val_%s' % randint(13, 1024)
501 self.client.object_put(
503 *(pm[:3] + tuple(terms) + pm[3:] + args),
505 format, hashmap, delimiter = pm[:3]
506 self.assertEqual(SP.mock_calls[-3:], [
507 call('format', format, iff=format),
508 call('hashmap', hashmap, iff=hashmap),
509 call('delimiter', delimiter, iff=delimiter)])
511 im, inm, etag, clen, ctype, trenc,
512 cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
513 perms, public, metas = pm[3:]
515 call('If-Match', im),
516 call('If-None-Match', inm),
518 call('Content-Length', clen),
519 call('Content-Type', ctype),
520 call('Transfer-Encoding', trenc),
521 call('X-Copy-From', cp),
522 call('X-Move-From', mv),
523 call('X-Source-Account', srcacc),
524 call('X-Source-Version', srcvrs),
525 call('Content-Encoding', conenc),
526 call('Content-Disposition', condis),
527 call('X-Object-Manifest', mnf)]
530 for ptype, pval in perms.items():
532 perm_str += ';' if perm_str else ''
533 perm_str += '%s=%s' % (ptype, ','.join(pval))
534 exp += [call('X-Object-Sharing', perm_str)]
535 exp += [call('X-Object-Public', public)]
536 for k, v in metas.items():
537 exp += [call('X-Object-Meta-%s' % k, v)]
538 self.assertEqual(SH.mock_calls[- len(exp):], exp)
539 acc, cont = self.client.account, self.client.container
540 self.assertEqual(put.mock_calls[-1], call(
541 '/%s/%s/%s' % (acc, cont, obj),
543 success=kwargs.pop('success', 201),
546 @patch('%s.set_param' % rest_pkg)
547 @patch('%s.set_header' % rest_pkg)
548 @patch('%s.copy' % rest_pkg, return_value=FR())
549 def test_object_copy(self, copy, SH, SP):
555 (None, 'ifnonematch'),
556 (None, 'destinationaccount'),
557 (None, 'content-type'),
558 (None, 'content-encoding'),
559 (None, 'content-disp'),
560 (None, 'source-version'),
561 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
563 (dict(), dict(k2='v2', k3='v3')),
565 (dict(), dict(success=400), dict(k='v', v='k'))):
566 args, kwargs = pm[-2:]
568 self.client.object_copy(obj, dest, *(pm + args), **kwargs)
570 self.assertEqual(SP.mock_calls[-2:], [
571 call('format', format, iff=format),
572 call('ignore_content_type', iff=ict)])
573 im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
574 exp = [call('If-Match', im),
575 call('If-None-Match', inm),
576 call('Destination', dest),
577 call('Destination-Account', da),
578 call('Content-Type', ct),
579 call('Content-Encoding', ce),
580 call('Content-Disposition', cd),
581 call('X-Source-Version', sv)]
584 for ptype, pval in perms.items():
586 perm_str += ';' if perm_str else ''
587 perm_str += '%s=%s' % (ptype, ','.join(pval))
588 exp += [call('X-Object-Sharing', perm_str)]
589 exp += [call('X-Object-Public', public)]
590 for k, v in metas.items():
591 exp += [call('X-Object-Meta-%s' % k, v)]
592 self.assertEqual(SH.mock_calls[- len(exp):], exp)
593 acc, cont = self.client.account, self.client.container
594 self.assertEqual(copy.mock_calls[-1], call(
595 '/%s/%s/%s' % (acc, cont, obj),
597 success=kwargs.pop('success', 201),
600 @patch('%s.set_param' % rest_pkg)
601 @patch('%s.set_header' % rest_pkg)
602 @patch('%s.move' % rest_pkg, return_value=FR())
603 def test_object_move(self, move, SH, SP):
608 (None, 'ifnonematch'),
609 (None, 'destination'),
610 (None, 'destinationaccount'),
611 (None, 'content-type'),
612 (None, 'content-encoding'),
613 (None, 'content-disp'),
614 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
616 (dict(), dict(k2='v2', k3='v3')),
618 (dict(), dict(success=400), dict(k='v', v='k'))):
619 args, kwargs = pm[-2:]
621 self.client.object_move(obj, *(pm + args), **kwargs)
623 self.assertEqual(SP.mock_calls[-2:], [
624 call('format', format, iff=format),
625 call('ignore_content_type', iff=ict)])
626 im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
627 exp = [call('If-Match', im),
628 call('If-None-Match', inm),
629 call('Destination', d),
630 call('Destination-Account', da),
631 call('Content-Type', ct),
632 call('Content-Encoding', ce),
633 call('Content-Disposition', cd)]
636 for ptype, pval in perms.items():
638 perm_str += ';' if perm_str else ''
639 perm_str += '%s=%s' % (ptype, ','.join(pval))
640 exp += [call('X-Object-Sharing', perm_str)]
641 exp += [call('X-Object-Public', public)]
642 for k, v in metas.items():
643 exp += [call('X-Object-Meta-%s' % k, v)]
644 self.assertEqual(SH.mock_calls[- len(exp):], exp)
645 acc, cont = self.client.account, self.client.container
646 self.assertEqual(move.mock_calls[-1], call(
647 '/%s/%s/%s' % (acc, cont, obj),
649 success=kwargs.pop('success', 201),
652 @patch('%s.set_param' % rest_pkg)
653 @patch('%s.set_header' % rest_pkg)
654 @patch('%s.post' % rest_pkg, return_value=FR())
655 def test_object_post(self, post, SH, SP):
659 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
661 (dict(), dict(k2='v2', k3='v3')),
663 (dict(), dict(success=400), dict(k='v', v='k'))):
664 args, kwargs = pm[-2:]
667 for i in range(len(terms)):
669 terms[i] = 'val_%s' % randint(13, 1024)
670 self.client.object_post(
672 *(pm[:2] + tuple(terms) + pm[2:] + args),
674 format, update = pm[:2]
675 self.assertEqual(SP.mock_calls[-2:], [
676 call('format', format, iff=format),
677 call('update', iff=update)])
679 im, inm, clen, ctype, crng, trenc, cenc,
680 condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
682 call('If-Match', im),
683 call('If-None-Match', inm),
684 call('Content-Length', clen, iff=not trenc),
685 call('Content-Type', ctype),
686 call('Content-Range', crng),
687 call('Transfer-Encoding', trenc),
688 call('Content-Encoding', cenc),
689 call('Content-Disposition', condis),
690 call('X-Source-Object', srcobj),
691 call('X-Source-Account', srcacc),
692 call('X-Source-Version', srcvrs),
693 call('X-Object-Bytes', obytes),
694 call('X-Object-Manifest', mnfs)]
695 perms, public, metas = pm[2:]
698 for ptype, pval in perms.items():
700 perm_str += ';' if perm_str else ''
701 perm_str += '%s=%s' % (ptype, ','.join(pval))
702 exp += [call('X-Object-Sharing', perm_str)]
703 exp += [call('X-Object-Public', public)]
704 for k, v in metas.items():
705 exp += [call('X-Object-Meta-%s' % k, v)]
706 self.assertEqual(SH.mock_calls[- len(exp):], exp)
707 acc, cont = self.client.account, self.client.container
708 self.assertEqual(post.mock_calls[-1], call(
709 '/%s/%s/%s' % (acc, cont, obj),
711 success=kwargs.pop('success', (202, 204)),
714 @patch('%s.set_param' % rest_pkg)
715 @patch('%s.delete' % rest_pkg, return_value=FR())
716 def test_object_delete(self, delete, SP):
721 (dict(), dict(success=400), dict(k='v', v='k'))):
722 args, kwargs = pm[-2:]
724 self.client.object_delete(
729 self.assertEqual(SP.mock_calls[-2:], [
730 call('until', until, iff=until),
731 call('delimiter', dlm, iff=dlm)])
732 acc, cont = self.client.account, self.client.container
733 self.assertEqual(delete.mock_calls[-1], call(
734 '/%s/%s/%s' % (acc, cont, obj),
736 success=kwargs.pop('success', 204),
740 class PithosClient(TestCase):
744 def _create_temp_file(self, num_of_blocks):
745 self.files.append(NamedTemporaryFile())
746 tmpFile = self.files[-1]
747 file_size = num_of_blocks * 4 * 1024 * 1024
748 print('\n\tCreate tmp file')
749 tmpFile.write(urandom(file_size))
755 def assert_dicts_are_equal(self, d1, d2):
756 for k, v in d1.items():
757 self.assertTrue(k in d2)
758 if isinstance(v, dict):
759 self.assert_dicts_are_equal(v, d2[k])
761 self.assertEqual(unicode(v), unicode(d2[k]))
764 self.url = 'https://www.example.com/pithos'
765 self.token = 'p17h0570k3n'
766 self.client = pithos.PithosClient(self.url, self.token)
767 self.client.account = user_id
768 self.client.container = 'c0nt@1n3r_i'
778 # Pithos+ methods that extend storage API
780 @patch('%s.account_head' % pithos_pkg, return_value=FR())
781 def test_get_account_info(self, AH):
782 FR.headers = account_info
783 for until in (None, 'un71L-d473'):
784 r = self.client.get_account_info(until=until)
785 self.assert_dicts_are_equal(r, account_info)
786 self.assertEqual(AH.mock_calls[-1], call(until=until))
788 self.assertRaises(ClientError, self.client.get_account_info)
790 @patch('%s.account_post' % pithos_pkg, return_value=FR())
791 def test_del_account_meta(self, AP):
792 keys = ['k1', 'k2', 'k3']
794 self.client.del_account_meta(key)
797 call(update=True, metadata={key: ''}))
799 @patch('%s.container_head' % pithos_pkg, return_value=FR())
800 def test_get_container_info(self, CH):
801 FR.headers = container_info
802 r = self.client.get_container_info()
803 self.assert_dicts_are_equal(r, container_info)
805 r = self.client.get_container_info(until=u)
806 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
808 @patch('%s.account_get' % pithos_pkg, return_value=FR())
809 def test_list_containers(self, get):
810 FR.json = container_list
811 r = self.client.list_containers()
812 get.assert_called_once_with()
813 for i in range(len(r)):
814 self.assert_dicts_are_equal(r[i], container_list[i])
816 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
817 @patch('%s.container_post' % pithos_pkg, return_value=FR())
818 @patch('%s.object_put' % pithos_pkg, return_value=FR())
819 def test_upload_object(self, OP, CP, GCI):
821 tmpFile = self._create_temp_file(num_of_blocks)
824 self.client.upload_object(obj, tmpFile)
825 self.assertEqual(GCI.mock_calls[-1], call())
826 [call1, call2] = OP.mock_calls
828 (args1, kwargs1) = call1[1:3]
829 (args2, kwargs2) = call2[1:3]
830 self.assertEqual(args1, (obj,))
836 hashes=['s0m3h@5h'] * num_of_blocks,
837 bytes=num_of_blocks * 4 * 1024 * 1024),
838 content_encoding=None,
839 content_type='application/octet-stream',
840 content_disposition=None,
843 for k, v in expected1.items():
845 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
846 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
848 self.assertEqual(v, kwargs1[k])
850 (args2, kwargs2) = call2[1:3]
851 self.assertEqual(args2, (obj,))
854 hashes=['s0m3h@5h'] * num_of_blocks,
855 bytes=num_of_blocks * 4 * 1024 * 1024),
856 content_type='application/octet-stream',
860 for k, v in expected2.items():
862 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
863 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
865 self.assertEqual(v, kwargs2[k])
871 from progress.bar import ShadyBar
872 blck_bar = ShadyBar('Mock blck calc.')
873 upld_bar = ShadyBar('Mock uplds')
878 if blck_bar and upld_bar:
881 for i in blck_bar.iter(range(n)):
886 for i in upld_bar.iter(range(n)):
891 self.client.upload_object(
893 hash_cb=blck_gen, upload_cb=upld_gen)
895 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
896 self.assertEqual(OP.mock_calls[i], c)
901 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
902 self.client.upload_object(obj, tmpFile,
903 content_type=ctype, sharing=sharing)
904 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
905 self.assert_dicts_are_equal(
906 OP.mock_calls[-2][2]['permissions'],
913 if_etag_match='if etag match',
916 content_disposition=ctype + 'd15p051710n',
918 content_encoding='802.11')
919 self.client.upload_object(obj, tmpFile, **kwargs)
920 kwargs.pop('if_not_exist')
921 ematch = kwargs.pop('if_etag_match')
922 etag = kwargs.pop('etag')
923 for arg, val in kwargs.items():
924 self.assertEqual(OP.mock_calls[-2][2][arg], val)
925 self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
926 self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
927 self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
929 def test_get_object_info(self):
930 FR.headers = object_info
933 pithos.PithosClient, 'object_head',
934 return_value=FR()) as head:
935 r = self.client.get_object_info(obj)
936 self.assertEqual(r, object_info)
937 r = self.client.get_object_info(obj, version=version)
938 self.assertEqual(head.mock_calls, [
939 call(obj, version=None),
940 call(obj, version=version)])
942 pithos.PithosClient, 'object_head',
943 side_effect=ClientError('Obj not found', 404)):
946 self.client.get_object_info,
947 obj, version=version)
949 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
950 def test_get_object_meta(self, GOI):
951 for version in (None, 'v3r510n'):
952 r = self.client.get_object_meta(obj, version)
953 for k in [k for k in object_info if k.startswith('x-object-meta')]:
954 self.assertEqual(r.pop(k), object_info[k])
955 self.assertFalse(len(r))
956 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
958 @patch('%s.object_post' % pithos_pkg, return_value=FR())
959 def test_del_object_meta(self, post):
960 metakey = '50m3m3t4k3y'
961 self.client.del_object_meta(obj, metakey)
962 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
964 @patch('%s.object_put' % pithos_pkg, return_value=FR())
965 def test_copy_object(self, put):
966 src_cont = 'src-c0nt41n3r'
968 dst_cont = 'dst-c0nt41n3r'
975 copy_from='/%s/%s' % (src_cont, src_obj),
980 self.client.copy_object(src_cont, src_obj, dst_cont)
981 self.assertEqual(put.mock_calls[-1], expected)
982 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
983 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
985 source_version='src-v3r510n',
986 source_account='src-4cc0un7',
988 content_type='c0n73n7Typ3',
990 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
991 for k, v in kwargs.items():
992 self.assertEqual(v, put.mock_calls[-1][2][k])
994 @patch('%s.object_put' % pithos_pkg, return_value=FR())
995 def test_move_object(self, put):
996 src_cont = 'src-c0nt41n3r'
998 dst_cont = 'dst-c0nt41n3r'
1003 source_account=None,
1005 move_from='/%s/%s' % (src_cont, src_obj),
1008 source_version=None,
1010 self.client.move_object(src_cont, src_obj, dst_cont)
1011 self.assertEqual(put.mock_calls[-1], expected)
1012 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1013 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1015 source_version='src-v3r510n',
1016 source_account='src-4cc0un7',
1018 content_type='c0n73n7Typ3',
1020 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1021 for k, v in kwargs.items():
1022 self.assertEqual(v, put.mock_calls[-1][2][k])
1024 # Pithos+ only methods
1026 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1027 def test_purge_container(self, CD):
1028 self.client.purge_container()
1029 self.assertTrue('until' in CD.mock_calls[-1][2])
1030 cont = self.client.container
1031 self.client.purge_container('another-container')
1032 self.assertEqual(self.client.container, cont)
1034 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1035 def test_upload_object_unchunked(self, put):
1037 tmpFile = self._create_temp_file(num_of_blocks)
1040 data=num_of_blocks * 4 * 1024 * 1024,
1042 content_encoding='some content_encoding',
1043 content_type='some content-type',
1044 content_disposition='some content_disposition',
1046 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1047 self.client.upload_object_unchunked(obj, tmpFile)
1048 self.assertEqual(put.mock_calls[-1][1], (obj,))
1050 sorted(put.mock_calls[-1][2].keys()),
1051 sorted(expected.keys()))
1052 kwargs = dict(expected)
1053 kwargs.pop('success')
1054 kwargs['size'] = kwargs.pop('data')
1055 kwargs['sharing'] = kwargs.pop('permissions')
1057 self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1058 pmc = put.mock_calls[-1][2]
1059 for k, v in expected.items():
1061 self.assertEqual(len(pmc[k]), v)
1063 self.assertEqual(pmc[k], v)
1066 self.client.upload_object_unchunked,
1067 obj, tmpFile, withHashFile=True)
1069 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1070 def test_create_object_by_manifestation(self, put):
1071 manifest = '%s/%s' % (self.client.container, obj)
1074 content_encoding='some content_encoding',
1075 content_type='some content-type',
1076 content_disposition='some content_disposition',
1078 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1079 self.client.create_object_by_manifestation(obj)
1080 expected = dict(content_length=0, manifest=manifest)
1082 expected['permissions' if k == 'sharing' else k] = None
1083 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1084 self.client.create_object_by_manifestation(obj, **kwargs)
1085 expected.update(kwargs)
1086 expected['permissions'] = expected.pop('sharing')
1087 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1089 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1090 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1091 def test_download_object(self, GET, GOH):
1093 tmpFile = self._create_temp_file(num_of_blocks)
1094 FR.content = tmpFile.read(4 * 1024 * 1024)
1095 tmpFile = self._create_temp_file(num_of_blocks)
1096 num_of_blocks = len(object_hashmap['hashes'])
1101 if_match='if and only if',
1102 if_none_match='if and only not',
1103 if_modified_since='what if not?',
1104 if_unmodified_since='this happens if not!',
1105 async_headers=dict(Range='bytes=0-88888888'))
1107 self.client.download_object(obj, tmpFile)
1108 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1109 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1110 for k, v in kwargs.items():
1111 if k == 'async_headers':
1112 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1113 elif k in ('resume', 'range_str'):
1116 self.assertEqual(GET.mock_calls[-1][2][k], None)
1118 # Check ranges are consecutive
1121 for c in GET.mock_calls:
1122 rng_str = c[2]['async_headers']['Range']
1123 (start, rng_str) = rng_str.split('=')
1124 (start, end) = rng_str.split('-')
1125 starts.append(start)
1128 for i, start in enumerate(sorted(starts)):
1130 int(ends[i - 1]) == int(start) - 1
1132 # With progress bars
1134 from progress.bar import ShadyBar
1135 dl_bar = ShadyBar('Mock dl')
1142 for i in dl_bar.iter(range(n)):
1147 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1148 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1151 kwargs.pop('async_headers')
1152 kwargs.pop('resume')
1153 self.client.download_object(obj, tmpFile, **kwargs)
1154 for k, v in kwargs.items():
1155 if k == 'range_str':
1157 GET.mock_calls[-1][2]['data_range'],
1160 self.assertEqual(GET.mock_calls[-1][2][k], v)
1162 # ALl options on no tty
1167 tmpFile.isatty = foo
1168 self.client.download_object(obj, tmpFile, **kwargs)
1169 for k, v in kwargs.items():
1170 if k == 'range_str':
1171 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1173 self.assertEqual(GET.mock_calls[-1][2][k], v)
1175 def test_get_object_hashmap(self):
1176 FR.json = object_hashmap
1177 for empty in (304, 412):
1179 pithos.PithosClient, 'object_get',
1180 side_effect=ClientError('Empty', status=empty)):
1181 r = self.client.get_object_hashmap(obj)
1182 self.assertEqual(r, {})
1188 if_etag_not_match=None,
1189 if_modified_since=None,
1190 if_unmodified_since=None)
1192 version='s0m3v3r51on',
1193 if_match='if match',
1194 if_none_match='if non match',
1195 if_modified_since='some date here',
1196 if_unmodified_since='some date here',
1199 pithos.PithosClient, 'object_get',
1200 return_value=FR()) as get:
1201 r = self.client.get_object_hashmap(obj)
1202 self.assertEqual(r, object_hashmap)
1203 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1204 r = self.client.get_object_hashmap(obj, **kwargs)
1205 exp_args['if_etag_match'] = kwargs.pop('if_match')
1206 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1207 exp_args.update(kwargs)
1208 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1210 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1211 def test_set_account_group(self, post):
1212 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1213 self.client.set_account_group(group, usernames)
1214 post.assert_called_once_with(update=True, groups={group: usernames})
1216 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1217 def test_del_account_group(self, post):
1218 group = 'aU53rGr0up'
1219 self.client.del_account_group(group)
1220 post.assert_called_once_with(update=True, groups={group: []})
1222 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1223 def test_get_account_quota(self, GAI):
1224 key = 'x-account-policy-quota'
1225 r = self.client.get_account_quota()
1226 GAI.assert_called_once_with()
1227 self.assertEqual(r[key], account_info[key])
1229 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1230 def test_get_account_versioning(self, GAI):
1231 key = 'x-account-policy-versioning'
1232 r = self.client.get_account_versioning()
1233 GAI.assert_called_once_with()
1234 self.assertEqual(r[key], account_info[key])
1236 def test_get_account_meta(self):
1237 key = 'x-account-meta-'
1239 pithos.PithosClient, 'get_account_info',
1240 return_value=account_info):
1241 r = self.client.get_account_meta()
1242 keys = [k for k in r if k.startswith(key)]
1243 self.assertFalse(keys)
1244 acc_info = dict(account_info)
1245 acc_info['%sk1' % key] = 'v1'
1246 acc_info['%sk2' % key] = 'v2'
1247 acc_info['%sk3' % key] = 'v3'
1249 pithos.PithosClient, 'get_account_info',
1250 return_value=acc_info):
1251 r = self.client.get_account_meta()
1252 for k in [k for k in acc_info if k.startswith(key)]:
1253 self.assertEqual(r[k], acc_info[k])
1255 def test_get_account_group(self):
1256 key = 'x-account-group-'
1258 pithos.PithosClient, 'get_account_info',
1259 return_value=account_info):
1260 r = self.client.get_account_group()
1261 keys = [k for k in r if k.startswith(key)]
1262 self.assertFalse(keys)
1263 acc_info = dict(account_info)
1264 acc_info['%sk1' % key] = 'g1'
1265 acc_info['%sk2' % key] = 'g2'
1266 acc_info['%sk3' % key] = 'g3'
1268 pithos.PithosClient, 'get_account_info',
1269 return_value=acc_info):
1270 r = self.client.get_account_group()
1271 for k in [k for k in acc_info if k.startswith(key)]:
1272 self.assertEqual(r[k], acc_info[k])
1274 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1275 def test_set_account_meta(self, post):
1276 metas = dict(k1='v1', k2='v2', k3='v3')
1277 self.client.set_account_meta(metas)
1278 post.assert_called_once_with(update=True, metadata=metas)
1280 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1281 def test_set_account_quota(self, post):
1283 self.client.set_account_quota(qu)
1284 post.assert_called_once_with(update=True, quota=qu)
1286 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1287 def test_set_account_versioning(self, post):
1288 vrs = 'n3wV3r51on1ngTyp3'
1289 self.client.set_account_versioning(vrs)
1290 post.assert_called_once_with(update=True, versioning=vrs)
1292 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1293 def test_del_container(self, delete):
1295 dict(delimiter=None, until=None),
1296 dict(delimiter='X', until='50m3d473')):
1297 self.client.del_container(**kwarg)
1298 expected = dict(kwarg)
1299 expected['success'] = (204, 404, 409)
1300 self.assertEqual(delete.mock_calls[-1], call(**expected))
1301 for status_code in (404, 409):
1302 FR.status_code = status_code
1303 self.assertRaises(ClientError, self.client.del_container)
1305 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1306 def test_get_container_versioning(self, GCI):
1307 key = 'x-container-policy-versioning'
1309 bu_cnt = self.client.container
1310 for container in (None, cont):
1311 r = self.client.get_container_versioning(container=container)
1312 self.assertEqual(r[key], container_info[key])
1313 self.assertEqual(GCI.mock_calls[-1], call())
1314 self.assertEqual(bu_cnt, self.client.container)
1316 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1317 def test_get_container_quota(self, GCI):
1318 key = 'x-container-policy-quota'
1320 bu_cnt = self.client.container
1321 for container in (None, cont):
1322 r = self.client.get_container_quota(container=container)
1323 self.assertEqual(r[key], container_info[key])
1324 self.assertEqual(GCI.mock_calls[-1], call())
1325 self.assertEqual(bu_cnt, self.client.container)
1327 def test_get_container_meta(self):
1328 somedate = '50m3d473'
1329 key = 'x-container-meta'
1330 metaval = '50m3m374v41'
1331 container_plus = dict(container_info)
1332 container_plus[key] = metaval
1333 for ret in ((container_info, {}), (container_plus, {key: metaval})):
1335 pithos.PithosClient,
1336 'get_container_info',
1337 return_value=ret[0]) as GCI:
1338 for until in (None, somedate):
1339 r = self.client.get_container_meta(until=until)
1340 self.assertEqual(r, ret[1])
1341 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1343 def test_get_container_object_meta(self):
1344 somedate = '50m3d473'
1345 key = 'x-container-object-meta'
1346 metaval = '50m3m374v41'
1347 container_plus = dict(container_info)
1348 container_plus[key] = metaval
1350 (container_info, {key: ''}),
1351 (container_plus, {key: metaval})):
1353 pithos.PithosClient,
1354 'get_container_info',
1355 return_value=ret[0]) as GCI:
1356 for until in (None, somedate):
1357 r = self.client.get_container_object_meta(until=until)
1358 self.assertEqual(r, ret[1])
1359 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1361 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1362 def test_set_container_meta(self, post):
1363 metas = dict(k1='v1', k2='v2', k3='v3')
1364 self.client.set_container_meta(metas)
1365 post.assert_called_once_with(update=True, metadata=metas)
1367 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1368 def test_del_container_meta(self, AP):
1369 self.client.del_container_meta('somekey')
1370 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1372 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1373 def test_set_container_limit(self, post):
1375 self.client.set_container_limit(qu)
1376 post.assert_called_once_with(update=True, quota=qu)
1378 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1379 def test_set_container_versioning(self, post):
1380 vrs = 'n3wV3r51on1ngTyp3'
1381 self.client.set_container_versioning(vrs)
1382 post.assert_called_once_with(update=True, versioning=vrs)
1384 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1385 def test_del_object(self, delete):
1387 dict(delimiter=None, until=None),
1388 dict(delimiter='X', until='50m3d473')):
1389 self.client.del_object(obj, **kwarg)
1390 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1392 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1393 def test_set_object_meta(self, post):
1394 metas = dict(k1='v1', k2='v2', k3='v3')
1397 self.client.set_object_meta,
1398 obj, 'Non dict arg')
1399 self.client.set_object_meta(obj, metas)
1400 post.assert_called_once_with(obj, update=True, metadata=metas)
1402 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1403 def test_publish_object(self, post):
1404 oinfo = dict(object_info)
1406 oinfo['x-object-public'] = val
1408 pithos.PithosClient, 'get_object_info',
1409 return_value=oinfo) as GOF:
1410 r = self.client.publish_object(obj)
1412 post.mock_calls[-1],
1413 call(obj, public=True, update=True))
1414 self.assertEqual(GOF.mock_calls[-1], call(obj))
1415 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1417 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1418 def test_unpublish_object(self, post):
1419 self.client.unpublish_object(obj)
1420 post.assert_called_once_with(obj, public=False, update=True)
1422 def test_get_object_sharing(self):
1423 info = dict(object_info)
1424 expected = dict(read='u1,g1,u2', write='u1')
1425 info['x-object-sharing'] = '; '.join(
1426 ['%s=%s' % (k, v) for k, v in expected.items()])
1428 pithos.PithosClient, 'get_object_info',
1429 return_value=info) as GOF:
1430 r = self.client.get_object_sharing(obj)
1431 self.assertEqual(GOF.mock_calls[-1], call(obj))
1432 self.assert_dicts_are_equal(r, expected)
1433 info['x-object-sharing'] = '//'.join(
1434 ['%s=%s' % (k, v) for k, v in expected.items()])
1437 self.client.get_object_sharing,
1439 info['x-object-sharing'] = '; '.join(
1440 ['%s:%s' % (k, v) for k, v in expected.items()])
1443 self.client.get_object_sharing,
1445 info['x-object-sharing'] = 'read=%s' % expected['read']
1446 r = self.client.get_object_sharing(obj)
1447 expected.pop('write')
1448 self.assert_dicts_are_equal(r, expected)
1450 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1451 def test_set_object_sharing(self, OP):
1452 read_perms = ['u1', 'g1', 'u2', 'g2']
1453 write_perms = ['u1', 'g1']
1455 dict(read_permition=read_perms, write_permition=write_perms),
1456 dict(read_permition=read_perms),
1457 dict(write_permition=write_perms),
1459 self.client.set_object_sharing(obj, **kwargs)
1460 kwargs['read'] = kwargs.pop('read_permition', '')
1461 kwargs['write'] = kwargs.pop('write_permition', '')
1464 call(obj, update=True, permissions=kwargs))
1466 @patch('%s.set_object_sharing' % pithos_pkg)
1467 def test_del_object_sharing(self, SOS):
1468 self.client.del_object_sharing(obj)
1469 SOS.assert_called_once_with(obj)
1471 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1472 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1473 def test_append_object(self, post, GCI):
1475 tmpFile = self._create_temp_file(num_of_blocks)
1477 file_size = tmpFile.tell()
1478 for turn in range(2):
1482 from progress.bar import ShadyBar
1483 apn_bar = ShadyBar('Mock append')
1490 for i in apn_bar.iter(range(n)):
1497 self.client.append_object(
1499 upload_cb=append_gen if turn else None)
1500 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1501 (args, kwargs) = post.mock_calls[-1][1:3]
1502 self.assertEqual(args, (obj,))
1503 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1504 fsize = num_of_blocks * int(kwargs['content_length'])
1505 self.assertEqual(fsize, file_size)
1506 self.assertEqual(kwargs['content_range'], 'bytes */*')
1507 exp = 'application/octet-stream'
1508 self.assertEqual(kwargs['content_type'], exp)
1509 self.assertEqual(kwargs['update'], True)
1511 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1512 def test_truncate_object(self, post):
1514 self.client.truncate_object(obj, upto_bytes)
1515 post.assert_called_once_with(
1518 object_bytes=upto_bytes,
1519 content_range='bytes 0-%s/*' % upto_bytes,
1520 content_type='application/octet-stream',
1521 source_object='/%s/%s' % (self.client.container, obj))
1523 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1524 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1525 def test_overwrite_object(self, post, GCI):
1527 tmpFile = self._create_temp_file(num_of_blocks)
1529 file_size = tmpFile.tell()
1530 info = dict(object_info)
1531 info['content-length'] = file_size
1532 block_size = container_info['x-container-block-size']
1534 pithos.PithosClient, 'get_object_info',
1535 return_value=info) as GOI:
1538 (file_size + 1, file_size + 2)):
1542 self.client.overwrite_object,
1543 obj, start, end, tmpFile)
1544 for start, end in ((0, 144), (144, 233), (233, file_size)):
1547 exp_size = end - start + 1
1548 if not start or exp_size > block_size:
1550 from progress.bar import ShadyBar
1551 owr_bar = ShadyBar('Mock append')
1558 for i in owr_bar.iter(range(n)):
1562 if exp_size > block_size:
1563 exp_size = exp_size % block_size or block_size
1565 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1566 self.assertEqual(GOI.mock_calls[-1], call(obj))
1567 self.assertEqual(GCI.mock_calls[-1], call())
1568 (args, kwargs) = post.mock_calls[-1][1:3]
1569 self.assertEqual(args, (obj,))
1570 self.assertEqual(len(kwargs['data']), exp_size)
1571 self.assertEqual(kwargs['content_length'], exp_size)
1572 self.assertEqual(kwargs['update'], True)
1573 exp = 'application/octet-stream'
1574 self.assertEqual(kwargs['content_type'], exp)
1576 @patch('%s.set_param' % pithos_pkg)
1577 @patch('%s.get' % pithos_pkg, return_value=FR())
1578 def test_get_sharing_accounts(self, get, SP):
1582 dict(limit='50m3-11m17'),
1584 dict(limit='50m3-11m17', marker='X')):
1585 r = self.client.get_sharing_accounts(**kws)
1586 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1587 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1588 limit, marker = kws.get('limit', None), kws.get('marker', None)
1589 self.assertEqual(SP.mock_calls[-2], call(
1591 iff=limit is not None))
1592 self.assertEqual(SP.mock_calls[-1], call(
1594 iff=marker is not None))
1595 for i in range(len(r)):
1596 self.assert_dicts_are_equal(r[i], sharers[i])
1598 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1599 def test_get_object_versionlist(self, get):
1600 info = dict(object_info)
1601 info['versions'] = ['v1', 'v2']
1603 r = self.client.get_object_versionlist(obj)
1604 get.assert_called_once_with(obj, format='json', version='list')
1605 self.assertEqual(r, info['versions'])
1607 if __name__ == '__main__':
1608 from sys import argv
1609 from kamaki.clients.test import runTestCase
1611 if not argv[1:] or argv[1] == 'PithosClient':
1613 runTestCase(PithosClient, 'Pithos Client', argv[2:])
1614 if not argv[1:] or argv[1] == 'PithosRestClient':
1616 runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1618 print('TestCase %s not found' % argv[1])