Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / test.py @ 776b275c

History | View | Annotate | Download (73.5 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, public is not None)]
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, public is not None)]
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, iff=perms)]
641
            else:
642
                exp += [call('X-Object-Sharing', '', iff={})]
643
            exp += [call('X-Object-Public', public, public is not None)]
644
            for k, v in metas.items():
645
                exp += [call('X-Object-Meta-%s' % k, v)]
646
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
647
            acc, cont = self.client.account, self.client.container
648
            self.assertEqual(move.mock_calls[-1], call(
649
                '/%s/%s/%s' % (acc, cont, obj),
650
                *args,
651
                success=kwargs.pop('success', 201),
652
                **kwargs))
653

    
654
    @patch('%s.set_param' % rest_pkg)
655
    @patch('%s.set_header' % rest_pkg)
656
    @patch('%s.post' % rest_pkg, return_value=FR())
657
    def test_object_post(self, post, SH, SP):
658
        for pm in product(
659
                ('json', 'f0rm47'),
660
                (False, True),
661
                (dict(), dict(read=['u1', 'g2'], write=['u1'])),
662
                (False, True),
663
                (dict(), dict(k2='v2', k3='v3')),
664
                ((), ('someval',)),
665
                (dict(), dict(success=400), dict(k='v', v='k'))):
666
            args, kwargs = pm[-2:]
667
            pm = pm[:-2]
668
            terms = [None] * 13
669
            for i in range(len(terms)):
670
                if randint(0, 2):
671
                    terms[i] = 'val_%s' % randint(13, 1024)
672
            self.client.object_post(
673
                obj,
674
                *(pm[:2] + tuple(terms) + pm[2:] + args),
675
                **kwargs)
676
            format, update = pm[:2]
677
            self.assertEqual(SP.mock_calls[-2:], [
678
                call('format', format, iff=format),
679
                call('update', '', iff=update)])
680
            (
681
                im, inm, clen, ctype, crng, trenc, cenc,
682
                condis, srcobj, srcacc, srcvrs, obytes, mnfs) = terms
683
            exp = [
684
                call('If-Match', im),
685
                call('If-None-Match', inm),
686
                call('Content-Length', clen, iff=not trenc),
687
                call('Content-Type', ctype),
688
                call('Content-Range', crng),
689
                call('Transfer-Encoding', trenc),
690
                call('Content-Encoding', cenc),
691
                call('Content-Disposition', condis),
692
                call('X-Source-Object', srcobj),
693
                call('X-Source-Account', srcacc),
694
                call('X-Source-Version', srcvrs),
695
                call('X-Object-Bytes', obytes),
696
                call('X-Object-Manifest', mnfs)]
697
            perms, public, metas = pm[2:]
698
            if perms:
699
                perm_str = ''
700
                for ptype, pval in perms.items():
701
                    if pval:
702
                        perm_str += ';' if perm_str else ''
703
                        perm_str += '%s=%s' % (ptype, ','.join(pval))
704
                exp += [call('X-Object-Sharing', perm_str, iff=perms)]
705
            else:
706
                exp += [call('X-Object-Sharing', '', iff={})]
707
            exp += [call('X-Object-Public', public, public is not None)]
708
            for k, v in metas.items():
709
                exp += [call('X-Object-Meta-%s' % k, v)]
710
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
711
            acc, cont = self.client.account, self.client.container
712
            self.assertEqual(post.mock_calls[-1], call(
713
                '/%s/%s/%s' % (acc, cont, obj),
714
                *args,
715
                success=kwargs.pop('success', (202, 204)),
716
                **kwargs))
717

    
718
    @patch('%s.set_param' % rest_pkg)
719
    @patch('%s.delete' % rest_pkg, return_value=FR())
720
    def test_object_delete(self, delete, SP):
721
        for pm in product(
722
                (None, 'until'),
723
                (None, 'delim'),
724
                ((), ('someval',)),
725
                (dict(), dict(success=400), dict(k='v', v='k'))):
726
            args, kwargs = pm[-2:]
727
            pm = pm[:-2]
728
            self.client.object_delete(
729
                obj,
730
                *(pm + args),
731
                **kwargs)
732
            until, dlm = pm[-2:]
733
            self.assertEqual(SP.mock_calls[-2:], [
734
                call('until', until, iff=until),
735
                call('delimiter', dlm, iff=dlm)])
736
            acc, cont = self.client.account, self.client.container
737
            self.assertEqual(delete.mock_calls[-1], call(
738
                '/%s/%s/%s' % (acc, cont, obj),
739
                *args,
740
                success=kwargs.pop('success', 204),
741
                **kwargs))
742

    
743

    
744
class PithosMethods(TestCase):
745

    
746
    def test__range_up(self):
747
        from kamaki.clients.pithos import _range_up
748
        for args, expected in (
749
                ((0, 100, 1000, '10'), '0-10'),
750
                ((0, 100, 1000, '-10'), ''),
751
                ((900, 1000, 1000, '-10'), '990-1000'),
752
                ((150, 250, 1000, '10'), ''),
753
                ((10, 200, 1000, '130-170'), '130-170'),
754
                ((150, 200, 1000, '130-170'), '150-170'),
755
                ((100, 150, 1000, '130-170'), '130-150'),
756
                ((200, 250, 1000, '130-170'), ''),
757
                ((100, 250, 1000, '30-170,200-270'), '100-170,200-250'),
758
                ((40, 950, 1000, '-170,200-270,50',), '830-950,200-270,40-50'),
759
                ((740, 900, 1000, '-170,200-270,50',), '830-900'),
760
                ((42, 333, 800, '100,50-200,-600',), '42-100,50-200,200-333')):
761
            self.assertEqual(_range_up(*args), expected)
762

    
763

    
764
class PithosClient(TestCase):
765

    
766
    files = []
767

    
768
    def _create_temp_file(self, num_of_blocks):
769
        self.files.append(NamedTemporaryFile())
770
        tmpFile = self.files[-1]
771
        file_size = num_of_blocks * 4 * 1024 * 1024
772
        print('\n\tCreate tmp file')
773
        tmpFile.write(urandom(file_size))
774
        tmpFile.flush()
775
        tmpFile.seek(0)
776
        print('\t\tDone')
777
        return tmpFile
778

    
779
    def assert_dicts_are_equal(self, d1, d2):
780
        for k, v in d1.items():
781
            self.assertTrue(k in d2)
782
            if isinstance(v, dict):
783
                self.assert_dicts_are_equal(v, d2[k])
784
            else:
785
                self.assertEqual(unicode(v), unicode(d2[k]))
786

    
787
    def setUp(self):
788
        self.url = 'https://www.example.com/pithos'
789
        self.token = 'p17h0570k3n'
790
        self.client = pithos.PithosClient(self.url, self.token)
791
        self.client.account = user_id
792
        self.client.container = 'c0nt@1n3r_i'
793

    
794
    def tearDown(self):
795
        FR.headers = dict()
796
        FR.status_code = 200
797
        FR.json = dict()
798
        FR.content = FR.json
799
        for f in self.files:
800
            f.close()
801

    
802
    #  Pithos+ methods that extend storage API
803

    
804
    @patch('%s.account_head' % pithos_pkg, return_value=FR())
805
    def test_get_account_info(self, AH):
806
        FR.headers = account_info
807
        for until in (None, 'un71L-d473'):
808
            r = self.client.get_account_info(until=until)
809
            self.assert_dicts_are_equal(r, account_info)
810
            self.assertEqual(AH.mock_calls[-1], call(until=until))
811
        FR.status_code = 401
812
        self.assertRaises(ClientError, self.client.get_account_info)
813

    
814
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
815
    def test_del_account_meta(self, AP):
816
        keys = ['k1', 'k2', 'k3']
817
        for key in keys:
818
            self.client.del_account_meta(key)
819
            self.assertEqual(
820
                AP.mock_calls[-1],
821
                call(update=True, metadata={key: ''}))
822

    
823
    @patch('%s.container_head' % pithos_pkg, return_value=FR())
824
    def test_get_container_info(self, CH):
825
        FR.headers = container_info
826
        r = self.client.get_container_info()
827
        self.assert_dicts_are_equal(r, container_info)
828
        u = 'some date'
829
        r = self.client.get_container_info(until=u)
830
        self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
831

    
832
    @patch('%s.account_get' % pithos_pkg, return_value=FR())
833
    def test_list_containers(self, get):
834
        FR.json = container_list
835
        r = self.client.list_containers()
836
        get.assert_called_once_with()
837
        for i in range(len(r)):
838
            self.assert_dicts_are_equal(r[i], container_list[i])
839

    
840
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
841
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
842
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
843
    def test_upload_object(self, OP, CP, GCI):
844
        num_of_blocks = 8
845
        tmpFile = self._create_temp_file(num_of_blocks)
846

    
847
        # Without kwargs
848
        exp_headers = dict(id='container id', name='container name')
849
        FR.headers = dict(exp_headers)
850
        r = self.client.upload_object(obj, tmpFile)
851
        self.assert_dicts_are_equal(r, exp_headers)
852
        self.assertEqual(GCI.mock_calls[-1], call())
853

    
854
        [call1, call2] = OP.mock_calls
855
        (args1, kwargs1) = call1[1:3]
856
        (args2, kwargs2) = call2[1:3]
857
        self.assertEqual(args1, (obj,))
858
        expected1 = dict(
859
            hashmap=True,
860
            success=(201, 409),
861
            format='json',
862
            json=dict(
863
                hashes=['s0m3h@5h'] * num_of_blocks,
864
                bytes=num_of_blocks * 4 * 1024 * 1024),
865
            content_encoding=None,
866
            content_type='application/octet-stream',
867
            content_disposition=None,
868
            public=None,
869
            permissions=None)
870
        for k, v in expected1.items():
871
            if k == 'json':
872
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
873
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
874
            else:
875
                self.assertEqual(v, kwargs1[k])
876

    
877
        (args2, kwargs2) = call2[1:3]
878
        self.assertEqual(args2, (obj,))
879
        expected2 = dict(
880
            json=dict(
881
                hashes=['s0m3h@5h'] * num_of_blocks,
882
                bytes=num_of_blocks * 4 * 1024 * 1024),
883
            content_type='application/octet-stream',
884
            hashmap=True,
885
            success=201,
886
            format='json')
887
        for k, v in expected2.items():
888
            if k == 'json':
889
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
890
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
891
            else:
892
                self.assertEqual(v, kwargs2[k])
893

    
894
        mock_offset = 2
895

    
896
        #  With progress bars
897
        try:
898
            from progress.bar import ShadyBar
899
            blck_bar = ShadyBar('Mock blck calc.')
900
            upld_bar = ShadyBar('Mock uplds')
901
        except ImportError:
902
            blck_bar = None
903
            upld_bar = None
904

    
905
        if blck_bar and upld_bar:
906

    
907
            def blck_gen(n):
908
                for i in blck_bar.iter(range(n)):
909
                    yield
910
                yield
911

    
912
            def upld_gen(n):
913
                for i in upld_bar.iter(range(n)):
914
                    yield
915
                yield
916

    
917
            tmpFile.seek(0)
918
            r = self.client.upload_object(
919
                obj, tmpFile,
920
                hash_cb=blck_gen, upload_cb=upld_gen)
921
            self.assert_dicts_are_equal(r, exp_headers)
922

    
923
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
924
                self.assertEqual(OP.mock_calls[i], c)
925

    
926
        #  With content-type
927
        tmpFile.seek(0)
928
        ctype = 'video/mpeg'
929
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
930
        r = self.client.upload_object(obj, tmpFile,
931
            content_type=ctype, sharing=sharing)
932
        self.assert_dicts_are_equal(r, exp_headers)
933
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
934
        self.assert_dicts_are_equal(
935
            OP.mock_calls[-2][2]['permissions'],
936
            sharing)
937

    
938
        # With other args
939
        tmpFile.seek(0)
940
        kwargs = dict(
941
            etag='s0m3E74g',
942
            if_etag_match='if etag match',
943
            if_not_exist=True,
944
            content_type=ctype,
945
            content_disposition=ctype + 'd15p051710n',
946
            public=True,
947
            content_encoding='802.11',
948
            container_info_cache={})
949
        r = self.client.upload_object(obj, tmpFile, **kwargs)
950
        self.assert_dicts_are_equal(r, exp_headers)
951

    
952
        kwargs.pop('if_not_exist')
953
        ematch = kwargs.pop('if_etag_match')
954
        etag = kwargs.pop('etag')
955
        self.assert_dicts_are_equal(
956
            kwargs.pop('container_info_cache'),
957
            {self.client.container: container_info})
958
        for arg, val in kwargs.items():
959
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
960
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
961
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
962
        self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
963

    
964
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
965
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
966
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
967
    def test_upload_from_string(self, OP, CP, GCI):
968
        num_of_blocks = 2
969
        tmpFile = self._create_temp_file(num_of_blocks)
970
        tmpFile.seek(0)
971
        src_str = tmpFile.read()
972

    
973
        exp_headers = dict(id='container id', name='container name')
974
        FR.headers = dict(exp_headers)
975
        r = self.client.upload_from_string(obj, src_str)
976
        self.assert_dicts_are_equal(r, exp_headers)
977
        self.assertEqual(GCI.mock_calls[-1], call())
978

    
979
        [call1, call2] = OP.mock_calls
980
        (args1, kwargs1) = call1[1:3]
981
        (args2, kwargs2) = call2[1:3]
982
        self.assertEqual(args1, (obj,))
983
        expected1 = dict(
984
            hashmap=True,
985
            success=(201, 409),
986
            format='json',
987
            json=dict(
988
                hashes=['s0m3h@5h'] * num_of_blocks,
989
                bytes=num_of_blocks * 4 * 1024 * 1024),
990
            content_encoding=None,
991
            content_type='application/octet-stream',
992
            content_disposition=None,
993
            public=None,
994
            permissions=None)
995
        for k, v in expected1.items():
996
            if k == 'json':
997
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
998
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
999
            else:
1000
                self.assertEqual(v, kwargs1[k])
1001

    
1002
        (args2, kwargs2) = call2[1:3]
1003
        self.assertEqual(args2, (obj,))
1004
        expected2 = dict(
1005
            json=dict(
1006
                hashes=['s0m3h@5h'] * num_of_blocks,
1007
                bytes=num_of_blocks * 4 * 1024 * 1024),
1008
            content_type='application/octet-stream',
1009
            hashmap=True,
1010
            success=201,
1011
            format='json')
1012
        for k, v in expected2.items():
1013
            if k == 'json':
1014
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
1015
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
1016
            else:
1017
                self.assertEqual(v, kwargs2[k])
1018

    
1019
        mock_offset = 2
1020

    
1021
        #  With progress bars
1022
        try:
1023
            from progress.bar import ShadyBar
1024
            blck_bar = ShadyBar('Mock blck calc.')
1025
            upld_bar = ShadyBar('Mock uplds')
1026
        except ImportError:
1027
            blck_bar = None
1028
            upld_bar = None
1029

    
1030
        if blck_bar and upld_bar:
1031

    
1032
            def blck_gen(n):
1033
                for i in blck_bar.iter(range(n)):
1034
                    yield
1035
                yield
1036

    
1037
            def upld_gen(n):
1038
                for i in upld_bar.iter(range(n)):
1039
                    yield
1040
                yield
1041

    
1042
            tmpFile.seek(0)
1043
            r = self.client.upload_object(
1044
                obj, tmpFile,
1045
                hash_cb=blck_gen, upload_cb=upld_gen)
1046
            self.assert_dicts_are_equal(r, exp_headers)
1047

    
1048
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
1049
                self.assertEqual(OP.mock_calls[i], c)
1050

    
1051
        #  With content-type
1052
        tmpFile.seek(0)
1053
        ctype = 'video/mpeg'
1054
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
1055
        r = self.client.upload_object(
1056
            obj, tmpFile,
1057
            content_type=ctype, sharing=sharing)
1058
        self.assert_dicts_are_equal(r, exp_headers)
1059
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
1060
        self.assert_dicts_are_equal(
1061
            OP.mock_calls[-2][2]['permissions'],
1062
            sharing)
1063

    
1064
        # With other args
1065
        tmpFile.seek(0)
1066
        kwargs = dict(
1067
            etag='s0m3E74g',
1068
            if_etag_match='if etag match',
1069
            if_not_exist=True,
1070
            content_type=ctype,
1071
            content_disposition=ctype + 'd15p051710n',
1072
            public=True,
1073
            content_encoding='802.11',
1074
            container_info_cache={})
1075
        r = self.client.upload_object(obj, tmpFile, **kwargs)
1076
        self.assert_dicts_are_equal(r, exp_headers)
1077

    
1078
        kwargs.pop('if_not_exist')
1079
        ematch = kwargs.pop('if_etag_match')
1080
        etag = kwargs.pop('etag')
1081
        self.assert_dicts_are_equal(
1082
            kwargs.pop('container_info_cache'),
1083
            {self.client.container: container_info})
1084
        for arg, val in kwargs.items():
1085
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
1086
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
1087
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
1088
        self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
1089

    
1090
    def test_get_object_info(self):
1091
        FR.headers = object_info
1092
        version = 'v3r510n'
1093
        with patch.object(
1094
                pithos.PithosClient, 'object_head',
1095
                return_value=FR()) as head:
1096
            r = self.client.get_object_info(obj)
1097
            self.assertEqual(r, object_info)
1098
            r = self.client.get_object_info(obj, version=version)
1099
            self.assertEqual(head.mock_calls, [
1100
                call(obj, version=None),
1101
                call(obj, version=version)])
1102
        with patch.object(
1103
                pithos.PithosClient, 'object_head',
1104
                side_effect=ClientError('Obj not found', 404)):
1105
            self.assertRaises(
1106
                ClientError,
1107
                self.client.get_object_info,
1108
                obj, version=version)
1109

    
1110
    @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
1111
    def test_get_object_meta(self, GOI):
1112
        for version in (None, 'v3r510n'):
1113
            r = self.client.get_object_meta(obj, version)
1114
            for k in [k for k in object_info if k.startswith('x-object-meta')]:
1115
                self.assertEqual(r.pop(k), object_info[k])
1116
            self.assertFalse(len(r))
1117
            self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
1118

    
1119
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1120
    def test_del_object_meta(self, post):
1121
        metakey = '50m3m3t4k3y'
1122
        self.client.del_object_meta(obj, metakey)
1123
        post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
1124

    
1125
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1126
    def test_copy_object(self, put):
1127
        src_cont = 'src-c0nt41n3r'
1128
        src_obj = 'src-0bj'
1129
        dst_cont = 'dst-c0nt41n3r'
1130
        dst_obj = 'dst-0bj'
1131
        expected = call(
1132
            src_obj,
1133
            content_length=0,
1134
            source_account=None,
1135
            success=201,
1136
            copy_from='/%s/%s' % (src_cont, src_obj),
1137
            delimiter=None,
1138
            content_type=None,
1139
            source_version=None,
1140
            public=False)
1141
        self.client.copy_object(src_cont, src_obj, dst_cont)
1142
        self.assertEqual(put.mock_calls[-1], expected)
1143
        self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
1144
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1145
        kwargs = dict(
1146
            source_version='src-v3r510n',
1147
            source_account='src-4cc0un7',
1148
            public=True,
1149
            content_type='c0n73n7Typ3',
1150
            delimiter='5')
1151
        self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
1152
        for k, v in kwargs.items():
1153
            self.assertEqual(v, put.mock_calls[-1][2][k])
1154

    
1155
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1156
    def test_move_object(self, put):
1157
        src_cont = 'src-c0nt41n3r'
1158
        src_obj = 'src-0bj'
1159
        dst_cont = 'dst-c0nt41n3r'
1160
        dst_obj = 'dst-0bj'
1161
        expected = call(
1162
            src_obj,
1163
            content_length=0,
1164
            source_account=None,
1165
            success=201,
1166
            move_from='/%s/%s' % (src_cont, src_obj),
1167
            delimiter=None,
1168
            content_type=None,
1169
            source_version=None,
1170
            public=False)
1171
        self.client.move_object(src_cont, src_obj, dst_cont)
1172
        self.assertEqual(put.mock_calls[-1], expected)
1173
        self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
1174
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1175
        kwargs = dict(
1176
            source_version='src-v3r510n',
1177
            source_account='src-4cc0un7',
1178
            public=True,
1179
            content_type='c0n73n7Typ3',
1180
            delimiter='5')
1181
        self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
1182
        for k, v in kwargs.items():
1183
            self.assertEqual(v, put.mock_calls[-1][2][k])
1184

    
1185
    #  Pithos+ only methods
1186

    
1187
    @patch('%s.container_put' % pithos_pkg, return_value=FR())
1188
    def test_create_container(self, CP):
1189
        FR.headers = container_info
1190
        cont = 'an0th3r_c0n741n3r'
1191

    
1192
        r = self.client.create_container()
1193
        self.assert_dicts_are_equal(r, container_info)
1194
        CP.assert_called_once_with(quota=None, versioning=None, metadata=None)
1195

    
1196
        bu_cont = self.client.container
1197
        r = self.client.create_container(cont)
1198
        self.assertEqual(self.client.container, bu_cont)
1199
        self.assert_dicts_are_equal(r, container_info)
1200
        self.assertEqual(
1201
            CP.mock_calls[-1],
1202
            call(quota=None, versioning=None, metadata=None))
1203

    
1204
        meta = dict(k1='v1', k2='v2')
1205
        r = self.client.create_container(cont, 42, 'auto', meta)
1206
        self.assertEqual(self.client.container, bu_cont)
1207
        self.assert_dicts_are_equal(r, container_info)
1208
        self.assertEqual(
1209
            CP.mock_calls[-1],
1210
            call(quota=42, versioning='auto', metadata=meta))
1211

    
1212
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1213
    def test_purge_container(self, CD):
1214
        self.client.purge_container()
1215
        self.assertTrue('until' in CD.mock_calls[-1][2])
1216
        cont = self.client.container
1217
        self.client.purge_container('another-container')
1218
        self.assertEqual(self.client.container, cont)
1219

    
1220
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1221
    def test_upload_object_unchunked(self, put):
1222
        num_of_blocks = 8
1223
        tmpFile = self._create_temp_file(num_of_blocks)
1224
        expected = dict(
1225
                success=201,
1226
                data=num_of_blocks * 4 * 1024 * 1024,
1227
                etag='some-etag',
1228
                content_encoding='some content_encoding',
1229
                content_type='some content-type',
1230
                content_disposition='some content_disposition',
1231
                public=True,
1232
                permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1233
        r = self.client.upload_object_unchunked(obj, tmpFile)
1234
        self.assert_dicts_are_equal(r, FR.headers)
1235
        self.assertEqual(put.mock_calls[-1][1], (obj,))
1236
        self.assertEqual(
1237
            sorted(put.mock_calls[-1][2].keys()),
1238
            sorted(expected.keys()))
1239
        kwargs = dict(expected)
1240
        kwargs.pop('success')
1241
        kwargs['size'] = kwargs.pop('data')
1242
        kwargs['sharing'] = kwargs.pop('permissions')
1243
        tmpFile.seek(0)
1244
        r = self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1245
        self.assert_dicts_are_equal(r, FR.headers)
1246
        pmc = put.mock_calls[-1][2]
1247
        for k, v in expected.items():
1248
            if k == 'data':
1249
                self.assertEqual(len(pmc[k]), v)
1250
            else:
1251
                self.assertEqual(pmc[k], v)
1252
        self.assertRaises(
1253
            ClientError,
1254
            self.client.upload_object_unchunked,
1255
            obj, tmpFile, withHashFile=True)
1256

    
1257
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1258
    def test_create_object_by_manifestation(self, put):
1259
        manifest = '%s/%s' % (self.client.container, obj)
1260
        kwargs = dict(
1261
                etag='some-etag',
1262
                content_encoding='some content_encoding',
1263
                content_type='some content-type',
1264
                content_disposition='some content_disposition',
1265
                public=True,
1266
                sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1267
        r = self.client.create_object_by_manifestation(obj)
1268
        self.assert_dicts_are_equal(r, FR.headers)
1269
        expected = dict(content_length=0, manifest=manifest)
1270
        for k in kwargs:
1271
            expected['permissions' if k == 'sharing' else k] = None
1272
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1273
        r = self.client.create_object_by_manifestation(obj, **kwargs)
1274
        self.assert_dicts_are_equal(r, FR.headers)
1275
        expected.update(kwargs)
1276
        expected['permissions'] = expected.pop('sharing')
1277
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1278

    
1279
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1280
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1281
    def test_download_to_string(self, GET, GOH):
1282
        FR.content = 'some sample content'
1283
        num_of_blocks = len(object_hashmap['hashes'])
1284
        r = self.client.download_to_string(obj)
1285
        expected_content = FR.content * num_of_blocks
1286
        self.assertEqual(expected_content, r)
1287
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1288
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1289

    
1290
        kwargs = dict(
1291
            version='version',
1292
            range_str='10-20',
1293
            if_match='if and only if',
1294
            if_none_match='if and only not',
1295
            if_modified_since='what if not?',
1296
            if_unmodified_since='this happens if not!')
1297
        expargs = dict(kwargs)
1298
        expargs.pop('range_str')
1299
        for k in expargs:
1300
            expargs[k] = None
1301
        GOH.assert_called_once_with(obj, **expargs)
1302

    
1303
        r = self.client.download_to_string(obj, **kwargs)
1304
        expargs['data_range'] = 'bytes=%s' % kwargs['range_str']
1305
        for k, v in expargs.items():
1306
            self.assertEqual(
1307
                GET.mock_calls[-1][2][k],
1308
                v or kwargs.get(k))
1309

    
1310
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1311
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1312
    def test_download_object(self, GET, GOH):
1313
        num_of_blocks = 8
1314
        tmpFile = self._create_temp_file(num_of_blocks)
1315
        FR.content = tmpFile.read(4 * 1024 * 1024)
1316
        tmpFile = self._create_temp_file(num_of_blocks)
1317
        num_of_blocks = len(object_hashmap['hashes'])
1318
        kwargs = dict(
1319
            resume=True,
1320
            version='version',
1321
            range_str='10-20',
1322
            if_match='if and only if',
1323
            if_none_match='if and only not',
1324
            if_modified_since='what if not?',
1325
            if_unmodified_since='this happens if not!',
1326
            async_headers=dict(Range='bytes=0-88888888'))
1327

    
1328
        self.client.download_object(obj, tmpFile)
1329
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1330
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1331
        for k, v in kwargs.items():
1332
            if k == 'async_headers':
1333
                self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1334
            elif k in ('resume', 'range_str'):
1335
                continue
1336
            else:
1337
                self.assertEqual(GET.mock_calls[-1][2][k], None)
1338

    
1339
        #  Check ranges are consecutive
1340
        starts = []
1341
        ends = []
1342
        for c in GET.mock_calls:
1343
            rng_str = c[2]['async_headers']['Range']
1344
            (start, rng_str) = rng_str.split('=')
1345
            (start, end) = rng_str.split('-')
1346
            starts.append(start)
1347
            ends.append(end)
1348
        ends = sorted(ends)
1349
        for i, start in enumerate(sorted(starts)):
1350
            if i:
1351
                int(ends[i - 1]) == int(start) - 1
1352

    
1353
        #  With progress bars
1354
        try:
1355
            from progress.bar import ShadyBar
1356
            dl_bar = ShadyBar('Mock dl')
1357
        except ImportError:
1358
            dl_bar = None
1359

    
1360
        if dl_bar:
1361

    
1362
            def blck_gen(n):
1363
                for i in dl_bar.iter(range(n)):
1364
                    yield
1365
                yield
1366

    
1367
            tmpFile.seek(0)
1368
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1369
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1370

    
1371
        tmpFile.seek(0)
1372
        kwargs.pop('async_headers')
1373
        kwargs.pop('resume')
1374
        self.client.download_object(obj, tmpFile, **kwargs)
1375
        for k, v in kwargs.items():
1376
            if k == 'range_str':
1377
                self.assertEqual(
1378
                    GET.mock_calls[-1][2]['data_range'],
1379
                    'bytes=%s' % v)
1380
            else:
1381
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1382

    
1383
        #  ALl options on no tty
1384
        def foo():
1385
            return True
1386

    
1387
        tmpFile.seek(0)
1388
        tmpFile.isatty = foo
1389
        self.client.download_object(obj, tmpFile, **kwargs)
1390
        for k, v in kwargs.items():
1391
            if k == 'range_str':
1392
                self.assertTrue('data_range' in GET.mock_calls[-1][2])
1393
            else:
1394
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1395

    
1396
    def test_get_object_hashmap(self):
1397
        FR.json = object_hashmap
1398
        for empty in (304, 412):
1399
            with patch.object(
1400
                    pithos.PithosClient, 'object_get',
1401
                    side_effect=ClientError('Empty', status=empty)):
1402
                r = self.client.get_object_hashmap(obj)
1403
                self.assertEqual(r, {})
1404
        exp_args = dict(
1405
            hashmap=True,
1406
            version=None,
1407
            if_etag_match=None,
1408
            if_etag_not_match=None,
1409
            if_modified_since=None,
1410
            if_unmodified_since=None)
1411
        kwargs = dict(
1412
            version='s0m3v3r51on',
1413
            if_match='if match',
1414
            if_none_match='if non match',
1415
            if_modified_since='some date here',
1416
            if_unmodified_since='some date here')
1417
        with patch.object(
1418
                pithos.PithosClient, 'object_get', return_value=FR()) as get:
1419
            r = self.client.get_object_hashmap(obj)
1420
            self.assertEqual(r, object_hashmap)
1421
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1422
            r = self.client.get_object_hashmap(obj, **kwargs)
1423
            exp_args['if_etag_match'] = kwargs.pop('if_match')
1424
            exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1425
            exp_args.update(kwargs)
1426
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1427

    
1428
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1429
    def test_set_account_group(self, post):
1430
        (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1431
        self.client.set_account_group(group, usernames)
1432
        post.assert_called_once_with(update=True, groups={group: usernames})
1433

    
1434
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1435
    def test_del_account_group(self, post):
1436
        group = 'aU53rGr0up'
1437
        self.client.del_account_group(group)
1438
        post.assert_called_once_with(update=True, groups={group: []})
1439

    
1440
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1441
    def test_get_account_quota(self, GAI):
1442
        key = 'x-account-policy-quota'
1443
        r = self.client.get_account_quota()
1444
        GAI.assert_called_once_with()
1445
        self.assertEqual(r[key], account_info[key])
1446

    
1447
    #@patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1448
    #def test_get_account_versioning(self, GAI):
1449
    #    key = 'x-account-policy-versioning'
1450
    #    r = self.client.get_account_versioning()
1451
    #    GAI.assert_called_once_with()
1452
    #    self.assertEqual(r[key], account_info[key])
1453

    
1454
    def test_get_account_meta(self):
1455
        key = 'x-account-meta-'
1456
        with patch.object(
1457
                pithos.PithosClient, 'get_account_info',
1458
                return_value=account_info):
1459
            r = self.client.get_account_meta()
1460
            keys = [k for k in r if k.startswith(key)]
1461
            self.assertFalse(keys)
1462
        acc_info = dict(account_info)
1463
        acc_info['%sk1' % key] = 'v1'
1464
        acc_info['%sk2' % key] = 'v2'
1465
        acc_info['%sk3' % key] = 'v3'
1466
        with patch.object(
1467
                pithos.PithosClient, 'get_account_info',
1468
                return_value=acc_info):
1469
            r = self.client.get_account_meta()
1470
            for k in [k for k in acc_info if k.startswith(key)]:
1471
                self.assertEqual(r[k], acc_info[k])
1472

    
1473
    def test_get_account_group(self):
1474
        key = 'x-account-group-'
1475
        with patch.object(
1476
                pithos.PithosClient, 'get_account_info',
1477
                return_value=account_info):
1478
            r = self.client.get_account_group()
1479
            keys = [k for k in r if k.startswith(key)]
1480
            self.assertFalse(keys)
1481
        acc_info = dict(account_info)
1482
        acc_info['%sk1' % key] = 'g1'
1483
        acc_info['%sk2' % key] = 'g2'
1484
        acc_info['%sk3' % key] = 'g3'
1485
        with patch.object(
1486
                pithos.PithosClient, 'get_account_info',
1487
                return_value=acc_info):
1488
            r = self.client.get_account_group()
1489
            for k in [k for k in acc_info if k.startswith(key)]:
1490
                self.assertEqual(r[k], acc_info[k])
1491

    
1492
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1493
    def test_set_account_meta(self, post):
1494
        metas = dict(k1='v1', k2='v2', k3='v3')
1495
        self.client.set_account_meta(metas)
1496
        post.assert_called_once_with(update=True, metadata=metas)
1497

    
1498
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1499
    #def test_set_account_quota(self, post):
1500
    #    qu = 1024
1501
    #    self.client.set_account_quota(qu)
1502
    #    post.assert_called_once_with(update=True, quota=qu)
1503

    
1504
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1505
    #def test_set_account_versioning(self, post):
1506
    #    vrs = 'n3wV3r51on1ngTyp3'
1507
    #    self.client.set_account_versioning(vrs)
1508
    #    post.assert_called_once_with(update=True, versioning=vrs)
1509

    
1510
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1511
    def test_del_container(self, delete):
1512
        for kwarg in (
1513
                dict(delimiter=None, until=None),
1514
                dict(delimiter='X', until='50m3d473')):
1515
            self.client.del_container(**kwarg)
1516
            expected = dict(kwarg)
1517
            expected['success'] = (204, 404, 409)
1518
            self.assertEqual(delete.mock_calls[-1], call(**expected))
1519
        for status_code in (404, 409):
1520
            FR.status_code = status_code
1521
            self.assertRaises(ClientError, self.client.del_container)
1522

    
1523
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1524
    def test_get_container_versioning(self, GCI):
1525
        key = 'x-container-policy-versioning'
1526
        cont = 'c0n7-417'
1527
        bu_cnt = self.client.container
1528
        for container in (None, cont):
1529
            r = self.client.get_container_versioning(container=container)
1530
            self.assertEqual(r[key], container_info[key])
1531
            self.assertEqual(GCI.mock_calls[-1], call())
1532
            self.assertEqual(bu_cnt, self.client.container)
1533

    
1534
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1535
    def test_get_container_limit(self, GCI):
1536
        key = 'x-container-policy-quota'
1537
        cont = 'c0n7-417'
1538
        bu_cnt = self.client.container
1539
        for container in (None, cont):
1540
            r = self.client.get_container_limit(container=container)
1541
            self.assertEqual(r[key], container_info[key])
1542
            self.assertEqual(GCI.mock_calls[-1], call())
1543
            self.assertEqual(bu_cnt, self.client.container)
1544

    
1545
    def test_get_container_meta(self):
1546
        somedate = '50m3d473'
1547
        key = 'x-container-meta'
1548
        metaval = '50m3m374v41'
1549
        container_plus = dict(container_info)
1550
        container_plus[key] = metaval
1551
        for ret in ((container_info, {}), (container_plus, {key: metaval})):
1552
            with patch.object(
1553
                    pithos.PithosClient,
1554
                    'get_container_info',
1555
                    return_value=ret[0]) as GCI:
1556
                for until in (None, somedate):
1557
                    r = self.client.get_container_meta(until=until)
1558
                    self.assertEqual(r, ret[1])
1559
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1560

    
1561
    def test_get_container_object_meta(self):
1562
        somedate = '50m3d473'
1563
        key = 'x-container-object-meta'
1564
        metaval = '50m3m374v41'
1565
        container_plus = dict(container_info)
1566
        container_plus[key] = metaval
1567
        for ret in (
1568
                (container_info, {key: ''}),
1569
                (container_plus, {key: metaval})):
1570
            with patch.object(
1571
                    pithos.PithosClient,
1572
                    'get_container_info',
1573
                    return_value=ret[0]) as GCI:
1574
                for until in (None, somedate):
1575
                    r = self.client.get_container_object_meta(until=until)
1576
                    self.assertEqual(r, ret[1])
1577
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1578

    
1579
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1580
    def test_set_container_meta(self, post):
1581
        metas = dict(k1='v1', k2='v2', k3='v3')
1582
        self.client.set_container_meta(metas)
1583
        post.assert_called_once_with(update=True, metadata=metas)
1584

    
1585
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1586
    def test_del_container_meta(self, AP):
1587
        self.client.del_container_meta('somekey')
1588
        AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1589

    
1590
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1591
    def test_set_container_limit(self, post):
1592
        qu = 1024
1593
        self.client.set_container_limit(qu)
1594
        post.assert_called_once_with(update=True, quota=qu)
1595

    
1596
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1597
    def test_set_container_versioning(self, post):
1598
        vrs = 'n3wV3r51on1ngTyp3'
1599
        self.client.set_container_versioning(vrs)
1600
        post.assert_called_once_with(update=True, versioning=vrs)
1601

    
1602
    @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1603
    def test_del_object(self, delete):
1604
        for kwarg in (
1605
                dict(delimiter=None, until=None),
1606
                dict(delimiter='X', until='50m3d473')):
1607
            self.client.del_object(obj, **kwarg)
1608
            self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1609

    
1610
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1611
    def test_set_object_meta(self, post):
1612
        metas = dict(k1='v1', k2='v2', k3='v3')
1613
        self.assertRaises(
1614
            AssertionError,
1615
            self.client.set_object_meta,
1616
            obj, 'Non dict arg')
1617
        self.client.set_object_meta(obj, metas)
1618
        post.assert_called_once_with(obj, update=True, metadata=metas)
1619

    
1620
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1621
    def test_publish_object(self, post):
1622
        oinfo = dict(object_info)
1623
        val = 'pubL1c'
1624
        oinfo['x-object-public'] = 'https://www.example.com/' + val
1625
        with patch.object(
1626
                pithos.PithosClient, 'get_object_info',
1627
                return_value=oinfo) as GOF:
1628
            r = self.client.publish_object(obj)
1629
            self.assertEqual(
1630
                post.mock_calls[-1],
1631
                call(obj, public=True, update=True))
1632
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1633
            self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1634

    
1635
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1636
    def test_unpublish_object(self, post):
1637
        self.client.unpublish_object(obj)
1638
        post.assert_called_once_with(obj, public=False, update=True)
1639

    
1640
    def test_get_object_sharing(self):
1641
        info = dict(object_info)
1642
        expected = dict(read='u1,g1,u2', write='u1')
1643
        info['x-object-sharing'] = '; '.join(
1644
            ['%s=%s' % (k, v) for k, v in expected.items()])
1645
        with patch.object(
1646
                pithos.PithosClient, 'get_object_info',
1647
                return_value=info) as GOF:
1648
            r = self.client.get_object_sharing(obj)
1649
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1650
            self.assert_dicts_are_equal(r, expected)
1651
            info['x-object-sharing'] = '//'.join(
1652
                ['%s=%s' % (k, v) for k, v in expected.items()])
1653
            self.assertRaises(
1654
                ValueError,
1655
                self.client.get_object_sharing,
1656
                obj)
1657
            info['x-object-sharing'] = '; '.join(
1658
                ['%s:%s' % (k, v) for k, v in expected.items()])
1659
            self.assertRaises(
1660
                ClientError,
1661
                self.client.get_object_sharing,
1662
                obj)
1663
            info['x-object-sharing'] = 'read=%s' % expected['read']
1664
            r = self.client.get_object_sharing(obj)
1665
            expected.pop('write')
1666
            self.assert_dicts_are_equal(r, expected)
1667

    
1668
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1669
    def test_set_object_sharing(self, OP):
1670
        read_perms = ['u1', 'g1', 'u2', 'g2']
1671
        write_perms = ['u1', 'g1']
1672
        for kwargs in (
1673
                dict(read_permission=read_perms, write_permission=write_perms),
1674
                dict(read_permission=read_perms),
1675
                dict(write_permission=write_perms),
1676
                dict()):
1677
            self.client.set_object_sharing(obj, **kwargs)
1678
            kwargs['read'] = kwargs.pop('read_permission', '')
1679
            kwargs['write'] = kwargs.pop('write_permission', '')
1680
            self.assertEqual(
1681
                OP.mock_calls[-1],
1682
                call(obj, update=True, permissions=kwargs))
1683

    
1684
    @patch('%s.set_object_sharing' % pithos_pkg)
1685
    def test_del_object_sharing(self, SOS):
1686
        self.client.del_object_sharing(obj)
1687
        SOS.assert_called_once_with(obj)
1688

    
1689
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1690
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1691
    def test_append_object(self, post, GCI):
1692
        num_of_blocks = 4
1693
        tmpFile = self._create_temp_file(num_of_blocks)
1694
        tmpFile.seek(0, 2)
1695
        file_size = tmpFile.tell()
1696
        for turn in range(2):
1697
            tmpFile.seek(0, 0)
1698

    
1699
            try:
1700
                from progress.bar import ShadyBar
1701
                apn_bar = ShadyBar('Mock append')
1702
            except ImportError:
1703
                apn_bar = None
1704

    
1705
            if apn_bar:
1706

    
1707
                def append_gen(n):
1708
                    for i in apn_bar.iter(range(n)):
1709
                        yield
1710
                    yield
1711

    
1712
            else:
1713
                append_gen = None
1714

    
1715
            self.client.append_object(
1716
                obj, tmpFile,
1717
                upload_cb=append_gen if turn else None)
1718
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1719
            (args, kwargs) = post.mock_calls[-1][1:3]
1720
            self.assertEqual(kwargs['obj'], obj)
1721
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1722
            fsize = num_of_blocks * int(kwargs['content_length'])
1723
            self.assertEqual(fsize, file_size)
1724
            self.assertEqual(kwargs['content_range'], 'bytes */*')
1725
            exp = 'application/octet-stream'
1726
            self.assertEqual(kwargs['content_type'], exp)
1727
            self.assertEqual(kwargs['update'], True)
1728

    
1729
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1730
    def test_truncate_object(self, post):
1731
        upto_bytes = 377
1732
        self.client.truncate_object(obj, upto_bytes)
1733
        post.assert_called_once_with(
1734
            obj,
1735
            update=True,
1736
            object_bytes=upto_bytes,
1737
            content_range='bytes 0-%s/*' % upto_bytes,
1738
            content_type='application/octet-stream',
1739
            source_object='/%s/%s' % (self.client.container, obj))
1740

    
1741
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1742
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1743
    def test_overwrite_object(self, post, GCI):
1744
        num_of_blocks = 4
1745
        tmpFile = self._create_temp_file(num_of_blocks)
1746
        tmpFile.seek(0, 2)
1747
        file_size = tmpFile.tell()
1748
        info = dict(object_info)
1749
        info['content-length'] = file_size
1750
        block_size = container_info['x-container-block-size']
1751
        with patch.object(
1752
                pithos.PithosClient, 'get_object_info',
1753
                return_value=info) as GOI:
1754
            for start, end in (
1755
                    (0, file_size + 1),
1756
                    (file_size + 1, file_size + 2)):
1757
                tmpFile.seek(0, 0)
1758
                self.assertRaises(
1759
                    ClientError,
1760
                    self.client.overwrite_object,
1761
                    obj, start, end, tmpFile)
1762
            for start, end in ((0, 144), (144, 233), (233, file_size)):
1763
                tmpFile.seek(0, 0)
1764
                owr_gen = None
1765
                exp_size = end - start + 1
1766
                if not start or exp_size > block_size:
1767
                    try:
1768
                        from progress.bar import ShadyBar
1769
                        owr_bar = ShadyBar('Mock append')
1770
                    except ImportError:
1771
                        owr_bar = None
1772

    
1773
                    if owr_bar:
1774

    
1775
                        def owr_gen(n):
1776
                            for i in owr_bar.iter(range(n)):
1777
                                yield
1778
                            yield
1779

    
1780
                    if exp_size > block_size:
1781
                        exp_size = exp_size % block_size or block_size
1782

    
1783
                self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1784
                self.assertEqual(GOI.mock_calls[-1], call(obj))
1785
                self.assertEqual(GCI.mock_calls[-1], call())
1786
                (args, kwargs) = post.mock_calls[-1][1:3]
1787
                self.assertEqual(args, (obj,))
1788
                self.assertEqual(len(kwargs['data']), exp_size)
1789
                self.assertEqual(kwargs['content_length'], exp_size)
1790
                self.assertEqual(kwargs['update'], True)
1791
                exp = 'application/octet-stream'
1792
                self.assertEqual(kwargs['content_type'], exp)
1793

    
1794
    @patch('%s.set_param' % pithos_pkg)
1795
    @patch('%s.get' % pithos_pkg, return_value=FR())
1796
    def test_get_sharing_accounts(self, get, SP):
1797
        FR.json = sharers
1798
        for kws in (
1799
                dict(),
1800
                dict(limit='50m3-11m17'),
1801
                dict(marker='X'),
1802
                dict(limit='50m3-11m17', marker='X')):
1803
            r = self.client.get_sharing_accounts(**kws)
1804
            self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1805
            self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1806
            limit, marker = kws.get('limit', None), kws.get('marker', None)
1807
            self.assertEqual(SP.mock_calls[-2], call(
1808
                'limit', limit,
1809
                iff=limit is not None))
1810
            self.assertEqual(SP.mock_calls[-1], call(
1811
                'marker', marker,
1812
                iff=marker is not None))
1813
            for i in range(len(r)):
1814
                self.assert_dicts_are_equal(r[i], sharers[i])
1815

    
1816
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1817
    def test_get_object_versionlist(self, get):
1818
        info = dict(object_info)
1819
        info['versions'] = ['v1', 'v2']
1820
        FR.json = info
1821
        r = self.client.get_object_versionlist(obj)
1822
        get.assert_called_once_with(obj, format='json', version='list')
1823
        self.assertEqual(r, info['versions'])
1824

    
1825
if __name__ == '__main__':
1826
    from sys import argv
1827
    from kamaki.clients.test import runTestCase
1828
    not_found = True
1829
    if not argv[1:] or argv[1] == 'PithosClient':
1830
        not_found = False
1831
        runTestCase(PithosClient, 'Pithos Client', argv[2:])
1832
    if not argv[1:] or argv[1] == 'PithosRestClient':
1833
        not_found = False
1834
        runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1835
    if not argv[1:] or argv[1] == 'PithosMethods':
1836
        not_found = False
1837
        runTestCase(PithosRestClient, 'Pithos Methods', argv[2:])
1838
    if not_found:
1839
        print('TestCase %s not found' % argv[1])