Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (32.3 kB)

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

    
34
from unittest import TestCase
35
from mock import patch, call, 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.astakos import AstakosClient
42
from kamaki.clients.connection.kamakicon import KamakiHTTPConnection as C
43

    
44
user_id = 'ac0un7-1d-5tr1ng'
45
obj = 'obj3c7N4m3'
46

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

    
135

    
136
class Pithos(TestCase):
137

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

    
146
        def release(self):
147
            pass
148

    
149
    files = []
150

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

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

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

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

    
185
    #  Pithos+ methods that extend storage API
186

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

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

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

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

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

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

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

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

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

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

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

    
318
        OP = PC.object_put
319
        mock_offset = 2
320

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

    
330
        if blck_bar and upld_bar:
331

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

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

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

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

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

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

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

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

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

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

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

    
439
    def test_replace_object_meta(self):
440
        PC.set_header = Mock()
441
        metas = dict(k1='new1', k2='new2', k3='new3')
442
        cont = self.client.container
443
        with patch.object(PC, 'post', return_value=self.FR()) as post:
444
            self.client.replace_object_meta(metas)
445
            self.assertEqual(post.mock_calls, [
446
                call('/%s/%s' % (user_id, cont),
447
                success=202)])
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
    def test_copy_object(self):
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
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
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
    def test_move_object(self):
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
        with patch.object(PC, 'object_put', return_value=self.FR()) as put:
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
    def test_delete_object(self):
513
        cont = self.client.container
514
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
515
            self.client.delete_object(obj)
516
            self.assertEqual(delete.mock_calls, [
517
                call('/%s/%s/%s' % (user_id, cont, obj), success=(204, 404))])
518
            self.FR.status_code = 404
519
            self.assertRaises(ClientError, self.client.delete_object, obj)
520

    
521
    def test_list_objects(self):
522
        self.FR.json = object_list
523
        acc = self.client.account
524
        cont = self.client.container
525
        PC.set_param = Mock()
526
        SP = PC.set_param
527
        with patch.object(PC, 'get', return_value=self.FR()) as get:
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
            self.FR.status_code = 304
535
            self.assertEqual(self.client.list_objects(), [])
536
            self.FR.status_code = 404
537
            self.assertRaises(ClientError, self.client.list_objects)
538

    
539
    def test_list_objects_in_path(self):
540
        self.FR.json = object_list
541
        path = '/some/awsome/path'
542
        acc = self.client.account
543
        cont = self.client.container
544
        PC.set_param = Mock()
545
        SP = PC.set_param
546
        with patch.object(PC, 'get', return_value=self.FR()) as get:
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
            self.FR.status_code = 404
553
            self.assertRaises(ClientError, self.client.list_objects)
554

    
555
    #  Pithos+ only methods
556

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

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

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

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

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

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

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

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

    
675
        if dl_bar:
676

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

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

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

    
698
        #  ALl options on no tty
699

    
700
        def foo():
701
            return True
702

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

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

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

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

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

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

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

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

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

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

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