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