Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / test.py @ 49cc29b2

History | View | Annotate | Download (66.8 kB)

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
        exp_headers = dict(id='container id', name='container name')
825
        FR.headers = dict(exp_headers)
826
        r = self.client.upload_object(obj, tmpFile)
827
        self.assert_dicts_are_equal(r, exp_headers)
828
        self.assertEqual(GCI.mock_calls[-1], call())
829
        [call1, call2] = OP.mock_calls
830

    
831
        (args1, kwargs1) = call1[1:3]
832
        (args2, kwargs2) = call2[1:3]
833
        self.assertEqual(args1, (obj,))
834
        expected1 = dict(
835
            hashmap=True,
836
            success=(201, 409),
837
            format='json',
838
            json=dict(
839
                hashes=['s0m3h@5h'] * num_of_blocks,
840
                bytes=num_of_blocks * 4 * 1024 * 1024),
841
            content_encoding=None,
842
            content_type='application/octet-stream',
843
            content_disposition=None,
844
            public=None,
845
            permissions=None)
846
        for k, v in expected1.items():
847
            if k == 'json':
848
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
849
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
850
            else:
851
                self.assertEqual(v, kwargs1[k])
852

    
853
        (args2, kwargs2) = call2[1:3]
854
        self.assertEqual(args2, (obj,))
855
        expected2 = dict(
856
            json=dict(
857
                hashes=['s0m3h@5h'] * num_of_blocks,
858
                bytes=num_of_blocks * 4 * 1024 * 1024),
859
            content_type='application/octet-stream',
860
            hashmap=True,
861
            success=201,
862
            format='json')
863
        for k, v in expected2.items():
864
            if k == 'json':
865
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
866
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
867
            else:
868
                self.assertEqual(v, kwargs2[k])
869

    
870
        mock_offset = 2
871

    
872
        #  With progress bars
873
        try:
874
            from progress.bar import ShadyBar
875
            blck_bar = ShadyBar('Mock blck calc.')
876
            upld_bar = ShadyBar('Mock uplds')
877
        except ImportError:
878
            blck_bar = None
879
            upld_bar = None
880

    
881
        if blck_bar and upld_bar:
882

    
883
            def blck_gen(n):
884
                for i in blck_bar.iter(range(n)):
885
                    yield
886
                yield
887

    
888
            def upld_gen(n):
889
                for i in upld_bar.iter(range(n)):
890
                    yield
891
                yield
892

    
893
            tmpFile.seek(0)
894
            r = self.client.upload_object(
895
                obj, tmpFile,
896
                hash_cb=blck_gen, upload_cb=upld_gen)
897
            self.assert_dicts_are_equal(r, exp_headers)
898

    
899
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
900
                self.assertEqual(OP.mock_calls[i], c)
901

    
902
        #  With content-type
903
        tmpFile.seek(0)
904
        ctype = 'video/mpeg'
905
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
906
        r = self.client.upload_object(obj, tmpFile,
907
            content_type=ctype, sharing=sharing)
908
        self.assert_dicts_are_equal(r, exp_headers)
909
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
910
        self.assert_dicts_are_equal(
911
            OP.mock_calls[-2][2]['permissions'],
912
            sharing)
913

    
914
        # With other args
915
        tmpFile.seek(0)
916
        kwargs = dict(
917
            etag='s0m3E74g',
918
            if_etag_match='if etag match',
919
            if_not_exist=True,
920
            content_type=ctype,
921
            content_disposition=ctype + 'd15p051710n',
922
            public=True,
923
            content_encoding='802.11',
924
            container_info_cache={})
925
        r = self.client.upload_object(obj, tmpFile, **kwargs)
926
        self.assert_dicts_are_equal(r, exp_headers)
927

    
928
        kwargs.pop('if_not_exist')
929
        ematch = kwargs.pop('if_etag_match')
930
        etag = kwargs.pop('etag')
931
        self.assert_dicts_are_equal(
932
            kwargs.pop('container_info_cache'),
933
            {self.client.container: container_info})
934
        for arg, val in kwargs.items():
935
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
936
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
937
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
938
        self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
939

    
940
    def test_get_object_info(self):
941
        FR.headers = object_info
942
        version = 'v3r510n'
943
        with patch.object(
944
                pithos.PithosClient, 'object_head',
945
                return_value=FR()) as head:
946
            r = self.client.get_object_info(obj)
947
            self.assertEqual(r, object_info)
948
            r = self.client.get_object_info(obj, version=version)
949
            self.assertEqual(head.mock_calls, [
950
                call(obj, version=None),
951
                call(obj, version=version)])
952
        with patch.object(
953
                pithos.PithosClient, 'object_head',
954
                side_effect=ClientError('Obj not found', 404)):
955
            self.assertRaises(
956
                ClientError,
957
                self.client.get_object_info,
958
                obj, version=version)
959

    
960
    @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
961
    def test_get_object_meta(self, GOI):
962
        for version in (None, 'v3r510n'):
963
            r = self.client.get_object_meta(obj, version)
964
            for k in [k for k in object_info if k.startswith('x-object-meta')]:
965
                self.assertEqual(r.pop(k), object_info[k])
966
            self.assertFalse(len(r))
967
            self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
968

    
969
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
970
    def test_del_object_meta(self, post):
971
        metakey = '50m3m3t4k3y'
972
        self.client.del_object_meta(obj, metakey)
973
        post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
974

    
975
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
976
    def test_copy_object(self, put):
977
        src_cont = 'src-c0nt41n3r'
978
        src_obj = 'src-0bj'
979
        dst_cont = 'dst-c0nt41n3r'
980
        dst_obj = 'dst-0bj'
981
        expected = call(
982
            src_obj,
983
            content_length=0,
984
            source_account=None,
985
            success=201,
986
            copy_from='/%s/%s' % (src_cont, src_obj),
987
            delimiter=None,
988
            content_type=None,
989
            source_version=None,
990
            public=False)
991
        self.client.copy_object(src_cont, src_obj, dst_cont)
992
        self.assertEqual(put.mock_calls[-1], expected)
993
        self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
994
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
995
        kwargs = dict(
996
            source_version='src-v3r510n',
997
            source_account='src-4cc0un7',
998
            public=True,
999
            content_type='c0n73n7Typ3',
1000
            delimiter='5')
1001
        self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
1002
        for k, v in kwargs.items():
1003
            self.assertEqual(v, put.mock_calls[-1][2][k])
1004

    
1005
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1006
    def test_move_object(self, put):
1007
        src_cont = 'src-c0nt41n3r'
1008
        src_obj = 'src-0bj'
1009
        dst_cont = 'dst-c0nt41n3r'
1010
        dst_obj = 'dst-0bj'
1011
        expected = call(
1012
            src_obj,
1013
            content_length=0,
1014
            source_account=None,
1015
            success=201,
1016
            move_from='/%s/%s' % (src_cont, src_obj),
1017
            delimiter=None,
1018
            content_type=None,
1019
            source_version=None,
1020
            public=False)
1021
        self.client.move_object(src_cont, src_obj, dst_cont)
1022
        self.assertEqual(put.mock_calls[-1], expected)
1023
        self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1024
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1025
        kwargs = dict(
1026
            source_version='src-v3r510n',
1027
            source_account='src-4cc0un7',
1028
            public=True,
1029
            content_type='c0n73n7Typ3',
1030
            delimiter='5')
1031
        self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1032
        for k, v in kwargs.items():
1033
            self.assertEqual(v, put.mock_calls[-1][2][k])
1034

    
1035
    #  Pithos+ only methods
1036

    
1037
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1038
    def test_purge_container(self, CD):
1039
        self.client.purge_container()
1040
        self.assertTrue('until' in CD.mock_calls[-1][2])
1041
        cont = self.client.container
1042
        self.client.purge_container('another-container')
1043
        self.assertEqual(self.client.container, cont)
1044

    
1045
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1046
    def test_upload_object_unchunked(self, put):
1047
        num_of_blocks = 8
1048
        tmpFile = self._create_temp_file(num_of_blocks)
1049
        expected = dict(
1050
                success=201,
1051
                data=num_of_blocks * 4 * 1024 * 1024,
1052
                etag='some-etag',
1053
                content_encoding='some content_encoding',
1054
                content_type='some content-type',
1055
                content_disposition='some content_disposition',
1056
                public=True,
1057
                permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1058
        r = self.client.upload_object_unchunked(obj, tmpFile)
1059
        self.assert_dicts_are_equal(r, FR.headers)
1060
        self.assertEqual(put.mock_calls[-1][1], (obj,))
1061
        self.assertEqual(
1062
            sorted(put.mock_calls[-1][2].keys()),
1063
            sorted(expected.keys()))
1064
        kwargs = dict(expected)
1065
        kwargs.pop('success')
1066
        kwargs['size'] = kwargs.pop('data')
1067
        kwargs['sharing'] = kwargs.pop('permissions')
1068
        tmpFile.seek(0)
1069
        r = self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1070
        self.assert_dicts_are_equal(r, FR.headers)
1071
        pmc = put.mock_calls[-1][2]
1072
        for k, v in expected.items():
1073
            if k == 'data':
1074
                self.assertEqual(len(pmc[k]), v)
1075
            else:
1076
                self.assertEqual(pmc[k], v)
1077
        self.assertRaises(
1078
            ClientError,
1079
            self.client.upload_object_unchunked,
1080
            obj, tmpFile, withHashFile=True)
1081

    
1082
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1083
    def test_create_object_by_manifestation(self, put):
1084
        manifest = '%s/%s' % (self.client.container, obj)
1085
        kwargs = dict(
1086
                etag='some-etag',
1087
                content_encoding='some content_encoding',
1088
                content_type='some content-type',
1089
                content_disposition='some content_disposition',
1090
                public=True,
1091
                sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1092
        r = self.client.create_object_by_manifestation(obj)
1093
        self.assert_dicts_are_equal(r, FR.headers)
1094
        expected = dict(content_length=0, manifest=manifest)
1095
        for k in kwargs:
1096
            expected['permissions' if k == 'sharing' else k] = None
1097
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1098
        r = self.client.create_object_by_manifestation(obj, **kwargs)
1099
        self.assert_dicts_are_equal(r, FR.headers)
1100
        expected.update(kwargs)
1101
        expected['permissions'] = expected.pop('sharing')
1102
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1103

    
1104
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1105
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1106
    def test_download_to_string(self, GET, GOH):
1107
        FR.content = 'some sample content'
1108
        num_of_blocks = len(object_hashmap['hashes'])
1109
        r = self.client.download_to_string(obj)
1110
        expected_content = FR.content * num_of_blocks
1111
        self.assertEqual(expected_content, r)
1112
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1113
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1114

    
1115
        kwargs = dict(
1116
            version='version',
1117
            range_str='10-20',
1118
            if_match='if and only if',
1119
            if_none_match='if and only not',
1120
            if_modified_since='what if not?',
1121
            if_unmodified_since='this happens if not!')
1122
        expargs = dict(kwargs)
1123
        expargs.pop('range_str')
1124
        for k in expargs:
1125
            expargs[k] = None
1126
        GOH.assert_called_once_with(obj, **expargs)
1127

    
1128
        r = self.client.download_to_string(obj, **kwargs)
1129
        expargs['data_range'] = 'bytes=%s' % kwargs['range_str']
1130
        for k, v in expargs.items():
1131
            self.assertEqual(
1132
                GET.mock_calls[-1][2][k],
1133
                v or kwargs.get(k))
1134

    
1135
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1136
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1137
    def test_download_object(self, GET, GOH):
1138
        num_of_blocks = 8
1139
        tmpFile = self._create_temp_file(num_of_blocks)
1140
        FR.content = tmpFile.read(4 * 1024 * 1024)
1141
        tmpFile = self._create_temp_file(num_of_blocks)
1142
        num_of_blocks = len(object_hashmap['hashes'])
1143
        kwargs = dict(
1144
            resume=True,
1145
            version='version',
1146
            range_str='10-20',
1147
            if_match='if and only if',
1148
            if_none_match='if and only not',
1149
            if_modified_since='what if not?',
1150
            if_unmodified_since='this happens if not!',
1151
            async_headers=dict(Range='bytes=0-88888888'))
1152

    
1153
        self.client.download_object(obj, tmpFile)
1154
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1155
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1156
        for k, v in kwargs.items():
1157
            if k == 'async_headers':
1158
                self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1159
            elif k in ('resume', 'range_str'):
1160
                continue
1161
            else:
1162
                self.assertEqual(GET.mock_calls[-1][2][k], None)
1163

    
1164
        #  Check ranges are consecutive
1165
        starts = []
1166
        ends = []
1167
        for c in GET.mock_calls:
1168
            rng_str = c[2]['async_headers']['Range']
1169
            (start, rng_str) = rng_str.split('=')
1170
            (start, end) = rng_str.split('-')
1171
            starts.append(start)
1172
            ends.append(end)
1173
        ends = sorted(ends)
1174
        for i, start in enumerate(sorted(starts)):
1175
            if i:
1176
                int(ends[i - 1]) == int(start) - 1
1177

    
1178
        #  With progress bars
1179
        try:
1180
            from progress.bar import ShadyBar
1181
            dl_bar = ShadyBar('Mock dl')
1182
        except ImportError:
1183
            dl_bar = None
1184

    
1185
        if dl_bar:
1186

    
1187
            def blck_gen(n):
1188
                for i in dl_bar.iter(range(n)):
1189
                    yield
1190
                yield
1191

    
1192
            tmpFile.seek(0)
1193
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1194
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1195

    
1196
        tmpFile.seek(0)
1197
        kwargs.pop('async_headers')
1198
        kwargs.pop('resume')
1199
        self.client.download_object(obj, tmpFile, **kwargs)
1200
        for k, v in kwargs.items():
1201
            if k == 'range_str':
1202
                self.assertEqual(
1203
                    GET.mock_calls[-1][2]['data_range'],
1204
                    'bytes=%s' % v)
1205
            else:
1206
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1207

    
1208
        #  ALl options on no tty
1209
        def foo():
1210
            return True
1211

    
1212
        tmpFile.seek(0)
1213
        tmpFile.isatty = foo
1214
        self.client.download_object(obj, tmpFile, **kwargs)
1215
        for k, v in kwargs.items():
1216
            if k == 'range_str':
1217
                self.assertTrue('data_range' in GET.mock_calls[-1][2])
1218
            else:
1219
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1220

    
1221
    def test_get_object_hashmap(self):
1222
        FR.json = object_hashmap
1223
        for empty in (304, 412):
1224
            with patch.object(
1225
                    pithos.PithosClient, 'object_get',
1226
                    side_effect=ClientError('Empty', status=empty)):
1227
                r = self.client.get_object_hashmap(obj)
1228
                self.assertEqual(r, {})
1229
        exp_args = dict(
1230
            hashmap=True,
1231
            data_range=None,
1232
            version=None,
1233
            if_etag_match=None,
1234
            if_etag_not_match=None,
1235
            if_modified_since=None,
1236
            if_unmodified_since=None)
1237
        kwargs = dict(
1238
            version='s0m3v3r51on',
1239
            if_match='if match',
1240
            if_none_match='if non match',
1241
            if_modified_since='some date here',
1242
            if_unmodified_since='some date here',
1243
            data_range='10-20')
1244
        with patch.object(
1245
                pithos.PithosClient, 'object_get',
1246
                return_value=FR()) as get:
1247
            r = self.client.get_object_hashmap(obj)
1248
            self.assertEqual(r, object_hashmap)
1249
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1250
            r = self.client.get_object_hashmap(obj, **kwargs)
1251
            exp_args['if_etag_match'] = kwargs.pop('if_match')
1252
            exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1253
            exp_args.update(kwargs)
1254
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1255

    
1256
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1257
    def test_set_account_group(self, post):
1258
        (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1259
        self.client.set_account_group(group, usernames)
1260
        post.assert_called_once_with(update=True, groups={group: usernames})
1261

    
1262
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1263
    def test_del_account_group(self, post):
1264
        group = 'aU53rGr0up'
1265
        self.client.del_account_group(group)
1266
        post.assert_called_once_with(update=True, groups={group: []})
1267

    
1268
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1269
    def test_get_account_quota(self, GAI):
1270
        key = 'x-account-policy-quota'
1271
        r = self.client.get_account_quota()
1272
        GAI.assert_called_once_with()
1273
        self.assertEqual(r[key], account_info[key])
1274

    
1275
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1276
    def test_get_account_versioning(self, GAI):
1277
        key = 'x-account-policy-versioning'
1278
        r = self.client.get_account_versioning()
1279
        GAI.assert_called_once_with()
1280
        self.assertEqual(r[key], account_info[key])
1281

    
1282
    def test_get_account_meta(self):
1283
        key = 'x-account-meta-'
1284
        with patch.object(
1285
                pithos.PithosClient, 'get_account_info',
1286
                return_value=account_info):
1287
            r = self.client.get_account_meta()
1288
            keys = [k for k in r if k.startswith(key)]
1289
            self.assertFalse(keys)
1290
        acc_info = dict(account_info)
1291
        acc_info['%sk1' % key] = 'v1'
1292
        acc_info['%sk2' % key] = 'v2'
1293
        acc_info['%sk3' % key] = 'v3'
1294
        with patch.object(
1295
                pithos.PithosClient, 'get_account_info',
1296
                return_value=acc_info):
1297
            r = self.client.get_account_meta()
1298
            for k in [k for k in acc_info if k.startswith(key)]:
1299
                self.assertEqual(r[k], acc_info[k])
1300

    
1301
    def test_get_account_group(self):
1302
        key = 'x-account-group-'
1303
        with patch.object(
1304
                pithos.PithosClient, 'get_account_info',
1305
                return_value=account_info):
1306
            r = self.client.get_account_group()
1307
            keys = [k for k in r if k.startswith(key)]
1308
            self.assertFalse(keys)
1309
        acc_info = dict(account_info)
1310
        acc_info['%sk1' % key] = 'g1'
1311
        acc_info['%sk2' % key] = 'g2'
1312
        acc_info['%sk3' % key] = 'g3'
1313
        with patch.object(
1314
                pithos.PithosClient, 'get_account_info',
1315
                return_value=acc_info):
1316
            r = self.client.get_account_group()
1317
            for k in [k for k in acc_info if k.startswith(key)]:
1318
                self.assertEqual(r[k], acc_info[k])
1319

    
1320
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1321
    def test_set_account_meta(self, post):
1322
        metas = dict(k1='v1', k2='v2', k3='v3')
1323
        self.client.set_account_meta(metas)
1324
        post.assert_called_once_with(update=True, metadata=metas)
1325

    
1326
    """
1327
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1328
    def test_set_account_quota(self, post):
1329
        qu = 1024
1330
        self.client.set_account_quota(qu)
1331
        post.assert_called_once_with(update=True, quota=qu)
1332
    """
1333

    
1334
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1335
    def test_set_account_versioning(self, post):
1336
        vrs = 'n3wV3r51on1ngTyp3'
1337
        self.client.set_account_versioning(vrs)
1338
        post.assert_called_once_with(update=True, versioning=vrs)
1339

    
1340
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1341
    def test_del_container(self, delete):
1342
        for kwarg in (
1343
                dict(delimiter=None, until=None),
1344
                dict(delimiter='X', until='50m3d473')):
1345
            self.client.del_container(**kwarg)
1346
            expected = dict(kwarg)
1347
            expected['success'] = (204, 404, 409)
1348
            self.assertEqual(delete.mock_calls[-1], call(**expected))
1349
        for status_code in (404, 409):
1350
            FR.status_code = status_code
1351
            self.assertRaises(ClientError, self.client.del_container)
1352

    
1353
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1354
    def test_get_container_versioning(self, GCI):
1355
        key = 'x-container-policy-versioning'
1356
        cont = 'c0n7-417'
1357
        bu_cnt = self.client.container
1358
        for container in (None, cont):
1359
            r = self.client.get_container_versioning(container=container)
1360
            self.assertEqual(r[key], container_info[key])
1361
            self.assertEqual(GCI.mock_calls[-1], call())
1362
            self.assertEqual(bu_cnt, self.client.container)
1363

    
1364
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1365
    def test_get_container_limit(self, GCI):
1366
        key = 'x-container-policy-quota'
1367
        cont = 'c0n7-417'
1368
        bu_cnt = self.client.container
1369
        for container in (None, cont):
1370
            r = self.client.get_container_limit(container=container)
1371
            self.assertEqual(r[key], container_info[key])
1372
            self.assertEqual(GCI.mock_calls[-1], call())
1373
            self.assertEqual(bu_cnt, self.client.container)
1374

    
1375
    def test_get_container_meta(self):
1376
        somedate = '50m3d473'
1377
        key = 'x-container-meta'
1378
        metaval = '50m3m374v41'
1379
        container_plus = dict(container_info)
1380
        container_plus[key] = metaval
1381
        for ret in ((container_info, {}), (container_plus, {key: metaval})):
1382
            with patch.object(
1383
                    pithos.PithosClient,
1384
                    'get_container_info',
1385
                    return_value=ret[0]) as GCI:
1386
                for until in (None, somedate):
1387
                    r = self.client.get_container_meta(until=until)
1388
                    self.assertEqual(r, ret[1])
1389
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1390

    
1391
    def test_get_container_object_meta(self):
1392
        somedate = '50m3d473'
1393
        key = 'x-container-object-meta'
1394
        metaval = '50m3m374v41'
1395
        container_plus = dict(container_info)
1396
        container_plus[key] = metaval
1397
        for ret in (
1398
                (container_info, {key: ''}),
1399
                (container_plus, {key: metaval})):
1400
            with patch.object(
1401
                    pithos.PithosClient,
1402
                    'get_container_info',
1403
                    return_value=ret[0]) as GCI:
1404
                for until in (None, somedate):
1405
                    r = self.client.get_container_object_meta(until=until)
1406
                    self.assertEqual(r, ret[1])
1407
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1408

    
1409
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1410
    def test_set_container_meta(self, post):
1411
        metas = dict(k1='v1', k2='v2', k3='v3')
1412
        self.client.set_container_meta(metas)
1413
        post.assert_called_once_with(update=True, metadata=metas)
1414

    
1415
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1416
    def test_del_container_meta(self, AP):
1417
        self.client.del_container_meta('somekey')
1418
        AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1419

    
1420
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1421
    def test_set_container_limit(self, post):
1422
        qu = 1024
1423
        self.client.set_container_limit(qu)
1424
        post.assert_called_once_with(update=True, quota=qu)
1425

    
1426
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1427
    def test_set_container_versioning(self, post):
1428
        vrs = 'n3wV3r51on1ngTyp3'
1429
        self.client.set_container_versioning(vrs)
1430
        post.assert_called_once_with(update=True, versioning=vrs)
1431

    
1432
    @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1433
    def test_del_object(self, delete):
1434
        for kwarg in (
1435
                dict(delimiter=None, until=None),
1436
                dict(delimiter='X', until='50m3d473')):
1437
            self.client.del_object(obj, **kwarg)
1438
            self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1439

    
1440
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1441
    def test_set_object_meta(self, post):
1442
        metas = dict(k1='v1', k2='v2', k3='v3')
1443
        self.assertRaises(
1444
            AssertionError,
1445
            self.client.set_object_meta,
1446
            obj, 'Non dict arg')
1447
        self.client.set_object_meta(obj, metas)
1448
        post.assert_called_once_with(obj, update=True, metadata=metas)
1449

    
1450
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1451
    def test_publish_object(self, post):
1452
        oinfo = dict(object_info)
1453
        val = 'pubL1c'
1454
        oinfo['x-object-public'] = val
1455
        with patch.object(
1456
                pithos.PithosClient, 'get_object_info',
1457
                return_value=oinfo) as GOF:
1458
            r = self.client.publish_object(obj)
1459
            self.assertEqual(
1460
                post.mock_calls[-1],
1461
                call(obj, public=True, update=True))
1462
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1463
            self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1464

    
1465
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1466
    def test_unpublish_object(self, post):
1467
        self.client.unpublish_object(obj)
1468
        post.assert_called_once_with(obj, public=False, update=True)
1469

    
1470
    def test_get_object_sharing(self):
1471
        info = dict(object_info)
1472
        expected = dict(read='u1,g1,u2', write='u1')
1473
        info['x-object-sharing'] = '; '.join(
1474
            ['%s=%s' % (k, v) for k, v in expected.items()])
1475
        with patch.object(
1476
                pithos.PithosClient, 'get_object_info',
1477
                return_value=info) as GOF:
1478
            r = self.client.get_object_sharing(obj)
1479
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1480
            self.assert_dicts_are_equal(r, expected)
1481
            info['x-object-sharing'] = '//'.join(
1482
                ['%s=%s' % (k, v) for k, v in expected.items()])
1483
            self.assertRaises(
1484
                ValueError,
1485
                self.client.get_object_sharing,
1486
                obj)
1487
            info['x-object-sharing'] = '; '.join(
1488
                ['%s:%s' % (k, v) for k, v in expected.items()])
1489
            self.assertRaises(
1490
                ClientError,
1491
                self.client.get_object_sharing,
1492
                obj)
1493
            info['x-object-sharing'] = 'read=%s' % expected['read']
1494
            r = self.client.get_object_sharing(obj)
1495
            expected.pop('write')
1496
            self.assert_dicts_are_equal(r, expected)
1497

    
1498
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1499
    def test_set_object_sharing(self, OP):
1500
        read_perms = ['u1', 'g1', 'u2', 'g2']
1501
        write_perms = ['u1', 'g1']
1502
        for kwargs in (
1503
                dict(read_permition=read_perms, write_permition=write_perms),
1504
                dict(read_permition=read_perms),
1505
                dict(write_permition=write_perms),
1506
                dict()):
1507
            self.client.set_object_sharing(obj, **kwargs)
1508
            kwargs['read'] = kwargs.pop('read_permition', '')
1509
            kwargs['write'] = kwargs.pop('write_permition', '')
1510
            self.assertEqual(
1511
                OP.mock_calls[-1],
1512
                call(obj, update=True, permissions=kwargs))
1513

    
1514
    @patch('%s.set_object_sharing' % pithos_pkg)
1515
    def test_del_object_sharing(self, SOS):
1516
        self.client.del_object_sharing(obj)
1517
        SOS.assert_called_once_with(obj)
1518

    
1519
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1520
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1521
    def test_append_object(self, post, GCI):
1522
        num_of_blocks = 4
1523
        tmpFile = self._create_temp_file(num_of_blocks)
1524
        tmpFile.seek(0, 2)
1525
        file_size = tmpFile.tell()
1526
        for turn in range(2):
1527
            tmpFile.seek(0, 0)
1528

    
1529
            try:
1530
                from progress.bar import ShadyBar
1531
                apn_bar = ShadyBar('Mock append')
1532
            except ImportError:
1533
                apn_bar = None
1534

    
1535
            if apn_bar:
1536

    
1537
                def append_gen(n):
1538
                    for i in apn_bar.iter(range(n)):
1539
                        yield
1540
                    yield
1541

    
1542
            else:
1543
                append_gen = None
1544

    
1545
            self.client.append_object(
1546
                obj, tmpFile,
1547
                upload_cb=append_gen if turn else None)
1548
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1549
            (args, kwargs) = post.mock_calls[-1][1:3]
1550
            self.assertEqual(args, (obj,))
1551
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1552
            fsize = num_of_blocks * int(kwargs['content_length'])
1553
            self.assertEqual(fsize, file_size)
1554
            self.assertEqual(kwargs['content_range'], 'bytes */*')
1555
            exp = 'application/octet-stream'
1556
            self.assertEqual(kwargs['content_type'], exp)
1557
            self.assertEqual(kwargs['update'], True)
1558

    
1559
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1560
    def test_truncate_object(self, post):
1561
        upto_bytes = 377
1562
        self.client.truncate_object(obj, upto_bytes)
1563
        post.assert_called_once_with(
1564
            obj,
1565
            update=True,
1566
            object_bytes=upto_bytes,
1567
            content_range='bytes 0-%s/*' % upto_bytes,
1568
            content_type='application/octet-stream',
1569
            source_object='/%s/%s' % (self.client.container, obj))
1570

    
1571
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1572
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1573
    def test_overwrite_object(self, post, GCI):
1574
        num_of_blocks = 4
1575
        tmpFile = self._create_temp_file(num_of_blocks)
1576
        tmpFile.seek(0, 2)
1577
        file_size = tmpFile.tell()
1578
        info = dict(object_info)
1579
        info['content-length'] = file_size
1580
        block_size = container_info['x-container-block-size']
1581
        with patch.object(
1582
                pithos.PithosClient, 'get_object_info',
1583
                return_value=info) as GOI:
1584
            for start, end in (
1585
                    (0, file_size + 1),
1586
                    (file_size + 1, file_size + 2)):
1587
                tmpFile.seek(0, 0)
1588
                self.assertRaises(
1589
                    ClientError,
1590
                    self.client.overwrite_object,
1591
                    obj, start, end, tmpFile)
1592
            for start, end in ((0, 144), (144, 233), (233, file_size)):
1593
                tmpFile.seek(0, 0)
1594
                owr_gen = None
1595
                exp_size = end - start + 1
1596
                if not start or exp_size > block_size:
1597
                    try:
1598
                        from progress.bar import ShadyBar
1599
                        owr_bar = ShadyBar('Mock append')
1600
                    except ImportError:
1601
                        owr_bar = None
1602

    
1603
                    if owr_bar:
1604

    
1605
                        def owr_gen(n):
1606
                            for i in owr_bar.iter(range(n)):
1607
                                yield
1608
                            yield
1609

    
1610
                    if exp_size > block_size:
1611
                        exp_size = exp_size % block_size or block_size
1612

    
1613
                self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1614
                self.assertEqual(GOI.mock_calls[-1], call(obj))
1615
                self.assertEqual(GCI.mock_calls[-1], call())
1616
                (args, kwargs) = post.mock_calls[-1][1:3]
1617
                self.assertEqual(args, (obj,))
1618
                self.assertEqual(len(kwargs['data']), exp_size)
1619
                self.assertEqual(kwargs['content_length'], exp_size)
1620
                self.assertEqual(kwargs['update'], True)
1621
                exp = 'application/octet-stream'
1622
                self.assertEqual(kwargs['content_type'], exp)
1623

    
1624
    @patch('%s.set_param' % pithos_pkg)
1625
    @patch('%s.get' % pithos_pkg, return_value=FR())
1626
    def test_get_sharing_accounts(self, get, SP):
1627
        FR.json = sharers
1628
        for kws in (
1629
                dict(),
1630
                dict(limit='50m3-11m17'),
1631
                dict(marker='X'),
1632
                dict(limit='50m3-11m17', marker='X')):
1633
            r = self.client.get_sharing_accounts(**kws)
1634
            self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1635
            self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1636
            limit, marker = kws.get('limit', None), kws.get('marker', None)
1637
            self.assertEqual(SP.mock_calls[-2], call(
1638
                'limit', limit,
1639
                iff=limit is not None))
1640
            self.assertEqual(SP.mock_calls[-1], call(
1641
                'marker', marker,
1642
                iff=marker is not None))
1643
            for i in range(len(r)):
1644
                self.assert_dicts_are_equal(r[i], sharers[i])
1645

    
1646
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1647
    def test_get_object_versionlist(self, get):
1648
        info = dict(object_info)
1649
        info['versions'] = ['v1', 'v2']
1650
        FR.json = info
1651
        r = self.client.get_object_versionlist(obj)
1652
        get.assert_called_once_with(obj, format='json', version='list')
1653
        self.assertEqual(r, info['versions'])
1654

    
1655
if __name__ == '__main__':
1656
    from sys import argv
1657
    from kamaki.clients.test import runTestCase
1658
    not_found = True
1659
    if not argv[1:] or argv[1] == 'PithosClient':
1660
        not_found = False
1661
        runTestCase(PithosClient, 'Pithos Client', argv[2:])
1662
    if not argv[1:] or argv[1] == 'PithosRestClient':
1663
        not_found = False
1664
        runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1665
    if not_found:
1666
        print('TestCase %s not found' % argv[1])