Statistics
| Branch: | Tag: | Revision:

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

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

    
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

    
123

    
124
class Pithos(TestCase):
125

    
126
    class FR(object):
127
        """FR stands for Fake Response"""
128
        json = dict()
129
        headers = dict()
130
        content = json
131
        status = None
132
        status_code = 200
133

    
134
        def release(self):
135
            pass
136

    
137
    files = []
138

    
139
    def _create_temp_file(self):
140
        self.files.append(NamedTemporaryFile())
141
        tmpFile = self.files[-1]
142
        num_of_blocks = 8
143
        file_size = num_of_blocks * 4 * 1024 * 1024
144
        print('\n\tCreate tmp file')
145
        tmpFile.write(urandom(file_size))
146
        tmpFile.flush()
147
        tmpFile.seek(0)
148
        print('\t\tDone')
149
        return tmpFile
150

    
151
    def assert_dicts_are_equal(self, d1, d2):
152
        for k, v in d1.items():
153
            self.assertTrue(k in d2)
154
            if isinstance(v, dict):
155
                self.assert_dicts_are_equal(v, d2[k])
156
            else:
157
                self.assertEqual(unicode(v), unicode(d2[k]))
158

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

    
166
    def tearDown(self):
167
        self.FR.headers = dict()
168
        self.FR.status_code = 200
169
        self.FR.json = dict()
170
        for f in self.files:
171
            f.close()
172

    
173
    #  Pithos+ methods that extend storage API
174

    
175
    def test_get_account_info(self):
176
        self.FR.headers = account_info
177
        self.FR.status_code = 204
178
        with patch.object(C, 'perform_request', return_value=self.FR()):
179
            r = self.client.get_account_info()
180
            self.assertEqual(self.client.http_client.url, self.url)
181
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
182
            self.assert_dicts_are_equal(r, account_info)
183
            PC.set_param = Mock()
184
            untils = ['date 1', 'date 2', 'date 3']
185
            for unt in untils:
186
                r = self.client.get_account_info(until=unt)
187
                self.assert_dicts_are_equal(r, account_info)
188
            for i in range(len(untils)):
189
                self.assertEqual(
190
                    PC.set_param.mock_calls[i],
191
                    call('until', untils[i], iff=untils[i]))
192
            self.FR.status_code = 401
193
            self.assertRaises(ClientError, self.client.get_account_info)
194

    
195
    def test_replace_account_meta(self):
196
        self.FR.status_code = 202
197
        metas = dict(k1='v1', k2='v2', k3='v3')
198
        PC.set_header = Mock()
199
        with patch.object(C, 'perform_request', return_value=self.FR()):
200
            self.client.replace_account_meta(metas)
201
            self.assertEqual(self.client.http_client.url, self.url)
202
            self.assertEqual(self.client.http_client.path, '/%s' % user_id)
203
            prfx = 'X-Account-Meta-'
204
            expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
205
            self.assertEqual(PC.set_header.mock_calls, expected)
206

    
207
    def test_del_account_meta(self):
208
        keys = ['k1', 'k2', 'k3']
209
        with patch.object(PC, 'account_post', return_value=self.FR()) as ap:
210
            expected = []
211
            for key in keys:
212
                self.client.del_account_meta(key)
213
                expected.append(call(update=True, metadata={key: ''}))
214
            self.assertEqual(ap.mock_calls, expected)
215

    
216
    def test_create_container(self):
217
        self.FR.status_code = 201
218
        with patch.object(PC, 'put', return_value=self.FR()) as put:
219
            cont = 's0m3c0n731n3r'
220
            self.client.create_container(cont)
221
            expected = [call('/%s/%s' % (user_id, cont), success=(201, 202))]
222
            self.assertEqual(put.mock_calls, expected)
223
            self.FR.status_code = 202
224
            self.assertRaises(ClientError, self.client.create_container, cont)
225

    
226
    def test_get_container_info(self):
227
        self.FR.headers = container_info
228
        with patch.object(PC, 'container_head', return_value=self.FR()) as ch:
229
            r = self.client.get_container_info()
230
            self.assert_dicts_are_equal(r, container_info)
231
            u = 'some date'
232
            r = self.client.get_container_info(until=u)
233
            self.assertEqual(ch.mock_calls, [call(until=None), call(until=u)])
234

    
235
    def test_delete_container(self):
236
        self.FR.status_code = 204
237
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
238
            cont = 's0m3c0n731n3r'
239
            self.client.delete_container(cont)
240
            self.FR.status_code = 404
241
            self.assertRaises(ClientError, self.client.delete_container, cont)
242
            self.FR.status_code = 409
243
            self.assertRaises(ClientError, self.client.delete_container, cont)
244
            acall = call('/%s/%s' % (user_id, cont), success=(204, 404, 409))
245
            self.assertEqual(delete.mock_calls, [acall] * 3)
246

    
247
    def test_list_containers(self):
248
        self.FR.json = container_list
249
        with patch.object(PC, 'account_get', return_value=self.FR()):
250
            r = self.client.list_containers()
251
            for i in range(len(r)):
252
                self.assert_dicts_are_equal(r[i], container_list[i])
253

    
254
    def test_upload_object(self):
255
        PC.get_container_info = Mock(return_value=container_info)
256
        PC.container_post = Mock(return_value=self.FR())
257
        PC.object_put = Mock(return_value=self.FR())
258
        tmpFile = self._create_temp_file()
259
        obj = 'objectName'
260

    
261
        # Without kwargs
262
        self.client.upload_object(obj, tmpFile)
263
        self.assertEqual(PC.get_container_info.mock_calls, [call()])
264
        [call1, call2] = PC.object_put.mock_calls
265

    
266
        (args1, kwargs1) = call1[1:3]
267
        (args2, kwargs2) = call2[1:3]
268
        self.assertEqual(args1, (obj,))
269
        expected1 = dict(
270
            hashmap=True,
271
            success=(201, 409),
272
            format='json',
273
            json=dict(
274
                hashes=['s0m3h@5h'] * num_of_blocks,
275
                bytes=file_size),
276
            etag=None,
277
            content_encoding=None,
278
            content_type='application/octet-stream',
279
            content_disposition=None,
280
            public=None,
281
            permissions=None)
282
        for k, v in expected1.items():
283
            if k == 'json':
284
                self.assertEqual(len(v['hashes']), len(kwargs1[k]['hashes']))
285
                self.assertEqual(v['bytes'], kwargs1[k]['bytes'])
286
            else:
287
                self.assertEqual(v, kwargs1[k])
288

    
289
        (args2, kwargs2) = call2[1:3]
290
        self.assertEqual(args2, (obj,))
291
        expected2 = dict(
292
            json=dict(
293
                hashes=['s0m3h@5h'] * num_of_blocks,
294
                bytes=file_size),
295
            content_type='application/octet-stream',
296
            hashmap=True,
297
            success=201,
298
            format='json')
299
        for k, v in expected2.items():
300
            if k == 'json':
301
                self.assertEqual(len(v['hashes']), len(kwargs2[k]['hashes']))
302
                self.assertEqual(v['bytes'], kwargs2[k]['bytes'])
303
            else:
304
                self.assertEqual(v, kwargs2[k])
305

    
306
        OP = PC.object_put
307
        mock_offset = 2
308

    
309
        #  With progress bars
310
        try:
311
            from progress.bar import ShadyBar
312
            blck_bar = ShadyBar('Mock blck calc.')
313
            upld_bar = ShadyBar('Mock uplds')
314
        except ImportError:
315
            blck_bar = None
316
            upld_bar = None
317

    
318
        if blck_bar and upld_bar:
319

    
320
            def blck_gen(n):
321
                for i in blck_bar.iter(range(n)):
322
                    yield
323
                yield
324

    
325
            def upld_gen(n):
326
                for i in upld_bar.iter(range(n)):
327
                    yield
328
                yield
329

    
330
            tmpFile.seek(0)
331
            self.client.upload_object(
332
                obj, tmpFile,
333
                hash_cb=blck_gen, upload_cb=upld_gen)
334

    
335
            for i, c in enumerate(OP.mock_calls[-mock_offset:]):
336
                self.assertEqual(OP.mock_calls[i], c)
337

    
338
        #  With content-type
339
        tmpFile.seek(0)
340
        ctype = 'video/mpeg'
341
        sharing = dict(read=['u1', 'g1', 'u2'], write=['u1'])
342
        self.client.upload_object(obj, tmpFile,
343
            content_type=ctype, sharing=sharing)
344
        self.assertEqual(OP.mock_calls[-1][2]['content_type'], ctype)
345
        self.assert_dicts_are_equal(
346
            OP.mock_calls[-2][2]['permissions'],
347
            sharing)
348

    
349
        # With other args
350
        tmpFile.seek(0)
351
        kwargs = dict(
352
            etag='s0m3E74g',
353
            content_type=ctype,
354
            content_disposition=ctype + 'd15p051710n',
355
            public=True,
356
            content_encoding='802.11')
357
        self.client.upload_object(obj, tmpFile, **kwargs)
358
        for arg, val in kwargs.items():
359
            self.assertEqual(OP.mock_calls[-2][2][arg], val)
360

    
361
    def test_create_object(self):
362
        PC.set_header = Mock()
363
        obj = 'r4nd0m0bj3c7'
364
        cont = self.client.container
365
        ctype = 'c0n73n7/typ3'
366
        exp_shd = [
367
            call('Content-Type', 'application/octet-stream'),
368
            call('Content-length', '0'),
369
            call('Content-Type', ctype), call('Content-length', '42')]
370
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)] * 2
371
        with patch.object(PC, 'put', return_value=self.FR()) as put:
372
            self.client.create_object(obj)
373
            self.client.create_object(obj,
374
                content_type=ctype, content_length=42)
375
            self.assertEqual(PC.set_header.mock_calls, exp_shd)
376
            self.assertEqual(put.mock_calls, exp_put)
377

    
378
    def test_create_directory(self):
379
        PC.set_header = Mock()
380
        obj = 'r4nd0m0bj3c7'
381
        cont = self.client.container
382
        exp_shd = [
383
            call('Content-Type', 'application/directory'),
384
            call('Content-length', '0')]
385
        exp_put = [call('/%s/%s/%s' % (user_id, cont, obj), success=201)]
386
        with patch.object(PC, 'put', return_value=self.FR()) as put:
387
            self.client.create_directory(obj)
388
            self.assertEqual(PC.set_header.mock_calls, exp_shd)
389
            self.assertEqual(put.mock_calls, exp_put)
390

    
391
    def test_get_object_info(self):
392
        self.FR.headers = object_info
393
        obj = 'r4nd0m0bj3c7'
394
        version = 'v3r510n'
395
        with patch.object(PC, 'object_head', return_value=self.FR()) as head:
396
            r = self.client.get_object_info(obj)
397
            self.assertEqual(r, object_info)
398
            r = self.client.get_object_info(obj, version=version)
399
            self.assertEqual(head.mock_calls, [
400
                call(obj, version=None),
401
                call(obj, version=version)])
402
        with patch.object(
403
                PC,
404
                'object_head',
405
                side_effect=ClientError('Obj not found', 404)):
406
            self.assertRaises(
407
                ClientError,
408
                self.client.get_object_info,
409
                obj, version=version)
410

    
411
    def test_get_object_meta(self):
412
        obj = 'r4nd0m0bj3c7'
413
        expected = dict()
414
        for k, v in object_info.items():
415
            expected[k] = v
416
        with patch.object(
417
                PC,
418
                'get_object_info',
419
                return_value=object_info):
420
            r = self.client.get_object_meta(obj)
421
            self.assert_dicts_are_equal(r, expected)
422

    
423
    def test_del_object_meta(self):
424
        obj = 'r4nd0m0bj3c7'
425
        metakey = '50m3m3t4k3y'
426
        with patch.object(PC, 'object_post', return_value=self.FR()) as post:
427
            self.client.del_object_meta(obj, metakey)
428
            self.assertEqual(
429
                post.mock_calls,
430
                [call(obj, update=True, metadata={metakey: ''})])
431

    
432
    def test_replace_object_meta(self):
433
        PC.set_header = Mock()
434
        metas = dict(k1='new1', k2='new2', k3='new3')
435
        cont = self.client.container
436
        with patch.object(PC, 'post', return_value=self.FR()) as post:
437
            self.client.replace_object_meta(metas)
438
            self.assertEqual(post.mock_calls, [
439
                call('/%s/%s' % (user_id, cont),
440
                success=202)])
441
            prfx = 'X-Object-Meta-'
442
            expected = [call('%s%s' % (prfx, k), v) for k, v in metas.items()]
443
            self.assertEqual(PC.set_header.mock_calls, expected)
444

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

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

    
505
    def test_delete_object(self):
506
        obj = 's0m30bj3c7'
507
        cont = self.client.container
508
        with patch.object(PC, 'delete', return_value=self.FR()) as delete:
509
            self.client.delete_object(obj)
510
            self.assertEqual(delete.mock_calls, [
511
                call('/%s/%s/%s' % (user_id, cont, obj), success=(204, 404))])
512
            self.FR.status_code = 404
513
            self.assertRaises(ClientError, self.client.delete_object, obj)
514

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

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

    
549
    #  Pithos+ only methods
550

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

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

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