Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (34.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, Mock
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
user_id = 'ac0un7-1d-5tr1ng'
44
obj = 'obj3c7N4m3'
45

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

    
134

    
135
class Pithos(TestCase):
136

    
137
    class FR(object):
138
        """FR stands for Fake Response"""
139
        json = dict()
140
        headers = dict()
141
        content = json
142
        status = None
143
        status_code = 200
144

    
145
        def release(self):
146
            pass
147

    
148
    files = []
149

    
150
    def _create_temp_file(self, num_of_blocks):
151
        self.files.append(NamedTemporaryFile())
152
        tmpFile = self.files[-1]
153
        file_size = num_of_blocks * 4 * 1024 * 1024
154
        print('\n\tCreate tmp file')
155
        tmpFile.write(urandom(file_size))
156
        tmpFile.flush()
157
        tmpFile.seek(0)
158
        print('\t\tDone')
159
        return tmpFile
160

    
161
    def assert_dicts_are_equal(self, d1, d2):
162
        for k, v in d1.items():
163
            self.assertTrue(k in d2)
164
            if isinstance(v, dict):
165
                self.assert_dicts_are_equal(v, d2[k])
166
            else:
167
                self.assertEqual(unicode(v), unicode(d2[k]))
168

    
169
    def setUp(self):
170
        self.url = 'https://www.example.com/pithos'
171
        self.token = 'p17h0570k3n'
172
        self.client = PC(self.url, self.token)
173
        self.client.account = user_id
174
        self.client.container = 'c0nt@1n3r_i'
175

    
176
    def tearDown(self):
177
        self.FR.headers = dict()
178
        self.FR.status_code = 200
179
        self.FR.json = dict()
180
        self.FR.content = self.FR.json
181
        for f in self.files:
182
            f.close()
183

    
184
    #  Pithos+ methods that extend storage API
185

    
186
    def test_get_account_info(self):
187
        self.FR.headers = account_info
188
        self.FR.status_code = 204
189
        with patch.object(C, 'perform_request', return_value=self.FR()):
190
            r = self.client.get_account_info()
191
            self.assertEqual(self.client.http_client.url, self.url)
192
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
193
            self.assert_dicts_are_equal(r, account_info)
194
            PC.set_param = Mock()
195
            untils = ['date 1', 'date 2', 'date 3']
196
            for unt in untils:
197
                r = self.client.get_account_info(until=unt)
198
                self.assert_dicts_are_equal(r, account_info)
199
            for i in range(len(untils)):
200
                self.assertEqual(
201
                    PC.set_param.mock_calls[i],
202
                    call('until', untils[i], iff=untils[i]))
203
            self.FR.status_code = 401
204
            self.assertRaises(ClientError, self.client.get_account_info)
205

    
206
    def test_replace_account_meta(self):
207
        self.FR.status_code = 202
208
        metas = dict(k1='v1', k2='v2', k3='v3')
209
        PC.set_header = Mock()
210
        with patch.object(C, 'perform_request', return_value=self.FR()):
211
            self.client.replace_account_meta(metas)
212
            self.assertEqual(self.client.http_client.url, self.url)
213
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
214
            prfx = 'X-Account-Meta-'
215
            expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
216
            self.assertEqual(PC.set_header.mock_calls, expected)
217

    
218
    def test_del_account_meta(self):
219
        keys = ['k1', 'k2', 'k3']
220
        with patch.object(PC, 'account_post', return_value=self.FR()) as ap:
221
            expected = []
222
            for key in keys:
223
                self.client.del_account_meta(key)
224
                expected.append(call(update=True, metadata={key: ''}))
225
            self.assertEqual(ap.mock_calls, expected)
226

    
227
    def test_create_container(self):
228
        self.FR.status_code = 201
229
        with patch.object(PC, 'put', return_value=self.FR()) as put:
230
            cont = 's0m3c0n731n3r'
231
            self.client.create_container(cont)
232
            expected = [call('/%s/%s' % (user_id, cont), success=(201, 202))]
233
            self.assertEqual(put.mock_calls, expected)
234
            self.FR.status_code = 202
235
            self.assertRaises(ClientError, self.client.create_container, cont)
236

    
237
    def test_get_container_info(self):
238
        self.FR.headers = container_info
239
        with patch.object(PC, 'container_head', return_value=self.FR()) as ch:
240
            r = self.client.get_container_info()
241
            self.assert_dicts_are_equal(r, container_info)
242
            u = 'some date'
243
            r = self.client.get_container_info(until=u)
244
            self.assertEqual(ch.mock_calls, [call(until=None), call(until=u)])
245

    
246
    def test_delete_container(self):
247
        self.FR.status_code = 204
248
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
249
            cont = 's0m3c0n731n3r'
250
            self.client.delete_container(cont)
251
            self.FR.status_code = 404
252
            self.assertRaises(ClientError, self.client.delete_container, cont)
253
            self.FR.status_code = 409
254
            self.assertRaises(ClientError, self.client.delete_container, cont)
255
            acall = call('/%s/%s' % (user_id, cont), success=(204, 404, 409))
256
            self.assertEqual(delete.mock_calls, [acall] * 3)
257

    
258
    def test_list_containers(self):
259
        self.FR.json = container_list
260
        with patch.object(PC, 'account_get', return_value=self.FR()):
261
            r = self.client.list_containers()
262
            for i in range(len(r)):
263
                self.assert_dicts_are_equal(r[i], container_list[i])
264

    
265
    def test_upload_object(self):
266
        PC.get_container_info = Mock(return_value=container_info)
267
        PC.container_post = Mock(return_value=self.FR())
268
        PC.object_put = Mock(return_value=self.FR())
269
        num_of_blocks = 8
270
        tmpFile = self._create_temp_file(num_of_blocks)
271

    
272
        # Without kwargs
273
        self.client.upload_object(obj, tmpFile)
274
        self.assertEqual(PC.get_container_info.mock_calls, [call()])
275
        [call1, call2] = PC.object_put.mock_calls
276

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

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

    
317
        OP = PC.object_put
318
        mock_offset = 2
319

    
320
        #  With progress bars
321
        try:
322
            from progress.bar import ShadyBar
323
            blck_bar = ShadyBar('Mock blck calc.')
324
            upld_bar = ShadyBar('Mock uplds')
325
        except ImportError:
326
            blck_bar = None
327
            upld_bar = None
328

    
329
        if blck_bar and upld_bar:
330

    
331
            def blck_gen(n):
332
                for i in blck_bar.iter(range(n)):
333
                    yield
334
                yield
335

    
336
            def upld_gen(n):
337
                for i in upld_bar.iter(range(n)):
338
                    yield
339
                yield
340

    
341
            tmpFile.seek(0)
342
            self.client.upload_object(
343
                obj, tmpFile,
344
                hash_cb=blck_gen, upload_cb=upld_gen)
345

    
346
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
347
                self.assertEqual(OP.mock_calls[i], c)
348

    
349
        #  With content-type
350
        tmpFile.seek(0)
351
        ctype = 'video/mpeg'
352
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
353
        self.client.upload_object(obj, tmpFile,
354
            content_type=ctype, sharing=sharing)
355
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
356
        self.assert_dicts_are_equal(
357
            OP.mock_calls[-2][2]['permissions'],
358
            sharing)
359

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

    
372
    def test_create_object(self):
373
        PC.set_header = Mock()
374
        cont = self.client.container
375
        ctype = 'c0n73n7/typ3'
376
        exp_shd = [
377
            call('Content-Type', 'application/octet-stream'),
378
            call('Content-length', '0'),
379
            call('Content-Type', ctype), call('Content-length', '42')]
380
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)] * 2
381
        with patch.object(PC, 'put', return_value=self.FR()) as put:
382
            self.client.create_object(obj)
383
            self.client.create_object(obj,
384
                content_type=ctype, content_length=42)
385
            self.assertEqual(PC.set_header.mock_calls, exp_shd)
386
            self.assertEqual(put.mock_calls, exp_put)
387

    
388
    def test_create_directory(self):
389
        PC.set_header = Mock()
390
        cont = self.client.container
391
        exp_shd = [
392
            call('Content-Type', 'application/directory'),
393
            call('Content-length', '0')]
394
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)]
395
        with patch.object(PC, 'put', return_value=self.FR()) as put:
396
            self.client.create_directory(obj)
397
            self.assertEqual(PC.set_header.mock_calls, exp_shd)
398
            self.assertEqual(put.mock_calls, exp_put)
399

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

    
419
    def test_get_object_meta(self):
420
        expected = dict()
421
        for k, v in object_info.items():
422
            expected[k] = v
423
        with patch.object(
424
                PC,
425
                'get_object_info',
426
                return_value=object_info):
427
            r = self.client.get_object_meta(obj)
428
            self.assert_dicts_are_equal(r, expected)
429

    
430
    def test_del_object_meta(self):
431
        metakey = '50m3m3t4k3y'
432
        with patch.object(PC, 'object_post', return_value=self.FR()) as post:
433
            self.client.del_object_meta(obj, metakey)
434
            self.assertEqual(
435
                post.mock_calls,
436
                [call(obj, update=True, metadata={metakey: ''})])
437

    
438
    def test_replace_object_meta(self):
439
        PC.set_header = Mock()
440
        metas = dict(k1='new1', k2='new2', k3='new3')
441
        cont = self.client.container
442
        with patch.object(PC, 'post', return_value=self.FR()) as post:
443
            self.client.replace_object_meta(metas)
444
            self.assertEqual(post.mock_calls, [
445
                call('/%s/%s' % (user_id, cont),
446
                success=202)])
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
    def test_copy_object(self):
452
        src_cont = 'src-c0nt41n3r'
453
        src_obj = 'src-0bj'
454
        dst_cont = 'dst-c0nt41n3r'
455
        dst_obj = 'dst-0bj'
456
        expected = call(
457
            src_obj,
458
            content_length=0,
459
            source_account=None,
460
            success=201,
461
            copy_from='/%s/%s' % (src_cont, src_obj),
462
            delimiter=None,
463
            content_type=None,
464
            source_version=None,
465
            public=False)
466
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
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
    def test_move_object(self):
482
        src_cont = 'src-c0nt41n3r'
483
        src_obj = 'src-0bj'
484
        dst_cont = 'dst-c0nt41n3r'
485
        dst_obj = 'dst-0bj'
486
        expected = call(
487
            src_obj,
488
            content_length=0,
489
            source_account=None,
490
            success=201,
491
            move_from='/%s/%s' % (src_cont, src_obj),
492
            delimiter=None,
493
            content_type=None,
494
            source_version=None,
495
            public=False)
496
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
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
    def test_delete_object(self):
512
        cont = self.client.container
513
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
514
            self.client.delete_object(obj)
515
            self.assertEqual(delete.mock_calls, [
516
                call('/%s/%s/%s' % (user_id, cont, obj), success=(204, 404))])
517
            self.FR.status_code = 404
518
            self.assertRaises(ClientError, self.client.delete_object, obj)
519

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

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

    
554
    #  Pithos+ only methods
555

    
556
    def test_purge_container(self):
557
        with patch.object(
558
                PC,
559
                'container_delete',
560
                return_value=self.FR()) as cd:
561
            self.client.purge_container()
562
            self.assertTrue('until' in cd.mock_calls[-1][2])
563
            cont = self.client.container
564
            self.client.purge_container('another-container')
565
            self.assertEqual(self.client.container, cont)
566

    
567
    def test_upload_object_unchunked(self):
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
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
580
            self.client.upload_object_unchunked(obj, tmpFile)
581
            self.assertEqual(put.mock_calls[-1][1], (obj,))
582
            self.assertEqual(
583
                sorted(put.mock_calls[-1][2].keys()),
584
                sorted(expected.keys()))
585
            kwargs = dict(expected)
586
            kwargs.pop('success')
587
            kwargs['size'] = kwargs.pop('data')
588
            kwargs['sharing'] = kwargs.pop('permissions')
589
            tmpFile.seek(0)
590
            self.client.upload_object_unchunked(obj, tmpFile, **kwargs)
591
            pmc = put.mock_calls[-1][2]
592
            for k, v in expected.items():
593
                if k == 'data':
594
                    self.assertEqual(len(pmc[k]), v)
595
                else:
596
                    self.assertEqual(pmc[k], v)
597
            self.assertRaises(
598
                ClientError,
599
                self.client.upload_object_unchunked,
600
                obj, tmpFile, withHashFile=True)
601

    
602
    def test_create_object_by_manifestation(self):
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
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
612
            self.client.create_object_by_manifestation(obj)
613
            expected = dict(content_length=0, manifest=manifest)
614
            for k in kwargs:
615
                expected['permissions' if k == 'sharing' else k] = None
616
            self.assertEqual(put.mock_calls[-1], call(obj, **expected))
617
            self.client.create_object_by_manifestation(obj, **kwargs)
618
            expected.update(kwargs)
619
            expected['permissions'] = expected.pop('sharing')
620
            self.assertEqual(put.mock_calls[-1], call(obj, **expected))
621

    
622
    def test_download_object(self):
623
        PC.get_object_hashmap = Mock(return_value=object_hashmap)
624
        num_of_blocks = 8
625
        tmpFile = self._create_temp_file(num_of_blocks)
626
        self.FR.content = tmpFile.read(4 * 1024 * 1024)
627
        tmpFile = self._create_temp_file(num_of_blocks)
628
        PC.object_get = Mock(return_value=self.FR())
629
        GET = PC.object_get
630
        num_of_blocks = len(object_hashmap['hashes'])
631

    
632
        kwargs = dict(
633
            resume=True,
634
            version='version',
635
            range_str='10-20',
636
            if_match='if and only if',
637
            if_none_match='if and only not',
638
            if_modified_since='what if not?',
639
            if_unmodified_since='this happens if not!',
640
            async_headers=dict(Range='bytes=0-88888888'))
641

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

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

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

    
674
        if dl_bar:
675

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

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

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

    
697
        #  ALl options on no tty
698

    
699
        def foo():
700
            return True
701

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

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

    
744
    def test_set_account_group(self):
745
        group = 'aU53rGr0up'
746
        usernames = ['u1', 'u2', 'u3']
747
        with patch.object(PC, 'account_post', return_value=self.FR()) as post:
748
            self.client.set_account_group(group, usernames)
749
            self.assertEqual(
750
                post.mock_calls[-1],
751
                call(update=True, groups={group: usernames}))
752

    
753
    def test_del_account_group(self):
754
        group = 'aU53rGr0up'
755
        with patch.object(PC, 'account_post', return_value=self.FR()) as post:
756
            self.client.del_account_group(group)
757
            self.assertEqual(
758
                post.mock_calls[-1],
759
                call(update=True, groups={group: []}))
760

    
761
    def test_get_account_quota(self):
762
        key = 'x-account-policy-quota'
763
        with patch.object(PC, 'get_account_info', return_value=account_info):
764
            r = self.client.get_account_quota()
765
            self.assertEqual(r[key], account_info[key])
766

    
767
    def test_get_account_versioning(self):
768
        key = 'x-account-policy-versioning'
769
        with patch.object(PC, 'get_account_info', return_value=account_info):
770
            r = self.client.get_account_versioning()
771
            self.assertEqual(r[key], account_info[key])
772

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

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

    
803
    def test_set_account_meta(self):
804
        metas = dict(k1='v1', k2='v2', k3='v3')
805
        with patch.object(PC, 'account_post', return_value=self.FR()) as post:
806
            self.client.set_account_meta(metas)
807
            self.assertEqual(
808
                post.mock_calls[-1],
809
                call(update=True, metadata=metas))
810

    
811
    def test_set_account_quota(self):
812
        qu = 1024
813
        with patch.object(PC, 'account_post', return_value=self.FR()) as post:
814
            self.client.set_account_quota(qu)
815
            self.assertEqual(post.mock_calls[-1], call(update=True, quota=qu))
816

    
817
    def test_set_account_versioning(self):
818
        vrs = 'n3wV3r51on1ngTyp3'
819
        with patch.object(PC, 'account_post', return_value=self.FR()) as post:
820
            self.client.set_account_versioning(vrs)
821
            self.assertEqual(
822
                post.mock_calls[-1],
823
                call(update=True, versioning=vrs))
824

    
825
    def test_del_container(self):
826
        kwarg_list = [
827
            dict(delimiter=None, until=None),
828
            dict(delimiter='X', until='50m3d473')]
829
        with patch.object(
830
                PC,
831
                'container_delete',
832
                return_value=self.FR()) as delete:
833
            for kwarg in kwarg_list:
834
                self.client.del_container(**kwarg)
835
                expected = dict(kwarg)
836
                expected['success'] = (204, 404, 409)
837
                self.assertEqual(delete.mock_calls[-1], call(**expected))
838
            for status_code in (404, 409):
839
                self.FR.status_code = status_code
840
                self.assertRaises(ClientError, self.client.del_container)
841

    
842
    def test_get_container_versioning(self):
843
        key = 'x-container-policy-versioning'
844
        cont = 'c0n7-417'
845
        bu_cnt = self.client.container
846
        with patch.object(
847
                PC,
848
                'get_container_info',
849
                return_value=container_info) as gci:
850
            for container in (None, cont):
851
                r = self.client.get_container_versioning(container=container)
852
                self.assertEqual(r[key], container_info[key])
853
                self.assertEqual(gci.mock_calls[-1], call())
854
                self.assertEqual(bu_cnt, self.client.container)
855

    
856
    def test_get_container_quota(self):
857
        key = 'x-container-policy-quota'
858
        cont = 'c0n7-417'
859
        bu_cnt = self.client.container
860
        with patch.object(
861
                PC,
862
                'get_container_info',
863
                return_value=container_info) as gci:
864
            for container in (None, cont):
865
                r = self.client.get_container_quota(container=container)
866
                self.assertEqual(r[key], container_info[key])
867
                self.assertEqual(gci.mock_calls[-1], call())
868
                self.assertEqual(bu_cnt, self.client.container)
869

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