Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / test.py @ c088076b

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(obj, tmpFile,
1032
            content_type=ctype, sharing=sharing)
1033
        self.assert_dicts_are_equal(r, exp_headers)
1034
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
1035
        self.assert_dicts_are_equal(
1036
            OP.mock_calls[-2][2]['permissions'],
1037
            sharing)
1038

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

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

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

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

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

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

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

    
1160
    #  Pithos+ only methods
1161

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1335
        if dl_bar:
1336

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1683
            if apn_bar:
1684

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

    
1690
            else:
1691
                append_gen = None
1692

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

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

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

    
1751
                    if owr_bar:
1752

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

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

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

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

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

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