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 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"""
160 class PithosRestClient(TestCase):
163 self.url = 'https://www.example.com/pithos'
164 self.token = 'p17h0570k3n'
165 self.client = pithos.PithosRestClient(self.url, self.token)
166 self.client.account = user_id
167 self.client.container = 'c0nt@1n3r_i'
174 @patch('%s.set_param' % rest_pkg)
175 @patch('%s.set_header' % rest_pkg)
176 @patch('%s.head' % rest_pkg, return_value=FR())
177 def test_account_head(self, head, SH, SP):
178 for params in product(
180 (None, '50m3-07h3r-d473'),
181 (None, 'y37-4n7h3r-d473'),
182 ((), ('someval',), ('v1', 'v2',)),
183 (dict(), dict(success=200), dict(k='v', v='k'))):
184 args, kwargs = params[-2], params[-1]
186 self.client.account_head(*(params + args), **kwargs)
188 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
189 IMS, IUS = params[1], params[2]
190 self.assertEqual(SH.mock_calls[-2:], [
191 call('If-Modified-Since', IMS),
192 call('If-Unmodified-Since', IUS)])
193 self.assertEqual(head.mock_calls[-1], call(
194 '/%s' % self.client.account,
196 success=kwargs.pop('success', 204),
199 @patch('%s.set_param' % rest_pkg)
200 @patch('%s.set_header' % rest_pkg)
201 @patch('%s.get' % rest_pkg, return_value=FR())
202 def test_account_get(self, get, SH, SP):
203 keys = ('limit', 'marker', 'format', 'shared', 'until')
204 for params in product(
210 (None, '50m3-07h3r-d473'),
211 (None, 'y37-4n7h3r-d473'),
212 ((), ('someval',), ('v1', 'v2',)),
213 (dict(), dict(success=200), dict(k='v', v='k'))):
214 args, kwargs = params[-2], params[-1]
216 self.client.account_get(*(params + args), **kwargs)
217 self.assertEqual(SP.mock_calls[-5:],
218 [call(keys[i], iff=X) if (
220 keys[i], X, iff=X) for i, X in enumerate(params[:5])])
221 IMS, IUS = params[5], params[6]
222 self.assertEqual(SH.mock_calls[-2:], [
223 call('If-Modified-Since', IMS),
224 call('If-Unmodified-Since', IUS)])
225 self.assertEqual(get.mock_calls[-1], call(
226 '/%s' % self.client.account,
228 success=kwargs.pop('success', (200, 204)),
231 @patch('%s.set_param' % rest_pkg)
232 @patch('%s.set_header' % rest_pkg)
233 @patch('%s.post' % rest_pkg, return_value=FR())
234 def test_account_post(self, post, SH, SP):
235 #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
238 ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
239 (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
241 (None, 'v3r510n1ng'),
242 ((), ('someval',), ('v1', 'v2',)),
243 (dict(), dict(success=200), dict(k='v', v='k'))):
244 args, kwargs = pm[-2:]
246 self.client.account_post(*(pm + args), **kwargs)
248 self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
252 call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
255 call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
257 call('X-Account-Policy-Quota', pm[3]),
258 call('X-Account-Policy-Versioning', pm[4])]
259 self.assertEqual(SH.mock_calls[- len(expected):], expected)
260 self.assertEqual(post.mock_calls[-1], call(
261 '/%s' % self.client.account,
263 success=kwargs.pop('success', 202),
266 @patch('%s.set_param' % rest_pkg)
267 @patch('%s.set_header' % rest_pkg)
268 @patch('%s.head' % rest_pkg, return_value=FR())
269 def test_container_head(self, head, SH, SP):
272 (None, '47h3r-d473'),
273 (None, 'y37-4n47h3r'),
275 (dict(), dict(success=200), dict(k='v', v='k'))):
276 args, kwargs = pm[-2:]
278 self.client.container_head(*(pm + args), **kwargs)
279 unt, ims, ius = pm[0:3]
280 self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
281 self.assertEqual(SH.mock_calls[-2:], [
282 call('If-Modified-Since', ims),
283 call('If-Unmodified-Since', ius)])
284 self.assertEqual(head.mock_calls[-1], call(
285 '/%s/%s' % (self.client.account, self.client.container),
287 success=kwargs.pop('success', 204),
290 @patch('%s.set_param' % rest_pkg)
291 @patch('%s.set_header' % rest_pkg)
292 @patch('%s.get' % rest_pkg, return_value=FR())
293 def test_container_get(self, get, SH, SP):
297 (None, 'some/prefix'),
299 (None, '/some/path'),
300 ('json', 'some-format'),
301 ([], ['k1', 'k2', 'k3']),
303 (None, 'unt1l-d473'),
304 (None, 'y37-4n47h3r'),
305 (None, '4n47h3r-d473'),
307 (dict(), dict(success=400), dict(k='v', v='k'))):
308 args, kwargs = pm[-2:]
310 self.client.container_get(*(pm + args), **kwargs)
311 lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
312 exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
313 exp += [call('path', path)] if path else [
314 call('prefix', prfx, iff=prfx),
315 call('delimiter', dlm, iff=dlm)]
316 exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
318 exp += [call('meta', ','.join(meta))]
319 exp += [call('until', unt, iff=unt)]
320 self.assertEqual(SP.mock_calls[- len(exp):], exp)
322 self.assertEqual(SH.mock_calls[-2:], [
323 call('If-Modified-Since', ims),
324 call('If-Unmodified-Since', ius)])
325 self.assertEqual(get.mock_calls[-1], call(
326 '/%s/%s' % (self.client.account, self.client.container),
328 success=kwargs.pop('success', 200),
331 @patch('%s.set_header' % rest_pkg)
332 @patch('%s.put' % rest_pkg, return_value=FR())
333 def test_container_put(self, put, SH):
336 (None, 'v3r51on1ng'),
337 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
339 (dict(), dict(success=400), dict(k='v', v='k'))):
340 args, kwargs = pm[-2:]
342 self.client.container_put(*(pm + args), **kwargs)
343 quota, versioning, metas = pm[-3:]
345 call('X-Container-Policy-Quota', quota),
346 call('X-Container-Policy-Versioning', versioning)] + [
347 call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
348 self.assertEqual(SH.mock_calls[- len(exp):], exp)
349 self.assertEqual(put.mock_calls[-1], call(
350 '/%s/%s' % (self.client.account, self.client.container),
352 success=kwargs.pop('success', (201, 202)),
355 @patch('%s.set_param' % rest_pkg)
356 @patch('%s.set_header' % rest_pkg)
357 @patch('%s.post' % rest_pkg, return_value=FR())
358 def test_container_post(self, post, SH, SP):
361 ('json', 'some-format'),
363 (None, 'v3r51on1ng'),
364 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
365 (None, 'content-type'),
367 (None, 'transfer-encoding'),
369 (dict(), dict(success=400), dict(k='v', v='k'))):
370 args, kwargs = pm[-2:]
372 self.client.container_post(*(pm + args), **kwargs)
374 self.assertEqual(SP.mock_calls[-2:], [
375 call('update', iff=upd),
376 call('format', frmt, iff=frmt)])
377 qta, vrs, metas, ctype, clen, trenc = pm[2:]
378 prfx = 'X-Container-Meta-'
380 call('X-Container-Policy-Quota', qta),
381 call('X-Container-Policy-Versioning', vrs)] + [
382 call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
383 call('Content-Type', ctype),
384 call('Content-Length', clen),
385 call('Transfer-Encoding', trenc)]
386 self.assertEqual(SH.mock_calls[- len(exp):], exp)
388 self.assertEqual(post.mock_calls[-1], call(
389 '/%s/%s' % (self.client.account, self.client.container),
391 success=kwargs.pop('success', 202),
394 @patch('%s.set_param' % rest_pkg)
395 @patch('%s.delete' % rest_pkg, return_value=FR())
396 def test_container_delete(self, delete, SP):
401 (dict(), dict(success=400), dict(k='v', v='k'))):
402 args, kwargs = pm[-2:]
404 self.client.container_delete(*(pm + args), **kwargs)
406 self.assertEqual(SP.mock_calls[-2:], [
407 call('until', unt, iff=unt),
408 call('delimiter', dlm, iff=dlm)])
409 self.assertEqual(delete.mock_calls[-1], call(
410 '/%s/%s' % (self.client.account, self.client.container),
412 success=kwargs.pop('success', 204),
415 @patch('%s.set_param' % rest_pkg)
416 @patch('%s.set_header' % rest_pkg)
417 @patch('%s.head' % rest_pkg, return_value=FR())
418 def test_object_head(self, head, SH, SP):
423 (None, '1f-m0d-51nc3'),
424 (None, '1f-unm0d-51nc3'),
426 (dict(), dict(success=400), dict(k='v', v='k'))):
427 args, kwargs = pm[-2:]
429 self.client.object_head(obj, *(pm + args), **kwargs)
430 vrs, etag, netag, ims, ius = pm[:5]
433 call('version', vrs, iff=vrs))
434 self.assertEqual(SH.mock_calls[-4:], [
435 call('If-Match', etag),
436 call('If-None-Match', netag),
437 call('If-Modified-Since', ims),
438 call('If-Unmodified-Since', ius)])
439 acc, cont = self.client.account, self.client.container
440 self.assertEqual(head.mock_calls[-1], call(
441 '/%s/%s/%s' % (acc, cont, obj),
443 success=kwargs.pop('success', 200),
446 @patch('%s.set_param' % rest_pkg)
447 @patch('%s.set_header' % rest_pkg)
448 @patch('%s.get' % rest_pkg, return_value=FR())
449 def test_object_get(self, get, SH, SP):
454 (None, 'range=74-63'),
461 (dict(), dict(success=400), dict(k='v', v='k'))):
462 args, kwargs = pm[-2:]
464 self.client.object_get(obj, *(pm + args), **kwargs)
465 format, hashmap, version = pm[:3]
466 self.assertEqual(SP.mock_calls[-3:], [
467 call('format', format, iff=format),
468 call('hashmap', hashmap, iff=hashmap),
469 call('version', version, iff=version)])
470 rng, ifrng, im, inm, ims, ius = pm[-6:]
471 self.assertEqual(SH.mock_calls[-6:], [
473 call('If-Range', '', ifrng and rng),
474 call('If-Match', im),
475 call('If-None-Match', inm),
476 call('If-Modified-Since', ims),
477 call('If-Unmodified-Since', ius)])
478 acc, cont = self.client.account, self.client.container
479 self.assertEqual(get.mock_calls[-1], call(
480 '/%s/%s/%s' % (acc, cont, obj),
482 success=kwargs.pop('success', 200),
485 @patch('%s.set_param' % rest_pkg)
486 @patch('%s.set_header' % rest_pkg)
487 @patch('%s.put' % rest_pkg, return_value=FR())
488 def test_object_put(self, put, SH, SP):
493 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
495 (dict(), dict(k2='v2', k3='v3')),
497 (dict(), dict(success=400), dict(k='v', v='k'))):
498 args, kwargs = pm[-2:]
501 for i in range(len(terms)):
503 terms[i] = 'val_%s' % randint(13, 1024)
504 self.client.object_put(
506 *(pm[:3] + tuple(terms) + pm[3:] + args),
508 format, hashmap, delimiter = pm[:3]
509 self.assertEqual(SP.mock_calls[-3:], [
510 call('format', format, iff=format),
511 call('hashmap', hashmap, iff=hashmap),
512 call('delimiter', delimiter, iff=delimiter)])
514 im, inm, etag, clen, ctype, trenc,
515 cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
516 perms, public, metas = pm[3:]
518 call('If-Match', im),
519 call('If-None-Match', inm),
521 call('Content-Length', clen),
522 call('Content-Type', ctype),
523 call('Transfer-Encoding', trenc),
524 call('X-Copy-From', cp),
525 call('X-Move-From', mv),
526 call('X-Source-Account', srcacc),
527 call('X-Source-Version', srcvrs),
528 call('Content-Encoding', conenc),
529 call('Content-Disposition', condis),
530 call('X-Object-Manifest', mnf)]
533 for ptype, pval in perms.items():
535 perm_str += ';' if perm_str else ''
536 perm_str += '%s=%s' % (ptype, ','.join(pval))
537 exp += [call('X-Object-Sharing', perm_str)]
538 exp += [call('X-Object-Public', public)]
539 for k, v in metas.items():
540 exp += [call('X-Object-Meta-%s' % k, v)]
541 self.assertEqual(SH.mock_calls[- len(exp):], exp)
542 acc, cont = self.client.account, self.client.container
543 self.assertEqual(put.mock_calls[-1], call(
544 '/%s/%s/%s' % (acc, cont, obj),
546 success=kwargs.pop('success', 201),
549 @patch('%s.set_param' % rest_pkg)
550 @patch('%s.set_header' % rest_pkg)
551 @patch('%s.copy' % rest_pkg, return_value=FR())
552 def test_object_copy(self, copy, SH, SP):
558 (None, 'ifnonematch'),
559 (None, 'destinationaccount'),
560 (None, 'content-type'),
561 (None, 'content-encoding'),
562 (None, 'content-disp'),
563 (None, 'source-version'),
564 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
566 (dict(), dict(k2='v2', k3='v3')),
568 (dict(), dict(success=400), dict(k='v', v='k'))):
569 args, kwargs = pm[-2:]
571 self.client.object_copy(obj, dest, *(pm + args), **kwargs)
573 self.assertEqual(SP.mock_calls[-2:], [
574 call('format', format, iff=format),
575 call('ignore_content_type', iff=ict)])
576 im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
577 exp = [call('If-Match', im),
578 call('If-None-Match', inm),
579 call('Destination', dest),
580 call('Destination-Account', da),
581 call('Content-Type', ct),
582 call('Content-Encoding', ce),
583 call('Content-Disposition', cd),
584 call('X-Source-Version', sv)]
587 for ptype, pval in perms.items():
589 perm_str += ';' if perm_str else ''
590 perm_str += '%s=%s' % (ptype, ','.join(pval))
591 exp += [call('X-Object-Sharing', perm_str)]
592 exp += [call('X-Object-Public', public)]
593 for k, v in metas.items():
594 exp += [call('X-Object-Meta-%s' % k, v)]
595 self.assertEqual(SH.mock_calls[- len(exp):], exp)
596 acc, cont = self.client.account, self.client.container
597 self.assertEqual(copy.mock_calls[-1], call(
598 '/%s/%s/%s' % (acc, cont, obj),
600 success=kwargs.pop('success', 201),
603 @patch('%s.set_param' % rest_pkg)
604 @patch('%s.set_header' % rest_pkg)
605 @patch('%s.move' % rest_pkg, return_value=FR())
606 def test_object_move(self, move, SH, SP):
611 (None, 'ifnonematch'),
612 (None, 'destination'),
613 (None, 'destinationaccount'),
614 (None, 'content-type'),
615 (None, 'content-encoding'),
616 (None, 'content-disp'),
617 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
619 (dict(), dict(k2='v2', k3='v3')),
621 (dict(), dict(success=400), dict(k='v', v='k'))):
622 args, kwargs = pm[-2:]
624 self.client.object_move(obj, *(pm + args), **kwargs)
626 self.assertEqual(SP.mock_calls[-2:], [
627 call('format', format, iff=format),
628 call('ignore_content_type', iff=ict)])
629 im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
630 exp = [call('If-Match', im),
631 call('If-None-Match', inm),
632 call('Destination', d),
633 call('Destination-Account', da),
634 call('Content-Type', ct),
635 call('Content-Encoding', ce),
636 call('Content-Disposition', cd)]
639 for ptype, pval in perms.items():
641 perm_str += ';' if perm_str else ''
642 perm_str += '%s=%s' % (ptype, ','.join(pval))
643 exp += [call('X-Object-Sharing', perm_str)]
644 exp += [call('X-Object-Public', public)]
645 for k, v in metas.items():
646 exp += [call('X-Object-Meta-%s' % k, v)]
647 self.assertEqual(SH.mock_calls[- len(exp):], exp)
648 acc, cont = self.client.account, self.client.container
649 self.assertEqual(move.mock_calls[-1], call(
650 '/%s/%s/%s' % (acc, cont, obj),
652 success=kwargs.pop('success', 201),
655 @patch('%s.set_param' % rest_pkg)
656 @patch('%s.set_header' % rest_pkg)
657 @patch('%s.post' % rest_pkg, return_value=FR())
658 def test_object_post(self, post, SH, SP):
662 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
664 (dict(), dict(k2='v2', k3='v3')),
666 (dict(), dict(success=400), dict(k='v', v='k'))):
667 args, kwargs = pm[-2:]
670 for i in range(len(terms)):
672 terms[i] = 'val_%s' % randint(13, 1024)
673 self.client.object_post(
675 *(pm[:2] + tuple(terms) + pm[2:] + args),
677 format, update = pm[:2]
678 self.assertEqual(SP.mock_calls[-2:], [
679 call('format', format, iff=format),
680 call('update', iff=update)])
682 im, inm, clen, ctype, crng, trenc, cenc,
683 condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
685 call('If-Match', im),
686 call('If-None-Match', inm),
687 call('Content-Length', clen, iff=not trenc),
688 call('Content-Type', ctype),
689 call('Content-Range', crng),
690 call('Transfer-Encoding', trenc),
691 call('Content-Encoding', cenc),
692 call('Content-Disposition', condis),
693 call('X-Source-Object', srcobj),
694 call('X-Source-Account', srcacc),
695 call('X-Source-Version', srcvrs),
696 call('X-Object-Bytes', obytes),
697 call('X-Object-Manifest', mnfs)]
698 perms, public, metas = pm[2:]
701 for ptype, pval in perms.items():
703 perm_str += ';' if perm_str else ''
704 perm_str += '%s=%s' % (ptype, ','.join(pval))
705 exp += [call('X-Object-Sharing', perm_str)]
706 exp += [call('X-Object-Public', public)]
707 for k, v in metas.items():
708 exp += [call('X-Object-Meta-%s' % k, v)]
709 self.assertEqual(SH.mock_calls[- len(exp):], exp)
710 acc, cont = self.client.account, self.client.container
711 self.assertEqual(post.mock_calls[-1], call(
712 '/%s/%s/%s' % (acc, cont, obj),
714 success=kwargs.pop('success', (202, 204)),
717 @patch('%s.set_param' % rest_pkg)
718 @patch('%s.delete' % rest_pkg, return_value=FR())
719 def test_object_delete(self, delete, SP):
724 (dict(), dict(success=400), dict(k='v', v='k'))):
725 args, kwargs = pm[-2:]
727 self.client.object_delete(
732 self.assertEqual(SP.mock_calls[-2:], [
733 call('until', until, iff=until),
734 call('delimiter', dlm, iff=dlm)])
735 acc, cont = self.client.account, self.client.container
736 self.assertEqual(delete.mock_calls[-1], call(
737 '/%s/%s/%s' % (acc, cont, obj),
739 success=kwargs.pop('success', 204),
743 class PithosClient(TestCase):
747 def _create_temp_file(self, num_of_blocks):
748 self.files.append(NamedTemporaryFile())
749 tmpFile = self.files[-1]
750 file_size = num_of_blocks * 4 * 1024 * 1024
751 print('\n\tCreate tmp file')
752 tmpFile.write(urandom(file_size))
758 def assert_dicts_are_equal(self, d1, d2):
759 for k, v in d1.items():
760 self.assertTrue(k in d2)
761 if isinstance(v, dict):
762 self.assert_dicts_are_equal(v, d2[k])
764 self.assertEqual(unicode(v), unicode(d2[k]))
767 self.url = 'https://www.example.com/pithos'
768 self.token = 'p17h0570k3n'
769 self.client = pithos.PithosClient(self.url, self.token)
770 self.client.account = user_id
771 self.client.container = 'c0nt@1n3r_i'
781 # Pithos+ methods that extend storage API
783 @patch('%s.account_head' % pithos_pkg, return_value=FR())
784 def test_get_account_info(self, AH):
785 FR.headers = account_info
786 for until in (None, 'un71L-d473'):
787 r = self.client.get_account_info(until=until)
788 self.assert_dicts_are_equal(r, account_info)
789 self.assertEqual(AH.mock_calls[-1], call(until=until))
791 self.assertRaises(ClientError, self.client.get_account_info)
793 @patch('%s.account_post' % pithos_pkg, return_value=FR())
794 def test_del_account_meta(self, AP):
795 keys = ['k1', 'k2', 'k3']
797 self.client.del_account_meta(key)
800 call(update=True, metadata={key: ''}))
802 @patch('%s.container_head' % pithos_pkg, return_value=FR())
803 def test_get_container_info(self, CH):
804 FR.headers = container_info
805 r = self.client.get_container_info()
806 self.assert_dicts_are_equal(r, container_info)
808 r = self.client.get_container_info(until=u)
809 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
811 @patch('%s.account_get' % pithos_pkg, return_value=FR())
812 def test_list_containers(self, get):
813 FR.json = container_list
814 r = self.client.list_containers()
815 get.assert_called_once_with()
816 for i in range(len(r)):
817 self.assert_dicts_are_equal(r[i], container_list[i])
819 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
820 @patch('%s.container_post' % pithos_pkg, return_value=FR())
821 @patch('%s.object_put' % pithos_pkg, return_value=FR())
822 def test_upload_object(self, OP, CP, GCI):
824 tmpFile = self._create_temp_file(num_of_blocks)
827 self.client.upload_object(obj, tmpFile)
828 self.assertEqual(GCI.mock_calls[-1], call())
829 [call1, call2] = OP.mock_calls
831 (args1, kwargs1) = call1[1:3]
832 (args2, kwargs2) = call2[1:3]
833 self.assertEqual(args1, (obj,))
839 hashes=['s0m3h@5h'] * num_of_blocks,
840 bytes=num_of_blocks * 4 * 1024 * 1024),
842 content_encoding=None,
843 content_type='application/octet-stream',
844 content_disposition=None,
847 for k, v in expected1.items():
849 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
850 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
852 self.assertEqual(v, kwargs1[k])
854 (args2, kwargs2) = call2[1:3]
855 self.assertEqual(args2, (obj,))
858 hashes=['s0m3h@5h'] * num_of_blocks,
859 bytes=num_of_blocks * 4 * 1024 * 1024),
860 content_type='application/octet-stream',
864 for k, v in expected2.items():
866 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
867 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
869 self.assertEqual(v, kwargs2[k])
875 from progress.bar import ShadyBar
876 blck_bar = ShadyBar('Mock blck calc.')
877 upld_bar = ShadyBar('Mock uplds')
882 if blck_bar and upld_bar:
885 for i in blck_bar.iter(range(n)):
890 for i in upld_bar.iter(range(n)):
895 self.client.upload_object(
897 hash_cb=blck_gen, upload_cb=upld_gen)
899 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
900 self.assertEqual(OP.mock_calls[i], c)
905 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
906 self.client.upload_object(obj, tmpFile,
907 content_type=ctype, sharing=sharing)
908 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
909 self.assert_dicts_are_equal(
910 OP.mock_calls[-2][2]['permissions'],
918 content_disposition=ctype + 'd15p051710n',
920 content_encoding='802.11')
921 self.client.upload_object(obj, tmpFile, **kwargs)
922 for arg, val in kwargs.items():
923 self.assertEqual(OP.mock_calls[-2][2][arg], val)
925 def test_get_object_info(self):
926 FR.headers = object_info
929 pithos.PithosClient, 'object_head',
930 return_value=FR()) as head:
931 r = self.client.get_object_info(obj)
932 self.assertEqual(r, object_info)
933 r = self.client.get_object_info(obj, version=version)
934 self.assertEqual(head.mock_calls, [
935 call(obj, version=None),
936 call(obj, version=version)])
938 pithos.PithosClient, 'object_head',
939 side_effect=ClientError('Obj not found', 404)):
942 self.client.get_object_info,
943 obj, version=version)
945 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
946 def test_get_object_meta(self, GOI):
947 for version in (None, 'v3r510n'):
948 r = self.client.get_object_meta(obj, version)
949 for k in [k for k in object_info if k.startswith('x-object-meta')]:
950 self.assertEqual(r.pop(k), object_info[k])
951 self.assertFalse(len(r))
952 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
954 @patch('%s.object_post' % pithos_pkg, return_value=FR())
955 def test_del_object_meta(self, post):
956 metakey = '50m3m3t4k3y'
957 self.client.del_object_meta(obj, metakey)
958 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
960 @patch('%s.object_put' % pithos_pkg, return_value=FR())
961 def test_copy_object(self, put):
962 src_cont = 'src-c0nt41n3r'
964 dst_cont = 'dst-c0nt41n3r'
971 copy_from='/%s/%s' % (src_cont, src_obj),
976 self.client.copy_object(src_cont, src_obj, dst_cont)
977 self.assertEqual(put.mock_calls[-1], expected)
978 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
979 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
981 source_version='src-v3r510n',
982 source_account='src-4cc0un7',
984 content_type='c0n73n7Typ3',
986 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
987 for k, v in kwargs.items():
988 self.assertEqual(v, put.mock_calls[-1][2][k])
990 @patch('%s.object_put' % pithos_pkg, return_value=FR())
991 def test_move_object(self, put):
992 src_cont = 'src-c0nt41n3r'
994 dst_cont = 'dst-c0nt41n3r'
1001 move_from='/%s/%s' % (src_cont, src_obj),
1004 source_version=None,
1006 self.client.move_object(src_cont, src_obj, dst_cont)
1007 self.assertEqual(put.mock_calls[-1], expected)
1008 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1009 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1011 source_version='src-v3r510n',
1012 source_account='src-4cc0un7',
1014 content_type='c0n73n7Typ3',
1016 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1017 for k, v in kwargs.items():
1018 self.assertEqual(v, put.mock_calls[-1][2][k])
1020 # Pithos+ only methods
1022 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1023 def test_purge_container(self, CD):
1024 self.client.purge_container()
1025 self.assertTrue('until' in CD.mock_calls[-1][2])
1026 cont = self.client.container
1027 self.client.purge_container('another-container')
1028 self.assertEqual(self.client.container, cont)
1030 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1031 def test_upload_object_unchunked(self, put):
1033 tmpFile = self._create_temp_file(num_of_blocks)
1036 data=num_of_blocks * 4 * 1024 * 1024,
1038 content_encoding='some content_encoding',
1039 content_type='some content-type',
1040 content_disposition='some content_disposition',
1042 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1043 self.client.upload_object_unchunked(obj, tmpFile)
1044 self.assertEqual(put.mock_calls[-1][1], (obj,))
1046 sorted(put.mock_calls[-1][2].keys()),
1047 sorted(expected.keys()))
1048 kwargs = dict(expected)
1049 kwargs.pop('success')
1050 kwargs['size'] = kwargs.pop('data')
1051 kwargs['sharing'] = kwargs.pop('permissions')
1053 self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1054 pmc = put.mock_calls[-1][2]
1055 for k, v in expected.items():
1057 self.assertEqual(len(pmc[k]), v)
1059 self.assertEqual(pmc[k], v)
1062 self.client.upload_object_unchunked,
1063 obj, tmpFile, withHashFile=True)
1065 @patch('%s.object_put' % pithos_pkg, return_value=FR())
1066 def test_create_object_by_manifestation(self, put):
1067 manifest = '%s/%s' % (self.client.container, obj)
1070 content_encoding='some content_encoding',
1071 content_type='some content-type',
1072 content_disposition='some content_disposition',
1074 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1075 self.client.create_object_by_manifestation(obj)
1076 expected = dict(content_length=0, manifest=manifest)
1078 expected['permissions' if k == 'sharing' else k] = None
1079 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1080 self.client.create_object_by_manifestation(obj, **kwargs)
1081 expected.update(kwargs)
1082 expected['permissions'] = expected.pop('sharing')
1083 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1085 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1086 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1087 def test_download_object(self, GET, GOH):
1089 tmpFile = self._create_temp_file(num_of_blocks)
1090 FR.content = tmpFile.read(4 * 1024 * 1024)
1091 tmpFile = self._create_temp_file(num_of_blocks)
1092 num_of_blocks = len(object_hashmap['hashes'])
1097 if_match='if and only if',
1098 if_none_match='if and only not',
1099 if_modified_since='what if not?',
1100 if_unmodified_since='this happens if not!',
1101 async_headers=dict(Range='bytes=0-88888888'))
1103 self.client.download_object(obj, tmpFile)
1104 self.assertEqual(len(GET.mock_calls), num_of_blocks)
1105 self.assertEqual(GET.mock_calls[-1][1], (obj,))
1106 for k, v in kwargs.items():
1107 if k == 'async_headers':
1108 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1109 elif k in ('resume', 'range_str'):
1112 self.assertEqual(GET.mock_calls[-1][2][k], None)
1114 # Check ranges are consecutive
1117 for c in GET.mock_calls:
1118 rng_str = c[2]['async_headers']['Range']
1119 (start, rng_str) = rng_str.split('=')
1120 (start, end) = rng_str.split('-')
1121 starts.append(start)
1124 for i, start in enumerate(sorted(starts)):
1126 int(ends[i - 1]) == int(start) - 1
1128 # With progress bars
1130 from progress.bar import ShadyBar
1131 dl_bar = ShadyBar('Mock dl')
1138 for i in dl_bar.iter(range(n)):
1143 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1144 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1147 kwargs.pop('async_headers')
1148 kwargs.pop('resume')
1149 self.client.download_object(obj, tmpFile, **kwargs)
1150 for k, v in kwargs.items():
1151 if k == 'range_str':
1153 GET.mock_calls[-1][2]['data_range'],
1156 self.assertEqual(GET.mock_calls[-1][2][k], v)
1158 # ALl options on no tty
1164 tmpFile.isatty = foo
1165 self.client.download_object(obj, tmpFile, **kwargs)
1166 for k, v in kwargs.items():
1167 if k == 'range_str':
1168 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1170 self.assertEqual(GET.mock_calls[-1][2][k], v)
1172 def test_get_object_hashmap(self):
1173 FR.json = object_hashmap
1174 for empty in (304, 412):
1176 pithos.PithosClient, 'object_get',
1177 side_effect=ClientError('Empty', status=empty)):
1178 r = self.client.get_object_hashmap(obj)
1179 self.assertEqual(r, {})
1185 if_etag_not_match=None,
1186 if_modified_since=None,
1187 if_unmodified_since=None)
1189 version='s0m3v3r51on',
1190 if_match='if match',
1191 if_none_match='if non match',
1192 if_modified_since='some date here',
1193 if_unmodified_since='some date here',
1196 pithos.PithosClient, 'object_get',
1197 return_value=FR()) as get:
1198 r = self.client.get_object_hashmap(obj)
1199 self.assertEqual(r, object_hashmap)
1200 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1201 r = self.client.get_object_hashmap(obj, **kwargs)
1202 exp_args['if_etag_match'] = kwargs.pop('if_match')
1203 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1204 exp_args.update(kwargs)
1205 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1207 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1208 def test_set_account_group(self, post):
1209 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1210 self.client.set_account_group(group, usernames)
1211 post.assert_called_once_with(update=True, groups={group: usernames})
1213 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1214 def test_del_account_group(self, post):
1215 group = 'aU53rGr0up'
1216 self.client.del_account_group(group)
1217 post.assert_called_once_with(update=True, groups={group: []})
1219 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1220 def test_get_account_quota(self, GAI):
1221 key = 'x-account-policy-quota'
1222 r = self.client.get_account_quota()
1223 GAI.assert_called_once_with()
1224 self.assertEqual(r[key], account_info[key])
1226 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1227 def test_get_account_versioning(self, GAI):
1228 key = 'x-account-policy-versioning'
1229 r = self.client.get_account_versioning()
1230 GAI.assert_called_once_with()
1231 self.assertEqual(r[key], account_info[key])
1233 def test_get_account_meta(self):
1234 key = 'x-account-meta-'
1236 pithos.PithosClient, 'get_account_info',
1237 return_value=account_info):
1238 r = self.client.get_account_meta()
1239 keys = [k for k in r if k.startswith(key)]
1240 self.assertFalse(keys)
1241 acc_info = dict(account_info)
1242 acc_info['%sk1' % key] = 'v1'
1243 acc_info['%sk2' % key] = 'v2'
1244 acc_info['%sk3' % key] = 'v3'
1246 pithos.PithosClient, 'get_account_info',
1247 return_value=acc_info):
1248 r = self.client.get_account_meta()
1249 for k in [k for k in acc_info if k.startswith(key)]:
1250 self.assertEqual(r[k], acc_info[k])
1252 def test_get_account_group(self):
1253 key = 'x-account-group-'
1255 pithos.PithosClient, 'get_account_info',
1256 return_value=account_info):
1257 r = self.client.get_account_group()
1258 keys = [k for k in r if k.startswith(key)]
1259 self.assertFalse(keys)
1260 acc_info = dict(account_info)
1261 acc_info['%sk1' % key] = 'g1'
1262 acc_info['%sk2' % key] = 'g2'
1263 acc_info['%sk3' % key] = 'g3'
1265 pithos.PithosClient, 'get_account_info',
1266 return_value=acc_info):
1267 r = self.client.get_account_group()
1268 for k in [k for k in acc_info if k.startswith(key)]:
1269 self.assertEqual(r[k], acc_info[k])
1271 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1272 def test_set_account_meta(self, post):
1273 metas = dict(k1='v1', k2='v2', k3='v3')
1274 self.client.set_account_meta(metas)
1275 post.assert_called_once_with(update=True, metadata=metas)
1277 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1278 def test_set_account_quota(self, post):
1280 self.client.set_account_quota(qu)
1281 post.assert_called_once_with(update=True, quota=qu)
1283 @patch('%s.account_post' % pithos_pkg, return_value=FR())
1284 def test_set_account_versioning(self, post):
1285 vrs = 'n3wV3r51on1ngTyp3'
1286 self.client.set_account_versioning(vrs)
1287 post.assert_called_once_with(update=True, versioning=vrs)
1289 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1290 def test_del_container(self, delete):
1292 dict(delimiter=None, until=None),
1293 dict(delimiter='X', until='50m3d473')):
1294 self.client.del_container(**kwarg)
1295 expected = dict(kwarg)
1296 expected['success'] = (204, 404, 409)
1297 self.assertEqual(delete.mock_calls[-1], call(**expected))
1298 for status_code in (404, 409):
1299 FR.status_code = status_code
1300 self.assertRaises(ClientError, self.client.del_container)
1302 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1303 def test_get_container_versioning(self, GCI):
1304 key = 'x-container-policy-versioning'
1306 bu_cnt = self.client.container
1307 for container in (None, cont):
1308 r = self.client.get_container_versioning(container=container)
1309 self.assertEqual(r[key], container_info[key])
1310 self.assertEqual(GCI.mock_calls[-1], call())
1311 self.assertEqual(bu_cnt, self.client.container)
1313 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1314 def test_get_container_quota(self, GCI):
1315 key = 'x-container-policy-quota'
1317 bu_cnt = self.client.container
1318 for container in (None, cont):
1319 r = self.client.get_container_quota(container=container)
1320 self.assertEqual(r[key], container_info[key])
1321 self.assertEqual(GCI.mock_calls[-1], call())
1322 self.assertEqual(bu_cnt, self.client.container)
1324 def test_get_container_meta(self):
1325 somedate = '50m3d473'
1326 key = 'x-container-meta'
1327 metaval = '50m3m374v41'
1328 container_plus = dict(container_info)
1329 container_plus[key] = metaval
1330 for ret in ((container_info, {}), (container_plus, {key: metaval})):
1332 pithos.PithosClient,
1333 'get_container_info',
1334 return_value=ret[0]) as GCI:
1335 for until in (None, somedate):
1336 r = self.client.get_container_meta(until=until)
1337 self.assertEqual(r, ret[1])
1338 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1340 def test_get_container_object_meta(self):
1341 somedate = '50m3d473'
1342 key = 'x-container-object-meta'
1343 metaval = '50m3m374v41'
1344 container_plus = dict(container_info)
1345 container_plus[key] = metaval
1347 (container_info, {key: ''}),
1348 (container_plus, {key: metaval})):
1350 pithos.PithosClient,
1351 'get_container_info',
1352 return_value=ret[0]) as GCI:
1353 for until in (None, somedate):
1354 r = self.client.get_container_object_meta(until=until)
1355 self.assertEqual(r, ret[1])
1356 self.assertEqual(GCI.mock_calls[-1], call(until=until))
1358 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1359 def test_set_container_meta(self, post):
1360 metas = dict(k1='v1', k2='v2', k3='v3')
1361 self.client.set_container_meta(metas)
1362 post.assert_called_once_with(update=True, metadata=metas)
1364 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1365 def test_del_container_meta(self, AP):
1366 self.client.del_container_meta('somekey')
1367 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1369 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1370 def test_set_container_quota(self, post):
1372 self.client.set_container_quota(qu)
1373 post.assert_called_once_with(update=True, quota=qu)
1375 @patch('%s.container_post' % pithos_pkg, return_value=FR())
1376 def test_set_container_versioning(self, post):
1377 vrs = 'n3wV3r51on1ngTyp3'
1378 self.client.set_container_versioning(vrs)
1379 post.assert_called_once_with(update=True, versioning=vrs)
1381 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1382 def test_del_object(self, delete):
1384 dict(delimiter=None, until=None),
1385 dict(delimiter='X', until='50m3d473')):
1386 self.client.del_object(obj, **kwarg)
1387 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1389 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1390 def test_set_object_meta(self, post):
1391 metas = dict(k1='v1', k2='v2', k3='v3')
1394 self.client.set_object_meta,
1395 obj, 'Non dict arg')
1396 self.client.set_object_meta(obj, metas)
1397 post.assert_called_once_with(obj, update=True, metadata=metas)
1399 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1400 def test_publish_object(self, post):
1401 oinfo = dict(object_info)
1403 oinfo['x-object-public'] = val
1405 pithos.PithosClient, 'get_object_info',
1406 return_value=oinfo) as GOF:
1407 r = self.client.publish_object(obj)
1409 post.mock_calls[-1],
1410 call(obj, public=True, update=True))
1411 self.assertEqual(GOF.mock_calls[-1], call(obj))
1412 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1414 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1415 def test_unpublish_object(self, post):
1416 self.client.unpublish_object(obj)
1417 post.assert_called_once_with(obj, public=False, update=True)
1419 def test_get_object_sharing(self):
1420 info = dict(object_info)
1421 expected = dict(read='u1,g1,u2', write='u1')
1422 info['x-object-sharing'] = '; '.join(
1423 ['%s=%s' % (k, v) for k, v in expected.items()])
1425 pithos.PithosClient, 'get_object_info',
1426 return_value=info) as GOF:
1427 r = self.client.get_object_sharing(obj)
1428 self.assertEqual(GOF.mock_calls[-1], call(obj))
1429 self.assert_dicts_are_equal(r, expected)
1430 info['x-object-sharing'] = '//'.join(
1431 ['%s=%s' % (k, v) for k, v in expected.items()])
1434 self.client.get_object_sharing,
1436 info['x-object-sharing'] = '; '.join(
1437 ['%s:%s' % (k, v) for k, v in expected.items()])
1440 self.client.get_object_sharing,
1442 info['x-object-sharing'] = 'read=%s' % expected['read']
1443 r = self.client.get_object_sharing(obj)
1444 expected.pop('write')
1445 self.assert_dicts_are_equal(r, expected)
1447 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1448 def test_set_object_sharing(self, OP):
1449 read_perms = ['u1', 'g1', 'u2', 'g2']
1450 write_perms = ['u1', 'g1']
1452 dict(read_permition=read_perms, write_permition=write_perms),
1453 dict(read_permition=read_perms),
1454 dict(write_permition=write_perms),
1456 self.client.set_object_sharing(obj, **kwargs)
1457 kwargs['read'] = kwargs.pop('read_permition', '')
1458 kwargs['write'] = kwargs.pop('write_permition', '')
1461 call(obj, update=True, permissions=kwargs))
1463 @patch('%s.set_object_sharing' % pithos_pkg)
1464 def test_del_object_sharing(self, SOS):
1465 self.client.del_object_sharing(obj)
1466 SOS.assert_called_once_with(obj)
1468 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1469 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1470 def test_append_object(self, post, GCI):
1472 tmpFile = self._create_temp_file(num_of_blocks)
1474 file_size = tmpFile.tell()
1475 for turn in range(2):
1479 from progress.bar import ShadyBar
1480 apn_bar = ShadyBar('Mock append')
1487 for i in apn_bar.iter(range(n)):
1494 self.client.append_object(
1496 upload_cb=append_gen if turn else None)
1497 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1498 (args, kwargs) = post.mock_calls[-1][1:3]
1499 self.assertEqual(args, (obj,))
1500 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1501 fsize = num_of_blocks * int(kwargs['content_length'])
1502 self.assertEqual(fsize, file_size)
1503 self.assertEqual(kwargs['content_range'], 'bytes */*')
1504 exp = 'application/octet-stream'
1505 self.assertEqual(kwargs['content_type'], exp)
1506 self.assertEqual(kwargs['update'], True)
1508 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1509 def test_truncate_object(self, post):
1511 self.client.truncate_object(obj, upto_bytes)
1512 post.assert_called_once_with(
1515 object_bytes=upto_bytes,
1516 content_range='bytes 0-%s/*' % upto_bytes,
1517 content_type='application/octet-stream',
1518 source_object='/%s/%s' % (self.client.container, obj))
1520 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1521 @patch('%s.object_post' % pithos_pkg, return_value=FR())
1522 def test_overwrite_object(self, post, GCI):
1524 tmpFile = self._create_temp_file(num_of_blocks)
1526 file_size = tmpFile.tell()
1527 info = dict(object_info)
1528 info['content-length'] = file_size
1529 block_size = container_info['x-container-block-size']
1531 pithos.PithosClient, 'get_object_info',
1532 return_value=info) as GOI:
1535 (file_size + 1, file_size + 2)):
1539 self.client.overwrite_object,
1540 obj, start, end, tmpFile)
1541 for start, end in ((0, 144), (144, 233), (233, file_size)):
1544 exp_size = end - start + 1
1545 if not start or exp_size > block_size:
1547 from progress.bar import ShadyBar
1548 owr_bar = ShadyBar('Mock append')
1555 for i in owr_bar.iter(range(n)):
1559 if exp_size > block_size:
1560 exp_size = exp_size % block_size or block_size
1562 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1563 self.assertEqual(GOI.mock_calls[-1], call(obj))
1564 self.assertEqual(GCI.mock_calls[-1], call())
1565 (args, kwargs) = post.mock_calls[-1][1:3]
1566 self.assertEqual(args, (obj,))
1567 self.assertEqual(len(kwargs['data']), exp_size)
1568 self.assertEqual(kwargs['content_length'], exp_size)
1569 self.assertEqual(kwargs['update'], True)
1570 exp = 'application/octet-stream'
1571 self.assertEqual(kwargs['content_type'], exp)
1573 @patch('%s.set_param' % pithos_pkg)
1574 @patch('%s.get' % pithos_pkg, return_value=FR())
1575 def test_get_sharing_accounts(self, get, SP):
1579 dict(limit='50m3-11m17'),
1581 dict(limit='50m3-11m17', marker='X')):
1582 r = self.client.get_sharing_accounts(**kws)
1583 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1584 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1585 limit, marker = kws.get('limit', None), kws.get('marker', None)
1586 self.assertEqual(SP.mock_calls[-2], call(
1588 iff=limit is not None))
1589 self.assertEqual(SP.mock_calls[-1], call(
1591 iff=marker is not None))
1592 for i in range(len(r)):
1593 self.assert_dicts_are_equal(r[i], sharers[i])
1595 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1596 def test_get_object_versionlist(self, get):
1597 info = dict(object_info)
1598 info['versions'] = ['v1', 'v2']
1600 r = self.client.get_object_versionlist(obj)
1601 get.assert_called_once_with(obj, format='json', version='list')
1602 self.assertEqual(r, info['versions'])
1604 if __name__ == '__main__':
1605 from sys import argv
1606 from kamaki.clients.test import runTestCase
1608 if not argv[1:] or argv[1] == 'PithosClient':
1610 runTestCase(PithosClient, 'Pithos Client', argv[2:])
1611 if not argv[1:] or argv[1] == 'PithosRestClient':
1613 runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1615 print('TestCase %s not found' % argv[1])