Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / test.py @ 5c2058e7

History | View | Annotate | Download (72.4 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)]
641
            exp += [call('X-Object-Public', public, public is not None)]
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, public is not None)]
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

    
830
        [call1, call2] = OP.mock_calls
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
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
941
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
942
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
943
    def test_upload_from_string(self, OP, CP, GCI):
944
        num_of_blocks = 2
945
        tmpFile = self._create_temp_file(num_of_blocks)
946
        tmpFile.seek(0)
947
        src_str = tmpFile.read()
948

    
949
        exp_headers = dict(id='container id', name='container name')
950
        FR.headers = dict(exp_headers)
951
        r = self.client.upload_from_string(obj, src_str)
952
        self.assert_dicts_are_equal(r, exp_headers)
953
        self.assertEqual(GCI.mock_calls[-1], call())
954

    
955
        [call1, call2] = OP.mock_calls
956
        (args1, kwargs1) = call1[1:3]
957
        (args2, kwargs2) = call2[1:3]
958
        self.assertEqual(args1, (obj,))
959
        expected1 = dict(
960
            hashmap=True,
961
            success=(201, 409),
962
            format='json',
963
            json=dict(
964
                hashes=['s0m3h@5h'] * num_of_blocks,
965
                bytes=num_of_blocks * 4 * 1024 * 1024),
966
            content_encoding=None,
967
            content_type='application/octet-stream',
968
            content_disposition=None,
969
            public=None,
970
            permissions=None)
971
        for k, v in expected1.items():
972
            if k == 'json':
973
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
974
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
975
            else:
976
                self.assertEqual(v, kwargs1[k])
977

    
978
        (args2, kwargs2) = call2[1:3]
979
        self.assertEqual(args2, (obj,))
980
        expected2 = dict(
981
            json=dict(
982
                hashes=['s0m3h@5h'] * num_of_blocks,
983
                bytes=num_of_blocks * 4 * 1024 * 1024),
984
            content_type='application/octet-stream',
985
            hashmap=True,
986
            success=201,
987
            format='json')
988
        for k, v in expected2.items():
989
            if k == 'json':
990
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
991
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
992
            else:
993
                self.assertEqual(v, kwargs2[k])
994

    
995
        mock_offset = 2
996

    
997
        #  With progress bars
998
        try:
999
            from progress.bar import ShadyBar
1000
            blck_bar = ShadyBar('Mock blck calc.')
1001
            upld_bar = ShadyBar('Mock uplds')
1002
        except ImportError:
1003
            blck_bar = None
1004
            upld_bar = None
1005

    
1006
        if blck_bar and upld_bar:
1007

    
1008
            def blck_gen(n):
1009
                for i in blck_bar.iter(range(n)):
1010
                    yield
1011
                yield
1012

    
1013
            def upld_gen(n):
1014
                for i in upld_bar.iter(range(n)):
1015
                    yield
1016
                yield
1017

    
1018
            tmpFile.seek(0)
1019
            r = self.client.upload_object(
1020
                obj, tmpFile,
1021
                hash_cb=blck_gen, upload_cb=upld_gen)
1022
            self.assert_dicts_are_equal(r, exp_headers)
1023

    
1024
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
1025
                self.assertEqual(OP.mock_calls[i], c)
1026

    
1027
        #  With content-type
1028
        tmpFile.seek(0)
1029
        ctype = 'video/mpeg'
1030
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
1031
        r = self.client.upload_object(
1032
            obj, tmpFile,
1033
            content_type=ctype, sharing=sharing)
1034
        self.assert_dicts_are_equal(r, exp_headers)
1035
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
1036
        self.assert_dicts_are_equal(
1037
            OP.mock_calls[-2][2]['permissions'],
1038
            sharing)
1039

    
1040
        # With other args
1041
        tmpFile.seek(0)
1042
        kwargs = dict(
1043
            etag='s0m3E74g',
1044
            if_etag_match='if etag match',
1045
            if_not_exist=True,
1046
            content_type=ctype,
1047
            content_disposition=ctype + 'd15p051710n',
1048
            public=True,
1049
            content_encoding='802.11',
1050
            container_info_cache={})
1051
        r = self.client.upload_object(obj, tmpFile, **kwargs)
1052
        self.assert_dicts_are_equal(r, exp_headers)
1053

    
1054
        kwargs.pop('if_not_exist')
1055
        ematch = kwargs.pop('if_etag_match')
1056
        etag = kwargs.pop('etag')
1057
        self.assert_dicts_are_equal(
1058
            kwargs.pop('container_info_cache'),
1059
            {self.client.container: container_info})
1060
        for arg, val in kwargs.items():
1061
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
1062
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_match'], ematch)
1063
        self.assertEqual(OP.mock_calls[-1][2]['if_etag_not_match'], '*')
1064
        self.assertEqual(OP.mock_calls[-1][2]['etag'], etag)
1065

    
1066
    def test_get_object_info(self):
1067
        FR.headers = object_info
1068
        version = 'v3r510n'
1069
        with patch.object(
1070
                pithos.PithosClient, 'object_head',
1071
                return_value=FR()) as head:
1072
            r = self.client.get_object_info(obj)
1073
            self.assertEqual(r, object_info)
1074
            r = self.client.get_object_info(obj, version=version)
1075
            self.assertEqual(head.mock_calls, [
1076
                call(obj, version=None),
1077
                call(obj, version=version)])
1078
        with patch.object(
1079
                pithos.PithosClient, 'object_head',
1080
                side_effect=ClientError('Obj not found', 404)):
1081
            self.assertRaises(
1082
                ClientError,
1083
                self.client.get_object_info,
1084
                obj, version=version)
1085

    
1086
    @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
1087
    def test_get_object_meta(self, GOI):
1088
        for version in (None, 'v3r510n'):
1089
            r = self.client.get_object_meta(obj, version)
1090
            for k in [k for k in object_info if k.startswith('x-object-meta')]:
1091
                self.assertEqual(r.pop(k), object_info[k])
1092
            self.assertFalse(len(r))
1093
            self.assertEqual(GOI.mock_calls[-1], call(obj, version=version))
1094

    
1095
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1096
    def test_del_object_meta(self, post):
1097
        metakey = '50m3m3t4k3y'
1098
        self.client.del_object_meta(obj, metakey)
1099
        post.assert_called_once_with(obj, update=True, metadata={metakey: ''})
1100

    
1101
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1102
    def test_copy_object(self, put):
1103
        src_cont = 'src-c0nt41n3r'
1104
        src_obj = 'src-0bj'
1105
        dst_cont = 'dst-c0nt41n3r'
1106
        dst_obj = 'dst-0bj'
1107
        expected = call(
1108
            src_obj,
1109
            content_length=0,
1110
            source_account=None,
1111
            success=201,
1112
            copy_from='/%s/%s' % (src_cont, src_obj),
1113
            delimiter=None,
1114
            content_type=None,
1115
            source_version=None,
1116
            public=False)
1117
        self.client.copy_object(src_cont, src_obj, dst_cont)
1118
        self.assertEqual(put.mock_calls[-1], expected)
1119
        self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
1120
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
1121
        kwargs = dict(
1122
            source_version='src-v3r510n',
1123
            source_account='src-4cc0un7',
1124
            public=True,
1125
            content_type='c0n73n7Typ3',
1126
            delimiter='5')
1127
        self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
1128
        for k, v in kwargs.items():
1129
            self.assertEqual(v, put.mock_calls[-1][2][k])
1130

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

    
1161
    #  Pithos+ only methods
1162

    
1163
    @patch('%s.container_put' % pithos_pkg, return_value=FR())
1164
    def test_create_container(self, CP):
1165
        FR.headers = container_info
1166
        cont = 'an0th3r_c0n741n3r'
1167

    
1168
        r = self.client.create_container()
1169
        self.assert_dicts_are_equal(r, container_info)
1170
        CP.assert_called_once_with(quota=None, versioning=None, metadata=None)
1171

    
1172
        bu_cont = self.client.container
1173
        r = self.client.create_container(cont)
1174
        self.assertEqual(self.client.container, bu_cont)
1175
        self.assert_dicts_are_equal(r, container_info)
1176
        self.assertEqual(
1177
            CP.mock_calls[-1],
1178
            call(quota=None, versioning=None, metadata=None))
1179

    
1180
        meta = dict(k1='v1', k2='v2')
1181
        r = self.client.create_container(cont, 42, 'auto', meta)
1182
        self.assertEqual(self.client.container, bu_cont)
1183
        self.assert_dicts_are_equal(r, container_info)
1184
        self.assertEqual(
1185
            CP.mock_calls[-1],
1186
            call(quota=42, versioning='auto', metadata=meta))
1187

    
1188
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1189
    def test_purge_container(self, CD):
1190
        self.client.purge_container()
1191
        self.assertTrue('until' in CD.mock_calls[-1][2])
1192
        cont = self.client.container
1193
        self.client.purge_container('another-container')
1194
        self.assertEqual(self.client.container, cont)
1195

    
1196
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1197
    def test_upload_object_unchunked(self, put):
1198
        num_of_blocks = 8
1199
        tmpFile = self._create_temp_file(num_of_blocks)
1200
        expected = dict(
1201
                success=201,
1202
                data=num_of_blocks * 4 * 1024 * 1024,
1203
                etag='some-etag',
1204
                content_encoding='some content_encoding',
1205
                content_type='some content-type',
1206
                content_disposition='some content_disposition',
1207
                public=True,
1208
                permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1209
        r = self.client.upload_object_unchunked(obj, tmpFile)
1210
        self.assert_dicts_are_equal(r, FR.headers)
1211
        self.assertEqual(put.mock_calls[-1][1], (obj,))
1212
        self.assertEqual(
1213
            sorted(put.mock_calls[-1][2].keys()),
1214
            sorted(expected.keys()))
1215
        kwargs = dict(expected)
1216
        kwargs.pop('success')
1217
        kwargs['size'] = kwargs.pop('data')
1218
        kwargs['sharing'] = kwargs.pop('permissions')
1219
        tmpFile.seek(0)
1220
        r = self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1221
        self.assert_dicts_are_equal(r, FR.headers)
1222
        pmc = put.mock_calls[-1][2]
1223
        for k, v in expected.items():
1224
            if k == 'data':
1225
                self.assertEqual(len(pmc[k]), v)
1226
            else:
1227
                self.assertEqual(pmc[k], v)
1228
        self.assertRaises(
1229
            ClientError,
1230
            self.client.upload_object_unchunked,
1231
            obj, tmpFile, withHashFile=True)
1232

    
1233
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1234
    def test_create_object_by_manifestation(self, put):
1235
        manifest = '%s/%s' % (self.client.container, obj)
1236
        kwargs = dict(
1237
                etag='some-etag',
1238
                content_encoding='some content_encoding',
1239
                content_type='some content-type',
1240
                content_disposition='some content_disposition',
1241
                public=True,
1242
                sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1243
        r = self.client.create_object_by_manifestation(obj)
1244
        self.assert_dicts_are_equal(r, FR.headers)
1245
        expected = dict(content_length=0, manifest=manifest)
1246
        for k in kwargs:
1247
            expected['permissions' if k == 'sharing' else k] = None
1248
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1249
        r = self.client.create_object_by_manifestation(obj, **kwargs)
1250
        self.assert_dicts_are_equal(r, FR.headers)
1251
        expected.update(kwargs)
1252
        expected['permissions'] = expected.pop('sharing')
1253
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1254

    
1255
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1256
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1257
    def test_download_to_string(self, GET, GOH):
1258
        FR.content = 'some sample content'
1259
        num_of_blocks = len(object_hashmap['hashes'])
1260
        r = self.client.download_to_string(obj)
1261
        expected_content = FR.content * num_of_blocks
1262
        self.assertEqual(expected_content, r)
1263
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1264
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1265

    
1266
        kwargs = dict(
1267
            version='version',
1268
            range_str='10-20',
1269
            if_match='if and only if',
1270
            if_none_match='if and only not',
1271
            if_modified_since='what if not?',
1272
            if_unmodified_since='this happens if not!')
1273
        expargs = dict(kwargs)
1274
        expargs.pop('range_str')
1275
        for k in expargs:
1276
            expargs[k] = None
1277
        GOH.assert_called_once_with(obj, **expargs)
1278

    
1279
        r = self.client.download_to_string(obj, **kwargs)
1280
        expargs['data_range'] = 'bytes=%s' % kwargs['range_str']
1281
        for k, v in expargs.items():
1282
            self.assertEqual(
1283
                GET.mock_calls[-1][2][k],
1284
                v or kwargs.get(k))
1285

    
1286
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1287
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1288
    def test_download_object(self, GET, GOH):
1289
        num_of_blocks = 8
1290
        tmpFile = self._create_temp_file(num_of_blocks)
1291
        FR.content = tmpFile.read(4 * 1024 * 1024)
1292
        tmpFile = self._create_temp_file(num_of_blocks)
1293
        num_of_blocks = len(object_hashmap['hashes'])
1294
        kwargs = dict(
1295
            resume=True,
1296
            version='version',
1297
            range_str='10-20',
1298
            if_match='if and only if',
1299
            if_none_match='if and only not',
1300
            if_modified_since='what if not?',
1301
            if_unmodified_since='this happens if not!',
1302
            async_headers=dict(Range='bytes=0-88888888'))
1303

    
1304
        self.client.download_object(obj, tmpFile)
1305
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1306
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1307
        for k, v in kwargs.items():
1308
            if k == 'async_headers':
1309
                self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1310
            elif k in ('resume', 'range_str'):
1311
                continue
1312
            else:
1313
                self.assertEqual(GET.mock_calls[-1][2][k], None)
1314

    
1315
        #  Check ranges are consecutive
1316
        starts = []
1317
        ends = []
1318
        for c in GET.mock_calls:
1319
            rng_str = c[2]['async_headers']['Range']
1320
            (start, rng_str) = rng_str.split('=')
1321
            (start, end) = rng_str.split('-')
1322
            starts.append(start)
1323
            ends.append(end)
1324
        ends = sorted(ends)
1325
        for i, start in enumerate(sorted(starts)):
1326
            if i:
1327
                int(ends[i - 1]) == int(start) - 1
1328

    
1329
        #  With progress bars
1330
        try:
1331
            from progress.bar import ShadyBar
1332
            dl_bar = ShadyBar('Mock dl')
1333
        except ImportError:
1334
            dl_bar = None
1335

    
1336
        if dl_bar:
1337

    
1338
            def blck_gen(n):
1339
                for i in dl_bar.iter(range(n)):
1340
                    yield
1341
                yield
1342

    
1343
            tmpFile.seek(0)
1344
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1345
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1346

    
1347
        tmpFile.seek(0)
1348
        kwargs.pop('async_headers')
1349
        kwargs.pop('resume')
1350
        self.client.download_object(obj, tmpFile, **kwargs)
1351
        for k, v in kwargs.items():
1352
            if k == 'range_str':
1353
                self.assertEqual(
1354
                    GET.mock_calls[-1][2]['data_range'],
1355
                    'bytes=%s' % v)
1356
            else:
1357
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1358

    
1359
        #  ALl options on no tty
1360
        def foo():
1361
            return True
1362

    
1363
        tmpFile.seek(0)
1364
        tmpFile.isatty = foo
1365
        self.client.download_object(obj, tmpFile, **kwargs)
1366
        for k, v in kwargs.items():
1367
            if k == 'range_str':
1368
                self.assertTrue('data_range' in GET.mock_calls[-1][2])
1369
            else:
1370
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1371

    
1372
    def test_get_object_hashmap(self):
1373
        FR.json = object_hashmap
1374
        for empty in (304, 412):
1375
            with patch.object(
1376
                    pithos.PithosClient, 'object_get',
1377
                    side_effect=ClientError('Empty', status=empty)):
1378
                r = self.client.get_object_hashmap(obj)
1379
                self.assertEqual(r, {})
1380
        exp_args = dict(
1381
            hashmap=True,
1382
            data_range=None,
1383
            version=None,
1384
            if_etag_match=None,
1385
            if_etag_not_match=None,
1386
            if_modified_since=None,
1387
            if_unmodified_since=None)
1388
        kwargs = dict(
1389
            version='s0m3v3r51on',
1390
            if_match='if match',
1391
            if_none_match='if non match',
1392
            if_modified_since='some date here',
1393
            if_unmodified_since='some date here',
1394
            data_range='10-20')
1395
        with patch.object(
1396
                pithos.PithosClient, 'object_get',
1397
                return_value=FR()) as get:
1398
            r = self.client.get_object_hashmap(obj)
1399
            self.assertEqual(r, object_hashmap)
1400
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1401
            r = self.client.get_object_hashmap(obj, **kwargs)
1402
            exp_args['if_etag_match'] = kwargs.pop('if_match')
1403
            exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1404
            exp_args.update(kwargs)
1405
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1406

    
1407
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1408
    def test_set_account_group(self, post):
1409
        (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1410
        self.client.set_account_group(group, usernames)
1411
        post.assert_called_once_with(update=True, groups={group: usernames})
1412

    
1413
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1414
    def test_del_account_group(self, post):
1415
        group = 'aU53rGr0up'
1416
        self.client.del_account_group(group)
1417
        post.assert_called_once_with(update=True, groups={group: []})
1418

    
1419
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1420
    def test_get_account_quota(self, GAI):
1421
        key = 'x-account-policy-quota'
1422
        r = self.client.get_account_quota()
1423
        GAI.assert_called_once_with()
1424
        self.assertEqual(r[key], account_info[key])
1425

    
1426
    #@patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1427
    #def test_get_account_versioning(self, GAI):
1428
    #    key = 'x-account-policy-versioning'
1429
    #    r = self.client.get_account_versioning()
1430
    #    GAI.assert_called_once_with()
1431
    #    self.assertEqual(r[key], account_info[key])
1432

    
1433
    def test_get_account_meta(self):
1434
        key = 'x-account-meta-'
1435
        with patch.object(
1436
                pithos.PithosClient, 'get_account_info',
1437
                return_value=account_info):
1438
            r = self.client.get_account_meta()
1439
            keys = [k for k in r if k.startswith(key)]
1440
            self.assertFalse(keys)
1441
        acc_info = dict(account_info)
1442
        acc_info['%sk1' % key] = 'v1'
1443
        acc_info['%sk2' % key] = 'v2'
1444
        acc_info['%sk3' % key] = 'v3'
1445
        with patch.object(
1446
                pithos.PithosClient, 'get_account_info',
1447
                return_value=acc_info):
1448
            r = self.client.get_account_meta()
1449
            for k in [k for k in acc_info if k.startswith(key)]:
1450
                self.assertEqual(r[k], acc_info[k])
1451

    
1452
    def test_get_account_group(self):
1453
        key = 'x-account-group-'
1454
        with patch.object(
1455
                pithos.PithosClient, 'get_account_info',
1456
                return_value=account_info):
1457
            r = self.client.get_account_group()
1458
            keys = [k for k in r if k.startswith(key)]
1459
            self.assertFalse(keys)
1460
        acc_info = dict(account_info)
1461
        acc_info['%sk1' % key] = 'g1'
1462
        acc_info['%sk2' % key] = 'g2'
1463
        acc_info['%sk3' % key] = 'g3'
1464
        with patch.object(
1465
                pithos.PithosClient, 'get_account_info',
1466
                return_value=acc_info):
1467
            r = self.client.get_account_group()
1468
            for k in [k for k in acc_info if k.startswith(key)]:
1469
                self.assertEqual(r[k], acc_info[k])
1470

    
1471
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1472
    def test_set_account_meta(self, post):
1473
        metas = dict(k1='v1', k2='v2', k3='v3')
1474
        self.client.set_account_meta(metas)
1475
        post.assert_called_once_with(update=True, metadata=metas)
1476

    
1477
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1478
    #def test_set_account_quota(self, post):
1479
    #    qu = 1024
1480
    #    self.client.set_account_quota(qu)
1481
    #    post.assert_called_once_with(update=True, quota=qu)
1482

    
1483
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1484
    #def test_set_account_versioning(self, post):
1485
    #    vrs = 'n3wV3r51on1ngTyp3'
1486
    #    self.client.set_account_versioning(vrs)
1487
    #    post.assert_called_once_with(update=True, versioning=vrs)
1488

    
1489
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1490
    def test_del_container(self, delete):
1491
        for kwarg in (
1492
                dict(delimiter=None, until=None),
1493
                dict(delimiter='X', until='50m3d473')):
1494
            self.client.del_container(**kwarg)
1495
            expected = dict(kwarg)
1496
            expected['success'] = (204, 404, 409)
1497
            self.assertEqual(delete.mock_calls[-1], call(**expected))
1498
        for status_code in (404, 409):
1499
            FR.status_code = status_code
1500
            self.assertRaises(ClientError, self.client.del_container)
1501

    
1502
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1503
    def test_get_container_versioning(self, GCI):
1504
        key = 'x-container-policy-versioning'
1505
        cont = 'c0n7-417'
1506
        bu_cnt = self.client.container
1507
        for container in (None, cont):
1508
            r = self.client.get_container_versioning(container=container)
1509
            self.assertEqual(r[key], container_info[key])
1510
            self.assertEqual(GCI.mock_calls[-1], call())
1511
            self.assertEqual(bu_cnt, self.client.container)
1512

    
1513
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1514
    def test_get_container_limit(self, GCI):
1515
        key = 'x-container-policy-quota'
1516
        cont = 'c0n7-417'
1517
        bu_cnt = self.client.container
1518
        for container in (None, cont):
1519
            r = self.client.get_container_limit(container=container)
1520
            self.assertEqual(r[key], container_info[key])
1521
            self.assertEqual(GCI.mock_calls[-1], call())
1522
            self.assertEqual(bu_cnt, self.client.container)
1523

    
1524
    def test_get_container_meta(self):
1525
        somedate = '50m3d473'
1526
        key = 'x-container-meta'
1527
        metaval = '50m3m374v41'
1528
        container_plus = dict(container_info)
1529
        container_plus[key] = metaval
1530
        for ret in ((container_info, {}), (container_plus, {key: metaval})):
1531
            with patch.object(
1532
                    pithos.PithosClient,
1533
                    'get_container_info',
1534
                    return_value=ret[0]) as GCI:
1535
                for until in (None, somedate):
1536
                    r = self.client.get_container_meta(until=until)
1537
                    self.assertEqual(r, ret[1])
1538
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1539

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

    
1558
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1559
    def test_set_container_meta(self, post):
1560
        metas = dict(k1='v1', k2='v2', k3='v3')
1561
        self.client.set_container_meta(metas)
1562
        post.assert_called_once_with(update=True, metadata=metas)
1563

    
1564
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1565
    def test_del_container_meta(self, AP):
1566
        self.client.del_container_meta('somekey')
1567
        AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1568

    
1569
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1570
    def test_set_container_limit(self, post):
1571
        qu = 1024
1572
        self.client.set_container_limit(qu)
1573
        post.assert_called_once_with(update=True, quota=qu)
1574

    
1575
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1576
    def test_set_container_versioning(self, post):
1577
        vrs = 'n3wV3r51on1ngTyp3'
1578
        self.client.set_container_versioning(vrs)
1579
        post.assert_called_once_with(update=True, versioning=vrs)
1580

    
1581
    @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1582
    def test_del_object(self, delete):
1583
        for kwarg in (
1584
                dict(delimiter=None, until=None),
1585
                dict(delimiter='X', until='50m3d473')):
1586
            self.client.del_object(obj, **kwarg)
1587
            self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1588

    
1589
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1590
    def test_set_object_meta(self, post):
1591
        metas = dict(k1='v1', k2='v2', k3='v3')
1592
        self.assertRaises(
1593
            AssertionError,
1594
            self.client.set_object_meta,
1595
            obj, 'Non dict arg')
1596
        self.client.set_object_meta(obj, metas)
1597
        post.assert_called_once_with(obj, update=True, metadata=metas)
1598

    
1599
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1600
    def test_publish_object(self, post):
1601
        oinfo = dict(object_info)
1602
        val = 'pubL1c'
1603
        oinfo['x-object-public'] = 'https://www.example.com/' + val
1604
        with patch.object(
1605
                pithos.PithosClient, 'get_object_info',
1606
                return_value=oinfo) as GOF:
1607
            r = self.client.publish_object(obj)
1608
            self.assertEqual(
1609
                post.mock_calls[-1],
1610
                call(obj, public=True, update=True))
1611
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1612
            self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1613

    
1614
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1615
    def test_unpublish_object(self, post):
1616
        self.client.unpublish_object(obj)
1617
        post.assert_called_once_with(obj, public=False, update=True)
1618

    
1619
    def test_get_object_sharing(self):
1620
        info = dict(object_info)
1621
        expected = dict(read='u1,g1,u2', write='u1')
1622
        info['x-object-sharing'] = '; '.join(
1623
            ['%s=%s' % (k, v) for k, v in expected.items()])
1624
        with patch.object(
1625
                pithos.PithosClient, 'get_object_info',
1626
                return_value=info) as GOF:
1627
            r = self.client.get_object_sharing(obj)
1628
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1629
            self.assert_dicts_are_equal(r, expected)
1630
            info['x-object-sharing'] = '//'.join(
1631
                ['%s=%s' % (k, v) for k, v in expected.items()])
1632
            self.assertRaises(
1633
                ValueError,
1634
                self.client.get_object_sharing,
1635
                obj)
1636
            info['x-object-sharing'] = '; '.join(
1637
                ['%s:%s' % (k, v) for k, v in expected.items()])
1638
            self.assertRaises(
1639
                ClientError,
1640
                self.client.get_object_sharing,
1641
                obj)
1642
            info['x-object-sharing'] = 'read=%s' % expected['read']
1643
            r = self.client.get_object_sharing(obj)
1644
            expected.pop('write')
1645
            self.assert_dicts_are_equal(r, expected)
1646

    
1647
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1648
    def test_set_object_sharing(self, OP):
1649
        read_perms = ['u1', 'g1', 'u2', 'g2']
1650
        write_perms = ['u1', 'g1']
1651
        for kwargs in (
1652
                dict(read_permission=read_perms, write_permission=write_perms),
1653
                dict(read_permission=read_perms),
1654
                dict(write_permission=write_perms),
1655
                dict()):
1656
            self.client.set_object_sharing(obj, **kwargs)
1657
            kwargs['read'] = kwargs.pop('read_permission', '')
1658
            kwargs['write'] = kwargs.pop('write_permission', '')
1659
            self.assertEqual(
1660
                OP.mock_calls[-1],
1661
                call(obj, update=True, permissions=kwargs))
1662

    
1663
    @patch('%s.set_object_sharing' % pithos_pkg)
1664
    def test_del_object_sharing(self, SOS):
1665
        self.client.del_object_sharing(obj)
1666
        SOS.assert_called_once_with(obj)
1667

    
1668
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1669
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1670
    def test_append_object(self, post, GCI):
1671
        num_of_blocks = 4
1672
        tmpFile = self._create_temp_file(num_of_blocks)
1673
        tmpFile.seek(0, 2)
1674
        file_size = tmpFile.tell()
1675
        for turn in range(2):
1676
            tmpFile.seek(0, 0)
1677

    
1678
            try:
1679
                from progress.bar import ShadyBar
1680
                apn_bar = ShadyBar('Mock append')
1681
            except ImportError:
1682
                apn_bar = None
1683

    
1684
            if apn_bar:
1685

    
1686
                def append_gen(n):
1687
                    for i in apn_bar.iter(range(n)):
1688
                        yield
1689
                    yield
1690

    
1691
            else:
1692
                append_gen = None
1693

    
1694
            self.client.append_object(
1695
                obj, tmpFile,
1696
                upload_cb=append_gen if turn else None)
1697
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1698
            (args, kwargs) = post.mock_calls[-1][1:3]
1699
            self.assertEqual(kwargs['obj'], obj)
1700
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1701
            fsize = num_of_blocks * int(kwargs['content_length'])
1702
            self.assertEqual(fsize, file_size)
1703
            self.assertEqual(kwargs['content_range'], 'bytes */*')
1704
            exp = 'application/octet-stream'
1705
            self.assertEqual(kwargs['content_type'], exp)
1706
            self.assertEqual(kwargs['update'], True)
1707

    
1708
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1709
    def test_truncate_object(self, post):
1710
        upto_bytes = 377
1711
        self.client.truncate_object(obj, upto_bytes)
1712
        post.assert_called_once_with(
1713
            obj,
1714
            update=True,
1715
            object_bytes=upto_bytes,
1716
            content_range='bytes 0-%s/*' % upto_bytes,
1717
            content_type='application/octet-stream',
1718
            source_object='/%s/%s' % (self.client.container, obj))
1719

    
1720
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1721
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1722
    def test_overwrite_object(self, post, GCI):
1723
        num_of_blocks = 4
1724
        tmpFile = self._create_temp_file(num_of_blocks)
1725
        tmpFile.seek(0, 2)
1726
        file_size = tmpFile.tell()
1727
        info = dict(object_info)
1728
        info['content-length'] = file_size
1729
        block_size = container_info['x-container-block-size']
1730
        with patch.object(
1731
                pithos.PithosClient, 'get_object_info',
1732
                return_value=info) as GOI:
1733
            for start, end in (
1734
                    (0, file_size + 1),
1735
                    (file_size + 1, file_size + 2)):
1736
                tmpFile.seek(0, 0)
1737
                self.assertRaises(
1738
                    ClientError,
1739
                    self.client.overwrite_object,
1740
                    obj, start, end, tmpFile)
1741
            for start, end in ((0, 144), (144, 233), (233, file_size)):
1742
                tmpFile.seek(0, 0)
1743
                owr_gen = None
1744
                exp_size = end - start + 1
1745
                if not start or exp_size > block_size:
1746
                    try:
1747
                        from progress.bar import ShadyBar
1748
                        owr_bar = ShadyBar('Mock append')
1749
                    except ImportError:
1750
                        owr_bar = None
1751

    
1752
                    if owr_bar:
1753

    
1754
                        def owr_gen(n):
1755
                            for i in owr_bar.iter(range(n)):
1756
                                yield
1757
                            yield
1758

    
1759
                    if exp_size > block_size:
1760
                        exp_size = exp_size % block_size or block_size
1761

    
1762
                self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1763
                self.assertEqual(GOI.mock_calls[-1], call(obj))
1764
                self.assertEqual(GCI.mock_calls[-1], call())
1765
                (args, kwargs) = post.mock_calls[-1][1:3]
1766
                self.assertEqual(args, (obj,))
1767
                self.assertEqual(len(kwargs['data']), exp_size)
1768
                self.assertEqual(kwargs['content_length'], exp_size)
1769
                self.assertEqual(kwargs['update'], True)
1770
                exp = 'application/octet-stream'
1771
                self.assertEqual(kwargs['content_type'], exp)
1772

    
1773
    @patch('%s.set_param' % pithos_pkg)
1774
    @patch('%s.get' % pithos_pkg, return_value=FR())
1775
    def test_get_sharing_accounts(self, get, SP):
1776
        FR.json = sharers
1777
        for kws in (
1778
                dict(),
1779
                dict(limit='50m3-11m17'),
1780
                dict(marker='X'),
1781
                dict(limit='50m3-11m17', marker='X')):
1782
            r = self.client.get_sharing_accounts(**kws)
1783
            self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1784
            self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1785
            limit, marker = kws.get('limit', None), kws.get('marker', None)
1786
            self.assertEqual(SP.mock_calls[-2], call(
1787
                'limit', limit,
1788
                iff=limit is not None))
1789
            self.assertEqual(SP.mock_calls[-1], call(
1790
                'marker', marker,
1791
                iff=marker is not None))
1792
            for i in range(len(r)):
1793
                self.assert_dicts_are_equal(r[i], sharers[i])
1794

    
1795
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1796
    def test_get_object_versionlist(self, get):
1797
        info = dict(object_info)
1798
        info['versions'] = ['v1', 'v2']
1799
        FR.json = info
1800
        r = self.client.get_object_versionlist(obj)
1801
        get.assert_called_once_with(obj, format='json', version='list')
1802
        self.assertEqual(r, info['versions'])
1803

    
1804
if __name__ == '__main__':
1805
    from sys import argv
1806
    from kamaki.clients.test import runTestCase
1807
    not_found = True
1808
    if not argv[1:] or argv[1] == 'PithosClient':
1809
        not_found = False
1810
        runTestCase(PithosClient, 'Pithos Client', argv[2:])
1811
    if not argv[1:] or argv[1] == 'PithosRestClient':
1812
        not_found = False
1813
        runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1814
    if not_found:
1815
        print('TestCase %s not found' % argv[1])