Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (71.3 kB)

1
# Copyright 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from unittest import TestCase
35
from mock import patch, call
36
from tempfile import NamedTemporaryFile
37
from os import urandom
38
from itertools import product
39
from random import randint
40

    
41
try:
42
    from collections import OrderedDict
43
except ImportError:
44
    from kamaki.clients.utils.ordereddict import OrderedDict
45

    
46
from kamaki.clients import pithos, ClientError
47

    
48

    
49
rest_pkg = 'kamaki.clients.pithos.rest_api.PithosRestClient'
50
pithos_pkg = 'kamaki.clients.pithos.PithosClient'
51

    
52
user_id = 'ac0un7-1d-5tr1ng'
53
obj = 'obj3c7N4m3'
54

    
55
account_info = {
56
    'content-language': 'en-us',
57
    'content-type': 'text/html; charset=utf-8',
58
    'date': 'Wed, 06 Mar 2013 13:25:51 GMT',
59
    'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
60
    'server': 'gunicorn/0.14.5',
61
    'vary': 'Accept-Language',
62
    'x-account-bytes-used': '751615526',
63
    'x-account-container-count': 7,
64
    'x-account-policy-quota': 53687091200,
65
    'x-account-policy-versioning': 'auto'}
66
container_info = {
67
    'content-language': 'en-us',
68
    'content-type': 'text/html; charset=utf-8',
69
    'date': 'Wed, 06 Mar 2013 15:11:05 GMT',
70
    'last-modified': 'Wed, 27 Feb 2013 15:56:13 GMT',
71
    'server': 'gunicorn/0.14.5',
72
    'vary': 'Accept-Language',
73
    'x-container-block-hash': 'sha256',
74
    'x-container-block-size': 4194304,
75
    'x-container-bytes-used': 309528938,
76
    'x-container-object-count': 14,
77
    'x-container-object-meta': '',
78
    'x-container-policy-quota': 53687091200,
79
    'x-container-policy-versioning': 'auto'}
80
object_info = {
81
    'content-language': 'en-us',
82
    'content-length': 254965,
83
    'content-type': 'application/octet-stream',
84
    'date': 'Thu, 07 Mar 2013 13:27:43 GMT',
85
    'etag': '',
86
    'last-modified': 'Mon, 04 Mar 2013 18:22:31 GMT',
87
    'server': 'gunicorn/0.14.5',
88
    'vary': 'Accept-Language',
89
    'x-object-hash': 'obj3c7h45h1s0bj3c7h45h411r34dY',
90
    'x-object-uuid': 'd0c747ca-34bd-49e0-8e98-1d07d8b0cbc7',
91
    'x-object-version': '525996',
92
    'x-object-version-timestamp': 'Mon, 04 Mar 2013 18:22:31 GMT',
93
    'x-object-meta-k1': 'v1',
94
    'x-object-meta-k2': 'v2'}
95
container_list = [
96
    dict(
97
        count=2,
98
        last_modified="2013-02-27T11:56:09.893033+00:00",
99
        bytes=677076979,
100
        name="pithos",
101
        x_container_policy=dict(quota="21474836480", versioning="auto")),
102
    dict(
103
        count=0,
104
        last_modified="2012-10-23T12:25:17.229187+00:00",
105
        bytes=0,
106
        name="trash",
107
        x_container_policy=dict(quota="21474836480", versioning="auto"))]
108
object_list = [
109
    dict(hash="",
110
        name="The_Secret_Garden.zip",
111
        x_object_public="/public/wdp9p",
112
        bytes=203304947,
113
        x_object_version_timestamp="1360237915.7027509",
114
        x_object_uuid="s0m3uu1df0r0bj0n3",
115
        last_modified="2013-02-07T11:51:55.702751+00:00",
116
        content_type="application/octet-stream",
117
        x_object_hash="0afdf29f71cd53126225c3f54ca",
118
        x_object_version=17737,
119
        x_object_modified_by=user_id),
120
    dict(hash="",
121
        name="The_Revealed_Garden.zip",
122
        x_object_public="/public/wpd7p",
123
        bytes=20330947,
124
        x_object_version_timestamp="13602915.7027509",
125
        x_object_uuid="s0m3uu1df0r0bj70w",
126
        last_modified="2013-02-07T11:51:55.702751+00:00",
127
        content_type="application/octet-stream",
128
        x_object_hash="0afdf29f71cd53126225c3f54ca",
129
        x_object_version=17737,
130
        x_object_modified_by=user_id)]
131
object_hashmap = dict(
132
    block_hash="sha256", block_size=4194304, bytes=33554432,
133
    hashes=[
134
        "4988438cc1c0292c085d289649b28cf547ba3db71c6efaac9f2df7e193d4d0af",
135
        "b214244aa56df7d1df7c6cac066e7cef268d9c2beb4dcf7ce68af667b0626f91",
136
        "17f365f25e0682565ded30576066bb13377a3d306967e4d74e06bb6bbc20f75f",
137
        "2524ae208932669fff89adf8a2fc0df3b67736ca0d3aadce7a2ce640f142af37",
138
        "5d807a2129d2fcd3c221c3da418ed52af3fc48d0817b62e0bb437acffccd3514",
139
        "609de22ce842d997f645fc49d5f14e0e3766dd51a6cbe66383b2bab82c8dfcd0",
140
        "3102851ac168c78be70e35ff5178c7b1ebebd589e5106d565ca1094d1ca8ff59",
141
        "bfe306dd24e92a8d85caf7055643f250fd319e8c4cdd4755ddabbf3ff97e83c7"])
142
sharers = [
143
    dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b1a-82d5"),
144
    dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="0b2a-f2d5"),
145
    dict(last_modified="2013-01-29T16:50:06.084674+00:00", name="2b1a-82d6")]
146

    
147

    
148
class FR(object):
149
    """FR stands for Fake Response"""
150
    json = dict()
151
    headers = dict()
152
    content = json
153
    status = None
154
    status_code = 200
155

    
156

    
157
class PithosRestClient(TestCase):
158

    
159
    def setUp(self):
160
        self.url = 'https://www.example.com/pithos'
161
        self.token = 'p17h0570k3n'
162
        self.client = pithos.PithosRestClient(self.url, self.token)
163
        self.client.account = user_id
164
        self.client.container = 'c0nt@1n3r_i'
165

    
166
    def tearDown(self):
167
        FR.headers = dict()
168
        FR.json = dict()
169
        FR.content = FR.json
170

    
171
    @patch('%s.set_param' % rest_pkg)
172
    @patch('%s.set_header' % rest_pkg)
173
    @patch('%s.head' % rest_pkg, return_value=FR())
174
    def test_account_head(self, head, SH, SP):
175
        for params in product(
176
                (None, '50m3-d473'),
177
                (None, '50m3-07h3r-d473'),
178
                (None, 'y37-4n7h3r-d473'),
179
                ((), ('someval',), ('v1', 'v2',)),
180
                (dict(), dict(success=200), dict(k='v', v='k'))):
181
            args, kwargs = params[-2], params[-1]
182
            params = params[:-2]
183
            self.client.account_head(*(params + args), **kwargs)
184
            unt = params[0]
185
            self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
186
            IMS, IUS = params[1], params[2]
187
            self.assertEqual(SH.mock_calls[-2:], [
188
                call('If-Modified-Since', IMS),
189
                call('If-Unmodified-Since', IUS)])
190
            self.assertEqual(head.mock_calls[-1], call(
191
                '/%s' % self.client.account,
192
                *args,
193
                success=kwargs.pop('success', 204),
194
                **kwargs))
195

    
196
    @patch('%s.set_param' % rest_pkg)
197
    @patch('%s.set_header' % rest_pkg)
198
    @patch('%s.get' % rest_pkg, return_value=FR())
199
    def test_account_get(self, get, SH, SP):
200
        keys = ('limit', 'marker', 'format', 'shared', 'until')
201
        for params in product(
202
                (None, 42),
203
                (None, 'X'),
204
                ('json', 'xml'),
205
                (False, True),
206
                (None, '50m3-d473'),
207
                (None, '50m3-07h3r-d473'),
208
                (None, 'y37-4n7h3r-d473'),
209
                ((), ('someval',), ('v1', 'v2',)),
210
                (dict(), dict(success=200), dict(k='v', v='k'))):
211
            args, kwargs = params[-2], params[-1]
212
            params = params[:-2]
213
            self.client.account_get(*(params + args), **kwargs)
214
            self.assertEqual(SP.mock_calls[-5:],
215
                [call(keys[i], iff=X) if (
216
                    i == 3) else call(
217
                        keys[i], X, iff=X) for i, X in enumerate(params[:5])])
218
            IMS, IUS = params[5], params[6]
219
            self.assertEqual(SH.mock_calls[-2:], [
220
                call('If-Modified-Since', IMS),
221
                call('If-Unmodified-Since', IUS)])
222
            self.assertEqual(get.mock_calls[-1], call(
223
                '/%s' % self.client.account,
224
                *args,
225
                success=kwargs.pop('success', (200, 204)),
226
                **kwargs))
227

    
228
    @patch('%s.set_param' % rest_pkg)
229
    @patch('%s.set_header' % rest_pkg)
230
    @patch('%s.post' % rest_pkg, return_value=FR())
231
    def test_account_post(self, post, SH, SP):
232
        #keys = ('update', 'groups', 'metadata', 'quota', 'versioning')
233
        for pm in product(
234
                (True, False),
235
                ({}, dict(g=['u1', 'u2']), dict(g1=[], g2=['u1', 'u2'])),
236
                (None, dict(k1='v1', k2='v2', k3='v2'), dict(k='v')),
237
                (None, 42),
238
                (None, 'v3r510n1ng'),
239
                ((), ('someval',), ('v1', 'v2',)),
240
                (dict(), dict(success=200), dict(k='v', v='k'))):
241
            args, kwargs = pm[-2:]
242
            pm = pm[:-2]
243
            self.client.account_post(*(pm + args), **kwargs)
244
            upd = pm[0]
245
            self.assertEqual(SP.mock_calls[-1], call('update', iff=upd))
246
            expected = []
247
            if pm[1]:
248
                expected += [
249
                call('X-Account-Group-%s' % k, v) for k, v in pm[1].items()]
250
            if pm[2]:
251
                expected = [
252
                call('X-Account-Meta-%s' % k, v) for k, v in pm[2].items()]
253
            expected = [
254
                call('X-Account-Policy-Quota', pm[3]),
255
                call('X-Account-Policy-Versioning', pm[4])]
256
            self.assertEqual(SH.mock_calls[- len(expected):], expected)
257
            self.assertEqual(post.mock_calls[-1], call(
258
                '/%s' % self.client.account,
259
                *args,
260
                success=kwargs.pop('success', 202),
261
                **kwargs))
262

    
263
    @patch('%s.set_param' % rest_pkg)
264
    @patch('%s.set_header' % rest_pkg)
265
    @patch('%s.head' % rest_pkg, return_value=FR())
266
    def test_container_head(self, head, SH, SP):
267
        for pm in product(
268
                (None, '4-d473'),
269
                (None, '47h3r-d473'),
270
                (None, 'y37-4n47h3r'),
271
                ((), ('someval',)),
272
                (dict(), dict(success=200), dict(k='v', v='k'))):
273
            args, kwargs = pm[-2:]
274
            pm = pm[:-2]
275
            self.client.container_head(*(pm + args), **kwargs)
276
            unt, ims, ius = pm[0:3]
277
            self.assertEqual(SP.mock_calls[-1], call('until', unt, iff=unt))
278
            self.assertEqual(SH.mock_calls[-2:], [
279
                call('If-Modified-Since', ims),
280
                call('If-Unmodified-Since', ius)])
281
            self.assertEqual(head.mock_calls[-1], call(
282
                '/%s/%s' % (self.client.account, self.client.container),
283
                *args,
284
                success=kwargs.pop('success', 204),
285
                **kwargs))
286

    
287
    @patch('%s.set_param' % rest_pkg)
288
    @patch('%s.set_header' % rest_pkg)
289
    @patch('%s.get' % rest_pkg, return_value=FR())
290
    def test_container_get(self, get, SH, SP):
291
        for pm in product(
292
                (None, 42),
293
                (None, 'X'),
294
                (None, 'some/prefix'),
295
                (None, 'delimiter'),
296
                (None, '/some/path'),
297
                ('json', 'some-format'),
298
                ([], ['k1', 'k2', 'k3']),
299
                (False, True),
300
                (None, 'unt1l-d473'),
301
                (None, 'y37-4n47h3r'),
302
                (None, '4n47h3r-d473'),
303
                ((), ('someval',)),
304
                (dict(), dict(success=400), dict(k='v', v='k'))):
305
            args, kwargs = pm[-2:]
306
            pm = pm[:-2]
307
            self.client.container_get(*(pm + args), **kwargs)
308
            lmt, mrk, prfx, dlm, path, frmt, meta, shr, unt = pm[:-2]
309
            exp = [call('limit', lmt, iff=lmt), call('marker', mrk, iff=mrk)]
310
            exp += [call('path', path)] if path else [
311
                call('prefix', prfx, iff=prfx),
312
                call('delimiter', dlm, iff=dlm)]
313
            exp += [call('format', frmt, iff=frmt), call('shared', iff=shr)]
314
            if meta:
315
                exp += [call('meta', ','.join(meta))]
316
            exp += [call('until', unt, iff=unt)]
317
            self.assertEqual(SP.mock_calls[- len(exp):], exp)
318
            ims, ius = pm[-2:]
319
            self.assertEqual(SH.mock_calls[-2:], [
320
                call('If-Modified-Since', ims),
321
                call('If-Unmodified-Since', ius)])
322
            self.assertEqual(get.mock_calls[-1], call(
323
                '/%s/%s' % (self.client.account, self.client.container),
324
                *args,
325
                success=kwargs.pop('success', 200),
326
                **kwargs))
327

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

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

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

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

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

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

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

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

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

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

    
739

    
740
class PithosClient(TestCase):
741

    
742
    files = []
743

    
744
    def _create_temp_file(self, num_of_blocks):
745
        self.files.append(NamedTemporaryFile())
746
        tmpFile = self.files[-1]
747
        file_size = num_of_blocks * 4 * 1024 * 1024
748
        print('\n\tCreate tmp file')
749
        tmpFile.write(urandom(file_size))
750
        tmpFile.flush()
751
        tmpFile.seek(0)
752
        print('\t\tDone')
753
        return tmpFile
754

    
755
    def assert_dicts_are_equal(self, d1, d2):
756
        for k, v in d1.items():
757
            self.assertTrue(k in d2)
758
            if isinstance(v, dict):
759
                self.assert_dicts_are_equal(v, d2[k])
760
            else:
761
                self.assertEqual(unicode(v), unicode(d2[k]))
762

    
763
    def setUp(self):
764
        self.url = 'https://www.example.com/pithos'
765
        self.token = 'p17h0570k3n'
766
        self.client = pithos.PithosClient(self.url, self.token)
767
        self.client.account = user_id
768
        self.client.container = 'c0nt@1n3r_i'
769

    
770
    def tearDown(self):
771
        FR.headers = dict()
772
        FR.status_code = 200
773
        FR.json = dict()
774
        FR.content = FR.json
775
        for f in self.files:
776
            f.close()
777

    
778
    #  Pithos+ methods that extend storage API
779

    
780
    @patch('%s.account_head' % pithos_pkg, return_value=FR())
781
    def test_get_account_info(self, AH):
782
        FR.headers = account_info
783
        for until in (None, 'un71L-d473'):
784
            r = self.client.get_account_info(until=until)
785
            self.assert_dicts_are_equal(r, account_info)
786
            self.assertEqual(AH.mock_calls[-1], call(until=until))
787
        FR.status_code = 401
788
        self.assertRaises(ClientError, self.client.get_account_info)
789

    
790
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
791
    def test_del_account_meta(self, AP):
792
        keys = ['k1', 'k2', 'k3']
793
        for key in keys:
794
            self.client.del_account_meta(key)
795
            self.assertEqual(
796
                AP.mock_calls[-1],
797
                call(update=True, metadata={key: ''}))
798

    
799
    @patch('%s.container_head' % pithos_pkg, return_value=FR())
800
    def test_get_container_info(self, CH):
801
        FR.headers = container_info
802
        r = self.client.get_container_info()
803
        self.assert_dicts_are_equal(r, container_info)
804
        u = 'some date'
805
        r = self.client.get_container_info(until=u)
806
        self.assertEqual(CH.mock_calls, [call(until=None), call(until=u)])
807

    
808
    @patch('%s.account_get' % pithos_pkg, return_value=FR())
809
    def test_list_containers(self, get):
810
        FR.json = container_list
811
        r = self.client.list_containers()
812
        get.assert_called_once_with()
813
        for i in range(len(r)):
814
            self.assert_dicts_are_equal(r[i], container_list[i])
815

    
816
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
817
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
818
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
819
    def test_upload_object(self, OP, CP, GCI):
820
        num_of_blocks = 8
821
        tmpFile = self._create_temp_file(num_of_blocks)
822

    
823
        # Without kwargs
824
        exp_headers = dict(id='container id', name='container name')
825
        FR.headers = dict(exp_headers)
826
        r = self.client.upload_object(obj, tmpFile)
827
        self.assert_dicts_are_equal(r, exp_headers)
828
        self.assertEqual(GCI.mock_calls[-1], call())
829

    
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_delete' % pithos_pkg, return_value=FR())
1163
    def test_purge_container(self, CD):
1164
        self.client.purge_container()
1165
        self.assertTrue('until' in CD.mock_calls[-1][2])
1166
        cont = self.client.container
1167
        self.client.purge_container('another-container')
1168
        self.assertEqual(self.client.container, cont)
1169

    
1170
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1171
    def test_upload_object_unchunked(self, put):
1172
        num_of_blocks = 8
1173
        tmpFile = self._create_temp_file(num_of_blocks)
1174
        expected = dict(
1175
                success=201,
1176
                data=num_of_blocks * 4 * 1024 * 1024,
1177
                etag='some-etag',
1178
                content_encoding='some content_encoding',
1179
                content_type='some content-type',
1180
                content_disposition='some content_disposition',
1181
                public=True,
1182
                permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1183
        r = self.client.upload_object_unchunked(obj, tmpFile)
1184
        self.assert_dicts_are_equal(r, FR.headers)
1185
        self.assertEqual(put.mock_calls[-1][1], (obj,))
1186
        self.assertEqual(
1187
            sorted(put.mock_calls[-1][2].keys()),
1188
            sorted(expected.keys()))
1189
        kwargs = dict(expected)
1190
        kwargs.pop('success')
1191
        kwargs['size'] = kwargs.pop('data')
1192
        kwargs['sharing'] = kwargs.pop('permissions')
1193
        tmpFile.seek(0)
1194
        r = self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
1195
        self.assert_dicts_are_equal(r, FR.headers)
1196
        pmc = put.mock_calls[-1][2]
1197
        for k, v in expected.items():
1198
            if k == 'data':
1199
                self.assertEqual(len(pmc[k]), v)
1200
            else:
1201
                self.assertEqual(pmc[k], v)
1202
        self.assertRaises(
1203
            ClientError,
1204
            self.client.upload_object_unchunked,
1205
            obj, tmpFile, withHashFile=True)
1206

    
1207
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
1208
    def test_create_object_by_manifestation(self, put):
1209
        manifest = '%s/%s' % (self.client.container, obj)
1210
        kwargs = dict(
1211
                etag='some-etag',
1212
                content_encoding='some content_encoding',
1213
                content_type='some content-type',
1214
                content_disposition='some content_disposition',
1215
                public=True,
1216
                sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
1217
        r = self.client.create_object_by_manifestation(obj)
1218
        self.assert_dicts_are_equal(r, FR.headers)
1219
        expected = dict(content_length=0, manifest=manifest)
1220
        for k in kwargs:
1221
            expected['permissions' if k == 'sharing' else k] = None
1222
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1223
        r = self.client.create_object_by_manifestation(obj, **kwargs)
1224
        self.assert_dicts_are_equal(r, FR.headers)
1225
        expected.update(kwargs)
1226
        expected['permissions'] = expected.pop('sharing')
1227
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
1228

    
1229
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1230
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1231
    def test_download_to_string(self, GET, GOH):
1232
        FR.content = 'some sample content'
1233
        num_of_blocks = len(object_hashmap['hashes'])
1234
        r = self.client.download_to_string(obj)
1235
        expected_content = FR.content * num_of_blocks
1236
        self.assertEqual(expected_content, r)
1237
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1238
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1239

    
1240
        kwargs = dict(
1241
            version='version',
1242
            range_str='10-20',
1243
            if_match='if and only if',
1244
            if_none_match='if and only not',
1245
            if_modified_since='what if not?',
1246
            if_unmodified_since='this happens if not!')
1247
        expargs = dict(kwargs)
1248
        expargs.pop('range_str')
1249
        for k in expargs:
1250
            expargs[k] = None
1251
        GOH.assert_called_once_with(obj, **expargs)
1252

    
1253
        r = self.client.download_to_string(obj, **kwargs)
1254
        expargs['data_range'] = 'bytes=%s' % kwargs['range_str']
1255
        for k, v in expargs.items():
1256
            self.assertEqual(
1257
                GET.mock_calls[-1][2][k],
1258
                v or kwargs.get(k))
1259

    
1260
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
1261
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1262
    def test_download_object(self, GET, GOH):
1263
        num_of_blocks = 8
1264
        tmpFile = self._create_temp_file(num_of_blocks)
1265
        FR.content = tmpFile.read(4 * 1024 * 1024)
1266
        tmpFile = self._create_temp_file(num_of_blocks)
1267
        num_of_blocks = len(object_hashmap['hashes'])
1268
        kwargs = dict(
1269
            resume=True,
1270
            version='version',
1271
            range_str='10-20',
1272
            if_match='if and only if',
1273
            if_none_match='if and only not',
1274
            if_modified_since='what if not?',
1275
            if_unmodified_since='this happens if not!',
1276
            async_headers=dict(Range='bytes=0-88888888'))
1277

    
1278
        self.client.download_object(obj, tmpFile)
1279
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
1280
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
1281
        for k, v in kwargs.items():
1282
            if k == 'async_headers':
1283
                self.assertTrue('Range' in GET.mock_calls[-1][2][k])
1284
            elif k in ('resume', 'range_str'):
1285
                continue
1286
            else:
1287
                self.assertEqual(GET.mock_calls[-1][2][k], None)
1288

    
1289
        #  Check ranges are consecutive
1290
        starts = []
1291
        ends = []
1292
        for c in GET.mock_calls:
1293
            rng_str = c[2]['async_headers']['Range']
1294
            (start, rng_str) = rng_str.split('=')
1295
            (start, end) = rng_str.split('-')
1296
            starts.append(start)
1297
            ends.append(end)
1298
        ends = sorted(ends)
1299
        for i, start in enumerate(sorted(starts)):
1300
            if i:
1301
                int(ends[i - 1]) == int(start) - 1
1302

    
1303
        #  With progress bars
1304
        try:
1305
            from progress.bar import ShadyBar
1306
            dl_bar = ShadyBar('Mock dl')
1307
        except ImportError:
1308
            dl_bar = None
1309

    
1310
        if dl_bar:
1311

    
1312
            def blck_gen(n):
1313
                for i in dl_bar.iter(range(n)):
1314
                    yield
1315
                yield
1316

    
1317
            tmpFile.seek(0)
1318
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
1319
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
1320

    
1321
        tmpFile.seek(0)
1322
        kwargs.pop('async_headers')
1323
        kwargs.pop('resume')
1324
        self.client.download_object(obj, tmpFile, **kwargs)
1325
        for k, v in kwargs.items():
1326
            if k == 'range_str':
1327
                self.assertEqual(
1328
                    GET.mock_calls[-1][2]['data_range'],
1329
                    'bytes=%s' % v)
1330
            else:
1331
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1332

    
1333
        #  ALl options on no tty
1334
        def foo():
1335
            return True
1336

    
1337
        tmpFile.seek(0)
1338
        tmpFile.isatty = foo
1339
        self.client.download_object(obj, tmpFile, **kwargs)
1340
        for k, v in kwargs.items():
1341
            if k == 'range_str':
1342
                self.assertTrue('data_range' in GET.mock_calls[-1][2])
1343
            else:
1344
                self.assertEqual(GET.mock_calls[-1][2][k], v)
1345

    
1346
    def test_get_object_hashmap(self):
1347
        FR.json = object_hashmap
1348
        for empty in (304, 412):
1349
            with patch.object(
1350
                    pithos.PithosClient, 'object_get',
1351
                    side_effect=ClientError('Empty', status=empty)):
1352
                r = self.client.get_object_hashmap(obj)
1353
                self.assertEqual(r, {})
1354
        exp_args = dict(
1355
            hashmap=True,
1356
            data_range=None,
1357
            version=None,
1358
            if_etag_match=None,
1359
            if_etag_not_match=None,
1360
            if_modified_since=None,
1361
            if_unmodified_since=None)
1362
        kwargs = dict(
1363
            version='s0m3v3r51on',
1364
            if_match='if match',
1365
            if_none_match='if non match',
1366
            if_modified_since='some date here',
1367
            if_unmodified_since='some date here',
1368
            data_range='10-20')
1369
        with patch.object(
1370
                pithos.PithosClient, 'object_get',
1371
                return_value=FR()) as get:
1372
            r = self.client.get_object_hashmap(obj)
1373
            self.assertEqual(r, object_hashmap)
1374
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1375
            r = self.client.get_object_hashmap(obj, **kwargs)
1376
            exp_args['if_etag_match'] = kwargs.pop('if_match')
1377
            exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
1378
            exp_args.update(kwargs)
1379
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
1380

    
1381
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1382
    def test_set_account_group(self, post):
1383
        (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
1384
        self.client.set_account_group(group, usernames)
1385
        post.assert_called_once_with(update=True, groups={group: usernames})
1386

    
1387
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1388
    def test_del_account_group(self, post):
1389
        group = 'aU53rGr0up'
1390
        self.client.del_account_group(group)
1391
        post.assert_called_once_with(update=True, groups={group: []})
1392

    
1393
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1394
    def test_get_account_quota(self, GAI):
1395
        key = 'x-account-policy-quota'
1396
        r = self.client.get_account_quota()
1397
        GAI.assert_called_once_with()
1398
        self.assertEqual(r[key], account_info[key])
1399

    
1400
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
1401
    def test_get_account_versioning(self, GAI):
1402
        key = 'x-account-policy-versioning'
1403
        r = self.client.get_account_versioning()
1404
        GAI.assert_called_once_with()
1405
        self.assertEqual(r[key], account_info[key])
1406

    
1407
    def test_get_account_meta(self):
1408
        key = 'x-account-meta-'
1409
        with patch.object(
1410
                pithos.PithosClient, 'get_account_info',
1411
                return_value=account_info):
1412
            r = self.client.get_account_meta()
1413
            keys = [k for k in r if k.startswith(key)]
1414
            self.assertFalse(keys)
1415
        acc_info = dict(account_info)
1416
        acc_info['%sk1' % key] = 'v1'
1417
        acc_info['%sk2' % key] = 'v2'
1418
        acc_info['%sk3' % key] = 'v3'
1419
        with patch.object(
1420
                pithos.PithosClient, 'get_account_info',
1421
                return_value=acc_info):
1422
            r = self.client.get_account_meta()
1423
            for k in [k for k in acc_info if k.startswith(key)]:
1424
                self.assertEqual(r[k], acc_info[k])
1425

    
1426
    def test_get_account_group(self):
1427
        key = 'x-account-group-'
1428
        with patch.object(
1429
                pithos.PithosClient, 'get_account_info',
1430
                return_value=account_info):
1431
            r = self.client.get_account_group()
1432
            keys = [k for k in r if k.startswith(key)]
1433
            self.assertFalse(keys)
1434
        acc_info = dict(account_info)
1435
        acc_info['%sk1' % key] = 'g1'
1436
        acc_info['%sk2' % key] = 'g2'
1437
        acc_info['%sk3' % key] = 'g3'
1438
        with patch.object(
1439
                pithos.PithosClient, 'get_account_info',
1440
                return_value=acc_info):
1441
            r = self.client.get_account_group()
1442
            for k in [k for k in acc_info if k.startswith(key)]:
1443
                self.assertEqual(r[k], acc_info[k])
1444

    
1445
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1446
    def test_set_account_meta(self, post):
1447
        metas = dict(k1='v1', k2='v2', k3='v3')
1448
        self.client.set_account_meta(metas)
1449
        post.assert_called_once_with(update=True, metadata=metas)
1450

    
1451
    """
1452
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1453
    def test_set_account_quota(self, post):
1454
        qu = 1024
1455
        self.client.set_account_quota(qu)
1456
        post.assert_called_once_with(update=True, quota=qu)
1457
    """
1458

    
1459
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
1460
    def test_set_account_versioning(self, post):
1461
        vrs = 'n3wV3r51on1ngTyp3'
1462
        self.client.set_account_versioning(vrs)
1463
        post.assert_called_once_with(update=True, versioning=vrs)
1464

    
1465
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
1466
    def test_del_container(self, delete):
1467
        for kwarg in (
1468
                dict(delimiter=None, until=None),
1469
                dict(delimiter='X', until='50m3d473')):
1470
            self.client.del_container(**kwarg)
1471
            expected = dict(kwarg)
1472
            expected['success'] = (204, 404, 409)
1473
            self.assertEqual(delete.mock_calls[-1], call(**expected))
1474
        for status_code in (404, 409):
1475
            FR.status_code = status_code
1476
            self.assertRaises(ClientError, self.client.del_container)
1477

    
1478
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1479
    def test_get_container_versioning(self, GCI):
1480
        key = 'x-container-policy-versioning'
1481
        cont = 'c0n7-417'
1482
        bu_cnt = self.client.container
1483
        for container in (None, cont):
1484
            r = self.client.get_container_versioning(container=container)
1485
            self.assertEqual(r[key], container_info[key])
1486
            self.assertEqual(GCI.mock_calls[-1], call())
1487
            self.assertEqual(bu_cnt, self.client.container)
1488

    
1489
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1490
    def test_get_container_limit(self, GCI):
1491
        key = 'x-container-policy-quota'
1492
        cont = 'c0n7-417'
1493
        bu_cnt = self.client.container
1494
        for container in (None, cont):
1495
            r = self.client.get_container_limit(container=container)
1496
            self.assertEqual(r[key], container_info[key])
1497
            self.assertEqual(GCI.mock_calls[-1], call())
1498
            self.assertEqual(bu_cnt, self.client.container)
1499

    
1500
    def test_get_container_meta(self):
1501
        somedate = '50m3d473'
1502
        key = 'x-container-meta'
1503
        metaval = '50m3m374v41'
1504
        container_plus = dict(container_info)
1505
        container_plus[key] = metaval
1506
        for ret in ((container_info, {}), (container_plus, {key: metaval})):
1507
            with patch.object(
1508
                    pithos.PithosClient,
1509
                    'get_container_info',
1510
                    return_value=ret[0]) as GCI:
1511
                for until in (None, somedate):
1512
                    r = self.client.get_container_meta(until=until)
1513
                    self.assertEqual(r, ret[1])
1514
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1515

    
1516
    def test_get_container_object_meta(self):
1517
        somedate = '50m3d473'
1518
        key = 'x-container-object-meta'
1519
        metaval = '50m3m374v41'
1520
        container_plus = dict(container_info)
1521
        container_plus[key] = metaval
1522
        for ret in (
1523
                (container_info, {key: ''}),
1524
                (container_plus, {key: metaval})):
1525
            with patch.object(
1526
                    pithos.PithosClient,
1527
                    'get_container_info',
1528
                    return_value=ret[0]) as GCI:
1529
                for until in (None, somedate):
1530
                    r = self.client.get_container_object_meta(until=until)
1531
                    self.assertEqual(r, ret[1])
1532
                    self.assertEqual(GCI.mock_calls[-1], call(until=until))
1533

    
1534
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1535
    def test_set_container_meta(self, post):
1536
        metas = dict(k1='v1', k2='v2', k3='v3')
1537
        self.client.set_container_meta(metas)
1538
        post.assert_called_once_with(update=True, metadata=metas)
1539

    
1540
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1541
    def test_del_container_meta(self, AP):
1542
        self.client.del_container_meta('somekey')
1543
        AP.assert_called_once_with(update=True, metadata={'somekey': ''})
1544

    
1545
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1546
    def test_set_container_limit(self, post):
1547
        qu = 1024
1548
        self.client.set_container_limit(qu)
1549
        post.assert_called_once_with(update=True, quota=qu)
1550

    
1551
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
1552
    def test_set_container_versioning(self, post):
1553
        vrs = 'n3wV3r51on1ngTyp3'
1554
        self.client.set_container_versioning(vrs)
1555
        post.assert_called_once_with(update=True, versioning=vrs)
1556

    
1557
    @patch('%s.object_delete' % pithos_pkg, return_value=FR())
1558
    def test_del_object(self, delete):
1559
        for kwarg in (
1560
                dict(delimiter=None, until=None),
1561
                dict(delimiter='X', until='50m3d473')):
1562
            self.client.del_object(obj, **kwarg)
1563
            self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
1564

    
1565
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1566
    def test_set_object_meta(self, post):
1567
        metas = dict(k1='v1', k2='v2', k3='v3')
1568
        self.assertRaises(
1569
            AssertionError,
1570
            self.client.set_object_meta,
1571
            obj, 'Non dict arg')
1572
        self.client.set_object_meta(obj, metas)
1573
        post.assert_called_once_with(obj, update=True, metadata=metas)
1574

    
1575
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1576
    def test_publish_object(self, post):
1577
        oinfo = dict(object_info)
1578
        val = 'pubL1c'
1579
        oinfo['x-object-public'] = val
1580
        with patch.object(
1581
                pithos.PithosClient, 'get_object_info',
1582
                return_value=oinfo) as GOF:
1583
            r = self.client.publish_object(obj)
1584
            self.assertEqual(
1585
                post.mock_calls[-1],
1586
                call(obj, public=True, update=True))
1587
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1588
            self.assertEqual(r, '%s%s' % (self.url[:-6], val))
1589

    
1590
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1591
    def test_unpublish_object(self, post):
1592
        self.client.unpublish_object(obj)
1593
        post.assert_called_once_with(obj, public=False, update=True)
1594

    
1595
    def test_get_object_sharing(self):
1596
        info = dict(object_info)
1597
        expected = dict(read='u1,g1,u2', write='u1')
1598
        info['x-object-sharing'] = '; '.join(
1599
            ['%s=%s' % (k, v) for k, v in expected.items()])
1600
        with patch.object(
1601
                pithos.PithosClient, 'get_object_info',
1602
                return_value=info) as GOF:
1603
            r = self.client.get_object_sharing(obj)
1604
            self.assertEqual(GOF.mock_calls[-1], call(obj))
1605
            self.assert_dicts_are_equal(r, expected)
1606
            info['x-object-sharing'] = '//'.join(
1607
                ['%s=%s' % (k, v) for k, v in expected.items()])
1608
            self.assertRaises(
1609
                ValueError,
1610
                self.client.get_object_sharing,
1611
                obj)
1612
            info['x-object-sharing'] = '; '.join(
1613
                ['%s:%s' % (k, v) for k, v in expected.items()])
1614
            self.assertRaises(
1615
                ClientError,
1616
                self.client.get_object_sharing,
1617
                obj)
1618
            info['x-object-sharing'] = 'read=%s' % expected['read']
1619
            r = self.client.get_object_sharing(obj)
1620
            expected.pop('write')
1621
            self.assert_dicts_are_equal(r, expected)
1622

    
1623
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1624
    def test_set_object_sharing(self, OP):
1625
        read_perms = ['u1', 'g1', 'u2', 'g2']
1626
        write_perms = ['u1', 'g1']
1627
        for kwargs in (
1628
                dict(read_permition=read_perms, write_permition=write_perms),
1629
                dict(read_permition=read_perms),
1630
                dict(write_permition=write_perms),
1631
                dict()):
1632
            self.client.set_object_sharing(obj, **kwargs)
1633
            kwargs['read'] = kwargs.pop('read_permition', '')
1634
            kwargs['write'] = kwargs.pop('write_permition', '')
1635
            self.assertEqual(
1636
                OP.mock_calls[-1],
1637
                call(obj, update=True, permissions=kwargs))
1638

    
1639
    @patch('%s.set_object_sharing' % pithos_pkg)
1640
    def test_del_object_sharing(self, SOS):
1641
        self.client.del_object_sharing(obj)
1642
        SOS.assert_called_once_with(obj)
1643

    
1644
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1645
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1646
    def test_append_object(self, post, GCI):
1647
        num_of_blocks = 4
1648
        tmpFile = self._create_temp_file(num_of_blocks)
1649
        tmpFile.seek(0, 2)
1650
        file_size = tmpFile.tell()
1651
        for turn in range(2):
1652
            tmpFile.seek(0, 0)
1653

    
1654
            try:
1655
                from progress.bar import ShadyBar
1656
                apn_bar = ShadyBar('Mock append')
1657
            except ImportError:
1658
                apn_bar = None
1659

    
1660
            if apn_bar:
1661

    
1662
                def append_gen(n):
1663
                    for i in apn_bar.iter(range(n)):
1664
                        yield
1665
                    yield
1666

    
1667
            else:
1668
                append_gen = None
1669

    
1670
            self.client.append_object(
1671
                obj, tmpFile,
1672
                upload_cb=append_gen if turn else None)
1673
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1674
            (args, kwargs) = post.mock_calls[-1][1:3]
1675
            self.assertEqual(args, (obj,))
1676
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1677
            fsize = num_of_blocks * int(kwargs['content_length'])
1678
            self.assertEqual(fsize, file_size)
1679
            self.assertEqual(kwargs['content_range'], 'bytes */*')
1680
            exp = 'application/octet-stream'
1681
            self.assertEqual(kwargs['content_type'], exp)
1682
            self.assertEqual(kwargs['update'], True)
1683

    
1684
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1685
    def test_truncate_object(self, post):
1686
        upto_bytes = 377
1687
        self.client.truncate_object(obj, upto_bytes)
1688
        post.assert_called_once_with(
1689
            obj,
1690
            update=True,
1691
            object_bytes=upto_bytes,
1692
            content_range='bytes 0-%s/*' % upto_bytes,
1693
            content_type='application/octet-stream',
1694
            source_object='/%s/%s' % (self.client.container, obj))
1695

    
1696
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1697
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1698
    def test_overwrite_object(self, post, GCI):
1699
        num_of_blocks = 4
1700
        tmpFile = self._create_temp_file(num_of_blocks)
1701
        tmpFile.seek(0, 2)
1702
        file_size = tmpFile.tell()
1703
        info = dict(object_info)
1704
        info['content-length'] = file_size
1705
        block_size = container_info['x-container-block-size']
1706
        with patch.object(
1707
                pithos.PithosClient, 'get_object_info',
1708
                return_value=info) as GOI:
1709
            for start, end in (
1710
                    (0, file_size + 1),
1711
                    (file_size + 1, file_size + 2)):
1712
                tmpFile.seek(0, 0)
1713
                self.assertRaises(
1714
                    ClientError,
1715
                    self.client.overwrite_object,
1716
                    obj, start, end, tmpFile)
1717
            for start, end in ((0, 144), (144, 233), (233, file_size)):
1718
                tmpFile.seek(0, 0)
1719
                owr_gen = None
1720
                exp_size = end - start + 1
1721
                if not start or exp_size > block_size:
1722
                    try:
1723
                        from progress.bar import ShadyBar
1724
                        owr_bar = ShadyBar('Mock append')
1725
                    except ImportError:
1726
                        owr_bar = None
1727

    
1728
                    if owr_bar:
1729

    
1730
                        def owr_gen(n):
1731
                            for i in owr_bar.iter(range(n)):
1732
                                yield
1733
                            yield
1734

    
1735
                    if exp_size > block_size:
1736
                        exp_size = exp_size % block_size or block_size
1737

    
1738
                self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1739
                self.assertEqual(GOI.mock_calls[-1], call(obj))
1740
                self.assertEqual(GCI.mock_calls[-1], call())
1741
                (args, kwargs) = post.mock_calls[-1][1:3]
1742
                self.assertEqual(args, (obj,))
1743
                self.assertEqual(len(kwargs['data']), exp_size)
1744
                self.assertEqual(kwargs['content_length'], exp_size)
1745
                self.assertEqual(kwargs['update'], True)
1746
                exp = 'application/octet-stream'
1747
                self.assertEqual(kwargs['content_type'], exp)
1748

    
1749
    @patch('%s.set_param' % pithos_pkg)
1750
    @patch('%s.get' % pithos_pkg, return_value=FR())
1751
    def test_get_sharing_accounts(self, get, SP):
1752
        FR.json = sharers
1753
        for kws in (
1754
                dict(),
1755
                dict(limit='50m3-11m17'),
1756
                dict(marker='X'),
1757
                dict(limit='50m3-11m17', marker='X')):
1758
            r = self.client.get_sharing_accounts(**kws)
1759
            self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1760
            self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1761
            limit, marker = kws.get('limit', None), kws.get('marker', None)
1762
            self.assertEqual(SP.mock_calls[-2], call(
1763
                'limit', limit,
1764
                iff=limit is not None))
1765
            self.assertEqual(SP.mock_calls[-1], call(
1766
                'marker', marker,
1767
                iff=marker is not None))
1768
            for i in range(len(r)):
1769
                self.assert_dicts_are_equal(r[i], sharers[i])
1770

    
1771
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1772
    def test_get_object_versionlist(self, get):
1773
        info = dict(object_info)
1774
        info['versions'] = ['v1', 'v2']
1775
        FR.json = info
1776
        r = self.client.get_object_versionlist(obj)
1777
        get.assert_called_once_with(obj, format='json', version='list')
1778
        self.assertEqual(r, info['versions'])
1779

    
1780
if __name__ == '__main__':
1781
    from sys import argv
1782
    from kamaki.clients.test import runTestCase
1783
    not_found = True
1784
    if not argv[1:] or argv[1] == 'PithosClient':
1785
        not_found = False
1786
        runTestCase(PithosClient, 'Pithos Client', argv[2:])
1787
    if not argv[1:] or argv[1] == 'PithosRestClient':
1788
        not_found = False
1789
        runTestCase(PithosRestClient, 'PithosRest Client', argv[2:])
1790
    if not_found:
1791
        print('TestCase %s not found' % argv[1])