Unittest PithosRest.obejct_post
[kamaki] / kamaki / clients / pithos / test.py
1 # Copyright 2013 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
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.
15 #
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.
28 #
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.
33
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
40
41 try:
42     from collections import OrderedDict
43 except ImportError:
44     from kamaki.clients.commisioning.utils.ordereddict import OrderedDict
45
46 from kamaki.clients import ClientError
47 from kamaki.clients.pithos import PithosClient, PithosRestClient
48
49
50 rest_pkg = 'kamaki.clients.pithos.rest_api.PithosRestClient'
51 pithos_pkg = 'kamaki.clients.pithos.PithosClient'
52
53 user_id = 'ac0un7-1d-5tr1ng'
54 obj = 'obj3c7N4m3'
55
56 account_info = {
57     'content-language': 'en-us',
58     'content-type': 'text/html; charset=utf-8',
59     'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
60     'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
61     'server': 'gunicorn/0.14.5',
62     'vary': 'Accept-Language',
63     'x-account-bytes-used': '751615526',
64     'x-account-container-count': 7,
65     'x-account-policy-quota': 53687091200,
66     'x-account-policy-versioning': 'auto'}
67 container_info = {
68     'content-language': 'en-us',
69     'content-type': 'text/html; charset=utf-8',
70     'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
71     'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
72     'server': 'gunicorn/0.14.5',
73     'vary': 'Accept-Language',
74     'x-container-block-hash': 'sha256',
75     'x-container-block-size': 4194304,
76     'x-container-bytes-used': 309528938,
77     'x-container-object-count': 14,
78     'x-container-object-meta': '',
79     'x-container-policy-quota': 53687091200,
80     'x-container-policy-versioning': 'auto'}
81 object_info = {
82     'content-language': 'en-us',
83     'content-length': 254965,
84     'content-type': 'application/octet-stream',
85     'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
86     'etag': '',
87     'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
88     'server': 'gunicorn/0.14.5',
89     'vary': 'Accept-Language',
90     'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
91     'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
92     'x-object-version': '525996',
93     'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
94     'x-object-meta-k1': 'v1',
95     'x-object-meta-k2': 'v2'}
96 container_list = [
97     dict(
98         count=2,
99         last_modified="2013-02-27T11:56:09.893033+00:00",
100         bytes=677076979,
101         name="pithos",
102         x_container_policy=dict(quota="21474836480", versioning="auto")),
103     dict(
104         count=0,
105         last_modified="2012-10-23T12:25:17.229187+00:00",
106         bytes=0,
107         name="trash",
108         x_container_policy=dict(quota="21474836480", versioning="auto"))]
109 object_list = [
110     dict(hash="",
111         name="The_Secret_Garden.zip",
112         x_object_public="/public/wdp9p",
113         bytes=203304947,
114         x_object_version_timestamp="1360237915.7027509",
115         x_object_uuid="s0m3uu1df0r0bj0n3",
116         last_modified="2013-02-07T11:51:55.702751+00:00",
117         content_type="application/octet-stream",
118         x_object_hash="0afdf29f71cd53126225c3f54ca",
119         x_object_version=17737,
120         x_object_modified_by=user_id),
121     dict(hash="",
122         name="The_Revealed_Garden.zip",
123         x_object_public="/public/wpd7p",
124         bytes=20330947,
125         x_object_version_timestamp="13602915.7027509",
126         x_object_uuid="s0m3uu1df0r0bj70w",
127         last_modified="2013-02-07T11:51:55.702751+00:00",
128         content_type="application/octet-stream",
129         x_object_hash="0afdf29f71cd53126225c3f54ca",
130         x_object_version=17737,
131         x_object_modified_by=user_id)]
132 object_hashmap = dict(
133     block_hash="sha256", block_size=4194304, bytes=33554432,
134     hashes=[
135         "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
136         "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
137         "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
138         "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
139         "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
140         "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
141         "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
142         "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
143 sharers = [
144     dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
145     dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
146     dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
147
148
149 class FR(object):
150     """FR stands for Fake Response"""
151     json = dict()
152     headers = dict()
153     content = json
154     status = None
155     status_code = 200
156
157     def release(self):
158         pass
159
160
161 class PithosRest(TestCase):
162
163     def setUp(self):
164         self.url = 'https://www.example.com/pithos'
165         self.token = 'p17h0570k3n'
166         self.client = PithosRestClient(self.url, self.token)
167         self.client.account = user_id
168         self.client.container = 'c0nt@1n3r_i'
169
170     def tearDown(self):
171         FR.headers = dict()
172         FR.json = dict()
173         FR.content = FR.json
174
175     @patch('%s.set_param' % rest_pkg)
176     @patch('%s.set_header' % rest_pkg)
177     @patch('%s.head' % rest_pkg, return_value=FR())
178     def test_account_head(self, head, SH, SP):
179         for params in product(
180                 (None, '50m3-d473'),
181                 (None, '50m3-07h3r-d473'),
182                 (None, 'y37-4n7h3r-d473'),
183                 ((), ('someval',), ('v1', 'v2',)),
184                 (dict(), dict(success=200), dict(k='v', v='k'))):
185             args, kwargs = params[-2], params[-1]
186             params = params[:-2]
187             self.client.account_head(*(params + args), **kwargs)
188             unt = params[0]
189             self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
190             IMS, IUS = params[1], params[2]
191             self.assertEqual(SH.mock_calls[-2:], [
192                 call('If-Modified-Since', IMS),
193                 call('If-Unmodified-Since', IUS)])
194             self.assertEqual(head.mock_calls[-1], call(
195                 '/%s' % self.client.account,
196                 *args,
197                 success=kwargs.pop('success', 204),
198                 **kwargs))
199
200     @patch('%s.set_param' % rest_pkg)
201     @patch('%s.set_header' % rest_pkg)
202     @patch('%s.get' % rest_pkg, return_value=FR())
203     def test_account_get(self, get, SH, SP):
204         keys = ('limit', 'marker', 'format', 'shared', 'until')
205         for params in product(
206                 (None, 42),
207                 (None, 'X'),
208                 ('json', 'xml'),
209                 (False, True),
210                 (None, '50m3-d473'),
211                 (None, '50m3-07h3r-d473'),
212                 (None, 'y37-4n7h3r-d473'),
213                 ((), ('someval',), ('v1', 'v2',)),
214                 (dict(), dict(success=200), dict(k='v', v='k'))):
215             args, kwargs = params[-2], params[-1]
216             params = params[:-2]
217             self.client.account_get(*(params + args), **kwargs)
218             self.assertEqual(SP.mock_calls[-5:],
219                 [call(keys[i], iff=X) if (
220                     i == 3) else call(
221                         keys[i], X, iff=X) for i, X in enumerate(params[:5])])
222             IMS, IUS = params[5], params[6]
223             self.assertEqual(SH.mock_calls[-2:], [
224                 call('If-Modified-Since', IMS),
225                 call('If-Unmodified-Since', IUS)])
226             self.assertEqual(get.mock_calls[-1], call(
227                 '/%s' % self.client.account,
228                 *args,
229                 success=kwargs.pop('success', (200, 204)),
230                 **kwargs))
231
232     @patch('%s.set_param' % rest_pkg)
233     @patch('%s.set_header' % rest_pkg)
234     @patch('%s.post' % rest_pkg, return_value=FR())
235     def test_account_post(self, post, SH, SP):
236         #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
237         for pm in product(
238                 (True, False),
239                 ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
240                 (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
241                 (None, 42),
242                 (None, 'v3r510n1ng'),
243                 ((), ('someval',), ('v1', 'v2',)),
244                 (dict(), dict(success=200), dict(k='v', v='k'))):
245             args, kwargs = pm[-2:]
246             pm = pm[:-2]
247             self.client.account_post(*(pm + args), **kwargs)
248             upd = pm[0]
249             self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
250             expected = []
251             if pm[1]:
252                 expected += [
253                 call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
254             if pm[2]:
255                 expected = [
256                 call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
257             expected = [
258                 call('X-Account-Policy-Quota', pm[3]),
259                 call('X-Account-Policy-Versioning', pm[4])]
260             self.assertEqual(SH.mock_calls[- len(expected):], expected)
261             self.assertEqual(post.mock_calls[-1], call(
262                 '/%s' % self.client.account,
263                 *args,
264                 success=kwargs.pop('success', 202),
265                 **kwargs))
266
267     @patch('%s.set_param' % rest_pkg)
268     @patch('%s.set_header' % rest_pkg)
269     @patch('%s.head' % rest_pkg, return_value=FR())
270     def test_container_head(self, head, SH, SP):
271         for pm in product(
272                 (None, '4-d473'),
273                 (None, '47h3r-d473'),
274                 (None, 'y37-4n47h3r'),
275                 ((), ('someval',)),
276                 (dict(), dict(success=200), dict(k='v', v='k'))):
277             args, kwargs = pm[-2:]
278             pm = pm[:-2]
279             self.client.container_head(*(pm + args), **kwargs)
280             unt, ims, ius = pm[0:3]
281             self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
282             self.assertEqual(SH.mock_calls[-2:], [
283                 call('If-Modified-Since', ims),
284                 call('If-Unmodified-Since', ius)])
285             self.assertEqual(head.mock_calls[-1], call(
286                 '/%s/%s' % (self.client.account, self.client.container),
287                 *args,
288                 success=kwargs.pop('success', 204),
289                 **kwargs))
290
291     @patch('%s.set_param' % rest_pkg)
292     @patch('%s.set_header' % rest_pkg)
293     @patch('%s.get' % rest_pkg, return_value=FR())
294     def test_container_get(self, get, SH, SP):
295         for pm in product(
296                 (None, 42),
297                 (None, 'X'),
298                 (None, 'some/prefix'),
299                 (None, 'delimiter'),
300                 (None, '/some/path'),
301                 ('json', 'some-format'),
302                 ([], ['k1', 'k2', 'k3']),
303                 (False, True),
304                 (None, 'unt1l-d473'),
305                 (None, 'y37-4n47h3r'),
306                 (None, '4n47h3r-d473'),
307                 ((), ('someval',)),
308                 (dict(), dict(success=400), dict(k='v', v='k'))):
309             args, kwargs = pm[-2:]
310             pm = pm[:-2]
311             self.client.container_get(*(pm + args), **kwargs)
312             lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
313             exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
314             exp += [call('path', path)] if path else [
315                 call('prefix', prfx, iff=prfx),
316                 call('delimiter', dlm, iff=dlm)]
317             exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
318             if meta:
319                 exp += [call('meta', ','.join(meta))]
320             exp += [call('until', unt, iff=unt)]
321             self.assertEqual(SP.mock_calls[- len(exp):], exp)
322             ims, ius = pm[-2:]
323             self.assertEqual(SH.mock_calls[-2:], [
324                 call('If-Modified-Since', ims),
325                 call('If-Unmodified-Since', ius)])
326             self.assertEqual(get.mock_calls[-1], call(
327                 '/%s/%s' % (self.client.account, self.client.container),
328                 *args,
329                 success=kwargs.pop('success', 200),
330                 **kwargs))
331
332     @patch('%s.set_header' % rest_pkg)
333     @patch('%s.put' % rest_pkg, return_value=FR())
334     def test_container_put(self, put, SH):
335         for pm in product(
336                 (None, 42),
337                 (None, 'v3r51on1ng'),
338                 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
339                 ((), ('someval',)),
340                 (dict(), dict(success=400), dict(k='v', v='k'))):
341             args, kwargs = pm[-2:]
342             pm = pm[:-2]
343             self.client.container_put(*(pm + args), **kwargs)
344             quota, versioning, metas = pm[-3:]
345             exp = [
346                 call('X-Container-Policy-Quota', quota),
347                 call('X-Container-Policy-Versioning', versioning)] + [
348                 call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
349             self.assertEqual(SH.mock_calls[- len(exp):], exp)
350             self.assertEqual(put.mock_calls[-1], call(
351                 '/%s/%s' % (self.client.account, self.client.container),
352                 *args,
353                 success=kwargs.pop('success', (201, 202)),
354                 **kwargs))
355
356     @patch('%s.set_param' % rest_pkg)
357     @patch('%s.set_header' % rest_pkg)
358     @patch('%s.post' % rest_pkg, return_value=FR())
359     def test_container_post(self, post, SH, SP):
360         for pm in product(
361                 (True, False),
362                 ('json', 'some-format'),
363                 (None, 'quota'),
364                 (None, 'v3r51on1ng'),
365                 (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
366                 (None, 'content-type'),
367                 (None, 42),
368                 (None, 'transfer-encoding'),
369                 ((), ('someval',)),
370                 (dict(), dict(success=400), dict(k='v', v='k'))):
371             args, kwargs = pm[-2:]
372             pm = pm[:-2]
373             self.client.container_post(*(pm + args), **kwargs)
374             upd, frmt = pm[:2]
375             self.assertEqual(SP.mock_calls[-2:], [
376                 call('update', iff=upd),
377                 call('format', frmt, iff=frmt)])
378             qta, vrs, metas, ctype, clen, trenc = pm[2:]
379             prfx = 'X-Container-Meta-'
380             exp = [
381                 call('X-Container-Policy-Quota', qta),
382                 call('X-Container-Policy-Versioning', vrs)] + [
383                 call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
384                 call('Content-Type', ctype),
385                 call('Content-Length', clen),
386                 call('Transfer-Encoding', trenc)]
387             self.assertEqual(SH.mock_calls[- len(exp):], exp)
388             ims, ius = pm[-2:]
389             self.assertEqual(post.mock_calls[-1], call(
390                 '/%s/%s' % (self.client.account, self.client.container),
391                 *args,
392                 success=kwargs.pop('success', 202),
393                 **kwargs))
394
395     @patch('%s.set_param' % rest_pkg)
396     @patch('%s.delete' % rest_pkg, return_value=FR())
397     def test_container_delete(self, delete, SP):
398         for pm in product(
399                 (None, 'd473'),
400                 (None, 'd3l1m'),
401                 ((), ('someval',)),
402                 (dict(), dict(success=400), dict(k='v', v='k'))):
403             args, kwargs = pm[-2:]
404             pm = pm[:-2]
405             self.client.container_delete(*(pm + args), **kwargs)
406             unt, dlm = pm[-2:]
407             self.assertEqual(SP.mock_calls[-2:], [
408                 call('until', unt, iff=unt),
409                 call('delimiter', dlm, iff=dlm)])
410             self.assertEqual(delete.mock_calls[-1], call(
411                 '/%s/%s' % (self.client.account, self.client.container),
412                 *args,
413                 success=kwargs.pop('success', 204),
414                 **kwargs))
415
416     @patch('%s.set_param' % rest_pkg)
417     @patch('%s.set_header' % rest_pkg)
418     @patch('%s.head' % rest_pkg, return_value=FR())
419     def test_object_head(self, head, SH, SP):
420         for pm in product(
421                 (None, 'v3r510n'),
422                 (None, '1f-374g'),
423                 (None, '1f-n0-74g'),
424                 (None, '1f-m0d-51nc3'),
425                 (None, '1f-unm0d-51nc3'),
426                 ((), ('someval',)),
427                 (dict(), dict(success=400), dict(k='v', v='k'))):
428             args, kwargs = pm[-2:]
429             pm = pm[:-2]
430             self.client.object_head(obj, *(pm + args), **kwargs)
431             vrs, etag, netag, ims, ius = pm[:5]
432             self.assertEqual(
433                 SP.mock_calls[-1],
434                 call('version', vrs, iff=vrs))
435             self.assertEqual(SH.mock_calls[-4:], [
436                 call('If-Match', etag),
437                 call('If-None-Match', netag),
438                 call('If-Modified-Since', ims),
439                 call('If-Unmodified-Since', ius)])
440             acc, cont = self.client.account, self.client.container
441             self.assertEqual(head.mock_calls[-1], call(
442                 '/%s/%s/%s' % (acc, cont, obj),
443                 *args,
444                 success=kwargs.pop('success', 200),
445                 **kwargs))
446
447     @patch('%s.set_param' % rest_pkg)
448     @patch('%s.set_header' % rest_pkg)
449     @patch('%s.get' % rest_pkg, return_value=FR())
450     def test_object_get(self, get, SH, SP):
451         for pm in product(
452                 ('json', 'f0rm47'),
453                 (False, True),
454                 (None, 'v3r510n'),
455                 (None, 'range=74-63'),
456                 (False, True),
457                 (None, '3746'),
458                 (None, 'non-3746'),
459                 (None, '1f-m0d'),
460                 (None, '1f-unm0d'),
461                 ((), ('someval',)),
462                 (dict(), dict(success=400), dict(k='v', v='k'))):
463             args, kwargs = pm[-2:]
464             pm = pm[:-2]
465             self.client.object_get(obj, *(pm + args), **kwargs)
466             format, hashmap, version = pm[:3]
467             self.assertEqual(SP.mock_calls[-3:], [
468                 call('format', format, iff=format),
469                 call('hashmap', hashmap, iff=hashmap),
470                 call('version', version, iff=version)])
471             rng, ifrng, im, inm, ims, ius = pm[-6:]
472             self.assertEqual(SH.mock_calls[-6:], [
473                 call('Range', rng),
474                 call('If-Range', '', ifrng and rng),
475                 call('If-Match', im),
476                 call('If-None-Match', inm),
477                 call('If-Modified-Since', ims),
478                 call('If-Unmodified-Since', ius)])
479             acc, cont = self.client.account, self.client.container
480             self.assertEqual(get.mock_calls[-1], call(
481                 '/%s/%s/%s' % (acc, cont, obj),
482                 *args,
483                 success=kwargs.pop('success', 200),
484                 **kwargs))
485
486     @patch('%s.set_param' % rest_pkg)
487     @patch('%s.set_header' % rest_pkg)
488     @patch('%s.put' % rest_pkg, return_value=FR())
489     def test_object_put(self, put, SH, SP):
490         for pm in product(
491                 ('json', 'f0rm47'),
492                 (False, True),
493                 (None, 'delim',),
494                 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
495                 (False, True),
496                 (dict(), dict(k2='v2', k3='v3')),
497                 ((), ('someval',)),
498                 (dict(), dict(success=400), dict(k='v', v='k'))):
499             args, kwargs = pm[-2:]
500             pm = pm[:-2]
501             terms = [None] * 13
502             for i in range(len(terms)):
503                 if randint(0, 2):
504                     terms[i] = 'val_%s' % randint(13, 1024)
505             self.client.object_put(
506                 obj,
507                 *(pm[:3] + tuple(terms) + pm[3:] + args),
508                 **kwargs)
509             format, hashmap, delimiter = pm[:3]
510             self.assertEqual(SP.mock_calls[-3:], [
511                 call('format', format, iff=format),
512                 call('hashmap', hashmap, iff=hashmap),
513                 call('delimiter', delimiter, iff=delimiter)])
514             (
515                 im, inm, etag, clen, ctype, trenc,
516                 cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
517             perms, public, metas = pm[3:]
518             exp = [
519                 call('If-Match', im),
520                 call('If-None-Match', inm),
521                 call('ETag', etag),
522                 call('Content-Length', clen),
523                 call('Content-Type', ctype),
524                 call('Transfer-Encoding', trenc),
525                 call('X-Copy-From', cp),
526                 call('X-Move-From', mv),
527                 call('X-Source-Account', srcacc),
528                 call('X-Source-Version', srcvrs),
529                 call('Content-Encoding', conenc),
530                 call('Content-Disposition', condis),
531                 call('X-Object-Manifest', mnf)]
532             if perms:
533                 perm_str = ''
534                 for ptype, pval in perms.items():
535                     if pval:
536                         perm_str += ';' if perm_str else ''
537                         perm_str += '%s=%s' % (ptype, ','.join(pval))
538                 exp += [call('X-Object-Sharing', perm_str)]
539             exp += [call('X-Object-Public', public)]
540             for k, v in metas.items():
541                 exp += [call('X-Object-Meta-%s' % k, v)]
542             self.assertEqual(SH.mock_calls[- len(exp):], exp)
543             acc, cont = self.client.account, self.client.container
544             self.assertEqual(put.mock_calls[-1], call(
545                 '/%s/%s/%s' % (acc, cont, obj),
546                 *args,
547                 success=kwargs.pop('success', 201),
548                 **kwargs))
549
550     @patch('%s.set_param' % rest_pkg)
551     @patch('%s.set_header' % rest_pkg)
552     @patch('%s.copy' % rest_pkg, return_value=FR())
553     def test_object_copy(self, copy, SH, SP):
554         dest = 'dest1n4710n'
555         for pm in product(
556                 ('json', 'f0rm47'),
557                 (False, True),
558                 (None, 'ifmatch'),
559                 (None, 'ifnonematch'),
560                 (None, 'destinationaccount'),
561                 (None, 'content-type'),
562                 (None, 'content-encoding'),
563                 (None, 'content-disp'),
564                 (None, 'source-version'),
565                 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
566                 (False, True),
567                 (dict(), dict(k2='v2', k3='v3')),
568                 ((), ('someval',)),
569                 (dict(), dict(success=400), dict(k='v', v='k'))):
570             args, kwargs = pm[-2:]
571             pm = pm[:-2]
572             self.client.object_copy(obj, dest, *(pm + args), **kwargs)
573             format, ict = pm[:2]
574             self.assertEqual(SP.mock_calls[-2:], [
575                 call('format', format, iff=format),
576                 call('ignore_content_type', iff=ict)])
577             im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
578             exp = [call('If-Match', im),
579                 call('If-None-Match', inm),
580                 call('Destination', dest),
581                 call('Destination-Account', da),
582                 call('Content-Type', ct),
583                 call('Content-Encoding', ce),
584                 call('Content-Disposition', cd),
585                 call('X-Source-Version', sv)]
586             if perms:
587                 perm_str = ''
588                 for ptype, pval in perms.items():
589                     if pval:
590                         perm_str += ';' if perm_str else ''
591                         perm_str += '%s=%s' % (ptype, ','.join(pval))
592                 exp += [call('X-Object-Sharing', perm_str)]
593             exp += [call('X-Object-Public', public)]
594             for k, v in metas.items():
595                 exp += [call('X-Object-Meta-%s' % k, v)]
596             self.assertEqual(SH.mock_calls[- len(exp):], exp)
597             acc, cont = self.client.account, self.client.container
598             self.assertEqual(copy.mock_calls[-1], call(
599                 '/%s/%s/%s' % (acc, cont, obj),
600                 *args,
601                 success=kwargs.pop('success', 201),
602                 **kwargs))
603
604     @patch('%s.set_param' % rest_pkg)
605     @patch('%s.set_header' % rest_pkg)
606     @patch('%s.move' % rest_pkg, return_value=FR())
607     def test_object_move(self, move, SH, SP):
608         for pm in product(
609                 ('json', 'f0rm47'),
610                 (False, True),
611                 (None, 'ifmatch'),
612                 (None, 'ifnonematch'),
613                 (None, 'destination'),
614                 (None, 'destinationaccount'),
615                 (None, 'content-type'),
616                 (None, 'content-encoding'),
617                 (None, 'content-disp'),
618                 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
619                 (False, True),
620                 (dict(), dict(k2='v2', k3='v3')),
621                 ((), ('someval',)),
622                 (dict(), dict(success=400), dict(k='v', v='k'))):
623             args, kwargs = pm[-2:]
624             pm = pm[:-2]
625             self.client.object_move(obj, *(pm + args), **kwargs)
626             format, ict = pm[:2]
627             self.assertEqual(SP.mock_calls[-2:], [
628                 call('format', format, iff=format),
629                 call('ignore_content_type', iff=ict)])
630             im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
631             exp = [call('If-Match', im),
632                 call('If-None-Match', inm),
633                 call('Destination', d),
634                 call('Destination-Account', da),
635                 call('Content-Type', ct),
636                 call('Content-Encoding', ce),
637                 call('Content-Disposition', cd)]
638             if perms:
639                 perm_str = ''
640                 for ptype, pval in perms.items():
641                     if pval:
642                         perm_str += ';' if perm_str else ''
643                         perm_str += '%s=%s' % (ptype, ','.join(pval))
644                 exp += [call('X-Object-Sharing', perm_str)]
645             exp += [call('X-Object-Public', public)]
646             for k, v in metas.items():
647                 exp += [call('X-Object-Meta-%s' % k, v)]
648             self.assertEqual(SH.mock_calls[- len(exp):], exp)
649             acc, cont = self.client.account, self.client.container
650             self.assertEqual(move.mock_calls[-1], call(
651                 '/%s/%s/%s' % (acc, cont, obj),
652                 *args,
653                 success=kwargs.pop('success', 201),
654                 **kwargs))
655
656     @patch('%s.set_param' % rest_pkg)
657     @patch('%s.set_header' % rest_pkg)
658     @patch('%s.post' % rest_pkg, return_value=FR())
659     def test_object_post(self, post, SH, SP):
660         for pm in product(
661                 ('json', 'f0rm47'),
662                 (False, True),
663                 (dict(), dict(read=['u1', 'g2'], write=['u1'])),
664                 (False, True),
665                 (dict(), dict(k2='v2', k3='v3')),
666                 ((), ('someval',)),
667                 (dict(), dict(success=400), dict(k='v', v='k'))):
668             args, kwargs = pm[-2:]
669             pm = pm[:-2]
670             terms = [None] * 13
671             for i in range(len(terms)):
672                 if randint(0, 2):
673                     terms[i] = 'val_%s' % randint(13, 1024)
674             self.client.object_post(
675                 obj,
676                 *(pm[:2] + tuple(terms) + pm[2:] + args),
677                 **kwargs)
678             format, update = pm[:2]
679             self.assertEqual(SP.mock_calls[-2:], [
680                 call('format', format, iff=format),
681                 call('update', iff=update)])
682             (
683                 im, inm, clen, ctype, crng, trenc, cenc,
684                 condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
685             exp = [
686                 call('If-Match', im),
687                 call('If-None-Match', inm),
688                 call('Content-Length', clen, iff=not trenc),
689                 call('Content-Type', ctype),
690                 call('Content-Range', crng),
691                 call('Transfer-Encoding', trenc),
692                 call('Content-Encoding', cenc),
693                 call('Content-Disposition', condis),
694                 call('X-Source-Object', srcobj),
695                 call('X-Source-Account', srcacc),
696                 call('X-Source-Version', srcvrs),
697                 call('X-Object-Bytes', obytes),
698                 call('X-Object-Manifest', mnfs)]
699             perms, public, metas = pm[2:]
700             if perms:
701                 perm_str = ''
702                 for ptype, pval in perms.items():
703                     if pval:
704                         perm_str += ';' if perm_str else ''
705                         perm_str += '%s=%s' % (ptype, ','.join(pval))
706                 exp += [call('X-Object-Sharing', perm_str)]
707             exp += [call('X-Object-Public', public)]
708             for k, v in metas.items():
709                 exp += [call('X-Object-Meta-%s' % k, v)]
710             self.assertEqual(SH.mock_calls[- len(exp):], exp)
711             acc, cont = self.client.account, self.client.container
712             self.assertEqual(post.mock_calls[-1], call(
713                 '/%s/%s/%s' % (acc, cont, obj),
714                 *args,
715                 success=kwargs.pop('success', (202, 204)),
716                 **kwargs))
717
718
719 class Pithos(TestCase):
720
721     files = []
722
723     def _create_temp_file(self, num_of_blocks):
724         self.files.append(NamedTemporaryFile())
725         tmpFile = self.files[-1]
726         file_size = num_of_blocks * 4 * 1024 * 1024
727         print('\n\tCreate tmp file')
728         tmpFile.write(urandom(file_size))
729         tmpFile.flush()
730         tmpFile.seek(0)
731         print('\t\tDone')
732         return tmpFile
733
734     def assert_dicts_are_equal(self, d1, d2):
735         for k, v in d1.items():
736             self.assertTrue(k in d2)
737             if isinstance(v, dict):
738                 self.assert_dicts_are_equal(v, d2[k])
739             else:
740                 self.assertEqual(unicode(v), unicode(d2[k]))
741
742     def setUp(self):
743         self.url = 'https://www.example.com/pithos'
744         self.token = 'p17h0570k3n'
745         self.client = PithosClient(self.url, self.token)
746         self.client.account = user_id
747         self.client.container = 'c0nt@1n3r_i'
748
749     def tearDown(self):
750         FR.headers = dict()
751         FR.status_code = 200
752         FR.json = dict()
753         FR.content = FR.json
754         for f in self.files:
755             f.close()
756
757     #  Pithos+ methods that extend storage API
758
759     @patch('%s.account_head' % pithos_pkg, return_value=FR())
760     def test_get_account_info(self, AH):
761         FR.headers = account_info
762         for until in (None, 'un71L-d473'):
763             r = self.client.get_account_info(until=until)
764             self.assert_dicts_are_equal(r, account_info)
765             self.assertEqual(AH.mock_calls[-1], call(until=until))
766         FR.status_code = 401
767         self.assertRaises(ClientError, self.client.get_account_info)
768
769     @patch('%s.account_post' % pithos_pkg, return_value=FR())
770     def test_del_account_meta(self, AP):
771         keys = ['k1', 'k2', 'k3']
772         for key in keys:
773             self.client.del_account_meta(key)
774             self.assertEqual(
775                 AP.mock_calls[-1],
776                 call(update=True, metadata={key: ''}))
777
778     @patch('%s.container_head' % pithos_pkg, return_value=FR())
779     def test_get_container_info(self, CH):
780         FR.headers = container_info
781         r = self.client.get_container_info()
782         self.assert_dicts_are_equal(r, container_info)
783         u = 'some date'
784         r = self.client.get_container_info(until=u)
785         self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
786
787     @patch('%s.account_get' % pithos_pkg, return_value=FR())
788     def test_list_containers(self, get):
789         FR.json = container_list
790         r = self.client.list_containers()
791         get.assert_called_once_with()
792         for i in range(len(r)):
793             self.assert_dicts_are_equal(r[i], container_list[i])
794
795     @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
796     @patch('%s.container_post' % pithos_pkg, return_value=FR())
797     @patch('%s.object_put' % pithos_pkg, return_value=FR())
798     def test_upload_object(self, OP, CP, GCI):
799         num_of_blocks = 8
800         tmpFile = self._create_temp_file(num_of_blocks)
801
802         # Without kwargs
803         self.client.upload_object(obj, tmpFile)
804         self.assertEqual(GCI.mock_calls[-1], call())
805         [call1, call2] = OP.mock_calls
806
807         (args1, kwargs1) = call1[1:3]
808         (args2, kwargs2) = call2[1:3]
809         self.assertEqual(args1, (obj,))
810         expected1 = dict(
811             hashmap=True,
812             success=(201, 409),
813             format='json',
814             json=dict(
815                 hashes=['s0m3h@5h'] * num_of_blocks,
816                 bytes=num_of_blocks * 4 * 1024 * 1024),
817             etag=None,
818             content_encoding=None,
819             content_type='application/octet-stream',
820             content_disposition=None,
821             public=None,
822             permissions=None)
823         for k, v in expected1.items():
824             if k == 'json':
825                 self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
826                 self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
827             else:
828                 self.assertEqual(v, kwargs1[k])
829
830         (args2, kwargs2) = call2[1:3]
831         self.assertEqual(args2, (obj,))
832         expected2 = dict(
833             json=dict(
834                 hashes=['s0m3h@5h'] * num_of_blocks,
835                 bytes=num_of_blocks * 4 * 1024 * 1024),
836             content_type='application/octet-stream',
837             hashmap=True,
838             success=201,
839             format='json')
840         for k, v in expected2.items():
841             if k == 'json':
842                 self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
843                 self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
844             else:
845                 self.assertEqual(v, kwargs2[k])
846
847         mock_offset = 2
848
849         #  With progress bars
850         try:
851             from progress.bar import ShadyBar
852             blck_bar = ShadyBar('Mock blck calc.')
853             upld_bar = ShadyBar('Mock uplds')
854         except ImportError:
855             blck_bar = None
856             upld_bar = None
857
858         if blck_bar and upld_bar:
859
860             def blck_gen(n):
861                 for i in blck_bar.iter(range(n)):
862                     yield
863                 yield
864
865             def upld_gen(n):
866                 for i in upld_bar.iter(range(n)):
867                     yield
868                 yield
869
870             tmpFile.seek(0)
871             self.client.upload_object(
872                 obj, tmpFile,
873                 hash_cb=blck_gen, upload_cb=upld_gen)
874
875             for i, c in enumerate(OP.mock_calls[-mock_offset:]):
876                 self.assertEqual(OP.mock_calls[i], c)
877
878         #  With content-type
879         tmpFile.seek(0)
880         ctype = 'video/mpeg'
881         sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
882         self.client.upload_object(obj, tmpFile,
883             content_type=ctype, sharing=sharing)
884         self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
885         self.assert_dicts_are_equal(
886             OP.mock_calls[-2][2]['permissions'],
887             sharing)
888
889         # With other args
890         tmpFile.seek(0)
891         kwargs = dict(
892             etag='s0m3E74g',
893             content_type=ctype,
894             content_disposition=ctype + 'd15p051710n',
895             public=True,
896             content_encoding='802.11')
897         self.client.upload_object(obj, tmpFile, **kwargs)
898         for arg, val in kwargs.items():
899             self.assertEqual(OP.mock_calls[-2][2][arg], val)
900
901     def test_get_object_info(self):
902         FR.headers = object_info
903         version = 'v3r510n'
904         with patch.object(
905                 PithosClient, 'object_head',
906                 return_value=FR()) as head:
907             r = self.client.get_object_info(obj)
908             self.assertEqual(r, object_info)
909             r = self.client.get_object_info(obj, version=version)
910             self.assertEqual(head.mock_calls, [
911                 call(obj, version=None),
912                 call(obj, version=version)])
913         with patch.object(
914                 PithosClient, 'object_head',
915                 side_effect=ClientError('Obj not found', 404)):
916             self.assertRaises(
917                 ClientError,
918                 self.client.get_object_info,
919                 obj, version=version)
920
921     @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
922     def test_get_object_meta(self, GOI):
923         for version in (None, 'v3r510n'):
924             r = self.client.get_object_meta(obj, version)
925             for k in [k for k in object_info if k.startswith('x-object-meta')]:
926                 self.assertEqual(r.pop(k), object_info[k])
927             self.assertFalse(len(r))
928             self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
929
930     @patch('%s.object_post' % pithos_pkg, return_value=FR())
931     def test_del_object_meta(self, post):
932         metakey = '50m3m3t4k3y'
933         self.client.del_object_meta(obj, metakey)
934         post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
935
936     @patch('%s.object_put' % pithos_pkg, return_value=FR())
937     def test_copy_object(self, put):
938         src_cont = 'src-c0nt41n3r'
939         src_obj = 'src-0bj'
940         dst_cont = 'dst-c0nt41n3r'
941         dst_obj = 'dst-0bj'
942         expected = call(
943             src_obj,
944             content_length=0,
945             source_account=None,
946             success=201,
947             copy_from='/%s/%s' % (src_cont, src_obj),
948             delimiter=None,
949             content_type=None,
950             source_version=None,
951             public=False)
952         self.client.copy_object(src_cont, src_obj, dst_cont)
953         self.assertEqual(put.mock_calls[-1], expected)
954         self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
955         self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
956         kwargs = dict(
957             source_version='src-v3r510n',
958             source_account='src-4cc0un7',
959             public=True,
960             content_type='c0n73n7Typ3',
961             delimiter='5')
962         self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
963         for k, v in kwargs.items():
964             self.assertEqual(v, put.mock_calls[-1][2][k])
965
966     @patch('%s.object_put' % pithos_pkg, return_value=FR())
967     def test_move_object(self, put):
968         src_cont = 'src-c0nt41n3r'
969         src_obj = 'src-0bj'
970         dst_cont = 'dst-c0nt41n3r'
971         dst_obj = 'dst-0bj'
972         expected = call(
973             src_obj,
974             content_length=0,
975             source_account=None,
976             success=201,
977             move_from='/%s/%s' % (src_cont, src_obj),
978             delimiter=None,
979             content_type=None,
980             source_version=None,
981             public=False)
982         self.client.move_object(src_cont, src_obj, dst_cont)
983         self.assertEqual(put.mock_calls[-1], expected)
984         self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
985         self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
986         kwargs = dict(
987             source_version='src-v3r510n',
988             source_account='src-4cc0un7',
989             public=True,
990             content_type='c0n73n7Typ3',
991             delimiter='5')
992         self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
993         for k, v in kwargs.items():
994             self.assertEqual(v, put.mock_calls[-1][2][k])
995
996     #  Pithos+ only methods
997
998     @patch('%s.container_delete' % pithos_pkg, return_value=FR())
999     def test_purge_container(self, CD):
1000         self.client.purge_container()
1001         self.assertTrue('until' in CD.mock_calls[-1][2])
1002         cont = self.client.container
1003         self.client.purge_container('another-container')
1004         self.assertEqual(self.client.container, cont)
1005
1006     @patch('%s.object_put' % pithos_pkg, return_value=FR())
1007     def test_upload_object_unchunked(self, put):
1008         num_of_blocks = 8
1009         tmpFile = self._create_temp_file(num_of_blocks)
1010         expected = dict(
1011                 success=201,
1012                 data=num_of_blocks * 4 * 1024 * 1024,
1013                 etag='some-etag',
1014                 content_encoding='some content_encoding',
1015                 content_type='some content-type',
1016                 content_disposition='some content_disposition',
1017                 public=True,
1018                 permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1019         self.client.upload_object_unchunked(obj, tmpFile)
1020         self.assertEqual(put.mock_calls[-1][1], (obj,))
1021         self.assertEqual(
1022             sorted(put.mock_calls[-1][2].keys()),
1023             sorted(expected.keys()))
1024         kwargs = dict(expected)
1025         kwargs.pop('success')
1026         kwargs['size'] = kwargs.pop('data')
1027         kwargs['sharing'] = kwargs.pop('permissions')
1028         tmpFile.seek(0)
1029         self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1030         pmc = put.mock_calls[-1][2]
1031         for k, v in expected.items():
1032             if k == 'data':
1033                 self.assertEqual(len(pmc[k]), v)
1034             else:
1035                 self.assertEqual(pmc[k], v)
1036         self.assertRaises(
1037             ClientError,
1038             self.client.upload_object_unchunked,
1039             obj, tmpFile, withHashFile=True)
1040
1041     @patch('%s.object_put' % pithos_pkg, return_value=FR())
1042     def test_create_object_by_manifestation(self, put):
1043         manifest = '%s/%s' % (self.client.container, obj)
1044         kwargs = dict(
1045                 etag='some-etag',
1046                 content_encoding='some content_encoding',
1047                 content_type='some content-type',
1048                 content_disposition='some content_disposition',
1049                 public=True,
1050                 sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1051         self.client.create_object_by_manifestation(obj)
1052         expected = dict(content_length=0, manifest=manifest)
1053         for k in kwargs:
1054             expected['permissions' if k == 'sharing' else k] = None
1055         self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1056         self.client.create_object_by_manifestation(obj, **kwargs)
1057         expected.update(kwargs)
1058         expected['permissions'] = expected.pop('sharing')
1059         self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1060
1061     @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1062     @patch('%s.object_get' % pithos_pkg, return_value=FR())
1063     def test_download_object(self, GET, GOH):
1064         num_of_blocks = 8
1065         tmpFile = self._create_temp_file(num_of_blocks)
1066         FR.content = tmpFile.read(4 * 1024 * 1024)
1067         tmpFile = self._create_temp_file(num_of_blocks)
1068         num_of_blocks = len(object_hashmap['hashes'])
1069         kwargs = dict(
1070             resume=True,
1071             version='version',
1072             range_str='10-20',
1073             if_match='if and only if',
1074             if_none_match='if and only not',
1075             if_modified_since='what if not?',
1076             if_unmodified_since='this happens if not!',
1077             async_headers=dict(Range='bytes=0-88888888'))
1078
1079         self.client.download_object(obj, tmpFile)
1080         self.assertEqual(len(GET.mock_calls), num_of_blocks)
1081         self.assertEqual(GET.mock_calls[-1][1], (obj,))
1082         for k, v in kwargs.items():
1083             if k == 'async_headers':
1084                 self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1085             elif k in ('resume', 'range_str'):
1086                 continue
1087             else:
1088                 self.assertEqual(GET.mock_calls[-1][2][k], None)
1089
1090         #  Check ranges are consecutive
1091         starts = []
1092         ends = []
1093         for c in GET.mock_calls:
1094             rng_str = c[2]['async_headers']['Range']
1095             (start, rng_str) = rng_str.split('=')
1096             (start, end) = rng_str.split('-')
1097             starts.append(start)
1098             ends.append(end)
1099         ends = sorted(ends)
1100         for i, start in enumerate(sorted(starts)):
1101             if i:
1102                 int(ends[i - 1]) == int(start) - 1
1103
1104         #  With progress bars
1105         try:
1106             from progress.bar import ShadyBar
1107             dl_bar = ShadyBar('Mock dl')
1108         except ImportError:
1109             dl_bar = None
1110
1111         if dl_bar:
1112
1113             def blck_gen(n):
1114                 for i in dl_bar.iter(range(n)):
1115                     yield
1116                 yield
1117
1118             tmpFile.seek(0)
1119             self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1120             self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1121
1122         tmpFile.seek(0)
1123         kwargs.pop('async_headers')
1124         kwargs.pop('resume')
1125         self.client.download_object(obj, tmpFile, **kwargs)
1126         for k, v in kwargs.items():
1127             if k == 'range_str':
1128                 self.assertEqual(
1129                     GET.mock_calls[-1][2]['data_range'],
1130                     'bytes=%s' % v)
1131             else:
1132                 self.assertEqual(GET.mock_calls[-1][2][k], v)
1133
1134         #  ALl options on no tty
1135
1136         def foo():
1137             return True
1138
1139         tmpFile.seek(0)
1140         tmpFile.isatty = foo
1141         self.client.download_object(obj, tmpFile, **kwargs)
1142         for k, v in kwargs.items():
1143             if k == 'range_str':
1144                 self.assertTrue('data_range' in GET.mock_calls[-1][2])
1145             else:
1146                 self.assertEqual(GET.mock_calls[-1][2][k], v)
1147
1148     def test_get_object_hashmap(self):
1149         FR.json = object_hashmap
1150         for empty in (304, 412):
1151             with patch.object(
1152                     PithosClient, 'object_get',
1153                     side_effect=ClientError('Empty', status=empty)):
1154                 r = self.client.get_object_hashmap(obj)
1155                 self.assertEqual(r, {})
1156         exp_args = dict(
1157             hashmap=True,
1158             data_range=None,
1159             version=None,
1160             if_etag_match=None,
1161             if_etag_not_match=None,
1162             if_modified_since=None,
1163             if_unmodified_since=None)
1164         kwargs = dict(
1165             version='s0m3v3r51on',
1166             if_match='if match',
1167             if_none_match='if non match',
1168             if_modified_since='some date here',
1169             if_unmodified_since='some date here',
1170             data_range='10-20')
1171         with patch.object(
1172                 PithosClient, 'object_get',
1173                 return_value=FR()) as get:
1174             r = self.client.get_object_hashmap(obj)
1175             self.assertEqual(r, object_hashmap)
1176             self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1177             r = self.client.get_object_hashmap(obj, **kwargs)
1178             exp_args['if_etag_match'] = kwargs.pop('if_match')
1179             exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1180             exp_args.update(kwargs)
1181             self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1182
1183     @patch('%s.account_post' % pithos_pkg, return_value=FR())
1184     def test_set_account_group(self, post):
1185         (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1186         self.client.set_account_group(group, usernames)
1187         post.assert_called_once_with(update=True, groups={group: usernames})
1188
1189     @patch('%s.account_post' % pithos_pkg, return_value=FR())
1190     def test_del_account_group(self, post):
1191         group = 'aU53rGr0up'
1192         self.client.del_account_group(group)
1193         post.assert_called_once_with(update=True, groups={group: []})
1194
1195     @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1196     def test_get_account_quota(self, GAI):
1197         key = 'x-account-policy-quota'
1198         r = self.client.get_account_quota()
1199         GAI.assert_called_once_with()
1200         self.assertEqual(r[key], account_info[key])
1201
1202     @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1203     def test_get_account_versioning(self, GAI):
1204         key = 'x-account-policy-versioning'
1205         r = self.client.get_account_versioning()
1206         GAI.assert_called_once_with()
1207         self.assertEqual(r[key], account_info[key])
1208
1209     def test_get_account_meta(self):
1210         key = 'x-account-meta-'
1211         with patch.object(
1212                 PithosClient, 'get_account_info',
1213                 return_value=account_info):
1214             r = self.client.get_account_meta()
1215             keys = [k for k in r if k.startswith(key)]
1216             self.assertFalse(keys)
1217         acc_info = dict(account_info)
1218         acc_info['%sk1' % key] = 'v1'
1219         acc_info['%sk2' % key] = 'v2'
1220         acc_info['%sk3' % key] = 'v3'
1221         with patch.object(
1222                 PithosClient, 'get_account_info',
1223                 return_value=acc_info):
1224             r = self.client.get_account_meta()
1225             for k in [k for k in acc_info if k.startswith(key)]:
1226                 self.assertEqual(r[k], acc_info[k])
1227
1228     def test_get_account_group(self):
1229         key = 'x-account-group-'
1230         with patch.object(
1231                 PithosClient, 'get_account_info',
1232                 return_value=account_info):
1233             r = self.client.get_account_group()
1234             keys = [k for k in r if k.startswith(key)]
1235             self.assertFalse(keys)
1236         acc_info = dict(account_info)
1237         acc_info['%sk1' % key] = 'g1'
1238         acc_info['%sk2' % key] = 'g2'
1239         acc_info['%sk3' % key] = 'g3'
1240         with patch.object(
1241                 PithosClient, 'get_account_info',
1242                 return_value=acc_info):
1243             r = self.client.get_account_group()
1244             for k in [k for k in acc_info if k.startswith(key)]:
1245                 self.assertEqual(r[k], acc_info[k])
1246
1247     @patch('%s.account_post' % pithos_pkg, return_value=FR())
1248     def test_set_account_meta(self, post):
1249         metas = dict(k1='v1', k2='v2', k3='v3')
1250         self.client.set_account_meta(metas)
1251         post.assert_called_once_with(update=True, metadata=metas)
1252
1253     @patch('%s.account_post' % pithos_pkg, return_value=FR())
1254     def test_set_account_quota(self, post):
1255         qu = 1024
1256         self.client.set_account_quota(qu)
1257         post.assert_called_once_with(update=True, quota=qu)
1258
1259     @patch('%s.account_post' % pithos_pkg, return_value=FR())
1260     def test_set_account_versioning(self, post):
1261         vrs = 'n3wV3r51on1ngTyp3'
1262         self.client.set_account_versioning(vrs)
1263         post.assert_called_once_with(update=True, versioning=vrs)
1264
1265     @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1266     def test_del_container(self, delete):
1267         for kwarg in (
1268                 dict(delimiter=None, until=None),
1269                 dict(delimiter='X', until='50m3d473')):
1270             self.client.del_container(**kwarg)
1271             expected = dict(kwarg)
1272             expected['success'] = (204, 404, 409)
1273             self.assertEqual(delete.mock_calls[-1], call(**expected))
1274         for status_code in (404, 409):
1275             FR.status_code = status_code
1276             self.assertRaises(ClientError, self.client.del_container)
1277
1278     @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1279     def test_get_container_versioning(self, GCI):
1280         key = 'x-container-policy-versioning'
1281         cont = 'c0n7-417'
1282         bu_cnt = self.client.container
1283         for container in (None, cont):
1284             r = self.client.get_container_versioning(container=container)
1285             self.assertEqual(r[key], container_info[key])
1286             self.assertEqual(GCI.mock_calls[-1], call())
1287             self.assertEqual(bu_cnt, self.client.container)
1288
1289     @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1290     def test_get_container_quota(self, GCI):
1291         key = 'x-container-policy-quota'
1292         cont = 'c0n7-417'
1293         bu_cnt = self.client.container
1294         for container in (None, cont):
1295             r = self.client.get_container_quota(container=container)
1296             self.assertEqual(r[key], container_info[key])
1297             self.assertEqual(GCI.mock_calls[-1], call())
1298             self.assertEqual(bu_cnt, self.client.container)
1299
1300     def test_get_container_meta(self):
1301         somedate = '50m3d473'
1302         key = 'x-container-meta'
1303         metaval = '50m3m374v41'
1304         container_plus = dict(container_info)
1305         container_plus[key] = metaval
1306         for ret in ((container_info, {}), (container_plus, {key: metaval})):
1307             with patch.object(
1308                     PithosClient,
1309                     'get_container_info',
1310                     return_value=ret[0]) as GCI:
1311                 for until in (None, somedate):
1312                     r = self.client.get_container_meta(until=until)
1313                     self.assertEqual(r, ret[1])
1314                     self.assertEqual(GCI.mock_calls[-1], call(until=until))
1315
1316     def test_get_container_object_meta(self):
1317         somedate = '50m3d473'
1318         key = 'x-container-object-meta'
1319         metaval = '50m3m374v41'
1320         container_plus = dict(container_info)
1321         container_plus[key] = metaval
1322         for ret in (
1323                 (container_info, {key: ''}),
1324                 (container_plus, {key: metaval})):
1325             with patch.object(
1326                     PithosClient,
1327                     'get_container_info',
1328                     return_value=ret[0]) as GCI:
1329                 for until in (None, somedate):
1330                     r = self.client.get_container_object_meta(until=until)
1331                     self.assertEqual(r, ret[1])
1332                     self.assertEqual(GCI.mock_calls[-1], call(until=until))
1333
1334     @patch('%s.container_post' % pithos_pkg, return_value=FR())
1335     def test_set_container_meta(self, post):
1336         metas = dict(k1='v1', k2='v2', k3='v3')
1337         self.client.set_container_meta(metas)
1338         post.assert_called_once_with(update=True, metadata=metas)
1339
1340     @patch('%s.container_post' % pithos_pkg, return_value=FR())
1341     def test_del_container_meta(self, AP):
1342         self.client.del_container_meta('somekey')
1343         AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1344
1345     @patch('%s.container_post' % pithos_pkg, return_value=FR())
1346     def test_set_container_quota(self, post):
1347         qu = 1024
1348         self.client.set_container_quota(qu)
1349         post.assert_called_once_with(update=True, quota=qu)
1350
1351     @patch('%s.container_post' % pithos_pkg, return_value=FR())
1352     def test_set_container_versioning(self, post):
1353         vrs = 'n3wV3r51on1ngTyp3'
1354         self.client.set_container_versioning(vrs)
1355         post.assert_called_once_with(update=True, versioning=vrs)
1356
1357     @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1358     def test_del_object(self, delete):
1359         for kwarg in (
1360                 dict(delimiter=None, until=None),
1361                 dict(delimiter='X', until='50m3d473')):
1362             self.client.del_object(obj, **kwarg)
1363             self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1364
1365     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1366     def test_set_object_meta(self, post):
1367         metas = dict(k1='v1', k2='v2', k3='v3')
1368         self.assertRaises(
1369             AssertionError,
1370             self.client.set_object_meta,
1371             obj, 'Non dict arg')
1372         self.client.set_object_meta(obj, metas)
1373         post.assert_called_once_with(obj, update=True, metadata=metas)
1374
1375     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1376     def test_publish_object(self, post):
1377         oinfo = dict(object_info)
1378         val = 'pubL1c'
1379         oinfo['x-object-public'] = val
1380         with patch.object(
1381                 PithosClient, 'get_object_info',
1382                 return_value=oinfo) as GOF:
1383             r = self.client.publish_object(obj)
1384             self.assertEqual(
1385                 post.mock_calls[-1],
1386                 call(obj, public=True, update=True))
1387             self.assertEqual(GOF.mock_calls[-1], call(obj))
1388             self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1389
1390     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1391     def test_unpublish_object(self, post):
1392         self.client.unpublish_object(obj)
1393         post.assert_called_once_with(obj, public=False, update=True)
1394
1395     def test_get_object_sharing(self):
1396         info = dict(object_info)
1397         expected = dict(read='u1,g1,u2', write='u1')
1398         info['x-object-sharing'] = '; '.join(
1399             ['%s=%s' % (k, v) for k, v in expected.items()])
1400         with patch.object(
1401                 PithosClient, 'get_object_info',
1402                 return_value=info) as GOF:
1403             r = self.client.get_object_sharing(obj)
1404             self.assertEqual(GOF.mock_calls[-1], call(obj))
1405             self.assert_dicts_are_equal(r, expected)
1406             info['x-object-sharing'] = '//'.join(
1407                 ['%s=%s' % (k, v) for k, v in expected.items()])
1408             self.assertRaises(
1409                 ValueError,
1410                 self.client.get_object_sharing,
1411                 obj)
1412             info['x-object-sharing'] = '; '.join(
1413                 ['%s:%s' % (k, v) for k, v in expected.items()])
1414             self.assertRaises(
1415                 ClientError,
1416                 self.client.get_object_sharing,
1417                 obj)
1418             info['x-object-sharing'] = 'read=%s' % expected['read']
1419             r = self.client.get_object_sharing(obj)
1420             expected.pop('write')
1421             self.assert_dicts_are_equal(r, expected)
1422
1423     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1424     def test_set_object_sharing(self, OP):
1425         read_perms = ['u1', 'g1', 'u2', 'g2']
1426         write_perms = ['u1', 'g1']
1427         for kwargs in (
1428                 dict(read_permition=read_perms, write_permition=write_perms),
1429                 dict(read_permition=read_perms),
1430                 dict(write_permition=write_perms),
1431                 dict()):
1432             self.client.set_object_sharing(obj, **kwargs)
1433             kwargs['read'] = kwargs.pop('read_permition', '')
1434             kwargs['write'] = kwargs.pop('write_permition', '')
1435             self.assertEqual(
1436                 OP.mock_calls[-1],
1437                 call(obj, update=True, permissions=kwargs))
1438
1439     @patch('%s.set_object_sharing' % pithos_pkg)
1440     def test_del_object_sharing(self, SOS):
1441         self.client.del_object_sharing(obj)
1442         SOS.assert_called_once_with(obj)
1443
1444     @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1445     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1446     def test_append_object(self, post, GCI):
1447         num_of_blocks = 4
1448         tmpFile = self._create_temp_file(num_of_blocks)
1449         tmpFile.seek(0, 2)
1450         file_size = tmpFile.tell()
1451         for turn in range(2):
1452             tmpFile.seek(0, 0)
1453
1454             try:
1455                 from progress.bar import ShadyBar
1456                 apn_bar = ShadyBar('Mock append')
1457             except ImportError:
1458                 apn_bar = None
1459
1460             if apn_bar:
1461
1462                 def append_gen(n):
1463                     for i in apn_bar.iter(range(n)):
1464                         yield
1465                     yield
1466
1467             else:
1468                 append_gen = None
1469
1470             self.client.append_object(
1471                 obj, tmpFile,
1472                 upload_cb=append_gen if turn else None)
1473             self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1474             (args, kwargs) = post.mock_calls[-1][1:3]
1475             self.assertEqual(args, (obj,))
1476             self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1477             fsize = num_of_blocks * int(kwargs['content_length'])
1478             self.assertEqual(fsize, file_size)
1479             self.assertEqual(kwargs['content_range'], 'bytes */*')
1480             exp = 'application/octet-stream'
1481             self.assertEqual(kwargs['content_type'], exp)
1482             self.assertEqual(kwargs['update'], True)
1483
1484     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1485     def test_truncate_object(self, post):
1486         upto_bytes = 377
1487         self.client.truncate_object(obj, upto_bytes)
1488         post.assert_called_once_with(
1489             obj,
1490             update=True,
1491             object_bytes=upto_bytes,
1492             content_range='bytes 0-%s/*' % upto_bytes,
1493             content_type='application/octet-stream',
1494             source_object='/%s/%s' % (self.client.container, obj))
1495
1496     @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1497     @patch('%s.object_post' % pithos_pkg, return_value=FR())
1498     def test_overwrite_object(self, post, GCI):
1499         num_of_blocks = 4
1500         tmpFile = self._create_temp_file(num_of_blocks)
1501         tmpFile.seek(0, 2)
1502         file_size = tmpFile.tell()
1503         info = dict(object_info)
1504         info['content-length'] = file_size
1505         block_size = container_info['x-container-block-size']
1506         with patch.object(
1507                 PithosClient, 'get_object_info',
1508                 return_value=info) as GOI:
1509             for start, end in (
1510                     (0, file_size + 1),
1511                     (file_size + 1, file_size + 2)):
1512                 tmpFile.seek(0, 0)
1513                 self.assertRaises(
1514                     ClientError,
1515                     self.client.overwrite_object,
1516                     obj, start, end, tmpFile)
1517             for start, end in ((0, 144), (144, 233), (233, file_size)):
1518                 tmpFile.seek(0, 0)
1519                 owr_gen = None
1520                 exp_size = end - start + 1
1521                 if not start or exp_size > block_size:
1522                     try:
1523                         from progress.bar import ShadyBar
1524                         owr_bar = ShadyBar('Mock append')
1525                     except ImportError:
1526                         owr_bar = None
1527
1528                     if owr_bar:
1529
1530                         def owr_gen(n):
1531                             for i in owr_bar.iter(range(n)):
1532                                 yield
1533                             yield
1534
1535                     if exp_size > block_size:
1536                         exp_size = exp_size % block_size or block_size
1537
1538                 self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1539                 self.assertEqual(GOI.mock_calls[-1], call(obj))
1540                 self.assertEqual(GCI.mock_calls[-1], call())
1541                 (args, kwargs) = post.mock_calls[-1][1:3]
1542                 self.assertEqual(args, (obj,))
1543                 self.assertEqual(len(kwargs['data']), exp_size)
1544                 self.assertEqual(kwargs['content_length'], exp_size)
1545                 self.assertEqual(kwargs['update'], True)
1546                 exp = 'application/octet-stream'
1547                 self.assertEqual(kwargs['content_type'], exp)
1548
1549     @patch('%s.set_param' % pithos_pkg)
1550     @patch('%s.get' % pithos_pkg, return_value=FR())
1551     def test_get_sharing_accounts(self, get, SP):
1552         FR.json = sharers
1553         for kws in (
1554                 dict(),
1555                 dict(limit='50m3-11m17'),
1556                 dict(marker='X'),
1557                 dict(limit='50m3-11m17', marker='X')):
1558             r = self.client.get_sharing_accounts(**kws)
1559             self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1560             self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1561             limit, marker = kws.get('limit', None), kws.get('marker', None)
1562             self.assertEqual(SP.mock_calls[-2], call(
1563                 'limit', limit,
1564                 iff=limit is not None))
1565             self.assertEqual(SP.mock_calls[-1], call(
1566                 'marker', marker,
1567                 iff=marker is not None))
1568             for i in range(len(r)):
1569                 self.assert_dicts_are_equal(r[i], sharers[i])
1570
1571     @patch('%s.object_get' % pithos_pkg, return_value=FR())
1572     def test_get_object_versionlist(self, get):
1573         info = dict(object_info)
1574         info['versions'] = ['v1', 'v2']
1575         FR.json = info
1576         r = self.client.get_object_versionlist(obj)
1577         get.assert_called_once_with(obj, format='json', version='list')
1578         self.assertEqual(r, info['versions'])
1579
1580 if __name__ == '__main__':
1581     from sys import argv
1582     from kamaki.clients.test import runTestCase
1583     not_found = True
1584     if not argv[1:] or argv[1] == 'Pithos':
1585         not_found = False
1586         runTestCase(Pithos, 'Pithos Client', argv[2:])
1587     if not argv[1:] or argv[1] == 'PithosRest':
1588         not_found = False
1589         runTestCase(PithosRest, 'PithosRest Client', argv[2:])
1590     if not_found:
1591         print('TestCase %s not found' % argv[1])