Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / test / pithos.py @ 402b6f48

History | View | Annotate | Download (44.8 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

    
39
from kamaki.clients import ClientError
40
from kamaki.clients.pithos import PithosClient as PC
41
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection as C
42

    
43
client_pkg = 'kamaki.clients.Client'
44
pithos_pkg = 'kamaki.clients.pithos.PithosClient'
45

    
46
user_id = 'ac0un7-1d-5tr1ng'
47
obj = 'obj3c7N4m3'
48

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

    
141

    
142
class FR(object):
143
    """FR stands for Fake Response"""
144
    json = dict()
145
    headers = dict()
146
    content = json
147
    status = None
148
    status_code = 200
149

    
150
    def release(self):
151
        pass
152

    
153

    
154
class Pithos(TestCase):
155

    
156
    files = []
157

    
158
    def _create_temp_file(self, num_of_blocks):
159
        self.files.append(NamedTemporaryFile())
160
        tmpFile = self.files[-1]
161
        file_size = num_of_blocks * 4 * 1024 * 1024
162
        print('\n\tCreate tmp file')
163
        tmpFile.write(urandom(file_size))
164
        tmpFile.flush()
165
        tmpFile.seek(0)
166
        print('\t\tDone')
167
        return tmpFile
168

    
169
    def assert_dicts_are_equal(self, d1, d2):
170
        for k, v in d1.items():
171
            self.assertTrue(k in d2)
172
            if isinstance(v, dict):
173
                self.assert_dicts_are_equal(v, d2[k])
174
            else:
175
                self.assertEqual(unicode(v), unicode(d2[k]))
176

    
177
    def setUp(self):
178
        self.url = 'https://www.example.com/pithos'
179
        self.token = 'p17h0570k3n'
180
        self.client = PC(self.url, self.token)
181
        self.client.account = user_id
182
        self.client.container = 'c0nt@1n3r_i'
183

    
184
    def tearDown(self):
185
        FR.headers = dict()
186
        FR.status_code = 200
187
        FR.json = dict()
188
        FR.content = FR.json
189
        for f in self.files:
190
            f.close()
191

    
192
    #  Pithos+ methods that extend storage API
193

    
194
    @patch('%s.set_param' % client_pkg)
195
    def test_get_account_info(self, SP):
196
        FR.headers = account_info
197
        FR.status_code = 204
198
        with patch.object(C, 'perform_request', return_value=FR()):
199
            r = self.client.get_account_info()
200
            self.assertEqual(self.client.http_client.url, self.url)
201
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
202
            self.assert_dicts_are_equal(r, account_info)
203
            untils = ['date 1', 'date 2', 'date 3']
204
            for unt in untils:
205
                r = self.client.get_account_info(until=unt)
206
                self.assert_dicts_are_equal(r, account_info)
207
            for i in range(len(untils)):
208
                self.assertEqual(
209
                    PC.set_param.mock_calls[i + 1],
210
                    call('until', untils[i], iff=untils[i]))
211
            FR.status_code = 401
212
            self.assertRaises(ClientError, self.client.get_account_info)
213

    
214
    @patch('%s.set_header' % client_pkg)
215
    def test_replace_account_meta(self, SH):
216
        FR.status_code = 202
217
        metas = dict(k1='v1', k2='v2', k3='v3')
218
        with patch.object(C, 'perform_request', return_value=FR()):
219
            self.client.replace_account_meta(metas)
220
            self.assertEqual(self.client.http_client.url, self.url)
221
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
222
            prfx = 'X-Account-Meta-'
223
            expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
224
            self.assertEqual(PC.set_header.mock_calls, expected)
225

    
226
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
227
    def test_del_account_meta(self, ap):
228
        keys = ['k1', 'k2', 'k3']
229
        expected = []
230
        for key in keys:
231
            self.client.del_account_meta(key)
232
            expected.append(call(update=True, metadata={key: ''}))
233
        self.assertEqual(ap.mock_calls, expected)
234

    
235
    @patch('%s.put' % pithos_pkg, return_value=FR())
236
    def test_create_container(self, put):
237
        FR.status_code = 201
238
        cont = 's0m3c0n731n3r'
239
        self.client.create_container(cont)
240
        expected = [call('/%s/%s' % (user_id, cont), success=(201, 202))]
241
        self.assertEqual(put.mock_calls, expected)
242
        FR.status_code = 202
243
        self.assertRaises(ClientError, self.client.create_container, cont)
244

    
245
    @patch('%s.container_head' % pithos_pkg, return_value=FR())
246
    def test_get_container_info(self, ch):
247
        FR.headers = container_info
248
        r = self.client.get_container_info()
249
        self.assert_dicts_are_equal(r, container_info)
250
        u = 'some date'
251
        r = self.client.get_container_info(until=u)
252
        self.assertEqual(ch.mock_calls, [call(until=None), call(until=u)])
253

    
254
    @patch('%s.delete' % pithos_pkg, return_value=FR())
255
    def test_delete_container(self, delete):
256
        FR.status_code = 204
257
        cont = 's0m3c0n731n3r'
258
        self.client.delete_container(cont)
259
        for err_code in (404, 409):
260
            FR.status_code = err_code
261
            self.assertRaises(ClientError, self.client.delete_container, cont)
262
        acall = call('/%s/%s' % (user_id, cont), success=(204, 404, 409))
263
        self.assertEqual(delete.mock_calls, [acall] * 3)
264

    
265
    @patch('%s.account_get' % pithos_pkg, return_value=FR())
266
    def test_list_containers(self, get):
267
        FR.json = container_list
268
        r = self.client.list_containers()
269
        for i in range(len(r)):
270
            self.assert_dicts_are_equal(r[i], container_list[i])
271

    
272
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
273
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
274
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
275
    def test_upload_object(self, CI, CP, OP):
276
        num_of_blocks = 8
277
        tmpFile = self._create_temp_file(num_of_blocks)
278

    
279
        # Without kwargs
280
        self.client.upload_object(obj, tmpFile)
281
        self.assertEqual(PC.get_container_info.mock_calls, [call()])
282
        [call1, call2] = PC.object_put.mock_calls
283

    
284
        (args1, kwargs1) = call1[1:3]
285
        (args2, kwargs2) = call2[1:3]
286
        self.assertEqual(args1, (obj,))
287
        expected1 = dict(
288
            hashmap=True,
289
            success=(201, 409),
290
            format='json',
291
            json=dict(
292
                hashes=['s0m3h@5h'] * num_of_blocks,
293
                bytes=num_of_blocks * 4 * 1024 * 1024),
294
            etag=None,
295
            content_encoding=None,
296
            content_type='application/octet-stream',
297
            content_disposition=None,
298
            public=None,
299
            permissions=None)
300
        for k, v in expected1.items():
301
            if k == 'json':
302
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
303
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
304
            else:
305
                self.assertEqual(v, kwargs1[k])
306

    
307
        (args2, kwargs2) = call2[1:3]
308
        self.assertEqual(args2, (obj,))
309
        expected2 = dict(
310
            json=dict(
311
                hashes=['s0m3h@5h'] * num_of_blocks,
312
                bytes=num_of_blocks * 4 * 1024 * 1024),
313
            content_type='application/octet-stream',
314
            hashmap=True,
315
            success=201,
316
            format='json')
317
        for k, v in expected2.items():
318
            if k == 'json':
319
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
320
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
321
            else:
322
                self.assertEqual(v, kwargs2[k])
323

    
324
        OP = PC.object_put
325
        mock_offset = 2
326

    
327
        #  With progress bars
328
        try:
329
            from progress.bar import ShadyBar
330
            blck_bar = ShadyBar('Mock blck calc.')
331
            upld_bar = ShadyBar('Mock uplds')
332
        except ImportError:
333
            blck_bar = None
334
            upld_bar = None
335

    
336
        if blck_bar and upld_bar:
337

    
338
            def blck_gen(n):
339
                for i in blck_bar.iter(range(n)):
340
                    yield
341
                yield
342

    
343
            def upld_gen(n):
344
                for i in upld_bar.iter(range(n)):
345
                    yield
346
                yield
347

    
348
            tmpFile.seek(0)
349
            self.client.upload_object(
350
                obj, tmpFile,
351
                hash_cb=blck_gen, upload_cb=upld_gen)
352

    
353
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
354
                self.assertEqual(OP.mock_calls[i], c)
355

    
356
        #  With content-type
357
        tmpFile.seek(0)
358
        ctype = 'video/mpeg'
359
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
360
        self.client.upload_object(obj, tmpFile,
361
            content_type=ctype, sharing=sharing)
362
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
363
        self.assert_dicts_are_equal(
364
            OP.mock_calls[-2][2]['permissions'],
365
            sharing)
366

    
367
        # With other args
368
        tmpFile.seek(0)
369
        kwargs = dict(
370
            etag='s0m3E74g',
371
            content_type=ctype,
372
            content_disposition=ctype + 'd15p051710n',
373
            public=True,
374
            content_encoding='802.11')
375
        self.client.upload_object(obj, tmpFile, **kwargs)
376
        for arg, val in kwargs.items():
377
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
378

    
379
    @patch('%s.put' % pithos_pkg, return_value=FR())
380
    @patch('%s.set_header' % client_pkg)
381
    def test_create_object(self, SH, put):
382
        cont = self.client.container
383
        ctype = 'c0n73n7/typ3'
384
        exp_shd = [
385
            call('Content-Type', 'application/octet-stream'),
386
            call('Content-length', '0'),
387
            call('Content-Type', ctype), call('Content-length', '42')]
388
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)] * 2
389
        self.client.create_object(obj)
390
        self.client.create_object(obj, content_type=ctype, content_length=42)
391
        self.assertEqual(PC.set_header.mock_calls, exp_shd)
392
        self.assertEqual(put.mock_calls, exp_put)
393

    
394
    @patch('%s.put' % pithos_pkg, return_value=FR())
395
    @patch('%s.set_header' % client_pkg)
396
    def test_create_directory(self, SH, put):
397
        cont = self.client.container
398
        exp_shd = [
399
            call('Content-Type', 'application/directory'),
400
            call('Content-length', '0')]
401
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)]
402
        self.client.create_directory(obj)
403
        self.assertEqual(PC.set_header.mock_calls, exp_shd)
404
        self.assertEqual(put.mock_calls, exp_put)
405

    
406
    def test_get_object_info(self):
407
        FR.headers = object_info
408
        version = 'v3r510n'
409
        with patch.object(PC, 'object_head', return_value=FR()) as head:
410
            r = self.client.get_object_info(obj)
411
            self.assertEqual(r, object_info)
412
            r = self.client.get_object_info(obj, version=version)
413
            self.assertEqual(head.mock_calls, [
414
                call(obj, version=None),
415
                call(obj, version=version)])
416
        with patch.object(
417
                PC,
418
                'object_head',
419
                side_effect=ClientError('Obj not found', 404)):
420
            self.assertRaises(
421
                ClientError,
422
                self.client.get_object_info,
423
                obj, version=version)
424

    
425
    @patch('%s.get_object_info' % pithos_pkg, return_value=object_info)
426
    def test_get_object_meta(self, GOI):
427
        expected = dict()
428
        for k, v in object_info.items():
429
            expected[k] = v
430
        r = self.client.get_object_meta(obj)
431
        self.assert_dicts_are_equal(r, expected)
432

    
433
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
434
    def test_del_object_meta(self, post):
435
        metakey = '50m3m3t4k3y'
436
        self.client.del_object_meta(obj, metakey)
437
        expected = call(obj, update=True, metadata={metakey: ''})
438
        self.assertEqual(post.mock_calls[-1], expected)
439

    
440
    @patch('%s.post' % client_pkg, return_value=FR())
441
    @patch('%s.set_header' % client_pkg)
442
    def test_replace_object_meta(self, SH, post):
443
        metas = dict(k1='new1', k2='new2', k3='new3')
444
        cont = self.client.container
445
        self.client.replace_object_meta(metas)
446
        expected = call('/%s/%s' % (user_id, cont), success=202)
447
        self.assertEqual(post.mock_calls[-1], expected)
448
        prfx = 'X-Object-Meta-'
449
        expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
450
        self.assertEqual(PC.set_header.mock_calls, expected)
451

    
452
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
453
    def test_copy_object(self, put):
454
        src_cont = 'src-c0nt41n3r'
455
        src_obj = 'src-0bj'
456
        dst_cont = 'dst-c0nt41n3r'
457
        dst_obj = 'dst-0bj'
458
        expected = call(
459
            src_obj,
460
            content_length=0,
461
            source_account=None,
462
            success=201,
463
            copy_from='/%s/%s' % (src_cont, src_obj),
464
            delimiter=None,
465
            content_type=None,
466
            source_version=None,
467
            public=False)
468
        self.client.copy_object(src_cont, src_obj, dst_cont)
469
        self.assertEqual(put.mock_calls[-1], expected)
470
        self.client.copy_object(src_cont, src_obj, dst_cont, dst_obj)
471
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
472
        kwargs = dict(
473
            source_version='src-v3r510n',
474
            source_account='src-4cc0un7',
475
            public=True,
476
            content_type='c0n73n7Typ3',
477
            delimiter='5')
478
        self.client.copy_object(src_cont, src_obj, dst_cont, **kwargs)
479
        for k, v in kwargs.items():
480
            self.assertEqual(v, put.mock_calls[-1][2][k])
481

    
482
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
483
    def test_move_object(self, put):
484
        src_cont = 'src-c0nt41n3r'
485
        src_obj = 'src-0bj'
486
        dst_cont = 'dst-c0nt41n3r'
487
        dst_obj = 'dst-0bj'
488
        expected = call(
489
            src_obj,
490
            content_length=0,
491
            source_account=None,
492
            success=201,
493
            move_from='/%s/%s' % (src_cont, src_obj),
494
            delimiter=None,
495
            content_type=None,
496
            source_version=None,
497
            public=False)
498
        self.client.move_object(src_cont, src_obj, dst_cont)
499
        self.assertEqual(put.mock_calls[-1], expected)
500
        self.client.move_object(src_cont, src_obj, dst_cont, dst_obj)
501
        self.assertEqual(put.mock_calls[-1][1], (dst_obj,))
502
        kwargs = dict(
503
            source_version='src-v3r510n',
504
            source_account='src-4cc0un7',
505
            public=True,
506
            content_type='c0n73n7Typ3',
507
            delimiter='5')
508
        self.client.move_object(src_cont, src_obj, dst_cont, **kwargs)
509
        for k, v in kwargs.items():
510
            self.assertEqual(v, put.mock_calls[-1][2][k])
511

    
512
    @patch('%s.delete' % client_pkg, return_value=FR())
513
    def test_delete_object(self, delete):
514
        cont = self.client.container
515
        self.client.delete_object(obj)
516
        self.assertEqual(
517
            delete.mock_calls[-1],
518
            call('/%s/%s/%s' % (user_id, cont, obj), success=(204, 404)))
519
        FR.status_code = 404
520
        self.assertRaises(ClientError, self.client.delete_object, obj)
521

    
522
    @patch('%s.get' % client_pkg, return_value=FR())
523
    @patch('%s.set_param' % client_pkg)
524
    def test_list_objects(self, SP, get):
525
        FR.json = object_list
526
        acc = self.client.account
527
        cont = self.client.container
528
        SP = PC.set_param
529
        r = self.client.list_objects()
530
        for i in range(len(r)):
531
            self.assert_dicts_are_equal(r[i], object_list[i])
532
        self.assertEqual(get.mock_calls, [
533
            call('/%s/%s' % (acc, cont), success=(200, 204, 304, 404))])
534
        self.assertEqual(SP.mock_calls, [call('format', 'json')])
535
        FR.status_code = 304
536
        self.assertEqual(self.client.list_objects(), [])
537
        FR.status_code = 404
538
        self.assertRaises(ClientError, self.client.list_objects)
539

    
540
    @patch('%s.get' % client_pkg, return_value=FR())
541
    @patch('%s.set_param' % client_pkg)
542
    def test_list_objects_in_path(self, SP, get):
543
        FR.json = object_list
544
        path = '/some/awsome/path'
545
        acc = self.client.account
546
        cont = self.client.container
547
        SP = PC.set_param
548
        self.client.list_objects_in_path(path)
549
        self.assertEqual(get.mock_calls, [
550
            call('/%s/%s' % (acc, cont), success=(200, 204, 404))])
551
        self.assertEqual(SP.mock_calls, [
552
            call('format', 'json'), call('path', path)])
553
        FR.status_code = 404
554
        self.assertRaises(ClientError, self.client.list_objects)
555

    
556
    #  Pithos+ only methods
557

    
558
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
559
    def test_purge_container(self, cd):
560
        self.client.purge_container()
561
        self.assertTrue('until' in cd.mock_calls[-1][2])
562
        cont = self.client.container
563
        self.client.purge_container('another-container')
564
        self.assertEqual(self.client.container, cont)
565

    
566
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
567
    def test_upload_object_unchunked(self, put):
568
        num_of_blocks = 8
569
        tmpFile = self._create_temp_file(num_of_blocks)
570
        expected = dict(
571
                success=201,
572
                data=num_of_blocks * 4 * 1024 * 1024,
573
                etag='some-etag',
574
                content_encoding='some content_encoding',
575
                content_type='some content-type',
576
                content_disposition='some content_disposition',
577
                public=True,
578
                permissions=dict(read=['u1', 'g1', 'u2'], write=['u1']))
579
        self.client.upload_object_unchunked(obj, tmpFile)
580
        self.assertEqual(put.mock_calls[-1][1], (obj,))
581
        self.assertEqual(
582
            sorted(put.mock_calls[-1][2].keys()),
583
            sorted(expected.keys()))
584
        kwargs = dict(expected)
585
        kwargs.pop('success')
586
        kwargs['size'] = kwargs.pop('data')
587
        kwargs['sharing'] = kwargs.pop('permissions')
588
        tmpFile.seek(0)
589
        self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
590
        pmc = put.mock_calls[-1][2]
591
        for k, v in expected.items():
592
            if k == 'data':
593
                self.assertEqual(len(pmc[k]), v)
594
            else:
595
                self.assertEqual(pmc[k], v)
596
        self.assertRaises(
597
            ClientError,
598
            self.client.upload_object_unchunked,
599
            obj, tmpFile, withHashFile=True)
600

    
601
    @patch('%s.object_put' % pithos_pkg, return_value=FR())
602
    def test_create_object_by_manifestation(self, put):
603
        manifest = '%s/%s' % (self.client.container, obj)
604
        kwargs = dict(
605
                etag='some-etag',
606
                content_encoding='some content_encoding',
607
                content_type='some content-type',
608
                content_disposition='some content_disposition',
609
                public=True,
610
                sharing=dict(read=['u1', 'g1', 'u2'], write=['u1']))
611
        self.client.create_object_by_manifestation(obj)
612
        expected = dict(content_length=0, manifest=manifest)
613
        for k in kwargs:
614
            expected['permissions' if k == 'sharing' else k] = None
615
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
616
        self.client.create_object_by_manifestation(obj, **kwargs)
617
        expected.update(kwargs)
618
        expected['permissions'] = expected.pop('sharing')
619
        self.assertEqual(put.mock_calls[-1], call(obj, **expected))
620

    
621
    @patch('%s.get_object_hashmap' % pithos_pkg, return_value=object_hashmap)
622
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
623
    def test_download_object(self, GOH, GET):
624
        num_of_blocks = 8
625
        tmpFile = self._create_temp_file(num_of_blocks)
626
        FR.content = tmpFile.read(4 * 1024 * 1024)
627
        tmpFile = self._create_temp_file(num_of_blocks)
628
        GET = PC.object_get
629
        num_of_blocks = len(object_hashmap['hashes'])
630
        kwargs = dict(
631
            resume=True,
632
            version='version',
633
            range_str='10-20',
634
            if_match='if and only if',
635
            if_none_match='if and only not',
636
            if_modified_since='what if not?',
637
            if_unmodified_since='this happens if not!',
638
            async_headers=dict(Range='bytes=0-88888888'))
639

    
640
        self.client.download_object(obj, tmpFile)
641
        self.assertEqual(len(GET.mock_calls), num_of_blocks)
642
        self.assertEqual(GET.mock_calls[-1][1], (obj,))
643
        for k, v in kwargs.items():
644
            if k == 'async_headers':
645
                self.assertTrue('Range' in GET.mock_calls[-1][2][k])
646
            elif k in ('resume', 'range_str'):
647
                continue
648
            else:
649
                self.assertEqual(GET.mock_calls[-1][2][k], None)
650

    
651
        #  Check ranges are consecutive
652
        starts = []
653
        ends = []
654
        for c in GET.mock_calls:
655
            rng_str = c[2]['async_headers']['Range']
656
            (start, rng_str) = rng_str.split('=')
657
            (start, end) = rng_str.split('-')
658
            starts.append(start)
659
            ends.append(end)
660
        ends = sorted(ends)
661
        for i, start in enumerate(sorted(starts)):
662
            if i:
663
                int(ends[i - 1]) == int(start) - 1
664

    
665
        #  With progress bars
666
        try:
667
            from progress.bar import ShadyBar
668
            dl_bar = ShadyBar('Mock dl')
669
        except ImportError:
670
            dl_bar = None
671

    
672
        if dl_bar:
673

    
674
            def blck_gen(n):
675
                for i in dl_bar.iter(range(n)):
676
                    yield
677
                yield
678

    
679
            tmpFile.seek(0)
680
            self.client.download_object(obj, tmpFile, download_cb=blck_gen)
681
            self.assertEqual(len(GET.mock_calls), 2 * num_of_blocks)
682

    
683
        tmpFile.seek(0)
684
        kwargs.pop('async_headers')
685
        kwargs.pop('resume')
686
        self.client.download_object(obj, tmpFile, **kwargs)
687
        for k, v in kwargs.items():
688
            if k == 'range_str':
689
                self.assertEqual(
690
                    GET.mock_calls[-1][2]['data_range'],
691
                    'bytes=%s' % v)
692
            else:
693
                self.assertEqual(GET.mock_calls[-1][2][k], v)
694

    
695
        #  ALl options on no tty
696

    
697
        def foo():
698
            return True
699

    
700
        tmpFile.seek(0)
701
        tmpFile.isatty = foo
702
        self.client.download_object(obj, tmpFile, **kwargs)
703
        for k, v in kwargs.items():
704
            if k == 'range_str':
705
                self.assertTrue('data_range' in GET.mock_calls[-1][2])
706
            else:
707
                self.assertEqual(GET.mock_calls[-1][2][k], v)
708

    
709
    def test_get_object_hashmap(self):
710
        FR.json = object_hashmap
711
        for empty in (304, 412):
712
            with patch.object(
713
                    PC, 'object_get',
714
                    side_effect=ClientError('Empty', status=empty)):
715
                r = self.client.get_object_hashmap(obj)
716
                self.assertEqual(r, {})
717
        exp_args = dict(
718
            hashmap=True,
719
            data_range=None,
720
            version=None,
721
            if_etag_match=None,
722
            if_etag_not_match=None,
723
            if_modified_since=None,
724
            if_unmodified_since=None)
725
        kwargs = dict(
726
            version='s0m3v3r51on',
727
            if_match='if match',
728
            if_none_match='if non match',
729
            if_modified_since='some date here',
730
            if_unmodified_since='some date here',
731
            data_range='10-20')
732
        with patch.object(PC, 'object_get', return_value=FR()) as get:
733
            r = self.client.get_object_hashmap(obj)
734
            self.assertEqual(r, object_hashmap)
735
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
736
            r = self.client.get_object_hashmap(obj, **kwargs)
737
            exp_args['if_etag_match'] = kwargs.pop('if_match')
738
            exp_args['if_etag_not_match'] = kwargs.pop('if_none_match')
739
            exp_args.update(kwargs)
740
            self.assertEqual(get.mock_calls[-1], call(obj, **exp_args))
741

    
742
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
743
    def test_set_account_group(self, post):
744
        (group, usernames) = ('aU53rGr0up', ['u1', 'u2', 'u3'])
745
        self.client.set_account_group(group, usernames)
746
        self.assertEqual(
747
            post.mock_calls[-1],
748
            call(update=True, groups={group: usernames}))
749

    
750
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
751
    def test_del_account_group(self, post):
752
        group = 'aU53rGr0up'
753
        self.client.del_account_group(group)
754
        self.assertEqual(
755
            post.mock_calls[-1],
756
            call(update=True, groups={group: []}))
757

    
758
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
759
    def test_get_account_quota(self, GAI):
760
        key = 'x-account-policy-quota'
761
        r = self.client.get_account_quota()
762
        self.assertEqual(r[key], account_info[key])
763

    
764
    @patch('%s.get_account_info' % pithos_pkg, return_value=account_info)
765
    def test_get_account_versioning(self, GAI):
766
        key = 'x-account-policy-versioning'
767
        r = self.client.get_account_versioning()
768
        self.assertEqual(r[key], account_info[key])
769

    
770
    def test_get_account_meta(self):
771
        key = 'x-account-meta-'
772
        with patch.object(PC, 'get_account_info', return_value=account_info):
773
            r = self.client.get_account_meta()
774
            keys = [k for k in r if k.startswith(key)]
775
            self.assertFalse(keys)
776
        acc_info = dict(account_info)
777
        acc_info['%sk1' % key] = 'v1'
778
        acc_info['%sk2' % key] = 'v2'
779
        acc_info['%sk3' % key] = 'v3'
780
        with patch.object(PC, 'get_account_info', return_value=acc_info):
781
            r = self.client.get_account_meta()
782
            for k in [k for k in acc_info if k.startswith(key)]:
783
                self.assertEqual(r[k], acc_info[k])
784

    
785
    def test_get_account_group(self):
786
        key = 'x-account-group-'
787
        with patch.object(PC, 'get_account_info', return_value=account_info):
788
            r = self.client.get_account_group()
789
            keys = [k for k in r if k.startswith(key)]
790
            self.assertFalse(keys)
791
        acc_info = dict(account_info)
792
        acc_info['%sk1' % key] = 'g1'
793
        acc_info['%sk2' % key] = 'g2'
794
        acc_info['%sk3' % key] = 'g3'
795
        with patch.object(PC, 'get_account_info', return_value=acc_info):
796
            r = self.client.get_account_group()
797
            for k in [k for k in acc_info if k.startswith(key)]:
798
                self.assertEqual(r[k], acc_info[k])
799

    
800
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
801
    def test_set_account_meta(self, post):
802
        metas = dict(k1='v1', k2='v2', k3='v3')
803
        self.client.set_account_meta(metas)
804
        self.assertEqual(
805
            post.mock_calls[-1],
806
            call(update=True, metadata=metas))
807

    
808
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
809
    def test_set_account_quota(self, post):
810
        qu = 1024
811
        self.client.set_account_quota(qu)
812
        self.assertEqual(post.mock_calls[-1], call(update=True, quota=qu))
813

    
814
    @patch('%s.account_post' % pithos_pkg, return_value=FR())
815
    def test_set_account_versioning(self, post):
816
        vrs = 'n3wV3r51on1ngTyp3'
817
        self.client.set_account_versioning(vrs)
818
        self.assertEqual(
819
            post.mock_calls[-1],
820
            call(update=True, versioning=vrs))
821

    
822
    @patch('%s.container_delete' % pithos_pkg, return_value=FR())
823
    def test_del_container(self, delete):
824
        for kwarg in (
825
                dict(delimiter=None, until=None),
826
                dict(delimiter='X', until='50m3d473')):
827
            self.client.del_container(**kwarg)
828
            expected = dict(kwarg)
829
            expected['success'] = (204, 404, 409)
830
            self.assertEqual(delete.mock_calls[-1], call(**expected))
831
        for status_code in (404, 409):
832
            FR.status_code = status_code
833
            self.assertRaises(ClientError, self.client.del_container)
834

    
835
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
836
    def test_get_container_versioning(self, GCI):
837
        key = 'x-container-policy-versioning'
838
        cont = 'c0n7-417'
839
        bu_cnt = self.client.container
840
        for container in (None, cont):
841
            r = self.client.get_container_versioning(container=container)
842
            self.assertEqual(r[key], container_info[key])
843
            self.assertEqual(GCI.mock_calls[-1], call())
844
            self.assertEqual(bu_cnt, self.client.container)
845

    
846
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
847
    def test_get_container_quota(self, GCI):
848
        key = 'x-container-policy-quota'
849
        cont = 'c0n7-417'
850
        bu_cnt = self.client.container
851
        for container in (None, cont):
852
            r = self.client.get_container_quota(container=container)
853
            self.assertEqual(r[key], container_info[key])
854
            self.assertEqual(GCI.mock_calls[-1], call())
855
            self.assertEqual(bu_cnt, self.client.container)
856

    
857
    def test_get_container_meta(self):
858
        somedate = '50m3d473'
859
        key = 'x-container-meta'
860
        metaval = '50m3m374v41'
861
        container_plus = dict(container_info)
862
        container_plus[key] = metaval
863
        for ret in ((container_info, {}), (container_plus, {key: metaval})):
864
            with patch.object(
865
                    PC,
866
                    'get_container_info',
867
                    return_value=ret[0]) as gci:
868
                for until in (None, somedate):
869
                    r = self.client.get_container_meta(until=until)
870
                    self.assertEqual(r, ret[1])
871
                    self.assertEqual(gci.mock_calls[-1], call(until=until))
872

    
873
    def test_get_container_object_meta(self):
874
        somedate = '50m3d473'
875
        key = 'x-container-object-meta'
876
        metaval = '50m3m374v41'
877
        container_plus = dict(container_info)
878
        container_plus[key] = metaval
879
        for ret in (
880
                (container_info, {key: ''}),
881
                (container_plus, {key: metaval})):
882
            with patch.object(
883
                    PC,
884
                    'get_container_info',
885
                    return_value=ret[0]) as gci:
886
                for until in (None, somedate):
887
                    r = self.client.get_container_object_meta(until=until)
888
                    self.assertEqual(r, ret[1])
889
                    self.assertEqual(gci.mock_calls[-1], call(until=until))
890

    
891
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
892
    def test_set_container_meta(self, post):
893
        metas = dict(k1='v1', k2='v2', k3='v3')
894
        self.client.set_container_meta(metas)
895
        self.assertEqual(
896
            post.mock_calls[-1],
897
            call(update=True, metadata=metas))
898

    
899
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
900
    def test_del_container_meta(self, ap):
901
        self.client.del_container_meta('somekey')
902
        expected = [call(update=True, metadata={'somekey': ''})]
903
        self.assertEqual(ap.mock_calls, expected)
904

    
905
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
906
    def test_set_container_quota(self, post):
907
        qu = 1024
908
        self.client.set_container_quota(qu)
909
        self.assertEqual(post.mock_calls[-1], call(update=True, quota=qu))
910

    
911
    @patch('%s.container_post' % pithos_pkg, return_value=FR())
912
    def test_set_container_versioning(self, post):
913
        vrs = 'n3wV3r51on1ngTyp3'
914
        self.client.set_container_versioning(vrs)
915
        self.assertEqual(
916
            post.mock_calls[-1],
917
            call(update=True, versioning=vrs))
918

    
919
    @patch('%s.object_delete' % pithos_pkg, return_value=FR())
920
    def test_del_object(self, delete):
921
        for kwarg in (
922
                dict(delimiter=None, until=None),
923
                dict(delimiter='X', until='50m3d473')):
924
            self.client.del_object(obj, **kwarg)
925
            self.assertEqual(delete.mock_calls[-1], call(obj, **kwarg))
926

    
927
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
928
    def test_set_object_meta(self, post):
929
        metas = dict(k1='v1', k2='v2', k3='v3')
930
        self.assertRaises(
931
            AssertionError,
932
            self.client.set_object_meta,
933
            obj, 'Non dict arg')
934
        self.client.set_object_meta(obj, metas)
935
        self.assertEqual(
936
            post.mock_calls[-1],
937
            call(obj, update=True, metadata=metas))
938

    
939
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
940
    def test_publish_object(self, post):
941
        oinfo = dict(object_info)
942
        val = 'pubL1c'
943
        oinfo['x-object-public'] = val
944
        with patch.object(PC, 'get_object_info', return_value=oinfo) as gof:
945
            r = self.client.publish_object(obj)
946
            self.assertEqual(
947
                post.mock_calls[-1],
948
                call(obj, public=True, update=True))
949
            self.assertEqual(gof.mock_calls[-1], call(obj))
950
            self.assertEqual(r, '%s%s' % (self.url[:-6], val))
951

    
952
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
953
    def test_unpublish_object(self, post):
954
        self.client.unpublish_object(obj)
955
        self.assertEqual(
956
            post.mock_calls[-1],
957
            call(obj, public=False, update=True))
958

    
959
    def test_get_object_sharing(self):
960
        info = dict(object_info)
961
        expected = dict(read='u1,g1,u2', write='u1')
962
        info['x-object-sharing'] = '; '.join(
963
            ['%s=%s' % (k, v) for k, v in expected.items()])
964
        with patch.object(PC, 'get_object_info', return_value=info) as GOF:
965
            r = self.client.get_object_sharing(obj)
966
            self.assertEqual(GOF.mock_calls[-1], call(obj))
967
            self.assert_dicts_are_equal(r, expected)
968
            info['x-object-sharing'] = '//'.join(
969
                ['%s=%s' % (k, v) for k, v in expected.items()])
970
            self.assertRaises(
971
                ValueError,
972
                self.client.get_object_sharing,
973
                obj)
974
            info['x-object-sharing'] = '; '.join(
975
                ['%s:%s' % (k, v) for k, v in expected.items()])
976
            self.assertRaises(
977
                ClientError,
978
                self.client.get_object_sharing,
979
                obj)
980
            info['x-object-sharing'] = 'read=%s' % expected['read']
981
            r = self.client.get_object_sharing(obj)
982
            expected.pop('write')
983
            self.assert_dicts_are_equal(r, expected)
984

    
985
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
986
    def test_set_object_sharing(self, POST):
987
        read_perms = ['u1', 'g1', 'u2', 'g2']
988
        write_perms = ['u1', 'g1']
989
        for kwargs in (
990
                dict(read_permition=read_perms, write_permition=write_perms),
991
                dict(read_permition=read_perms),
992
                dict(write_permition=write_perms),
993
                dict()):
994
            self.client.set_object_sharing(obj, **kwargs)
995
            kwargs['read'] = kwargs.pop('read_permition', '')
996
            kwargs['write'] = kwargs.pop('write_permition', '')
997
            self.assertEqual(
998
                POST.mock_calls[-1],
999
                call(obj, update=True, permissions=kwargs))
1000

    
1001
    @patch('%s.set_object_sharing' % pithos_pkg)
1002
    def test_del_object_sharing(self, SOS):
1003
        self.client.del_object_sharing(obj)
1004
        self.assertEqual(SOS.mock_calls[-1], call(obj))
1005

    
1006
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1007
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1008
    def test_append_object(self, post, GCI):
1009
        num_of_blocks = 4
1010
        tmpFile = self._create_temp_file(num_of_blocks)
1011
        tmpFile.seek(0, 2)
1012
        file_size = tmpFile.tell()
1013
        for turn in range(2):
1014
            tmpFile.seek(0, 0)
1015

    
1016
            try:
1017
                from progress.bar import ShadyBar
1018
                apn_bar = ShadyBar('Mock append')
1019
            except ImportError:
1020
                apn_bar = None
1021

    
1022
            if apn_bar:
1023

    
1024
                def append_gen(n):
1025
                    for i in apn_bar.iter(range(n)):
1026
                        yield
1027
                    yield
1028

    
1029
            else:
1030
                append_gen = None
1031

    
1032
            self.client.append_object(
1033
                obj, tmpFile,
1034
                upload_cb=append_gen if turn else None)
1035
            self.assertEqual((turn + 1) * num_of_blocks, len(post.mock_calls))
1036
            (args, kwargs) = post.mock_calls[-1][1:3]
1037
            self.assertEqual(args, (obj,))
1038
            self.assertEqual(kwargs['content_length'], len(kwargs['data']))
1039
            fsize = num_of_blocks * int(kwargs['content_length'])
1040
            self.assertEqual(fsize, file_size)
1041
            self.assertEqual(kwargs['content_range'], 'bytes */*')
1042
            exp = 'application/octet-stream'
1043
            self.assertEqual(kwargs['content_type'], exp)
1044
            self.assertEqual(kwargs['update'], True)
1045

    
1046
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1047
    def test_truncate_object(self, post):
1048
        upto_bytes = 377
1049
        self.client.truncate_object(obj, upto_bytes)
1050
        self.assertEqual(post.mock_calls[-1], call(
1051
            obj,
1052
            update=True,
1053
            object_bytes=upto_bytes,
1054
            content_range='bytes 0-%s/*' % upto_bytes,
1055
            content_type='application/octet-stream',
1056
            source_object='/%s/%s' % (self.client.container, obj)))
1057

    
1058
    @patch('%s.get_container_info' % pithos_pkg, return_value=container_info)
1059
    @patch('%s.object_post' % pithos_pkg, return_value=FR())
1060
    def test_overwrite_object(self, post, GCI):
1061
        num_of_blocks = 4
1062
        tmpFile = self._create_temp_file(num_of_blocks)
1063
        tmpFile.seek(0, 2)
1064
        file_size = tmpFile.tell()
1065
        info = dict(object_info)
1066
        info['content-length'] = file_size
1067
        block_size = container_info['x-container-block-size']
1068
        with patch.object(PC, 'get_object_info', return_value=info) as GOI:
1069
            for start, end in (
1070
                    (0, file_size + 1),
1071
                    (file_size + 1, file_size + 2)):
1072
                tmpFile.seek(0, 0)
1073
                self.assertRaises(
1074
                    ClientError,
1075
                    self.client.overwrite_object,
1076
                    obj, start, end, tmpFile)
1077
            for start, end in ((0, 144), (144, 233), (233, file_size)):
1078
                tmpFile.seek(0, 0)
1079
                owr_gen = None
1080
                exp_size = end - start + 1
1081
                if not start or exp_size > block_size:
1082
                    try:
1083
                        from progress.bar import ShadyBar
1084
                        owr_bar = ShadyBar('Mock append')
1085
                    except ImportError:
1086
                        owr_bar = None
1087

    
1088
                    if owr_bar:
1089

    
1090
                        def owr_gen(n):
1091
                            for i in owr_bar.iter(range(n)):
1092
                                yield
1093
                            yield
1094

    
1095
                    if exp_size > block_size:
1096
                        exp_size = exp_size % block_size or block_size
1097

    
1098
                self.client.overwrite_object(obj, start, end, tmpFile, owr_gen)
1099
                self.assertEqual(GOI.mock_calls[-1], call(obj))
1100
                self.assertEqual(GCI.mock_calls[-1], call())
1101
                (args, kwargs) = post.mock_calls[-1][1:3]
1102
                self.assertEqual(args, (obj,))
1103
                self.assertEqual(len(kwargs['data']), exp_size)
1104
                self.assertEqual(kwargs['content_length'], exp_size)
1105
                self.assertEqual(kwargs['update'], True)
1106
                exp = 'application/octet-stream'
1107
                self.assertEqual(kwargs['content_type'], exp)
1108

    
1109
    @patch('%s.set_param' % client_pkg)
1110
    @patch('%s.get' % pithos_pkg, return_value=FR())
1111
    def test_get_sharing_accounts(self, get, SP):
1112
        FR.json = sharers
1113
        for kws in (
1114
                dict(),
1115
                dict(limit='50m3-11m17'),
1116
                dict(marker='X'),
1117
                dict(limit='50m3-11m17', marker='X')):
1118
            r = self.client.get_sharing_accounts(**kws)
1119
            self.assertEqual(SP.mock_calls[-3], call('format', 'json'))
1120
            limit, marker = kws.get('limit', None), kws.get('marker', None)
1121
            self.assertEqual(SP.mock_calls[-2], call(
1122
                'limit', limit,
1123
                iff=limit is not None))
1124
            self.assertEqual(SP.mock_calls[-1], call(
1125
                'marker', marker,
1126
                iff=marker is not None))
1127
            self.assertEqual(get.mock_calls[-1], call('', success=(200, 204)))
1128
            for i in range(len(r)):
1129
                self.assert_dicts_are_equal(r[i], sharers[i])
1130

    
1131
    @patch('%s.object_get' % pithos_pkg, return_value=FR())
1132
    def test_get_object_versionlist(self, get):
1133
        info = dict(object_info)
1134
        info['versions'] = ['v1', 'v2']
1135
        FR.json = info
1136
        r = self.client.get_object_versionlist(obj)
1137
        self.assertEqual(
1138
            get.mock_calls[-1],
1139
            call(obj, format='json', version='list'))
1140
        self.assertEqual(r, info['versions'])