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
39 from kamaki.clients import ClientError
40 from kamaki.clients.pithos import PithosClient, PithosRestClient
42 pithos_pkg = 'kamaki.clients.pithos.PithosClient'
44 user_id = 'ac0un7-1d-5tr1ng'
48 'content-language': 'en-us',
49 'content-type': 'text/html; charset=utf-8',
50 'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
51 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
52 'server': 'gunicorn/0.14.5',
53 'vary': 'Accept-Language',
54 'x-account-bytes-used': '751615526',
55 'x-account-container-count': 7,
56 'x-account-policy-quota': 53687091200,
57 'x-account-policy-versioning': 'auto'}
59 'content-language': 'en-us',
60 'content-type': 'text/html; charset=utf-8',
61 'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
62 'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
63 'server': 'gunicorn/0.14.5',
64 'vary': 'Accept-Language',
65 'x-container-block-hash': 'sha256',
66 'x-container-block-size': 4194304,
67 'x-container-bytes-used': 309528938,
68 'x-container-object-count': 14,
69 'x-container-object-meta': '',
70 'x-container-policy-quota': 53687091200,
71 'x-container-policy-versioning': 'auto'}
73 'content-language': 'en-us',
74 'content-length': 254965,
75 'content-type': 'application/octet-stream',
76 'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
78 'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
79 'server': 'gunicorn/0.14.5',
80 'vary': 'Accept-Language',
81 'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
82 'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
83 'x-object-version': '525996',
84 'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
85 'x-object-meta-k1': 'v1',
86 'x-object-meta-k2': 'v2'}
90 last_modified="2013-02-27T11:56:09.893033+00:00",
93 x_container_policy=dict(quota="21474836480", versioning="auto")),
96 last_modified="2012-10-23T12:25:17.229187+00:00",
99 x_container_policy=dict(quota="21474836480", versioning="auto"))]
102 name="The_Secret_Garden.zip",
103 x_object_public="/public/wdp9p",
105 x_object_version_timestamp="1360237915.7027509",
106 x_object_uuid="s0m3uu1df0r0bj0n3",
107 last_modified="2013-02-07T11:51:55.702751+00:00",
108 content_type="application/octet-stream",
109 x_object_hash="0afdf29f71cd53126225c3f54ca",
110 x_object_version=17737,
111 x_object_modified_by=user_id),
113 name="The_Revealed_Garden.zip",
114 x_object_public="/public/wpd7p",
116 x_object_version_timestamp="13602915.7027509",
117 x_object_uuid="s0m3uu1df0r0bj70w",
118 last_modified="2013-02-07T11:51:55.702751+00:00",
119 content_type="application/octet-stream",
120 x_object_hash="0afdf29f71cd53126225c3f54ca",
121 x_object_version=17737,
122 x_object_modified_by=user_id)]
123 object_hashmap = dict(
124 block_hash="sha256", block_size=4194304, bytes=33554432,
126 "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
127 "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
128 "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
129 "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
130 "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
131 "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
132 "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
133 "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
135 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
136 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
137 dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
141 """FR stands for Fake Response"""
152 class PithosRest(TestCase):
155 self.url = 'https://www.example.com/pithos'
156 self.token = 'p17h0570k3n'
157 self.client = PithosClient(self.url, self.token)
158 self.client.account = user_id
159 self.client.container = 'c0nt@1n3r_i'
170 class Pithos(TestCase):
174 def _create_temp_file(self, num_of_blocks):
175 self.files.append(NamedTemporaryFile())
176 tmpFile = self.files[-1]
177 file_size = num_of_blocks * 4 * 1024 * 1024
178 print('\n\tCreate tmp file')
179 tmpFile.write(urandom(file_size))
185 def assert_dicts_are_equal(self, d1, d2):
186 for k, v in d1.items():
187 self.assertTrue(k in d2)
188 if isinstance(v, dict):
189 self.assert_dicts_are_equal(v, d2[k])
191 self.assertEqual(unicode(v), unicode(d2[k]))
194 self.url = 'https://www.example.com/pithos'
195 self.token = 'p17h0570k3n'
196 self.client = PithosClient(self.url, self.token)
197 self.client.account = user_id
198 self.client.container = 'c0nt@1n3r_i'
208 # Pithos+ methods that extend storage API
210 @patch('%s.account_head' % pithos_pkg, return_value=FR())
211 def test_get_account_info(self, AH):
212 FR.headers = account_info
213 for until in (None, 'un71L-d473'):
214 r = self.client.get_account_info(until=until)
215 self.assert_dicts_are_equal(r, account_info)
216 self.assertEqual(AH.mock_calls[-1], call(until=until))
218 self.assertRaises(ClientError, self.client.get_account_info)
220 @patch('%s.account_post' % pithos_pkg, return_value=FR())
221 def test_del_account_meta(self, AP):
222 keys = ['k1', 'k2', 'k3']
224 self.client.del_account_meta(key)
227 call(update=True, metadata={key: ''}))
229 @patch('%s.container_head' % pithos_pkg, return_value=FR())
230 def test_get_container_info(self, CH):
231 FR.headers = container_info
232 r = self.client.get_container_info()
233 self.assert_dicts_are_equal(r, container_info)
235 r = self.client.get_container_info(until=u)
236 self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
238 @patch('%s.account_get' % pithos_pkg, return_value=FR())
239 def test_list_containers(self, get):
240 FR.json = container_list
241 r = self.client.list_containers()
242 get.assert_called_once_with()
243 for i in range(len(r)):
244 self.assert_dicts_are_equal(r[i], container_list[i])
246 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
247 @patch('%s.container_post' % pithos_pkg, return_value=FR())
248 @patch('%s.object_put' % pithos_pkg, return_value=FR())
249 def test_upload_object(self, OP, CP, GCI):
251 tmpFile = self._create_temp_file(num_of_blocks)
254 self.client.upload_object(obj, tmpFile)
255 self.assertEqual(GCI.mock_calls[-1], call())
256 [call1, call2] = OP.mock_calls
258 (args1, kwargs1) = call1[1:3]
259 (args2, kwargs2) = call2[1:3]
260 self.assertEqual(args1, (obj,))
266 hashes=['s0m3h@5h'] * num_of_blocks,
267 bytes=num_of_blocks * 4 * 1024 * 1024),
269 content_encoding=None,
270 content_type='application/octet-stream',
271 content_disposition=None,
274 for k, v in expected1.items():
276 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
277 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
279 self.assertEqual(v, kwargs1[k])
281 (args2, kwargs2) = call2[1:3]
282 self.assertEqual(args2, (obj,))
285 hashes=['s0m3h@5h'] * num_of_blocks,
286 bytes=num_of_blocks * 4 * 1024 * 1024),
287 content_type='application/octet-stream',
291 for k, v in expected2.items():
293 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
294 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
296 self.assertEqual(v, kwargs2[k])
302 from progress.bar import ShadyBar
303 blck_bar = ShadyBar('Mock blck calc.')
304 upld_bar = ShadyBar('Mock uplds')
309 if blck_bar and upld_bar:
312 for i in blck_bar.iter(range(n)):
317 for i in upld_bar.iter(range(n)):
322 self.client.upload_object(
324 hash_cb=blck_gen, upload_cb=upld_gen)
326 for i, c in enumerate(OP.mock_calls[-mock_offset:]):
327 self.assertEqual(OP.mock_calls[i], c)
332 sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
333 self.client.upload_object(obj, tmpFile,
334 content_type=ctype, sharing=sharing)
335 self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
336 self.assert_dicts_are_equal(
337 OP.mock_calls[-2][2]['permissions'],
345 content_disposition=ctype + 'd15p051710n',
347 content_encoding='802.11')
348 self.client.upload_object(obj, tmpFile, **kwargs)
349 for arg, val in kwargs.items():
350 self.assertEqual(OP.mock_calls[-2][2][arg], val)
352 def test_get_object_info(self):
353 FR.headers = object_info
356 PithosClient, 'object_head',
357 return_value=FR()) as head:
358 r = self.client.get_object_info(obj)
359 self.assertEqual(r, object_info)
360 r = self.client.get_object_info(obj, version=version)
361 self.assertEqual(head.mock_calls, [
362 call(obj, version=None),
363 call(obj, version=version)])
365 PithosClient, 'object_head',
366 side_effect=ClientError('Obj not found', 404)):
369 self.client.get_object_info,
370 obj, version=version)
372 @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
373 def test_get_object_meta(self, GOI):
374 for version in (None, 'v3r510n'):
375 r = self.client.get_object_meta(obj, version)
376 for k in [k for k in object_info if k.startswith('x-object-meta')]:
377 self.assertEqual(r.pop(k), object_info[k])
378 self.assertFalse(len(r))
379 self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
381 @patch('%s.object_post' % pithos_pkg, return_value=FR())
382 def test_del_object_meta(self, post):
383 metakey = '50m3m3t4k3y'
384 self.client.del_object_meta(obj, metakey)
385 post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
387 @patch('%s.object_put' % pithos_pkg, return_value=FR())
388 def test_copy_object(self, put):
389 src_cont = 'src-c0nt41n3r'
391 dst_cont = 'dst-c0nt41n3r'
398 copy_from='/%s/%s' % (src_cont, src_obj),
403 self.client.copy_object(src_cont, src_obj, dst_cont)
404 self.assertEqual(put.mock_calls[-1], expected)
405 self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
406 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
408 source_version='src-v3r510n',
409 source_account='src-4cc0un7',
411 content_type='c0n73n7Typ3',
413 self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
414 for k, v in kwargs.items():
415 self.assertEqual(v, put.mock_calls[-1][2][k])
417 @patch('%s.object_put' % pithos_pkg, return_value=FR())
418 def test_move_object(self, put):
419 src_cont = 'src-c0nt41n3r'
421 dst_cont = 'dst-c0nt41n3r'
428 move_from='/%s/%s' % (src_cont, src_obj),
433 self.client.move_object(src_cont, src_obj, dst_cont)
434 self.assertEqual(put.mock_calls[-1], expected)
435 self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
436 self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
438 source_version='src-v3r510n',
439 source_account='src-4cc0un7',
441 content_type='c0n73n7Typ3',
443 self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
444 for k, v in kwargs.items():
445 self.assertEqual(v, put.mock_calls[-1][2][k])
447 # Pithos+ only methods
449 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
450 def test_purge_container(self, CD):
451 self.client.purge_container()
452 self.assertTrue('until' in CD.mock_calls[-1][2])
453 cont = self.client.container
454 self.client.purge_container('another-container')
455 self.assertEqual(self.client.container, cont)
457 @patch('%s.object_put' % pithos_pkg, return_value=FR())
458 def test_upload_object_unchunked(self, put):
460 tmpFile = self._create_temp_file(num_of_blocks)
463 data=num_of_blocks * 4 * 1024 * 1024,
465 content_encoding='some content_encoding',
466 content_type='some content-type',
467 content_disposition='some content_disposition',
469 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
470 self.client.upload_object_unchunked(obj, tmpFile)
471 self.assertEqual(put.mock_calls[-1][1], (obj,))
473 sorted(put.mock_calls[-1][2].keys()),
474 sorted(expected.keys()))
475 kwargs = dict(expected)
476 kwargs.pop('success')
477 kwargs['size'] = kwargs.pop('data')
478 kwargs['sharing'] = kwargs.pop('permissions')
480 self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
481 pmc = put.mock_calls[-1][2]
482 for k, v in expected.items():
484 self.assertEqual(len(pmc[k]), v)
486 self.assertEqual(pmc[k], v)
489 self.client.upload_object_unchunked,
490 obj, tmpFile, withHashFile=True)
492 @patch('%s.object_put' % pithos_pkg, return_value=FR())
493 def test_create_object_by_manifestation(self, put):
494 manifest = '%s/%s' % (self.client.container, obj)
497 content_encoding='some content_encoding',
498 content_type='some content-type',
499 content_disposition='some content_disposition',
501 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
502 self.client.create_object_by_manifestation(obj)
503 expected = dict(content_length=0, manifest=manifest)
505 expected['permissions' if k == 'sharing' else k] = None
506 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
507 self.client.create_object_by_manifestation(obj, **kwargs)
508 expected.update(kwargs)
509 expected['permissions'] = expected.pop('sharing')
510 self.assertEqual(put.mock_calls[-1], call(obj, **expected))
512 @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
513 @patch('%s.object_get' % pithos_pkg, return_value=FR())
514 def test_download_object(self, GET, GOH):
516 tmpFile = self._create_temp_file(num_of_blocks)
517 FR.content = tmpFile.read(4 * 1024 * 1024)
518 tmpFile = self._create_temp_file(num_of_blocks)
519 num_of_blocks = len(object_hashmap['hashes'])
524 if_match='if and only if',
525 if_none_match='if and only not',
526 if_modified_since='what if not?',
527 if_unmodified_since='this happens if not!',
528 async_headers=dict(Range='bytes=0-88888888'))
530 self.client.download_object(obj, tmpFile)
531 self.assertEqual(len(GET.mock_calls), num_of_blocks)
532 self.assertEqual(GET.mock_calls[-1][1], (obj,))
533 for k, v in kwargs.items():
534 if k == 'async_headers':
535 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
536 elif k in ('resume', 'range_str'):
539 self.assertEqual(GET.mock_calls[-1][2][k], None)
541 # Check ranges are consecutive
544 for c in GET.mock_calls:
545 rng_str = c[2]['async_headers']['Range']
546 (start, rng_str) = rng_str.split('=')
547 (start, end) = rng_str.split('-')
551 for i, start in enumerate(sorted(starts)):
553 int(ends[i - 1]) == int(start) - 1
557 from progress.bar import ShadyBar
558 dl_bar = ShadyBar('Mock dl')
565 for i in dl_bar.iter(range(n)):
570 self.client.download_object(obj, tmpFile, download_cb=blck_gen)
571 self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
574 kwargs.pop('async_headers')
576 self.client.download_object(obj, tmpFile, **kwargs)
577 for k, v in kwargs.items():
580 GET.mock_calls[-1][2]['data_range'],
583 self.assertEqual(GET.mock_calls[-1][2][k], v)
585 # ALl options on no tty
592 self.client.download_object(obj, tmpFile, **kwargs)
593 for k, v in kwargs.items():
595 self.assertTrue('data_range' in GET.mock_calls[-1][2])
597 self.assertEqual(GET.mock_calls[-1][2][k], v)
599 def test_get_object_hashmap(self):
600 FR.json = object_hashmap
601 for empty in (304, 412):
603 PithosClient, 'object_get',
604 side_effect=ClientError('Empty', status=empty)):
605 r = self.client.get_object_hashmap(obj)
606 self.assertEqual(r, {})
612 if_etag_not_match=None,
613 if_modified_since=None,
614 if_unmodified_since=None)
616 version='s0m3v3r51on',
618 if_none_match='if non match',
619 if_modified_since='some date here',
620 if_unmodified_since='some date here',
623 PithosClient, 'object_get',
624 return_value=FR()) as get:
625 r = self.client.get_object_hashmap(obj)
626 self.assertEqual(r, object_hashmap)
627 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
628 r = self.client.get_object_hashmap(obj, **kwargs)
629 exp_args['if_etag_match'] = kwargs.pop('if_match')
630 exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
631 exp_args.update(kwargs)
632 self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
634 @patch('%s.account_post' % pithos_pkg, return_value=FR())
635 def test_set_account_group(self, post):
636 (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
637 self.client.set_account_group(group, usernames)
638 post.assert_called_once_with(update=True, groups={group: usernames})
640 @patch('%s.account_post' % pithos_pkg, return_value=FR())
641 def test_del_account_group(self, post):
643 self.client.del_account_group(group)
644 post.assert_called_once_with(update=True, groups={group: []})
646 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
647 def test_get_account_quota(self, GAI):
648 key = 'x-account-policy-quota'
649 r = self.client.get_account_quota()
650 GAI.assert_called_once_with()
651 self.assertEqual(r[key], account_info[key])
653 @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
654 def test_get_account_versioning(self, GAI):
655 key = 'x-account-policy-versioning'
656 r = self.client.get_account_versioning()
657 GAI.assert_called_once_with()
658 self.assertEqual(r[key], account_info[key])
660 def test_get_account_meta(self):
661 key = 'x-account-meta-'
663 PithosClient, 'get_account_info',
664 return_value=account_info):
665 r = self.client.get_account_meta()
666 keys = [k for k in r if k.startswith(key)]
667 self.assertFalse(keys)
668 acc_info = dict(account_info)
669 acc_info['%sk1' % key] = 'v1'
670 acc_info['%sk2' % key] = 'v2'
671 acc_info['%sk3' % key] = 'v3'
673 PithosClient, 'get_account_info',
674 return_value=acc_info):
675 r = self.client.get_account_meta()
676 for k in [k for k in acc_info if k.startswith(key)]:
677 self.assertEqual(r[k], acc_info[k])
679 def test_get_account_group(self):
680 key = 'x-account-group-'
682 PithosClient, 'get_account_info',
683 return_value=account_info):
684 r = self.client.get_account_group()
685 keys = [k for k in r if k.startswith(key)]
686 self.assertFalse(keys)
687 acc_info = dict(account_info)
688 acc_info['%sk1' % key] = 'g1'
689 acc_info['%sk2' % key] = 'g2'
690 acc_info['%sk3' % key] = 'g3'
692 PithosClient, 'get_account_info',
693 return_value=acc_info):
694 r = self.client.get_account_group()
695 for k in [k for k in acc_info if k.startswith(key)]:
696 self.assertEqual(r[k], acc_info[k])
698 @patch('%s.account_post' % pithos_pkg, return_value=FR())
699 def test_set_account_meta(self, post):
700 metas = dict(k1='v1', k2='v2', k3='v3')
701 self.client.set_account_meta(metas)
702 post.assert_called_once_with(update=True, metadata=metas)
704 @patch('%s.account_post' % pithos_pkg, return_value=FR())
705 def test_set_account_quota(self, post):
707 self.client.set_account_quota(qu)
708 post.assert_called_once_with(update=True, quota=qu)
710 @patch('%s.account_post' % pithos_pkg, return_value=FR())
711 def test_set_account_versioning(self, post):
712 vrs = 'n3wV3r51on1ngTyp3'
713 self.client.set_account_versioning(vrs)
714 post.assert_called_once_with(update=True, versioning=vrs)
716 @patch('%s.container_delete' % pithos_pkg, return_value=FR())
717 def test_del_container(self, delete):
719 dict(delimiter=None, until=None),
720 dict(delimiter='X', until='50m3d473')):
721 self.client.del_container(**kwarg)
722 expected = dict(kwarg)
723 expected['success'] = (204, 404, 409)
724 self.assertEqual(delete.mock_calls[-1], call(**expected))
725 for status_code in (404, 409):
726 FR.status_code = status_code
727 self.assertRaises(ClientError, self.client.del_container)
729 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
730 def test_get_container_versioning(self, GCI):
731 key = 'x-container-policy-versioning'
733 bu_cnt = self.client.container
734 for container in (None, cont):
735 r = self.client.get_container_versioning(container=container)
736 self.assertEqual(r[key], container_info[key])
737 self.assertEqual(GCI.mock_calls[-1], call())
738 self.assertEqual(bu_cnt, self.client.container)
740 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
741 def test_get_container_quota(self, GCI):
742 key = 'x-container-policy-quota'
744 bu_cnt = self.client.container
745 for container in (None, cont):
746 r = self.client.get_container_quota(container=container)
747 self.assertEqual(r[key], container_info[key])
748 self.assertEqual(GCI.mock_calls[-1], call())
749 self.assertEqual(bu_cnt, self.client.container)
751 def test_get_container_meta(self):
752 somedate = '50m3d473'
753 key = 'x-container-meta'
754 metaval = '50m3m374v41'
755 container_plus = dict(container_info)
756 container_plus[key] = metaval
757 for ret in ((container_info, {}), (container_plus, {key: metaval})):
760 'get_container_info',
761 return_value=ret[0]) as GCI:
762 for until in (None, somedate):
763 r = self.client.get_container_meta(until=until)
764 self.assertEqual(r, ret[1])
765 self.assertEqual(GCI.mock_calls[-1], call(until=until))
767 def test_get_container_object_meta(self):
768 somedate = '50m3d473'
769 key = 'x-container-object-meta'
770 metaval = '50m3m374v41'
771 container_plus = dict(container_info)
772 container_plus[key] = metaval
774 (container_info, {key: ''}),
775 (container_plus, {key: metaval})):
778 'get_container_info',
779 return_value=ret[0]) as GCI:
780 for until in (None, somedate):
781 r = self.client.get_container_object_meta(until=until)
782 self.assertEqual(r, ret[1])
783 self.assertEqual(GCI.mock_calls[-1], call(until=until))
785 @patch('%s.container_post' % pithos_pkg, return_value=FR())
786 def test_set_container_meta(self, post):
787 metas = dict(k1='v1', k2='v2', k3='v3')
788 self.client.set_container_meta(metas)
789 post.assert_called_once_with(update=True, metadata=metas)
791 @patch('%s.container_post' % pithos_pkg, return_value=FR())
792 def test_del_container_meta(self, AP):
793 self.client.del_container_meta('somekey')
794 AP.assert_called_once_with(update=True, metadata={'somekey': ''})
796 @patch('%s.container_post' % pithos_pkg, return_value=FR())
797 def test_set_container_quota(self, post):
799 self.client.set_container_quota(qu)
800 post.assert_called_once_with(update=True, quota=qu)
802 @patch('%s.container_post' % pithos_pkg, return_value=FR())
803 def test_set_container_versioning(self, post):
804 vrs = 'n3wV3r51on1ngTyp3'
805 self.client.set_container_versioning(vrs)
806 post.assert_called_once_with(update=True, versioning=vrs)
808 @patch('%s.object_delete' % pithos_pkg, return_value=FR())
809 def test_del_object(self, delete):
811 dict(delimiter=None, until=None),
812 dict(delimiter='X', until='50m3d473')):
813 self.client.del_object(obj, **kwarg)
814 self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
816 @patch('%s.object_post' % pithos_pkg, return_value=FR())
817 def test_set_object_meta(self, post):
818 metas = dict(k1='v1', k2='v2', k3='v3')
821 self.client.set_object_meta,
823 self.client.set_object_meta(obj, metas)
824 post.assert_called_once_with(obj, update=True, metadata=metas)
826 @patch('%s.object_post' % pithos_pkg, return_value=FR())
827 def test_publish_object(self, post):
828 oinfo = dict(object_info)
830 oinfo['x-object-public'] = val
832 PithosClient, 'get_object_info',
833 return_value=oinfo) as GOF:
834 r = self.client.publish_object(obj)
837 call(obj, public=True, update=True))
838 self.assertEqual(GOF.mock_calls[-1], call(obj))
839 self.assertEqual(r, '%s%s' % (self.url[:-6], val))
841 @patch('%s.object_post' % pithos_pkg, return_value=FR())
842 def test_unpublish_object(self, post):
843 self.client.unpublish_object(obj)
844 post.assert_called_once_with(obj, public=False, update=True)
846 def test_get_object_sharing(self):
847 info = dict(object_info)
848 expected = dict(read='u1,g1,u2', write='u1')
849 info['x-object-sharing'] = '; '.join(
850 ['%s=%s' % (k, v) for k, v in expected.items()])
852 PithosClient, 'get_object_info',
853 return_value=info) as GOF:
854 r = self.client.get_object_sharing(obj)
855 self.assertEqual(GOF.mock_calls[-1], call(obj))
856 self.assert_dicts_are_equal(r, expected)
857 info['x-object-sharing'] = '//'.join(
858 ['%s=%s' % (k, v) for k, v in expected.items()])
861 self.client.get_object_sharing,
863 info['x-object-sharing'] = '; '.join(
864 ['%s:%s' % (k, v) for k, v in expected.items()])
867 self.client.get_object_sharing,
869 info['x-object-sharing'] = 'read=%s' % expected['read']
870 r = self.client.get_object_sharing(obj)
871 expected.pop('write')
872 self.assert_dicts_are_equal(r, expected)
874 @patch('%s.object_post' % pithos_pkg, return_value=FR())
875 def test_set_object_sharing(self, OP):
876 read_perms = ['u1', 'g1', 'u2', 'g2']
877 write_perms = ['u1', 'g1']
879 dict(read_permition=read_perms, write_permition=write_perms),
880 dict(read_permition=read_perms),
881 dict(write_permition=write_perms),
883 self.client.set_object_sharing(obj, **kwargs)
884 kwargs['read'] = kwargs.pop('read_permition', '')
885 kwargs['write'] = kwargs.pop('write_permition', '')
888 call(obj, update=True, permissions=kwargs))
890 @patch('%s.set_object_sharing' % pithos_pkg)
891 def test_del_object_sharing(self, SOS):
892 self.client.del_object_sharing(obj)
893 SOS.assert_called_once_with(obj)
895 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
896 @patch('%s.object_post' % pithos_pkg, return_value=FR())
897 def test_append_object(self, post, GCI):
899 tmpFile = self._create_temp_file(num_of_blocks)
901 file_size = tmpFile.tell()
902 for turn in range(2):
906 from progress.bar import ShadyBar
907 apn_bar = ShadyBar('Mock append')
914 for i in apn_bar.iter(range(n)):
921 self.client.append_object(
923 upload_cb=append_gen if turn else None)
924 self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
925 (args, kwargs) = post.mock_calls[-1][1:3]
926 self.assertEqual(args, (obj,))
927 self.assertEqual(kwargs['content_length'], len(kwargs['data']))
928 fsize = num_of_blocks * int(kwargs['content_length'])
929 self.assertEqual(fsize, file_size)
930 self.assertEqual(kwargs['content_range'], 'bytes */*')
931 exp = 'application/octet-stream'
932 self.assertEqual(kwargs['content_type'], exp)
933 self.assertEqual(kwargs['update'], True)
935 @patch('%s.object_post' % pithos_pkg, return_value=FR())
936 def test_truncate_object(self, post):
938 self.client.truncate_object(obj, upto_bytes)
939 post.assert_called_once_with(
942 object_bytes=upto_bytes,
943 content_range='bytes 0-%s/*' % upto_bytes,
944 content_type='application/octet-stream',
945 source_object='/%s/%s' % (self.client.container, obj))
947 @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
948 @patch('%s.object_post' % pithos_pkg, return_value=FR())
949 def test_overwrite_object(self, post, GCI):
951 tmpFile = self._create_temp_file(num_of_blocks)
953 file_size = tmpFile.tell()
954 info = dict(object_info)
955 info['content-length'] = file_size
956 block_size = container_info['x-container-block-size']
958 PithosClient, 'get_object_info',
959 return_value=info) as GOI:
962 (file_size + 1, file_size + 2)):
966 self.client.overwrite_object,
967 obj, start, end, tmpFile)
968 for start, end in ((0, 144), (144, 233), (233, file_size)):
971 exp_size = end - start + 1
972 if not start or exp_size > block_size:
974 from progress.bar import ShadyBar
975 owr_bar = ShadyBar('Mock append')
982 for i in owr_bar.iter(range(n)):
986 if exp_size > block_size:
987 exp_size = exp_size % block_size or block_size
989 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
990 self.assertEqual(GOI.mock_calls[-1], call(obj))
991 self.assertEqual(GCI.mock_calls[-1], call())
992 (args, kwargs) = post.mock_calls[-1][1:3]
993 self.assertEqual(args, (obj,))
994 self.assertEqual(len(kwargs['data']), exp_size)
995 self.assertEqual(kwargs['content_length'], exp_size)
996 self.assertEqual(kwargs['update'], True)
997 exp = 'application/octet-stream'
998 self.assertEqual(kwargs['content_type'], exp)
1000 @patch('%s.set_param' % pithos_pkg)
1001 @patch('%s.get' % pithos_pkg, return_value=FR())
1002 def test_get_sharing_accounts(self, get, SP):
1006 dict(limit='50m3-11m17'),
1008 dict(limit='50m3-11m17', marker='X')):
1009 r = self.client.get_sharing_accounts(**kws)
1010 self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1011 self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1012 limit, marker = kws.get('limit', None), kws.get('marker', None)
1013 self.assertEqual(SP.mock_calls[-2], call(
1015 iff=limit is not None))
1016 self.assertEqual(SP.mock_calls[-1], call(
1018 iff=marker is not None))
1019 for i in range(len(r)):
1020 self.assert_dicts_are_equal(r[i], sharers[i])
1022 @patch('%s.object_get' % pithos_pkg, return_value=FR())
1023 def test_get_object_versionlist(self, get):
1024 info = dict(object_info)
1025 info['versions'] = ['v1', 'v2']
1027 r = self.client.get_object_versionlist(obj)
1028 get.assert_called_once_with(obj, format='json', version='list')
1029 self.assertEqual(r, info['versions'])
1031 if __name__ == '__main__':
1032 from sys import argv
1033 from kamaki.clients.test import runTestCase
1035 if not argv[1:] or argv[1] == 'Pithos':
1037 runTestCase(Pithos, 'Pithos Client', argv[2:])
1038 if not argv[1:] or argv[1] == 'PithosRest':
1040 runTestCase(PithosRest, 'PithosRest Client', argv[2:])
1042 print('TestCase %s not found' % argv[1])