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