Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / pithos / test.py @ 7a3c66e1

History | View | Annotate | Download (73.7 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', 'public', 'until')
201
        for params in product(
202
                (None, 42),
203
                (None, 'X'),
204
                ('json', 'xml'),
205
                (False, True),
206
                (False, True),
207
                (None, '50m3-d473'),
208
                (None, '50m3-07h3r-d473'),
209
                (None, 'y37-4n7h3r-d473'),
210
                ((), ('someval',), ('v1', 'v2',)),
211
                (dict(), dict(success=200), dict(k='v', v='k'))):
212
            args, kwargs = params[-2], params[-1]
213
            params = params[:-2]
214
            self.client.account_get(*(params + args), **kwargs)
215
            self.assertEqual(SP.mock_calls[-6:],
216
                [call(keys[i], iff=X) if (i in (3, 4)) else call(
217
                    keys[i], X, iff=X) for i, X in enumerate(params[:6])])
218
            IMS, IUS = params[6], params[7]
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
                (False, True),
301
                (None, 'unt1l-d473'),
302
                (None, 'y37-4n47h3r'),
303
                (None, '4n47h3r-d473'),
304
                ((), ('someval',)),
305
                (dict(), dict(success=400), dict(k='v', v='k'))):
306
            args, kwargs = pm[-2:]
307
            pm = pm[:-2]
308
            self.client.container_get(*(pm + args), **kwargs)
309
            lmt, mrk, prfx, dlm, path, frmt, meta, shr, pbl, unt = pm[:-2]
310
            exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
311
            exp += [call('path', path)] if path else [
312
                call('prefix', prfx, iff=prfx),
313
                call('delimiter', dlm, iff=dlm)]
314
            exp += [
315
                call('format', frmt, iff=frmt),
316
                call('shared', iff=shr),
317
                call('public', iff=pbl)]
318
            if meta:
319
                exp += [call('meta', ','.join(meta))]
320
            exp += [call('until', unt, iff=unt)]
321
            self.assertEqual(SP.mock_calls[- len(exp):], exp)
322
            ims, ius = pm[-2:]
323
            self.assertEqual(SH.mock_calls[-2:], [
324
                call('If-Modified-Since', ims),
325
                call('If-Unmodified-Since', ius)])
326
            self.assertEqual(get.mock_calls[-1], call(
327
                '/%s/%s' % (self.client.account, self.client.container),
328
                *args,
329
                success=kwargs.pop('success', 200),
330
                **kwargs))
331

    
332
    @patch('%s.set_header' % rest_pkg)
333
    @patch('%s.put' % rest_pkg, return_value=FR())
334
    def test_container_put(self, put, SH):
335
        for pm in product(
336
                (None, 42),
337
                (None, 'v3r51on1ng'),
338
                (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
339
                ((), ('someval',)),
340
                (dict(), dict(success=400), dict(k='v', v='k'))):
341
            args, kwargs = pm[-2:]
342
            pm = pm[:-2]
343
            self.client.container_put(*(pm + args), **kwargs)
344
            quota, versioning, metas = pm[-3:]
345
            exp = [
346
                call('X-Container-Policy-Quota', quota),
347
                call('X-Container-Policy-Versioning', versioning)] + [
348
                call('X-Container-Meta-%s' % k, v) for k, v in metas.items()]
349
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
350
            self.assertEqual(put.mock_calls[-1], call(
351
                '/%s/%s' % (self.client.account, self.client.container),
352
                *args,
353
                success=kwargs.pop('success', (201, 202)),
354
                **kwargs))
355

    
356
    @patch('%s.set_param' % rest_pkg)
357
    @patch('%s.set_header' % rest_pkg)
358
    @patch('%s.post' % rest_pkg, return_value=FR())
359
    def test_container_post(self, post, SH, SP):
360
        for pm in product(
361
                (True, False),
362
                ('json', 'some-format'),
363
                (None, 'quota'),
364
                (None, 'v3r51on1ng'),
365
                (dict(), dict(k1='v2'), dict(k2='v2', k3='v3')),
366
                (None, 'content-type'),
367
                (None, 42),
368
                (None, 'transfer-encoding'),
369
                ((), ('someval',)),
370
                (dict(), dict(success=400), dict(k='v', v='k'))):
371
            args, kwargs = pm[-2:]
372
            pm = pm[:-2]
373
            self.client.container_post(*(pm + args), **kwargs)
374
            upd, frmt = pm[:2]
375
            self.assertEqual(SP.mock_calls[-2:], [
376
                call('update', '', iff=upd),
377
                call('format', frmt, iff=frmt)])
378
            qta, vrs, metas, ctype, clen, trenc = pm[2:]
379
            prfx = 'X-Container-Meta-'
380
            exp = [
381
                call('X-Container-Policy-Quota', qta),
382
                call('X-Container-Policy-Versioning', vrs)] + [
383
                call('%s%s' % (prfx, k), v) for k, v in metas.items()] + [
384
                call('Content-Type', ctype),
385
                call('Content-Length', clen),
386
                call('Transfer-Encoding', trenc)]
387
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
388
            ims, ius = pm[-2:]
389
            self.assertEqual(post.mock_calls[-1], call(
390
                '/%s/%s' % (self.client.account, self.client.container),
391
                *args,
392
                success=kwargs.pop('success', 202),
393
                **kwargs))
394

    
395
    @patch('%s.set_param' % rest_pkg)
396
    @patch('%s.delete' % rest_pkg, return_value=FR())
397
    def test_container_delete(self, delete, SP):
398
        for pm in product(
399
                (None, 'd473'),
400
                (None, 'd3l1m'),
401
                ((), ('someval',)),
402
                (dict(), dict(success=400), dict(k='v', v='k'))):
403
            args, kwargs = pm[-2:]
404
            pm = pm[:-2]
405
            self.client.container_delete(*(pm + args), **kwargs)
406
            unt, dlm = pm[-2:]
407
            self.assertEqual(SP.mock_calls[-2:], [
408
                call('until', unt, iff=unt),
409
                call('delimiter', dlm, iff=dlm)])
410
            self.assertEqual(delete.mock_calls[-1], call(
411
                '/%s/%s' % (self.client.account, self.client.container),
412
                *args,
413
                success=kwargs.pop('success', 204),
414
                **kwargs))
415

    
416
    @patch('%s.set_param' % rest_pkg)
417
    @patch('%s.set_header' % rest_pkg)
418
    @patch('%s.head' % rest_pkg, return_value=FR())
419
    def test_object_head(self, head, SH, SP):
420
        for pm in product(
421
                (None, 'v3r510n'),
422
                (None, '1f-374g'),
423
                (None, '1f-n0-74g'),
424
                (None, '1f-m0d-51nc3'),
425
                (None, '1f-unm0d-51nc3'),
426
                ((), ('someval',)),
427
                (dict(), dict(success=400), dict(k='v', v='k'))):
428
            args, kwargs = pm[-2:]
429
            pm = pm[:-2]
430
            self.client.object_head(obj, *(pm + args), **kwargs)
431
            vrs, etag, netag, ims, ius = pm[:5]
432
            self.assertEqual(
433
                SP.mock_calls[-1],
434
                call('version', vrs, iff=vrs))
435
            self.assertEqual(SH.mock_calls[-4:], [
436
                call('If-Match', etag),
437
                call('If-None-Match', netag),
438
                call('If-Modified-Since', ims),
439
                call('If-Unmodified-Since', ius)])
440
            acc, cont = self.client.account, self.client.container
441
            self.assertEqual(head.mock_calls[-1], call(
442
                '/%s/%s/%s' % (acc, cont, obj),
443
                *args,
444
                success=kwargs.pop('success', 200),
445
                **kwargs))
446

    
447
    @patch('%s.set_param' % rest_pkg)
448
    @patch('%s.set_header' % rest_pkg)
449
    @patch('%s.get' % rest_pkg, return_value=FR())
450
    def test_object_get(self, get, SH, SP):
451
        for pm in product(
452
                ('json', 'f0rm47'),
453
                (False, True),
454
                (None, 'v3r510n'),
455
                (None, 'range=74-63'),
456
                (False, True),
457
                (None, '3746'),
458
                (None, 'non-3746'),
459
                (None, '1f-m0d'),
460
                (None, '1f-unm0d'),
461
                ((), ('someval',)),
462
                (dict(), dict(success=400), dict(k='v', v='k'))):
463
            args, kwargs = pm[-2:]
464
            pm = pm[:-2]
465
            self.client.object_get(obj, *(pm + args), **kwargs)
466
            format, hashmap, version = pm[:3]
467
            self.assertEqual(SP.mock_calls[-3:], [
468
                call('format', format, iff=format),
469
                call('hashmap', hashmap, iff=hashmap),
470
                call('version', version, iff=version)])
471
            rng, ifrng, im, inm, ims, ius = pm[-6:]
472
            self.assertEqual(SH.mock_calls[-6:], [
473
                call('Range', rng),
474
                call('If-Range', '', ifrng and rng),
475
                call('If-Match', im),
476
                call('If-None-Match', inm),
477
                call('If-Modified-Since', ims),
478
                call('If-Unmodified-Since', ius)])
479
            acc, cont = self.client.account, self.client.container
480
            self.assertEqual(get.mock_calls[-1], call(
481
                '/%s/%s/%s' % (acc, cont, obj),
482
                *args,
483
                success=kwargs.pop('success', 200),
484
                **kwargs))
485

    
486
    @patch('%s.set_param' % rest_pkg)
487
    @patch('%s.set_header' % rest_pkg)
488
    @patch('%s.put' % rest_pkg, return_value=FR())
489
    def test_object_put(self, put, SH, SP):
490
        for pm in product(
491
                ('json', 'f0rm47'),
492
                (False, True),
493
                (None, 'delim',),
494
                (dict(), dict(read=['u1', 'g2'], write=['u1'])),
495
                (False, True),
496
                (dict(), dict(k2='v2', k3='v3')),
497
                ((), ('someval',)),
498
                (dict(), dict(success=400), dict(k='v', v='k'))):
499
            args, kwargs = pm[-2:]
500
            pm = pm[:-2]
501
            terms = [None] * 13
502
            for i in range(len(terms)):
503
                if randint(0, 2):
504
                    terms[i] = 'val_%s' % randint(13, 1024)
505
            self.client.object_put(
506
                obj,
507
                *(pm[:3] + tuple(terms) + pm[3:] + args),
508
                **kwargs)
509
            format, hashmap, delimiter = pm[:3]
510
            self.assertEqual(SP.mock_calls[-3:], [
511
                call('format', format, iff=format),
512
                call('hashmap', hashmap, iff=hashmap),
513
                call('delimiter', delimiter, iff=delimiter)])
514
            (
515
                im, inm, etag, clen, ctype, trenc,
516
                cp, mv, srcacc, srcvrs, conenc, condis, mnf) = terms
517
            perms, public, metas = pm[3:]
518
            exp = [
519
                call('If-Match', im),
520
                call('If-None-Match', inm),
521
                call('ETag', etag),
522
                call('Content-Length', clen),
523
                call('Content-Type', ctype),
524
                call('Transfer-Encoding', trenc),
525
                call('X-Copy-From', cp),
526
                call('X-Move-From', mv),
527
                call('X-Source-Account', srcacc),
528
                call('X-Source-Version', srcvrs),
529
                call('Content-Encoding', conenc),
530
                call('Content-Disposition', condis),
531
                call('X-Object-Manifest', mnf)]
532
            if perms:
533
                perm_str = ''
534
                for ptype, pval in perms.items():
535
                    if pval:
536
                        perm_str += ';' if perm_str else ''
537
                        perm_str += '%s=%s' % (ptype, ','.join(pval))
538
                exp += [call('X-Object-Sharing', perm_str)]
539
            exp += [call('X-Object-Public', public, public is not None)]
540
            for k, v in metas.items():
541
                exp += [call('X-Object-Meta-%s' % k, v)]
542
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
543
            acc, cont = self.client.account, self.client.container
544
            self.assertEqual(put.mock_calls[-1], call(
545
                '/%s/%s/%s' % (acc, cont, obj),
546
                *args,
547
                success=kwargs.pop('success', 201),
548
                **kwargs))
549

    
550
    @patch('%s.set_param' % rest_pkg)
551
    @patch('%s.set_header' % rest_pkg)
552
    @patch('%s.copy' % rest_pkg, return_value=FR())
553
    def test_object_copy(self, copy, SH, SP):
554
        dest = 'dest1n4710n'
555
        for pm in product(
556
                ('json', 'f0rm47'),
557
                (False, True),
558
                (None, 'ifmatch'),
559
                (None, 'ifnonematch'),
560
                (None, 'destinationaccount'),
561
                (None, 'content-type'),
562
                (None, 'content-encoding'),
563
                (None, 'content-disp'),
564
                (None, 'source-version'),
565
                (dict(), dict(read=['u1', 'g2'], write=['u1'])),
566
                (False, True),
567
                (dict(), dict(k2='v2', k3='v3')),
568
                ((), ('someval',)),
569
                (dict(), dict(success=400), dict(k='v', v='k'))):
570
            args, kwargs = pm[-2:]
571
            pm = pm[:-2]
572
            self.client.object_copy(obj, dest, *(pm + args), **kwargs)
573
            format, ict = pm[:2]
574
            self.assertEqual(SP.mock_calls[-2:], [
575
                call('format', format, iff=format),
576
                call('ignore_content_type', iff=ict)])
577
            im, inm, da, ct, ce, cd, sv, perms, public, metas = pm[2:]
578
            exp = [call('If-Match', im),
579
                call('If-None-Match', inm),
580
                call('Destination', dest),
581
                call('Destination-Account', da),
582
                call('Content-Type', ct),
583
                call('Content-Encoding', ce),
584
                call('Content-Disposition', cd),
585
                call('X-Source-Version', sv)]
586
            if perms:
587
                perm_str = ''
588
                for ptype, pval in perms.items():
589
                    if pval:
590
                        perm_str += ';' if perm_str else ''
591
                        perm_str += '%s=%s' % (ptype, ','.join(pval))
592
                exp += [call('X-Object-Sharing', perm_str)]
593
            exp += [call('X-Object-Public', public, public is not None)]
594
            for k, v in metas.items():
595
                exp += [call('X-Object-Meta-%s' % k, v)]
596
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
597
            acc, cont = self.client.account, self.client.container
598
            self.assertEqual(copy.mock_calls[-1], call(
599
                '/%s/%s/%s' % (acc, cont, obj),
600
                *args,
601
                success=kwargs.pop('success', 201),
602
                **kwargs))
603

    
604
    @patch('%s.set_param' % rest_pkg)
605
    @patch('%s.set_header' % rest_pkg)
606
    @patch('%s.move' % rest_pkg, return_value=FR())
607
    def test_object_move(self, move, SH, SP):
608
        for pm in product(
609
                ('json', 'f0rm47'),
610
                (False, True),
611
                (None, 'ifmatch'),
612
                (None, 'ifnonematch'),
613
                (None, 'destination'),
614
                (None, 'destinationaccount'),
615
                (None, 'content-type'),
616
                (None, 'content-encoding'),
617
                (None, 'content-disp'),
618
                (dict(), dict(read=['u1', 'g2'], write=['u1'])),
619
                (False, True),
620
                (dict(), dict(k2='v2', k3='v3')),
621
                ((), ('someval',)),
622
                (dict(), dict(success=400), dict(k='v', v='k'))):
623
            args, kwargs = pm[-2:]
624
            pm = pm[:-2]
625
            self.client.object_move(obj, *(pm + args), **kwargs)
626
            format, ict = pm[:2]
627
            self.assertEqual(SP.mock_calls[-2:], [
628
                call('format', format, iff=format),
629
                call('ignore_content_type', iff=ict)])
630
            im, inm, d, da, ct, ce, cd, perms, public, metas = pm[2:]
631
            exp = [call('If-Match', im),
632
                call('If-None-Match', inm),
633
                call('Destination', d),
634
                call('Destination-Account', da),
635
                call('Content-Type', ct),
636
                call('Content-Encoding', ce),
637
                call('Content-Disposition', cd)]
638
            if perms:
639
                perm_str = ''
640
                for ptype, pval in perms.items():
641
                    if pval:
642
                        perm_str += ';' if perm_str else ''
643
                        perm_str += '%s=%s' % (ptype, ','.join(pval))
644
                exp += [call('X-Object-Sharing', perm_str, iff=perms)]
645
            else:
646
                exp += [call('X-Object-Sharing', '', iff={})]
647
            exp += [call('X-Object-Public', public, public is not None)]
648
            for k, v in metas.items():
649
                exp += [call('X-Object-Meta-%s' % k, v)]
650
            self.assertEqual(SH.mock_calls[- len(exp):], exp)
651
            acc, cont = self.client.account, self.client.container
652
            self.assertEqual(move.mock_calls[-1], call(
653
                '/%s/%s/%s' % (acc, cont, obj),
654
                *args,
655
                success=kwargs.pop('success', 201),
656
                **kwargs))
657

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

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

    
747

    
748
class PithosMethods(TestCase):
749

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

    
767

    
768
class PithosClient(TestCase):
769

    
770
    files = []
771

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

    
783
    def assert_dicts_are_equal(self, d1, d2):
784
        for k, v in d1.items():
785
            self.assertTrue(k in d2)
786
            if isinstance(v, dict):
787
                self.assert_dicts_are_equal(v, d2[k])
788
            else:
789
                self.assertEqual(unicode(v), unicode(d2[k]))
790

    
791
    def setUp(self):
792
        self.url = 'https://www.example.com/pithos'
793
        self.token = 'p17h0570k3n'
794
        self.client = pithos.PithosClient(self.url, self.token)
795
        self.client.account = user_id
796
        self.client.container = 'c0nt@1n3r_i'
797

    
798
    def tearDown(self):
799
        FR.headers = dict()
800
        FR.status_code = 200
801
        FR.json = dict()
802
        FR.content = FR.json
803
        for f in self.files:
804
            f.close()
805

    
806
    #  Pithos+ methods that extend storage API
807

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

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

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

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

    
844
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
845
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
846
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
847
    def test_upload_object(self, OP, CP, GCI):
848
        num_of_blocks = 8
849
        tmpFile = self._create_temp_file(num_of_blocks)
850

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

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

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

    
898
        mock_offset = 2
899

    
900
        #  With progress bars
901
        try:
902
            from progress.bar import ShadyBar
903
            blck_bar = ShadyBar('Mock blck calc.')
904
            upld_bar = ShadyBar('Mock uplds')
905
        except ImportError:
906
            blck_bar = None
907
            upld_bar = None
908

    
909
        if blck_bar and upld_bar:
910

    
911
            def blck_gen(n):
912
                for i in blck_bar.iter(range(n)):
913
                    yield
914
                yield
915

    
916
            def upld_gen(n):
917
                for i in upld_bar.iter(range(n)):
918
                    yield
919
                yield
920

    
921
            tmpFile.seek(0)
922
            r = self.client.upload_object(
923
                obj, tmpFile,
924
                hash_cb=blck_gen, upload_cb=upld_gen)
925
            self.assert_dicts_are_equal(r, exp_headers)
926

    
927
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
928
                self.assertEqual(OP.mock_calls[i], c)
929

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

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

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

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

    
977
        exp_headers = dict(id='container id', name='container name')
978
        FR.headers = dict(exp_headers)
979
        r = self.client.upload_from_string(obj, src_str)
980
        self.assert_dicts_are_equal(r, exp_headers)
981
        self.assertEqual(GCI.mock_calls[-1], call())
982

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

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

    
1023
        mock_offset = 2
1024

    
1025
        #  With progress bars
1026
        try:
1027
            from progress.bar import ShadyBar
1028
            blck_bar = ShadyBar('Mock blck calc.')
1029
            upld_bar = ShadyBar('Mock uplds')
1030
        except ImportError:
1031
            blck_bar = None
1032
            upld_bar = None
1033

    
1034
        if blck_bar and upld_bar:
1035

    
1036
            def blck_gen(n):
1037
                for i in blck_bar.iter(range(n)):
1038
                    yield
1039
                yield
1040

    
1041
            def upld_gen(n):
1042
                for i in upld_bar.iter(range(n)):
1043
                    yield
1044
                yield
1045

    
1046
            tmpFile.seek(0)
1047
            r = self.client.upload_object(
1048
                obj, tmpFile,
1049
                hash_cb=blck_gen, upload_cb=upld_gen)
1050
            self.assert_dicts_are_equal(r, exp_headers)
1051

    
1052
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
1053
                self.assertEqual(OP.mock_calls[i], c)
1054

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

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

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

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

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

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

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

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

    
1189
    #  Pithos+ only methods
1190

    
1191
    @patch('%s.container_put' % pithos_pkg, return_value=FR())
1192
    def test_create_container(self, CP):
1193
        FR.headers = container_info
1194
        cont = 'an0th3r_c0n741n3r'
1195

    
1196
        r = self.client.create_container()
1197
        self.assert_dicts_are_equal(r, container_info)
1198
        CP.assert_called_once_with(quota=None, versioning=None, metadata=None)
1199

    
1200
        bu_cont = self.client.container
1201
        r = self.client.create_container(cont)
1202
        self.assertEqual(self.client.container, bu_cont)
1203
        self.assert_dicts_are_equal(r, container_info)
1204
        self.assertEqual(
1205
            CP.mock_calls[-1],
1206
            call(quota=None, versioning=None, metadata=None))
1207

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

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

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

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

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

    
1294
        kwargs = dict(
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
        expargs = dict(kwargs)
1302
        expargs.pop('range_str')
1303
        for k in expargs:
1304
            expargs[k] = None
1305
        GOH.assert_called_once_with(obj, **expargs)
1306

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

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

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

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

    
1357
        #  With progress bars
1358
        try:
1359
            from progress.bar import ShadyBar
1360
            dl_bar = ShadyBar('Mock dl')
1361
        except ImportError:
1362
            dl_bar = None
1363

    
1364
        if dl_bar:
1365

    
1366
            def blck_gen(n):
1367
                for i in dl_bar.iter(range(n)):
1368
                    yield
1369
                yield
1370

    
1371
            tmpFile.seek(0)
1372
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1373
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1374

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

    
1387
        #  ALl options on no tty
1388
        def foo():
1389
            return True
1390

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

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

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

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

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

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

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

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

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

    
1502
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1503
    #def test_set_account_quota(self, post):
1504
    #    qu = 1024
1505
    #    self.client.set_account_quota(qu)
1506
    #    post.assert_called_once_with(update=True, quota=qu)
1507

    
1508
    #@patch('%s.account_post' % pithos_pkg, return_value=FR())
1509
    #def test_set_account_versioning(self, post):
1510
    #    vrs = 'n3wV3r51on1ngTyp3'
1511
    #    self.client.set_account_versioning(vrs)
1512
    #    post.assert_called_once_with(update=True, versioning=vrs)
1513

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

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

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

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

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

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

    
1589
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1590
    def test_del_container_meta(self, AP):
1591
        self.client.del_container_meta('somekey')
1592
        AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1593

    
1594
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1595
    def test_set_container_limit(self, post):
1596
        qu = 1024
1597
        self.client.set_container_limit(qu)
1598
        post.assert_called_once_with(update=True, quota=qu)
1599

    
1600
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1601
    def test_set_container_versioning(self, post):
1602
        vrs = 'n3wV3r51on1ngTyp3'
1603
        self.client.set_container_versioning(vrs)
1604
        post.assert_called_once_with(update=True, versioning=vrs)
1605

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

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

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

    
1639
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1640
    def test_unpublish_object(self, post):
1641
        self.client.unpublish_object(obj)
1642
        post.assert_called_once_with(obj, public=False, update=True)
1643

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

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

    
1688
    @patch('%s.set_object_sharing' % pithos_pkg)
1689
    def test_del_object_sharing(self, SOS):
1690
        self.client.del_object_sharing(obj)
1691
        SOS.assert_called_once_with(obj)
1692

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

    
1703
            try:
1704
                from progress.bar import ShadyBar
1705
                apn_bar = ShadyBar('Mock append')
1706
            except ImportError:
1707
                apn_bar = None
1708

    
1709
            if apn_bar:
1710

    
1711
                def append_gen(n):
1712
                    for i in apn_bar.iter(range(n)):
1713
                        yield
1714
                    yield
1715

    
1716
            else:
1717
                append_gen = None
1718

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

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

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

    
1777
                    if owr_bar:
1778

    
1779
                        def owr_gen(n):
1780
                            for i in owr_bar.iter(range(n)):
1781
                                yield
1782
                            yield
1783

    
1784
                    if exp_size > block_size:
1785
                        exp_size = exp_size % block_size or block_size
1786

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

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

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

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