Statistics
| Branch: | Tag: | Revision:

root / kamaki / clients / storage / test.py @ 3e6f33ca

History | View | Annotate | Download (21.1 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.storage import StorageClient as SC
41
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection as C
42

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

    
144

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

    
153
    def release(self):
154
        pass
155

    
156

    
157
class Storage(TestCase):
158

    
159
    files = []
160

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

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

    
180
    def setUp(self):
181
        self.url = 'https://www.example.com/pithos'
182
        self.token = 'p17h0570k3n'
183
        self.client = SC(self.url, self.token)
184
        self.client.account = user_id
185
        self.client.container = 'c0nt@1n3r_i'
186

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

    
195
    #  Pithos+ methods that extend storage API
196

    
197
    @patch('%s.head' % client_pkg, return_value=FR())
198
    def test_get_account_info(self, head):
199
        FR.headers = account_info
200
        r = self.client.get_account_info()
201
        self.assert_dicts_are_equal(account_info, r)
202
        self.assertEqual(
203
            head.mock_calls[-1],
204
            call('/%s' % self.client.account, success=(204, 401)))
205
        FR.status_code = 401
206
        self.assertRaises(ClientError, self.client.get_account_info)
207

    
208
    @patch('%s.post' % storage_pkg, return_value=FR())
209
    @patch('%s.set_header' % storage_pkg)
210
    def test_replace_account_meta(self, SH, post):
211
        metas = dict(k1='v1', k2='v2', k3='v3')
212
        self.client.replace_account_meta(metas)
213
        prfx = 'X-Account-Meta-'
214
        expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
215
        self.assertEqual(SH.mock_calls, expected)
216
        self.assertEqual(
217
            post.mock_calls[-1],
218
            call('/%s' % self.client.account, success=202))
219

    
220
    @patch('%s.post' % storage_pkg, return_value=FR())
221
    @patch('%s.get_account_info' % storage_pkg, return_value=account_info)
222
    def test_del_account_meta(self, GAI, post):
223
        prfx = 'x-account-meta-'
224
        keys = [k[len(prfx):] for k in account_info if k.startswith(prfx)]
225
        for key in keys:
226
            self.client.del_account_meta(key)
227
            self.assertEqual(
228
                post.mock_calls[-1],
229
                call('/%s' % self.client.account, success=202))
230
        self.assertEqual(len(keys), len(post.mock_calls))
231
        self.assertRaises(ClientError, self.client.del_account_meta, 'k4')
232

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

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

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

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

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

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

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

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

323
        OP = PC.object_put
324
        mock_offset = 2
325

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

335
        if blck_bar and upld_bar:
336

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
556
if __name__ == '__main__':
557
    from sys import argv
558
    from kamaki.clients.test import runTestCase
559
    runTestCase(Storage, 'Storage Client', argv[1:])