Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 9d4502a8

History | View | Annotate | Download (105.2 kB)

1
#!/usr/bin/env python
2
#coding=utf8
3

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

    
37
from pithos.tools.lib.client import Pithos_Client, Fault
38
from pithos.tools.lib.util import get_user, get_auth, get_url
39

    
40
from xml.dom import minidom
41
from StringIO import StringIO
42
from hashlib import new as newhasher
43
from binascii import hexlify
44
from httplib import HTTPConnection, HTTPSConnection
45
from urlparse import urlparse
46

    
47
import json
48
import unittest
49
import time as _time
50
import types
51
import hashlib
52
import mimetypes
53
import random
54
import datetime
55
import string
56
import re
57

    
58
DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
59
                "%A, %d-%b-%y %H:%M:%S GMT",
60
                "%a, %d %b %Y %H:%M:%S GMT"]
61

    
62
from pithos.api.settings import AUTHENTICATION_USERS
63
AUTHENTICATION_USERS = AUTHENTICATION_USERS or {}
64
OTHER_ACCOUNTS = AUTHENTICATION_USERS.copy()
65
OTHER_ACCOUNTS.pop(get_auth())
66

    
67
class BaseTestCase(unittest.TestCase):
68
    #TODO unauthorized request
69
    def setUp(self):
70
        self.client = Pithos_Client(get_url(), get_auth(), get_user())
71
        
72
        #keep track of initial containers
73
        self.initial_containers = self.client.list_containers()
74
        if self.initial_containers == '':
75
            self.initial_containers = []
76
        
77
        self._clean_account()
78
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
79

    
80
        #keep track of initial account groups
81
        self.initial_groups = self.client.retrieve_account_groups()
82

    
83
        #keep track of initial account meta
84
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
85
        
86
        self.extended = {
87
            'container':(
88
                'name',
89
                'count',
90
                'bytes',
91
                'last_modified',
92
                'x_container_policy'),
93
            'object':(
94
                'name',
95
                'hash',
96
                'bytes',
97
                'content_type',
98
                'content_encoding',
99
                'last_modified',)}
100
        self.return_codes = (400, 401, 403, 404, 503,)
101

    
102
    def tearDown(self):
103
        #delete additionally created meta
104
        l = []
105
        for m in self.client.retrieve_account_metadata(restricted=True):
106
            if m not in self.initial_meta:
107
                l.append(m)
108
        self.client.delete_account_metadata(l)
109

    
110
        #delete additionally created groups
111
        l = []
112
        for g in self.client.retrieve_account_groups():
113
            if g not in self.initial_groups:
114
                l.append(g)
115
        self.client.unset_account_groups(l)
116
        self._clean_account()
117

    
118
    def _clean_account(self):
119
        for c in self.client.list_containers():
120
#             if c not in self.initial_containers:
121
                self.client.delete_container(c, delimiter='/')
122
                self.client.delete_container(c)
123
    
124
    def assert_status(self, status, codes):
125
        l = [elem for elem in self.return_codes]
126
        if type(codes) == types.ListType:
127
            l.extend(codes)
128
        else:
129
            l.append(codes)
130
        self.assertTrue(status in l)
131

    
132
    def assert_extended(self, data, format, type, size=10000):
133
        if format == 'xml':
134
            self._assert_xml(data, type, size)
135
        elif format == 'json':
136
            self._assert_json(data, type, size)
137

    
138
    def _assert_json(self, data, type, size):
139
        convert = lambda s: s.lower()
140
        info = [convert(elem) for elem in self.extended[type]]
141
        self.assertTrue(len(data) <= size)
142
        for item in info:
143
            for i in data:
144
                if 'subdir' in i.keys():
145
                    continue
146
                self.assertTrue(item in i.keys())
147

    
148
    def _assert_xml(self, data, type, size):
149
        convert = lambda s: s.lower()
150
        info = [convert(elem) for elem in self.extended[type]]
151
        try:
152
            info.remove('content_encoding')
153
        except ValueError:
154
            pass
155
        xml = data
156
        entities = xml.getElementsByTagName(type)
157
        self.assertTrue(len(entities) <= size)
158
        for e in entities:
159
            for item in info:
160
                self.assertTrue(e.getElementsByTagName(item))
161

    
162
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
163
        """
164
        asserts that a Fault with a specific status is raised
165
        when callableObj is called with the specific arguments
166
        """
167
        try:
168
            r = callableObj(*args, **kwargs)
169
            self.fail('Should never reach here')
170
        except Fault, f:
171
            if type(status) == types.ListType:
172
                self.failUnless(f.status in status)
173
            else:
174
                self.failUnless(f.status == status)
175

    
176
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
177
        """
178
        asserts that a Fault with a specific status is not raised
179
        when callableObj is called with the specific arguments
180
        """
181
        try:
182
            r = callableObj(*args, **kwargs)
183
        except Fault, f:
184
            self.failIfEqual(f.status, status)
185

    
186
    def assert_container_exists(self, container):
187
        """
188
        asserts the existence of a container
189
        """
190
        try:
191
            self.client.retrieve_container_metadata(container)
192
        except Fault, f:
193
            self.failIf(f.status == 404)
194

    
195
    def assert_container_not_exists(self, container):
196
        """
197
        asserts there is no such a container
198
        """
199
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
200
                                 container)
201

    
202
    def assert_object_exists(self, container, object):
203
        """
204
        asserts the existence of an object
205
        """
206
        try:
207
            self.client.retrieve_object_metadata(container, object)
208
        except Fault, f:
209
            self.failIf(f.status == 404)
210

    
211
    def assert_object_not_exists(self, container, object):
212
        """
213
        asserts there is no such an object
214
        """
215
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
216
                                 container, object)
217

    
218
    def assert_versionlist_structure(self, versionlist):
219
        self.assertTrue(type(versionlist) == types.ListType)
220
        for elem in versionlist:
221
            self.assertTrue(type(elem) == types.ListType)
222
            self.assertEqual(len(elem), 2)
223

    
224
    def upload_random_data(self, container, name, length=1024, type=None,
225
                           enc=None, **meta):
226
        data = get_random_data(length)
227
        return self.upload_data(container, name, data, type, enc, **meta)
228

    
229
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
230
                    **meta):
231
        obj = {}
232
        obj['name'] = name
233
        try:
234
            obj['data'] = data
235
            obj['hash'] = compute_md5_hash(obj['data'])
236

    
237
            args = {}
238
            args['etag'] = etag if etag else obj['hash']
239

    
240
            try:
241
                guess = mimetypes.guess_type(name)
242
                type = type if type else guess[0]
243
                enc = enc if enc else guess[1]
244
            except:
245
                pass
246
            args['content_type'] = type if type else 'plain/text'
247
            args['content_encoding'] = enc if enc else None
248

    
249
            obj['meta'] = args
250

    
251
            path = '/%s/%s' % (container, name)
252
            self.client.create_object(container, name, f=StringIO(obj['data']),
253
                                      meta=meta, **args)
254

    
255
            return obj
256
        except IOError:
257
            return
258

    
259
class AccountHead(BaseTestCase):
260
    def setUp(self):
261
        BaseTestCase.setUp(self)
262
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
263
        self.containers.sort()
264
        
265
        for item in self.containers:
266
            self.client.create_container(item)
267

    
268
        meta = {'foo':'bar'}
269
        self.client.update_account_metadata(**meta)
270
        #self.updated_meta = self.initial_meta.update(meta)
271

    
272
    def test_get_account_meta(self):
273
        meta = self.client.retrieve_account_metadata()
274

    
275
        containers = self.client.list_containers()
276
        l = str(len(containers))
277
        self.assertEqual(meta['x-account-container-count'], l)
278
        size1 = 0
279
        size2 = 0
280
        for c in containers:
281
            m = self.client.retrieve_container_metadata(c)
282
            csum = sum([o['bytes'] for o in self.client.list_objects(c, format='json')])
283
            self.assertEqual(int(m['x-container-bytes-used']), csum)
284
            size1 += int(m['x-container-bytes-used'])
285
            size2 += csum
286
        self.assertEqual(meta['x-account-bytes-used'], str(size1))
287
        self.assertEqual(meta['x-account-bytes-used'], str(size2))
288

    
289
    def test_get_account_403(self):
290
        self.assert_raises_fault(403,
291
                                 self.invalid_client.retrieve_account_metadata)
292

    
293
    def test_get_account_meta_until(self):
294
        t = datetime.datetime.utcnow()
295
        past = t - datetime.timedelta(minutes=15)
296
        past = int(_time.mktime(past.timetuple()))
297

    
298
        meta = {'premium':True}
299
        self.client.update_account_metadata(**meta)
300
        meta = self.client.retrieve_account_metadata(restricted=True,
301
                                                     until=past)
302
        self.assertTrue('premium' not in meta)
303

    
304
        meta = self.client.retrieve_account_metadata(restricted=True)
305
        self.assertTrue('premium' in meta)
306

    
307
    def test_get_account_meta_until_invalid_date(self):
308
        meta = {'premium':True}
309
        self.client.update_account_metadata(**meta)
310
        meta = self.client.retrieve_account_metadata(restricted=True,
311
                                                     until='kshfksfh')
312
        self.assertTrue('premium' in meta)
313

    
314
class AccountGet(BaseTestCase):
315
    def setUp(self):
316
        BaseTestCase.setUp(self)
317
        #create some containers
318
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
319
        self.containers.sort()
320
        
321
        for item in self.containers:
322
            self.client.create_container(item)
323

    
324
    def test_list(self):
325
        #list containers
326
        containers = self.client.list_containers()
327
        self.assertEquals(self.containers, containers)
328

    
329
    def test_list_403(self):
330
        self.assert_raises_fault(403, self.invalid_client.list_containers)
331

    
332
    def test_list_with_limit(self):
333
        limit = 2
334
        containers = self.client.list_containers(limit=limit)
335
        self.assertEquals(len(containers), limit)
336
        self.assertEquals(self.containers[:2], containers)
337

    
338
    def test_list_with_marker(self):
339
        l = 2
340
        m = 'bananas'
341
        containers = self.client.list_containers(limit=l, marker=m)
342
        i = self.containers.index(m) + 1
343
        self.assertEquals(self.containers[i:(i+l)], containers)
344

    
345
        m = 'oranges'
346
        containers = self.client.list_containers(limit=l, marker=m)
347
        i = self.containers.index(m) + 1
348
        self.assertEquals(self.containers[i:(i+l)], containers)
349

    
350
    def test_list_json_with_marker(self):
351
        l = 2
352
        m = 'bananas'
353
        containers = self.client.list_containers(limit=l, marker=m, format='json')
354
        self.assert_extended(containers, 'json', 'container', l)
355
        self.assertEqual(containers[0]['name'], 'kiwis')
356
        self.assertEqual(containers[1]['name'], 'oranges')
357

    
358
    def test_list_xml_with_marker(self):
359
        l = 2
360
        m = 'oranges'
361
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
362
        self.assert_extended(xml, 'xml', 'container', l)
363
        nodes = xml.getElementsByTagName('name')
364
        self.assertTrue(len(nodes) <= l)
365
        names = [n.childNodes[0].data for n in nodes]
366
        self.assertTrue('pears' in names or 'pears' > name for name in names)
367

    
368
    def test_if_modified_since(self):
369
        t = datetime.datetime.utcnow()
370
        t2 = t - datetime.timedelta(minutes=10)
371

    
372
        #add a new container
373
        self.client.create_container('dummy')
374

    
375
        for f in DATE_FORMATS:
376
            past = t2.strftime(f)
377
            try:
378
                c = self.client.list_containers(if_modified_since=past)
379
                self.assertEqual(len(c), len(self.containers) + 1)
380
            except Fault, f:
381
                self.failIf(f.status == 304) #fail if not modified
382
    
383
    def test_if_modified_since_invalid_date(self):
384
        c = self.client.list_containers(if_modified_since='')
385
        self.assertEqual(len(c), len(self.containers))
386

    
387
    def test_if_not_modified_since(self):
388
        now = datetime.datetime.utcnow()
389
        since = now + datetime.timedelta(1)
390

    
391
        for f in DATE_FORMATS:
392
            args = {'if_modified_since':'%s' %since.strftime(f)}
393

    
394
            #assert not modified
395
            self.assert_raises_fault(304, self.client.list_containers, **args)
396

    
397
    def test_if_unmodified_since(self):
398
        now = datetime.datetime.utcnow()
399
        since = now + datetime.timedelta(1)
400

    
401
        for f in DATE_FORMATS:
402
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
403

    
404
            #assert success
405
            self.assertEqual(self.containers, c)
406

    
407
    def test_if_unmodified_since_precondition_failed(self):
408
        t = datetime.datetime.utcnow()
409
        t2 = t - datetime.timedelta(minutes=10)
410

    
411
        #add a new container
412
        self.client.create_container('dummy')
413

    
414
        for f in DATE_FORMATS:
415
            past = t2.strftime(f)
416

    
417
            args = {'if_unmodified_since':'%s' %past}
418

    
419
            #assert precondition failed
420
            self.assert_raises_fault(412, self.client.list_containers, **args)
421

    
422
class AccountPost(BaseTestCase):
423
    def setUp(self):
424
        BaseTestCase.setUp(self)
425
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
426
        self.containers.sort()
427
        
428
        for item in self.containers:
429
            self.client.create_container(item)
430

    
431
        meta = {'foo':'bar'}
432
        self.client.update_account_metadata(**meta)
433
        self.updated_meta = self.initial_meta.update(meta)
434

    
435
    def test_update_meta(self):
436
        with AssertMappingInvariant(self.client.retrieve_account_groups):
437
            meta = {'test':'test', 'tost':'tost'}
438
            self.client.update_account_metadata(**meta)
439

    
440
            meta.update(self.initial_meta)
441
            self.assertEqual(meta,
442
                             self.client.retrieve_account_metadata(
443
                                restricted=True))
444

    
445
    def test_invalid_account_update_meta(self):
446
        meta = {'test':'test', 'tost':'tost'}
447
        self.assert_raises_fault(403,
448
                                 self.invalid_client.update_account_metadata,
449
                                 **meta)
450

    
451
    def test_reset_meta(self):
452
        with AssertMappingInvariant(self.client.retrieve_account_groups):
453
            meta = {'test':'test', 'tost':'tost'}
454
            self.client.update_account_metadata(**meta)
455

    
456
            meta = {'test':'test33'}
457
            self.client.reset_account_metadata(**meta)
458

    
459
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
460

    
461
    def test_delete_meta(self):
462
        with AssertMappingInvariant(self.client.retrieve_account_groups):
463
            meta = {'test':'test', 'tost':'tost'}
464
            self.client.update_account_metadata(**meta)
465

    
466
            self.client.delete_account_metadata(meta.keys())
467

    
468
            account_meta = self.client.retrieve_account_metadata(restricted=True)
469
            for m in meta:
470
                self.assertTrue(m not in account_meta.keys())
471

    
472
    def test_set_account_groups(self):
473
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
474
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
475
            self.client.set_account_groups(**groups)
476

    
477
            self.assertEqual(set(groups['pithosdev']),
478
                             set(self.client.retrieve_account_groups()['pithosdev']))
479

    
480
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
481
            self.client.set_account_groups(**more_groups)
482

    
483
            groups.update(more_groups)
484
            self.assertEqual(set(groups['clientsdev']),
485
                             set(self.client.retrieve_account_groups()['clientsdev']))
486

    
487
    def test_reset_account_groups(self):
488
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
489
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
490
                      'clientsdev':'pkanavos,mvasilak'}
491
            self.client.set_account_groups(**groups)
492

    
493
            self.assertEqual(set(groups['pithosdev'].split(',')),
494
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
495
            self.assertEqual(set(groups['clientsdev'].split(',')),
496
                             set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
497

    
498
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
499
            self.client.reset_account_groups(**groups)
500

    
501
            self.assertEqual(set(groups['pithosdev'].split(',')),
502
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
503

    
504
    def test_delete_account_groups(self):
505
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
506
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
507
                      'clientsdev':'pkanavos,mvasilak'}
508
            self.client.set_account_groups(**groups)
509

    
510
            self.client.unset_account_groups(groups.keys())
511

    
512
            self.assertEqual({}, self.client.retrieve_account_groups())
513

    
514
class ContainerHead(BaseTestCase):
515
    def setUp(self):
516
        BaseTestCase.setUp(self)
517
        self.container = 'apples'
518
        self.client.create_container(self.container)
519

    
520
    def test_get_meta(self):
521
        meta = {'trash':'true'}
522
        t1 = datetime.datetime.utcnow()
523
        o = self.upload_random_data(self.container, o_names[0], **meta)
524
        if o:
525
            headers = self.client.retrieve_container_metadata(self.container)
526
            self.assertEqual(headers['x-container-object-count'], '1')
527
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
528
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
529
            delta = (t2 - t1)
530
            threashold = datetime.timedelta(seconds=1)
531
            self.assertTrue(delta < threashold)
532
            self.assertTrue(headers['x-container-object-meta'])
533
            self.assertTrue('Trash' in headers['x-container-object-meta'])
534

    
535
class ContainerGet(BaseTestCase):
536
    def setUp(self):
537
        BaseTestCase.setUp(self)
538
        self.container = ['pears', 'apples']
539
        for c in self.container:
540
            self.client.create_container(c)
541
        self.obj = []
542
        for o in o_names[:8]:
543
            self.obj.append(self.upload_random_data(self.container[0], o))
544
        for o in o_names[8:]:
545
            self.obj.append(self.upload_random_data(self.container[1], o))
546
    
547
    def test_list_shared(self):
548
        self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
549
        objs = self.client.list_objects(self.container[0], shared=True)
550
        self.assertEqual(objs, [self.obj[0]['name']])
551
        
552
        # create child object
553
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
554
        objs = self.client.list_objects(self.container[0], shared=True)
555
        self.assertEqual(objs, [self.obj[0]['name']])
556
        
557
        # test inheritance
558
        self.client.create_folder(self.container[1], 'folder')
559
        self.client.share_object(self.container[1], 'folder', ('*',))
560
        self.upload_random_data(self.container[1], 'folder/object')
561
        objs = self.client.list_objects(self.container[1], shared=True)
562
        self.assertEqual(objs, ['folder', 'folder/object'])
563
    
564
    def test_list_public(self):
565
        self.client.publish_object(self.container[0], self.obj[0]['name'])
566
        objs = self.client.list_objects(self.container[0], public=True)
567
        self.assertEqual(objs, [self.obj[0]['name']])
568
        
569
        # create child object
570
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
571
        objs = self.client.list_objects(self.container[0], public=True)
572
        self.assertEqual(objs, [self.obj[0]['name']])
573
        
574
        # test inheritance
575
        self.client.create_folder(self.container[1], 'folder')
576
        self.client.publish_object(self.container[1], 'folder')
577
        self.upload_random_data(self.container[1], 'folder/object')
578
        objs = self.client.list_objects(self.container[1], public=True)
579
        self.assertEqual(objs, ['folder'])
580
    
581
    def test_list_shared_public(self):
582
        self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
583
        self.client.publish_object(self.container[0], self.obj[1]['name'])
584
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
585
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
586
        
587
        # create child object
588
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
589
        self.upload_random_data(self.container[0], strnextling(self.obj[1]['name']))
590
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
591
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
592
        
593
        # test inheritance
594
        self.client.create_folder(self.container[1], 'folder1')
595
        self.client.share_object(self.container[1], 'folder1', ('*',))
596
        self.upload_random_data(self.container[1], 'folder1/object')
597
        self.client.create_folder(self.container[1], 'folder2')
598
        self.client.publish_object(self.container[1], 'folder2')
599
        o = self.upload_random_data(self.container[1], 'folder2/object')
600
        objs = self.client.list_objects(self.container[1], shared=True, public=True)
601
        self.assertEqual(objs, ['folder1', 'folder1/object', 'folder2'])
602
    
603
    def test_list_objects(self):
604
        objects = self.client.list_objects(self.container[0])
605
        l = [elem['name'] for elem in self.obj[:8]]
606
        l.sort()
607
        self.assertEqual(objects, l)
608

    
609
    def test_list_objects_containing_slash(self):
610
        self.client.create_container('test')
611
        self.upload_random_data('test', '/objectname')
612

    
613
        objects = self.client.list_objects('test')
614
        self.assertEqual(objects, ['/objectname'])
615

    
616
        objects = self.client.list_objects('test', format='json')
617
        self.assertEqual(objects[0]['name'], '/objectname')
618

    
619
        objects = self.client.list_objects('test', format='xml')
620
        self.assert_extended(objects, 'xml', 'object')
621
        node_name = objects.getElementsByTagName('name')[0]
622
        self.assertEqual(node_name.firstChild.data, '/objectname')
623

    
624
    def test_list_objects_with_limit_marker(self):
625
        objects = self.client.list_objects(self.container[0], limit=2)
626
        l = [elem['name'] for elem in self.obj[:8]]
627
        l.sort()
628
        self.assertEqual(objects, l[:2])
629

    
630
        markers = ['How To Win Friends And Influence People.pdf',
631
                   'moms_birthday.jpg']
632
        limit = 4
633
        for m in markers:
634
            objects = self.client.list_objects(self.container[0], limit=limit,
635
                                               marker=m)
636
            l = [elem['name'] for elem in self.obj[:8]]
637
            l.sort()
638
            start = l.index(m) + 1
639
            end = start + limit
640
            end = end if len(l) >= end else len(l)
641
            self.assertEqual(objects, l[start:end])
642

    
643
    #takes too long
644
    def _test_list_limit_exceeds(self):
645
        self.client.create_container('pithos')
646

    
647
        for i in range(10001):
648
            self.client.create_zero_length_object('pithos', i)
649

    
650
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
651

    
652
    def test_list_empty_params(self):
653
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
654
        if objects:
655
            objects = objects.strip().split('\n')
656
        self.assertEqual(objects,
657
                         self.client.list_objects(self.container[0]))
658

    
659
    def test_list_pseudo_hierarchical_folders(self):
660
        objects = self.client.list_objects(self.container[1], prefix='photos',
661
                                           delimiter='/')
662
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
663
                           'photos/plants/'], objects)
664

    
665
        objects = self.client.list_objects(self.container[1],
666
                                           prefix='photos/animals',
667
                                           delimiter='/')
668
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
669
        self.assertEquals(l, objects)
670

    
671
        objects = self.client.list_objects(self.container[1], path='photos')
672
        self.assertEquals(['photos/me.jpg'], objects)
673

    
674
    def test_extended_list_json(self):
675
        objects = self.client.list_objects(self.container[1], format='json',
676
                                           limit=2, prefix='photos/animals',
677
                                           delimiter='/')
678
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
679
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
680

    
681
    def test_extended_list_xml(self):
682
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
683
                                       prefix='photos', delimiter='/')
684
        self.assert_extended(xml, 'xml', 'object', size=4)
685
        dirs = xml.getElementsByTagName('subdir')
686
        self.assertEqual(len(dirs), 2)
687
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
688
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
689

    
690
        objects = xml.getElementsByTagName('name')
691
        self.assertEqual(len(objects), 1)
692
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
693

    
694
    def test_list_meta_double_matching(self):
695
        meta = {'quality':'aaa', 'stock':'true'}
696
        self.client.update_object_metadata(self.container[0],
697
                                           self.obj[0]['name'], **meta)
698
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
699
        self.assertEqual(len(obj), 1)
700
        self.assertTrue(obj, self.obj[0]['name'])
701

    
702
    def test_list_using_meta(self):
703
        meta = {'quality':'aaa'}
704
        for o in self.obj[:2]:
705
            self.client.update_object_metadata(self.container[0], o['name'],
706
                                               **meta)
707
        meta = {'stock':'true'}
708
        for o in self.obj[3:5]:
709
            self.client.update_object_metadata(self.container[0], o['name'],
710
                                               **meta)
711

    
712
        obj = self.client.list_objects(self.container[0], meta='Quality')
713
        self.assertEqual(len(obj), 2)
714
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
715

    
716
        # test case insensitive
717
        obj = self.client.list_objects(self.container[0], meta='quality')
718
        self.assertEqual(len(obj), 2)
719
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
720

    
721
        # test multiple matches
722
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
723
        self.assertEqual(len(obj), 4)
724
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
725

    
726
        # test non 1-1 multiple match
727
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
728
        self.assertEqual(len(obj), 2)
729
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
730

    
731
    def test_if_modified_since(self):
732
        t = datetime.datetime.utcnow()
733
        t2 = t - datetime.timedelta(minutes=10)
734

    
735
        #add a new object
736
        self.upload_random_data(self.container[0], o_names[0])
737

    
738
        for f in DATE_FORMATS:
739
            past = t2.strftime(f)
740
            try:
741
                o = self.client.list_objects(self.container[0],
742
                                            if_modified_since=past)
743
                self.assertEqual(o,
744
                                 self.client.list_objects(self.container[0]))
745
            except Fault, f:
746
                self.failIf(f.status == 304) #fail if not modified
747

    
748
    def test_if_modified_since_invalid_date(self):
749
        headers = {'if-modified-since':''}
750
        o = self.client.list_objects(self.container[0], if_modified_since='')
751
        self.assertEqual(o, self.client.list_objects(self.container[0]))
752

    
753
    def test_if_not_modified_since(self):
754
        now = datetime.datetime.utcnow()
755
        since = now + datetime.timedelta(1)
756

    
757
        for f in DATE_FORMATS:
758
            args = {'if_modified_since':'%s' %since.strftime(f)}
759

    
760
            #assert not modified
761
            self.assert_raises_fault(304, self.client.list_objects,
762
                                     self.container[0], **args)
763

    
764
    def test_if_unmodified_since(self):
765
        now = datetime.datetime.utcnow()
766
        since = now + datetime.timedelta(1)
767

    
768
        for f in DATE_FORMATS:
769
            obj = self.client.list_objects(self.container[0],
770
                                           if_unmodified_since=since.strftime(f))
771

    
772
            #assert unmodified
773
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
774

    
775
    def test_if_unmodified_since_precondition_failed(self):
776
        t = datetime.datetime.utcnow()
777
        t2 = t - datetime.timedelta(minutes=10)
778

    
779
        #add a new container
780
        self.client.create_container('dummy')
781

    
782
        for f in DATE_FORMATS:
783
            past = t2.strftime(f)
784

    
785
            args = {'if_unmodified_since':'%s' %past}
786

    
787
            #assert precondition failed
788
            self.assert_raises_fault(412, self.client.list_objects,
789
                                     self.container[0], **args)
790

    
791
class ContainerPut(BaseTestCase):
792
    def setUp(self):
793
        BaseTestCase.setUp(self)
794
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
795
        self.containers.sort()
796
    
797
    def test_create(self):
798
        self.client.create_container(self.containers[0])
799
        containers = self.client.list_containers()
800
        self.assertTrue(self.containers[0] in containers)
801
        self.assert_container_exists(self.containers[0])
802

    
803
    def test_create_twice(self):
804
        self.client.create_container(self.containers[0])
805
        self.assertTrue(not self.client.create_container(self.containers[0]))
806

    
807
    def test_quota(self):
808
        self.client.create_container(self.containers[0])
809

    
810
        policy = {'quota':100}
811
        self.client.set_container_policies(self.containers[0], **policy)
812

    
813
        meta = self.client.retrieve_container_metadata(self.containers[0])
814
        self.assertTrue('x-container-policy-quota' in meta)
815
        self.assertEqual(meta['x-container-policy-quota'], '100')
816

    
817
        args = [self.containers[0], 'o1']
818
        kwargs = {'length':101}
819
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
820

    
821
        #reset quota
822
        policy = {'quota':0}
823
        self.client.set_container_policies(self.containers[0], **policy)
824

    
825
class ContainerPost(BaseTestCase):
826
    def setUp(self):
827
        BaseTestCase.setUp(self)
828
        self.container = 'apples'
829
        self.client.create_container(self.container)
830

    
831
    def test_update_meta(self):
832
        meta = {'test':'test33',
833
                'tost':'tost22'}
834
        self.client.update_container_metadata(self.container, **meta)
835
        headers = self.client.retrieve_container_metadata(self.container)
836
        for k,v in meta.items():
837
            k = 'x-container-meta-%s' % k
838
            self.assertTrue(headers[k])
839
            self.assertEqual(headers[k], v)
840

    
841
class ContainerDelete(BaseTestCase):
842
    def setUp(self):
843
        BaseTestCase.setUp(self)
844
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
845
        self.containers.sort()
846
        
847
        for c in self.containers:
848
            self.client.create_container(c)
849

    
850
    def test_delete(self):
851
        status = self.client.delete_container(self.containers[0])[0]
852
        self.assertEqual(status, 204)
853

    
854
    def test_delete_non_empty(self):
855
        self.upload_random_data(self.containers[1], o_names[0])
856
        self.assert_raises_fault(409, self.client.delete_container,
857
                                 self.containers[1])
858

    
859
    def test_delete_invalid(self):
860
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
861
    
862
    def test_delete_contents(self):
863
        self.client.create_folder(self.containers[0], 'folder-1')
864
        self.upload_random_data(self.containers[1], 'folder-1/%s' % o_names[0])
865
        self.client.create_folder(self.containers[0], 'folder-1/subfolder')
866
        self.client.create_folder(self.containers[0], 'folder-2/%s' % o_names[1])
867
                
868
        objects = self.client.list_objects(self.containers[0])
869
        self.client.delete_container(self.containers[0], delimiter='/')
870
        for o in objects:
871
            self.assert_object_not_exists(self.containers[0], o)
872
        self.assert_container_exists(self.containers[0])
873

    
874
class ObjectGet(BaseTestCase):
875
    def setUp(self):
876
        BaseTestCase.setUp(self)
877
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
878
        self.containers.sort()
879
        
880
        #create some containers
881
        for c in self.containers:
882
            self.client.create_container(c)
883

    
884
        #upload a file
885
        names = ('obj1', 'obj2')
886
        self.objects = []
887
        for n in names:
888
            self.objects.append(self.upload_random_data(self.containers[1], n))
889

    
890
    def test_versions(self):
891
        c = self.containers[1]
892
        o = self.objects[0]
893
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
894
        self.assert_versionlist_structure(b)
895

    
896
        #update meta
897
        meta = {'quality':'AAA', 'stock':True}
898
        self.client.update_object_metadata(c, o['name'], **meta)
899

    
900
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
901
        self.assert_versionlist_structure(a)
902
        self.assertEqual(len(b)+1, len(a))
903
        self.assertEqual(b, a[:-1])
904

    
905
        #get exact previous version metadata
906
        v = a[-2][0]
907
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
908
                                                      restricted=True,
909
                                                      version=v)
910
        (self.assertTrue(k not in v_meta) for k in meta.keys())
911
        
912
        #update obejct
913
        data = get_random_data()
914
        self.client.update_object(c, o['name'], StringIO(data))
915
        
916
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
917
        self.assert_versionlist_structure(aa)
918
        self.assertEqual(len(a)+1, len(aa))
919
        self.assertEqual(a, aa[:-1])
920

    
921
        #get exact previous version
922
        v = aa[-3][0]
923
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
924
        self.assertEqual(o['data'], v_data)
925
        self.assertEqual(self.client.retrieve_object(c, o['name']),
926
                         '%s%s' %(v_data, data))
927

    
928
    def test_get(self):
929
        #perform get
930
        o = self.client.retrieve_object(self.containers[1],
931
                                        self.objects[0]['name'],
932
                                        self.objects[0]['meta'])
933
        self.assertEqual(o, self.objects[0]['data'])
934

    
935
    def test_objects_with_trailing_spaces(self):
936
        self.client.create_container('test')
937
        #create 'a' object
938
        self.upload_random_data('test', 'a')
939
        #look for 'a ' object
940
        self.assert_raises_fault(404, self.client.retrieve_object,
941
                                 'test', 'a ')
942

    
943
        #delete 'a' object
944
        self.client.delete_object('test', 'a')
945
        self.assert_raises_fault(404, self.client.retrieve_object,
946
                                 'test', 'a')
947

    
948
        #create 'a ' object
949
        self.upload_random_data('test', 'a ')
950
        #look for 'a' object
951
        self.assert_raises_fault(404, self.client.retrieve_object,
952
                                 'test', 'a')
953

    
954
    def test_get_invalid(self):
955
        self.assert_raises_fault(404, self.client.retrieve_object,
956
                                 self.containers[0], self.objects[0]['name'])
957

    
958
    def test_get_partial(self):
959
        #perform get with range
960
        status, headers, data = self.client.request_object(self.containers[1],
961
                                                            self.objects[0]['name'],
962
                                                            range='bytes=0-499')
963

    
964
        #assert successful partial content
965
        self.assertEqual(status, 206)
966

    
967
        #assert content-type
968
        self.assertEqual(headers['content-type'],
969
                         self.objects[0]['meta']['content_type'])
970

    
971
        #assert content length
972
        self.assertEqual(int(headers['content-length']), 500)
973

    
974
        #assert content
975
        self.assertEqual(self.objects[0]['data'][:500], data)
976

    
977
    def test_get_final_500(self):
978
        #perform get with range
979
        headers = {'range':'bytes=-500'}
980
        status, headers, data = self.client.request_object(self.containers[1],
981
                                                            self.objects[0]['name'],
982
                                                            range='bytes=-500')
983

    
984
        #assert successful partial content
985
        self.assertEqual(status, 206)
986

    
987
        #assert content-type
988
        self.assertEqual(headers['content-type'],
989
                         self.objects[0]['meta']['content_type'])
990

    
991
        #assert content length
992
        self.assertEqual(int(headers['content-length']), 500)
993

    
994
        #assert content
995
        self.assertTrue(self.objects[0]['data'][-500:], data)
996

    
997
    def test_get_rest(self):
998
        #perform get with range
999
        offset = len(self.objects[0]['data']) - 500
1000
        status, headers, data = self.client.request_object(self.containers[1],
1001
                                                self.objects[0]['name'],
1002
                                                range='bytes=%s-' %offset)
1003

    
1004
        #assert successful partial content
1005
        self.assertEqual(status, 206)
1006

    
1007
        #assert content-type
1008
        self.assertEqual(headers['content-type'],
1009
                         self.objects[0]['meta']['content_type'])
1010

    
1011
        #assert content length
1012
        self.assertEqual(int(headers['content-length']), 500)
1013

    
1014
        #assert content
1015
        self.assertTrue(self.objects[0]['data'][-500:], data)
1016

    
1017
    def test_get_range_not_satisfiable(self):
1018
        #perform get with range
1019
        offset = len(self.objects[0]['data']) + 1
1020

    
1021
        #assert range not satisfiable
1022
        self.assert_raises_fault(416, self.client.retrieve_object,
1023
                                 self.containers[1], self.objects[0]['name'],
1024
                                 range='bytes=0-%s' %offset)
1025

    
1026
    def test_multiple_range(self):
1027
        #perform get with multiple range
1028
        ranges = ['0-499', '-500', '1000-']
1029
        bytes = 'bytes=%s' % ','.join(ranges)
1030
        status, headers, data = self.client.request_object(self.containers[1],
1031
                                                           self.objects[0]['name'],
1032
                                                           range=bytes)
1033

    
1034
        # assert partial content
1035
        self.assertEqual(status, 206)
1036

    
1037
        # assert Content-Type of the reply will be multipart/byteranges
1038
        self.assertTrue(headers['content-type'])
1039
        content_type_parts = headers['content-type'].split()
1040
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1041

    
1042
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1043
        cparts = data.split(boundary)[1:-1]
1044

    
1045
        # assert content parts are exactly 2
1046
        self.assertEqual(len(cparts), len(ranges))
1047

    
1048
        # for each content part assert headers
1049
        i = 0
1050
        for cpart in cparts:
1051
            content = cpart.split('\r\n')
1052
            headers = content[1:3]
1053
            content_range = headers[0].split(': ')
1054
            self.assertEqual(content_range[0], 'Content-Range')
1055

    
1056
            r = ranges[i].split('-')
1057
            if not r[0] and not r[1]:
1058
                pass
1059
            elif not r[0]:
1060
                start = len(self.objects[0]['data']) - int(r[1])
1061
                end = len(self.objects[0]['data'])
1062
            elif not r[1]:
1063
                start = int(r[0])
1064
                end = len(self.objects[0]['data'])
1065
            else:
1066
                start = int(r[0])
1067
                end = int(r[1]) + 1
1068
            fdata = self.objects[0]['data'][start:end]
1069
            sdata = '\r\n'.join(content[4:-1])
1070
            self.assertEqual(len(fdata), len(sdata))
1071
            self.assertEquals(fdata, sdata)
1072
            i+=1
1073

    
1074
    def test_multiple_range_not_satisfiable(self):
1075
        #perform get with multiple range
1076
        out_of_range = len(self.objects[0]['data']) + 1
1077
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1078
        bytes = 'bytes=%s' % ','.join(ranges)
1079

    
1080
        # assert partial content
1081
        self.assert_raises_fault(416, self.client.retrieve_object,
1082
                                 self.containers[1],
1083
                                 self.objects[0]['name'], range=bytes)
1084

    
1085
    def test_get_with_if_match(self):
1086
        #perform get with If-Match
1087
        etag = self.objects[0]['hash']
1088
        status, headers, data = self.client.request_object(self.containers[1],
1089
                                                           self.objects[0]['name'],
1090
                                                           if_match=etag)
1091
        #assert get success
1092
        self.assertEqual(status, 200)
1093

    
1094
        #assert content-type
1095
        self.assertEqual(headers['content-type'],
1096
                         self.objects[0]['meta']['content_type'])
1097

    
1098
        #assert response content
1099
        self.assertEqual(self.objects[0]['data'], data)
1100

    
1101
    def test_get_with_if_match_star(self):
1102
        #perform get with If-Match *
1103
        headers = {'if-match':'*'}
1104
        status, headers, data = self.client.request_object(self.containers[1],
1105
                                                self.objects[0]['name'],
1106
                                                **headers)
1107
        #assert get success
1108
        self.assertEqual(status, 200)
1109

    
1110
        #assert content-type
1111
        self.assertEqual(headers['content-type'],
1112
                         self.objects[0]['meta']['content_type'])
1113

    
1114
        #assert response content
1115
        self.assertEqual(self.objects[0]['data'], data)
1116

    
1117
    def test_get_with_multiple_if_match(self):
1118
        #perform get with If-Match
1119
        etags = [i['hash'] for i in self.objects if i]
1120
        etags = ','.join('"%s"' % etag for etag in etags)
1121
        status, headers, data = self.client.request_object(self.containers[1],
1122
                                                           self.objects[0]['name'],
1123
                                                           if_match=etags)
1124
        #assert get success
1125
        self.assertEqual(status, 200)
1126

    
1127
        #assert content-type
1128
        self.assertEqual(headers['content-type'],
1129
                         self.objects[0]['meta']['content_type'])
1130

    
1131
        #assert content-type
1132
        self.assertEqual(headers['content-type'],
1133
                         self.objects[0]['meta']['content_type'])
1134

    
1135
        #assert response content
1136
        self.assertEqual(self.objects[0]['data'], data)
1137

    
1138
    def test_if_match_precondition_failed(self):
1139
        #assert precondition failed
1140
        self.assert_raises_fault(412, self.client.retrieve_object,
1141
                                 self.containers[1],
1142
                                 self.objects[0]['name'], if_match='123')
1143

    
1144
    def test_if_none_match(self):
1145
        #perform get with If-None-Match
1146
        status, headers, data = self.client.request_object(self.containers[1],
1147
                                                           self.objects[0]['name'],
1148
                                                           if_none_match='123')
1149

    
1150
        #assert get success
1151
        self.assertEqual(status, 200)
1152

    
1153
        #assert content-type
1154
        self.assertEqual(headers['content_type'],
1155
                         self.objects[0]['meta']['content_type'])
1156

    
1157
    def test_if_none_match(self):
1158
        #perform get with If-None-Match * and assert not modified
1159
        self.assert_raises_fault(304, self.client.retrieve_object,
1160
                                 self.containers[1],
1161
                                 self.objects[0]['name'],
1162
                                 if_none_match='*')
1163

    
1164
    def test_if_none_match_not_modified(self):
1165
        #perform get with If-None-Match and assert not modified
1166
        self.assert_raises_fault(304, self.client.retrieve_object,
1167
                                 self.containers[1],
1168
                                 self.objects[0]['name'],
1169
                                 if_none_match=self.objects[0]['hash'])
1170

    
1171
        meta = self.client.retrieve_object_metadata(self.containers[1],
1172
                                                    self.objects[0]['name'])
1173
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1174

    
1175
    def test_if_modified_since(self):
1176
        t = datetime.datetime.utcnow()
1177
        t2 = t - datetime.timedelta(minutes=10)
1178

    
1179
        #modify the object
1180
        self.upload_data(self.containers[1],
1181
                           self.objects[0]['name'],
1182
                           self.objects[0]['data'][:200])
1183

    
1184
        for f in DATE_FORMATS:
1185
            past = t2.strftime(f)
1186

    
1187
            headers = {'if-modified-since':'%s' %past}
1188
            try:
1189
                o = self.client.retrieve_object(self.containers[1],
1190
                                                self.objects[0]['name'],
1191
                                                if_modified_since=past)
1192
                self.assertEqual(o,
1193
                                 self.client.retrieve_object(self.containers[1],
1194
                                                             self.objects[0]['name']))
1195
            except Fault, f:
1196
                self.failIf(f.status == 304)
1197

    
1198
    def test_if_modified_since_invalid_date(self):
1199
        o = self.client.retrieve_object(self.containers[1],
1200
                                        self.objects[0]['name'],
1201
                                        if_modified_since='')
1202
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1203
                                                        self.objects[0]['name']))
1204

    
1205
    def test_if_not_modified_since(self):
1206
        now = datetime.datetime.utcnow()
1207
        since = now + datetime.timedelta(1)
1208

    
1209
        for f in DATE_FORMATS:
1210
            #assert not modified
1211
            self.assert_raises_fault(304, self.client.retrieve_object,
1212
                                     self.containers[1], self.objects[0]['name'],
1213
                                     if_modified_since=since.strftime(f))
1214

    
1215
    def test_if_unmodified_since(self):
1216
        now = datetime.datetime.utcnow()
1217
        since = now + datetime.timedelta(1)
1218

    
1219
        for f in DATE_FORMATS:
1220
            t = since.strftime(f)
1221
            status, headers, data = self.client.request_object(self.containers[1],
1222
                                                               self.objects[0]['name'],
1223
                                                               if_unmodified_since=t)
1224
            #assert success
1225
            self.assertEqual(status, 200)
1226
            self.assertEqual(self.objects[0]['data'], data)
1227

    
1228
            #assert content-type
1229
            self.assertEqual(headers['content-type'],
1230
                             self.objects[0]['meta']['content_type'])
1231

    
1232
    def test_if_unmodified_since_precondition_failed(self):
1233
        t = datetime.datetime.utcnow()
1234
        t2 = t - datetime.timedelta(minutes=10)
1235

    
1236
        #modify the object
1237
        self.upload_data(self.containers[1],
1238
                           self.objects[0]['name'],
1239
                           self.objects[0]['data'][:200])
1240

    
1241
        for f in DATE_FORMATS:
1242
            past = t2.strftime(f)
1243
            #assert precondition failed
1244
            self.assert_raises_fault(412, self.client.retrieve_object,
1245
                                     self.containers[1], self.objects[0]['name'],
1246
                                     if_unmodified_since=past)
1247

    
1248
    def test_hashes(self):
1249
        l = 8388609
1250
        fname = 'largefile'
1251
        o = self.upload_random_data(self.containers[1], fname, l)
1252
        if o:
1253
            body = self.client.retrieve_object(self.containers[1], fname,
1254
                                               format='json')
1255
            hashes = body['hashes']
1256
            block_size = body['block_size']
1257
            block_hash = body['block_hash']
1258
            block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1259
            self.assertTrue(len(hashes), block_num)
1260
            i = 0
1261
            for h in hashes:
1262
                start = i * block_size
1263
                end = (i + 1) * block_size
1264
                hash = compute_block_hash(o['data'][start:end], block_hash)
1265
                self.assertEqual(h, hash)
1266
                i += 1
1267

    
1268
class ObjectPut(BaseTestCase):
1269
    def setUp(self):
1270
        BaseTestCase.setUp(self)
1271
        self.container = 'c1'
1272
        self.client.create_container(self.container)
1273

    
1274
    def test_upload(self):
1275
        name = o_names[0]
1276
        meta = {'test':'test1'}
1277
        o = self.upload_random_data(self.container, name, **meta)
1278

    
1279
        headers = self.client.retrieve_object_metadata(self.container,
1280
                                                       name,
1281
                                                       restricted=True)
1282
        self.assertTrue('test' in headers.keys())
1283
        self.assertEqual(headers['test'], meta['test'])
1284

    
1285
        #assert uploaded content
1286
        status, h, data = self.client.request_object(self.container, name)
1287
        self.assertEqual(len(o['data']), int(h['content-length']))
1288
        self.assertEqual(o['data'], data)
1289

    
1290
        #assert content-type
1291
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1292

    
1293
    def _test_maximum_upload_size_exceeds(self):
1294
        name = o_names[0]
1295
        meta = {'test':'test1'}
1296
        #upload 5GB
1297
        length= 5 * (1024 * 1024 * 1024) + 1
1298
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1299
                                 name, length, **meta)
1300

    
1301
    def test_upload_with_name_containing_slash(self):
1302
        name = '/%s' % o_names[0]
1303
        meta = {'test':'test1'}
1304
        o = self.upload_random_data(self.container, name, **meta)
1305

    
1306
        self.assertEqual(o['data'],
1307
                         self.client.retrieve_object(self.container, name))
1308

    
1309
        self.assertTrue(name in self.client.list_objects(self.container))
1310

    
1311
    def test_create_directory_marker(self):
1312
        self.client.create_directory_marker(self.container, 'foo')
1313
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1314
        self.assertEqual(meta['content-length'], '0')
1315
        self.assertEqual(meta['content-type'], 'application/directory')
1316

    
1317
    def test_upload_unprocessable_entity(self):
1318
        meta={'etag':'123', 'test':'test1'}
1319

    
1320
        #assert unprocessable entity
1321
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1322
                                 o_names[0], **meta)
1323

    
1324
    def test_chunked_transfer(self):
1325
        data = get_random_data()
1326
        objname = 'object'
1327
        self.client.create_object_using_chunks(self.container, objname,
1328
                                               StringIO(data))
1329

    
1330
        uploaded_data = self.client.retrieve_object(self.container, objname)
1331
        self.assertEqual(data, uploaded_data)
1332

    
1333
    def test_manifestation(self):
1334
        prefix = 'myobject/'
1335
        data = ''
1336
        for i in range(5):
1337
            part = '%s%d' %(prefix, i)
1338
            o = self.upload_random_data(self.container, part)
1339
            data += o['data']
1340

    
1341
        manifest = '%s/%s' %(self.container, prefix)
1342
        self.client.create_manifestation(self.container, 'large-object', manifest)
1343

    
1344
        self.assert_object_exists(self.container, 'large-object')
1345
        self.assertEqual(data, self.client.retrieve_object(self.container,
1346
                                                           'large-object'))
1347
        
1348
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1349
        hashes = r['hashes']
1350
        block_size = int(r['block_size'])
1351
        block_hash = r['block_hash']
1352
        l = len(data)
1353
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1354
        self.assertEqual(block_num, len(hashes))
1355
        
1356
        #wrong manifestation
1357
        self.client.create_manifestation(self.container, 'large-object',
1358
                                         '%s/invalid' % self.container)
1359
        self.assertEqual('', self.client.retrieve_object(self.container,
1360
                                                         'large-object'))
1361

    
1362
    def test_create_zero_length_object(self):
1363
        c = self.container
1364
        o = 'object'
1365
        zero = self.client.create_zero_length_object(c, o)
1366
        zero_meta = self.client.retrieve_object_metadata(c, o)
1367
        zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1368
        zero_data = self.client.retrieve_object(c, o)
1369

    
1370
        self.assertEqual(int(zero_meta['content-length']), 0)
1371
        hasher = newhasher('sha256')
1372
        hasher.update("")
1373
        emptyhash = hasher.digest()
1374
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1375
        self.assertEqual(zero_data, '')
1376

    
1377
    def test_create_object_by_hashmap(self):
1378
        c = self.container
1379
        o = 'object'
1380
        self.upload_random_data(c, o)
1381
        hashmap = self.client.retrieve_object(c, o, format='json')
1382
        o2 = 'object-copy'
1383
        self.client.create_object_by_hashmap(c, o2, hashmap)
1384
        self.assertEqual(self.client.retrieve_object(c, o),
1385
                         self.client.retrieve_object(c, o))
1386

    
1387
class ObjectCopy(BaseTestCase):
1388
    def setUp(self):
1389
        BaseTestCase.setUp(self)
1390
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1391
        self.containers.sort()
1392
        
1393
        for c in self.containers:
1394
            self.client.create_container(c)
1395
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1396

    
1397
    def test_copy(self):
1398
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1399
                             self.containers[0], self.obj['name']):
1400
            #perform copy
1401
            meta = {'test':'testcopy'}
1402
            status = self.client.copy_object(self.containers[0],
1403
                                              self.obj['name'],
1404
                                              self.containers[0],
1405
                                              'testcopy',
1406
                                              meta)[0]
1407

    
1408
            #assert copy success
1409
            self.assertEqual(status, 201)
1410

    
1411
            #assert access the new object
1412
            headers = self.client.retrieve_object_metadata(self.containers[0],
1413
                                                           'testcopy')
1414
            self.assertTrue('x-object-meta-test' in headers.keys())
1415
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1416

    
1417
            #assert etag is the same
1418
            self.assertEqual(headers['etag'], self.obj['hash'])
1419

    
1420
            #assert src object still exists
1421
            self.assert_object_exists(self.containers[0], self.obj['name'])
1422

    
1423
    def test_copy_from_different_container(self):
1424
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1425
                             self.containers[0], self.obj['name']):
1426
            meta = {'test':'testcopy'}
1427
            status = self.client.copy_object(self.containers[0],
1428
                                             self.obj['name'],
1429
                                             self.containers[1],
1430
                                             'testcopy',
1431
                                             meta)[0]
1432
            self.assertEqual(status, 201)
1433

    
1434
            # assert updated metadata
1435
            meta = self.client.retrieve_object_metadata(self.containers[1],
1436
                                                           'testcopy',
1437
                                                           restricted=True)
1438
            self.assertTrue('test' in meta.keys())
1439
            self.assertTrue(meta['test'], 'testcopy')
1440

    
1441
            #assert src object still exists
1442
            self.assert_object_exists(self.containers[0], self.obj['name'])
1443

    
1444
    def test_copy_invalid(self):
1445
        #copy from invalid object
1446
        meta = {'test':'testcopy'}
1447
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1448
                                 'test.py', self.containers[1], 'testcopy', meta)
1449

    
1450
        #copy from invalid container
1451
        meta = {'test':'testcopy'}
1452
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1453
                                 self.obj['name'], self.containers[1],
1454
                                 'testcopy', meta)
1455
    
1456
    def test_copy_dir(self):
1457
        self.client.create_folder(self.containers[0], 'dir')
1458
        self.client.create_folder(self.containers[0], 'dir/subdir')
1459
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1460
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1461
        self.client.create_folder(self.containers[0], 'dirs')
1462
        
1463
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1464
        self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1465
        for object in objects[:-1]:
1466
            self.assert_object_exists(self.containers[0], object)
1467
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1468
            meta0 = self.client.retrieve_object_metadata(self.containers[0], object)
1469
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1470
            t = ('content-length', 'x-object-hash', 'content-type')
1471
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1472
        self.assert_object_not_exists(self.containers[1], objects[-1])
1473
        
1474
class ObjectMove(BaseTestCase):
1475
    def setUp(self):
1476
        BaseTestCase.setUp(self)
1477
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1478
        self.containers.sort()
1479
        
1480
        for c in self.containers:
1481
            self.client.create_container(c)
1482
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1483

    
1484
    def test_move(self):
1485
        meta = self.client.retrieve_object_metadata(self.containers[0],
1486
                                                    self.obj['name'])
1487
        self.assertTrue('x-object-uuid' in meta)
1488
        uuid = meta['x-object-uuid']
1489

    
1490
        #perform move
1491
        meta = {'test':'testcopy'}
1492
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1493
        status = self.client.move_object(self.containers[0], self.obj['name'],
1494
                                         self.containers[0], 'testcopy',
1495
                                         meta)[0]
1496

    
1497
        #assert successful move
1498
        self.assertEqual(status, 201)
1499

    
1500
        #assert updated metadata
1501
        meta = self.client.retrieve_object_metadata(self.containers[0],
1502
                                                    'testcopy')
1503
        self.assertTrue('x-object-meta-test' in meta.keys())
1504
        self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1505

    
1506
        #assert same uuid
1507
        self.assertTrue(meta['x-object-uuid'], uuid)
1508

    
1509
        #assert src object no more exists
1510
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1511
    
1512
    
1513
    def test_move_dir(self):
1514
        meta = {}
1515
        self.client.create_folder(self.containers[0], 'dir')
1516
        meta['dir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir')
1517
        self.client.create_folder(self.containers[0], 'dir/subdir')
1518
        meta['dir/subdir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir')
1519
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1520
        meta['dir/object1.jpg'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/object1.jpg')
1521
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1522
        meta['dir/subdir/object2.pdf'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir/object2.pdf')
1523
        self.client.create_folder(self.containers[0], 'dirs')
1524
        meta['dirs'] = self.client.retrieve_object_metadata(self.containers[0], 'dirs')
1525
        
1526
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1527
        self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1528
        for object in objects[:-1]:
1529
            self.assert_object_not_exists(self.containers[0], object)
1530
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1531
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1532
            t = ('content-length', 'x-object-hash', 'content-type')
1533
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1534
        self.assert_object_exists(self.containers[0], objects[-1])
1535
        self.assert_object_not_exists(self.containers[1], objects[-1])
1536

    
1537
class ObjectPost(BaseTestCase):
1538
    def setUp(self):
1539
        BaseTestCase.setUp(self)
1540
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1541
        self.containers.sort()
1542
        
1543
        for c in self.containers:
1544
            self.client.create_container(c)
1545
        self.obj = []
1546
        for i in range(2):
1547
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1548

    
1549
    def test_update_meta(self):
1550
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1551
                                 self.containers[0],
1552
                                 self.obj[0]['name']):
1553
            #perform update metadata
1554
            more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1555
            status = self.client.update_object_metadata(self.containers[0],
1556
                                                        self.obj[0]['name'],
1557
                                                        **more)[0]
1558
            #assert request accepted
1559
            self.assertEqual(status, 202)
1560

    
1561
            #assert old metadata are still there
1562
            headers = self.client.retrieve_object_metadata(self.containers[0],
1563
                                                           self.obj[0]['name'],
1564
                                                           restricted=True)
1565
            #assert new metadata have been updated
1566
            for k,v in more.items():
1567
                self.assertTrue(k in headers.keys())
1568
                self.assertTrue(headers[k], v)
1569

    
1570
            #out of limits
1571
            more = {'f' * 114: 'b' * 257}
1572
            self.assert_raises_fault(400, self.client.update_object_metadata,
1573
                                                        self.containers[0],
1574
                                                        self.obj[0]['name'],
1575
                                                        **more)
1576
            
1577
            #perform update metadata
1578
            more = {'α': 'β' * 256}
1579
            status = self.client.update_object_metadata(self.containers[0],
1580
                                                        self.obj[0]['name'],
1581
                                                        **more)[0]
1582
            #assert request accepted
1583
            self.assertEqual(status, 202)
1584
            
1585
            #assert old metadata are still there
1586
            headers = self.client.retrieve_object_metadata(self.containers[0],
1587
                                                           self.obj[0]['name'],
1588
                                                           restricted=True)
1589
            #assert new metadata have been updated
1590
            for k,v in more.items():
1591
                self.assertTrue(k in headers.keys())
1592
                self.assertTrue(headers[k], v)
1593
            
1594
            #out of limits
1595
            more = {'α': 'β' * 257}
1596
            self.assert_raises_fault(400, self.client.update_object_metadata,
1597
                                                        self.containers[0],
1598
                                                        self.obj[0]['name'],
1599
                                                        **more)
1600
    
1601
    def test_update_object(self,
1602
                           first_byte_pos=0,
1603
                           last_byte_pos=499,
1604
                           instance_length = True,
1605
                           content_length = 500):
1606
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1607
                                 self.containers[0],
1608
                                 self.obj[0]['name']):
1609
            l = len(self.obj[0]['data'])
1610
            range = 'bytes %d-%d/%s' %(first_byte_pos,
1611
                                           last_byte_pos,
1612
                                            l if instance_length else '*')
1613
            partial = last_byte_pos - first_byte_pos + 1
1614
            length = first_byte_pos + partial
1615
            data = get_random_data(partial)
1616
            args = {'content_type':'application/octet-stream',
1617
                    'content_range':'%s' %range}
1618
            if content_length:
1619
                args['content_length'] = content_length
1620

    
1621
            r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1622
                                      StringIO(data), **args)
1623
            status = r[0]
1624
            etag = r[1]['etag']
1625
            if partial < 0 or (instance_length and l <= last_byte_pos):
1626
                self.assertEqual(status, 202)
1627
            else:
1628
                self.assertEqual(status, 204)
1629
                #check modified object
1630
                content = self.client.retrieve_object(self.containers[0],
1631
                                                  self.obj[0]['name'])
1632
                self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1633
                self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1634
                self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1635
                self.assertEqual(etag, compute_md5_hash(content))
1636

    
1637
    def test_update_object_lt_blocksize(self):
1638
        self.test_update_object(10, 20, content_length=None)
1639

    
1640
    def test_update_object_gt_blocksize(self):
1641
        o = self.upload_random_data(self.containers[0], o_names[1],
1642
                                length=4*1024*1024+5)
1643
        c = self.containers[0]
1644
        o_name = o['name']
1645
        o_data = o['data']
1646
        first_byte_pos = 4*1024*1024+1
1647
        last_byte_pos = 4*1024*1024+4
1648
        l = last_byte_pos - first_byte_pos + 1
1649
        data = get_random_data(l)
1650
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1651
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1652
        content = self.client.retrieve_object(c, o_name)
1653
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1654
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1655
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1656

    
1657
    def test_update_object_divided_by_blocksize(self):
1658
        o = self.upload_random_data(self.containers[0], o_names[1],
1659
                                length=4*1024*1024+5)
1660
        c = self.containers[0]
1661
        o_name = o['name']
1662
        o_data = o['data']
1663
        first_byte_pos = 4*1024*1024
1664
        last_byte_pos = 5*1024*1024
1665
        l = last_byte_pos - first_byte_pos + 1
1666
        data = get_random_data(l)
1667
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1668
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1669
        content = self.client.retrieve_object(c, o_name)
1670
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1671
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1672
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1673

    
1674
    def test_update_object_no_content_length(self):
1675
        self.test_update_object(content_length = None)
1676

    
1677
    def test_update_object_invalid_content_length(self):
1678
        with AssertContentInvariant(self.client.retrieve_object,
1679
                                    self.containers[0], self.obj[0]['name']):
1680
            self.assert_raises_fault(400, self.test_update_object,
1681
                                     content_length = 1000)
1682

    
1683
    def test_update_object_invalid_range(self):
1684
        with AssertContentInvariant(self.client.retrieve_object,
1685
                                    self.containers[0], self.obj[0]['name']):
1686
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1687

    
1688
    def test_update_object_invalid_range_and_length(self):
1689
        with AssertContentInvariant(self.client.retrieve_object,
1690
                                    self.containers[0], self.obj[0]['name']):
1691
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1692
                                     -1)
1693

    
1694
    def test_update_object_invalid_range_with_no_content_length(self):
1695
        with AssertContentInvariant(self.client.retrieve_object,
1696
                                    self.containers[0], self.obj[0]['name']):
1697
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1698
                                     content_length = None)
1699

    
1700
    def test_update_object_out_of_limits(self):
1701
        with AssertContentInvariant(self.client.retrieve_object,
1702
                                    self.containers[0], self.obj[0]['name']):
1703
            l = len(self.obj[0]['data'])
1704
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1705

    
1706
    def test_append(self):
1707
        data = get_random_data(500)
1708
        headers = {}
1709
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1710
                                  StringIO(data), content_length=500,
1711
                                  content_type='application/octet-stream')
1712

    
1713
        content = self.client.retrieve_object(self.containers[0],
1714
                                              self.obj[0]['name'])
1715
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1716
        self.assertEqual(content[:-500], self.obj[0]['data'])
1717

    
1718
    def test_update_with_chunked_transfer(self):
1719
        data = get_random_data(500)
1720
        dl = len(data)
1721
        fl = len(self.obj[0]['data'])
1722

    
1723
        self.client.update_object_using_chunks(self.containers[0],
1724
                                               self.obj[0]['name'],
1725
                                               StringIO(data),
1726
                                               offset=0,
1727
                                               content_type='application/octet-stream')
1728

    
1729
        #check modified object
1730
        content = self.client.retrieve_object(self.containers[0],
1731
                                              self.obj[0]['name'])
1732
        self.assertEqual(content[0:dl], data)
1733
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1734

    
1735
    def test_update_from_other_object(self):
1736
        c = self.containers[0]
1737
        src = o_names[0]
1738
        dest = 'object'
1739

    
1740
        source_data = self.client.retrieve_object(c, src)
1741
        source_meta = self.client.retrieve_object_metadata(c, src)
1742
        source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1743

    
1744
        #update zero length object
1745
        self.client.create_zero_length_object(c, dest)
1746
        source_object = '/%s/%s' % (c, src)
1747
        self.client.update_from_other_source(c, dest, source_object)
1748
        dest_data = self.client.retrieve_object(c, src)
1749
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1750
        dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1751
        self.assertEqual(source_data, dest_data)
1752
        self.assertEqual(source_hash, dest_hash)
1753

    
1754
        #test append
1755
        self.client.update_from_other_source(c, dest, source_object)
1756
        content = self.client.retrieve_object(c, dest)
1757
        self.assertEqual(source_data * 2, content)
1758

    
1759
    def test_update_range_from_other_object(self):
1760
        c = self.containers[0]
1761
        dest = 'object'
1762

    
1763
        #test update range
1764
        src = self.obj[1]['name']
1765
        src_data = self.client.retrieve_object(c, src)
1766

    
1767
        #update zero length object
1768
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1769
        source_object = '/%s/%s' % (c, src)
1770
        first_byte_pos = 4*1024*1024+1
1771
        last_byte_pos = 4*1024*1024+4
1772
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1773
        self.client.update_from_other_source(c, dest, source_object,
1774
                                             content_range=range)
1775
        content = self.client.retrieve_object(c, dest)
1776
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1777
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1778
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1779

    
1780
    def test_update_hashes_from_other_object(self):
1781
        c = self.containers[0]
1782
        dest = 'object'
1783

    
1784
        #test update range
1785
        src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1786

    
1787
        #update zero length object
1788
        prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1789
        source_object = '/%s/%s' % (c, o_names[0])
1790
        first_byte_pos = 4*1024*1024
1791
        last_byte_pos = 5*1024*1024
1792
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1793
        self.client.update_from_other_source(c, dest, source_object,
1794
                                             content_range=range)
1795
        content = self.client.retrieve_object(c, dest)
1796
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1797
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1798
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1799

    
1800

    
1801
    def test_update_zero_length_object(self):
1802
        c = self.containers[0]
1803
        o = 'object'
1804
        other = 'other'
1805
        zero = self.client.create_zero_length_object(c, o)
1806

    
1807
        data = get_random_data()
1808
        self.client.update_object(c, o, StringIO(data))
1809
        self.client.create_object(c, other, StringIO(data))
1810

    
1811
        self.assertEqual(self.client.retrieve_object(c, o),
1812
                         self.client.retrieve_object(c, other))
1813

    
1814
        self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1815
                         self.client.retrieve_object_hashmap(c, other)["hashes"])
1816

    
1817
class ObjectDelete(BaseTestCase):
1818
    def setUp(self):
1819
        BaseTestCase.setUp(self)
1820
        self.containers = ['c1', 'c2']
1821
        self.containers.extend(self.initial_containers)
1822
        
1823
        for c in self.containers:
1824
            self.client.create_container(c)
1825
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1826

    
1827
    def test_delete(self):
1828
        #perform delete object
1829
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1830

    
1831
    def test_delete_invalid(self):
1832
        #assert item not found
1833
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1834
                                 self.obj['name'])
1835
    
1836
    def test_delete_dir(self):
1837
        self.client.create_folder(self.containers[0], 'dir')
1838
        self.client.create_folder(self.containers[0], 'dir/subdir')
1839
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1840
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1841
        self.client.create_folder(self.containers[0], 'dirs')
1842
        
1843
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1844
        self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1845
        for object in objects[:-1]:
1846
            self.assert_object_not_exists(self.containers[0], object)
1847
        self.assert_object_exists(self.containers[0], objects[-1])
1848

    
1849
class ListSharing(BaseTestCase):
1850
    def setUp(self):
1851
        BaseTestCase.setUp(self)
1852
        for i in range(2):
1853
            self.client.create_container('c%s' %i)
1854
        self.client.create_container('c')
1855
        for i in range(2):
1856
            self.upload_random_data('c1', 'o%s' %i)
1857
        accounts = OTHER_ACCOUNTS.copy()
1858
        self.o1_sharing = accounts.popitem()
1859
        self.client.share_object('c1', 'o1', (self.o1_sharing[1],), read=True)
1860
        
1861
        l = []
1862
        for i in range(len(OTHER_ACCOUNTS) - 1):
1863
            l.append(accounts.popitem())
1864
    
1865
    def tearDown(self):
1866
        pass
1867
    
1868
    def test_list_other_shared(self):
1869
        self.other = Pithos_Client(get_url(),
1870
                              self.o1_sharing[0],
1871
                              self.o1_sharing[1])
1872
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1873

    
1874
    def test_list_my_shared(self):
1875
        my_shared_containers = self.client.list_containers(shared=True)
1876
        self.assertTrue('c1' in my_shared_containers)
1877
        self.assertTrue('c2' not in my_shared_containers)
1878

    
1879
        my_shared_objects = self.client.list_objects('c1', shared=True)
1880
        self.assertTrue('o1' in my_shared_objects)
1881
        self.assertTrue('o2' not in my_shared_objects)
1882

    
1883
class List(BaseTestCase):
1884
    def setUp(self):
1885
        BaseTestCase.setUp(self)
1886
        for i in range(1, 5):
1887
            c = 'c%s' % i
1888
            self.client.create_container(c)
1889
            for j in range(1, 3):
1890
                o = 'o%s' % j
1891
                self.upload_random_data(c, o)
1892
            if i < 3:
1893
                self.client.share_object(c, 'o1', ['papagian'], read=True)
1894
            if i%2 != 0:
1895
                self.client.publish_object(c, 'o2')
1896
    
1897
    def test_shared_public(self):
1898
        diff = lambda l: set(l) - set(self.initial_containers)
1899
        
1900
        func, kwargs = self.client.list_containers, {'shared':True}
1901
        l = func(**kwargs)
1902
        self.assertEqual(set(['c1', 'c2']), diff(l))
1903
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1904
        
1905
        func, kwargs = self.client.list_containers, {'public':True}
1906
        l = func(**kwargs)
1907
        self.assertEqual(set(['c1', 'c3']), diff(l))
1908
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1909
        
1910
        func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1911
        l = func(**kwargs)
1912
        self.assertEqual(set(['c1', 'c2', 'c3']), diff(l))
1913
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1914
        
1915
        
1916
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1917
        l = func(*args, **kwargs)
1918
        self.assertEqual(l, ['o1'])
1919
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1920
        
1921
        func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1922
        l = func(*args, **kwargs)
1923
        self.assertEqual(l, ['o2'])
1924
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1925
        
1926
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1927
        l = func(*args, **kwargs)
1928
        self.assertEqual(l, ['o1', 'o2'])
1929
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1930
        
1931
        
1932
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1933
        l = func(*args, **kwargs)
1934
        self.assertEqual(l, ['o1'])
1935
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1936
        
1937
        func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1938
        l = func(*args, **kwargs)
1939
        self.assertEqual(l, '')
1940
        self.assertEqual([], func(*args, format='json', **kwargs))
1941
        
1942
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True, 'public':True}
1943
        l = func(*args, **kwargs)
1944
        self.assertEqual(l, ['o1'])
1945
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1946
        
1947
        
1948
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1949
        l = func(*args, **kwargs)
1950
        self.assertEqual(l, '')
1951
        self.assertEqual([], func(*args, format='json', **kwargs))
1952
        
1953
        func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1954
        l = func(*args, **kwargs)
1955
        self.assertEqual(l, ['o2'])
1956
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1957
        
1958
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True, 'public':True}
1959
        l = func(*args, **kwargs)
1960
        self.assertEqual(l, ['o2'])
1961
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1962
        
1963
        
1964
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
1965
        l = func(*args, **kwargs)
1966
        self.assertEqual(l, '')
1967
        self.assertEqual([], func(*args, format='json', **kwargs))
1968
        
1969
        func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
1970
        l = func(*args, **kwargs)
1971
        self.assertEqual(l, '')
1972
        self.assertEqual([], func(*args, format='json', **kwargs))
1973
        
1974
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
1975
        l = func(*args, **kwargs)
1976
        self.assertEqual(l, '')
1977
        self.assertEqual([], func(*args, format='json', **kwargs))
1978

    
1979
class TestGreek(BaseTestCase):
1980
    def test_create_container(self):
1981
        self.client.create_container('φάκελος')
1982
        self.assert_container_exists('φάκελος')
1983

    
1984
        self.assertTrue('φάκελος' in self.client.list_containers())
1985

    
1986
    def test_create_object(self):
1987
        self.client.create_container('φάκελος')
1988
        self.upload_random_data('φάκελος', 'αντικείμενο')
1989

    
1990
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1991
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1992

    
1993
    def test_copy_object(self):
1994
        src_container = 'φάκελος'
1995
        src_object = 'αντικείμενο'
1996
        dest_container = 'αντίγραφα'
1997
        dest_object = 'ασφαλές-αντίγραφο'
1998

    
1999
        self.client.create_container(src_container)
2000
        self.upload_random_data(src_container, src_object)
2001

    
2002
        self.client.create_container(dest_container)
2003
        self.client.copy_object(src_container, src_object, dest_container,
2004
                                dest_object)
2005

    
2006
        self.assert_object_exists(src_container, src_object)
2007
        self.assert_object_exists(dest_container, dest_object)
2008
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2009

    
2010
    def test_move_object(self):
2011
        src_container = 'φάκελος'
2012
        src_object = 'αντικείμενο'
2013
        dest_container = 'αντίγραφα'
2014
        dest_object = 'ασφαλές-αντίγραφο'
2015

    
2016
        self.client.create_container(src_container)
2017
        self.upload_random_data(src_container, src_object)
2018

    
2019
        self.client.create_container(dest_container)
2020
        self.client.move_object(src_container, src_object, dest_container,
2021
                                dest_object)
2022

    
2023
        self.assert_object_not_exists(src_container, src_object)
2024
        self.assert_object_exists(dest_container, dest_object)
2025
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2026

    
2027
    def test_delete_object(self):
2028
        self.client.create_container('φάκελος')
2029
        self.upload_random_data('φάκελος', 'αντικείμενο')
2030
        self.assert_object_exists('φάκελος', 'αντικείμενο')
2031

    
2032
        self.client.delete_object('φάκελος', 'αντικείμενο')
2033
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2034
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2035

    
2036
    def test_delete_container(self):
2037
        self.client.create_container('φάκελος')
2038
        self.assert_container_exists('φάκελος')
2039

    
2040
        self.client.delete_container('φάκελος')
2041
        self.assert_container_not_exists('φάκελος')
2042
        self.assertTrue('φάκελος' not in self.client.list_containers())
2043

    
2044
    def test_account_meta(self):
2045
        meta = {'ποιότητα':'ΑΑΑ'}
2046
        self.client.update_account_metadata(**meta)
2047
        meta = self.client.retrieve_account_metadata(restricted=True)
2048
        self.assertTrue('ποιότητα' in meta.keys())
2049
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2050

    
2051
    def test_container_meta(self):
2052
        meta = {'ποιότητα':'ΑΑΑ'}
2053
        self.client.create_container('φάκελος', meta=meta)
2054

    
2055
        meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
2056
        self.assertTrue('ποιότητα' in meta.keys())
2057
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2058

    
2059
    def test_object_meta(self):
2060
        self.client.create_container('φάκελος')
2061
        meta = {'ποιότητα':'ΑΑΑ'}
2062
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2063

    
2064
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2065
                                                    restricted=True)
2066
        self.assertTrue('ποιότητα' in meta.keys())
2067
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2068

    
2069
    def test_list_meta_filtering(self):
2070
        self.client.create_container('φάκελος')
2071
        meta = {'ποιότητα':'ΑΑΑ'}
2072
        self.upload_random_data('φάκελος', 'ο1', **meta)
2073
        self.upload_random_data('φάκελος', 'ο2')
2074
        self.upload_random_data('φάκελος', 'ο3')
2075

    
2076
        meta = {'ποσότητα':'μεγάλη'}
2077
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2078
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2079
        self.assertEquals(objects, ['ο1', 'ο2'])
2080

    
2081
        objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
2082
        self.assertEquals(objects, ['ο2', 'ο3'])
2083

    
2084
        objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
2085
        self.assertEquals(objects, ['ο3'])
2086

    
2087
        meta = {'ποιότητα':'ΑΒ'}
2088
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2089
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2090
        self.assertEquals(objects, ['ο1'])
2091
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2092
        self.assertEquals(objects, ['ο2'])
2093

    
2094
        meta = {'έτος':'2011'}
2095
        self.client.update_object_metadata('φάκελος', 'ο3', **meta)
2096
        meta = {'έτος':'2012'}
2097
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2098
        objects = self.client.list_objects('φάκελος', meta='έτος<2012')
2099
        self.assertEquals(objects, ['ο3'])
2100
        objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
2101
        self.assertEquals(objects, ['ο2', 'ο3'])
2102
        objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
2103
        self.assertEquals(objects, '')
2104

    
2105
    def test_groups(self):
2106
        #create a group
2107
        groups = {'σεφς':'chazapis,διογένης'}
2108
        self.client.set_account_groups(**groups)
2109
        groups.update(self.initial_groups)
2110
        self.assertEqual(groups['σεφς'],
2111
                         self.client.retrieve_account_groups()['σεφς'])
2112

    
2113
        #check read access
2114
        self.client.create_container('φάκελος')
2115
        o = self.upload_random_data('φάκελος', 'ο1')
2116
        self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
2117
        chef = Pithos_Client(get_url(),
2118
                            '0009',
2119
                            'διογένης')
2120
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2121
                                     'φάκελος', 'ο1', account=get_user())
2122

    
2123
        #check write access
2124
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
2125
        new_data = get_random_data()
2126
        self.assert_not_raises_fault(403, chef.update_object,
2127
                                     'φάκελος', 'ο1', StringIO(new_data),
2128
                                     account=get_user())
2129

    
2130
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
2131
        self.assertEqual(server_data[:len(o['data'])], o['data'])
2132
        self.assertEqual(server_data[len(o['data']):], new_data)
2133

    
2134
    def test_manifestation(self):
2135
        self.client.create_container('κουβάς')
2136
        prefix = 'μέρη/'
2137
        data = ''
2138
        for i in range(5):
2139
            part = '%s%d' %(prefix, i)
2140
            o = self.upload_random_data('κουβάς', part)
2141
            data += o['data']
2142

    
2143
        self.client.create_container('φάκελος')
2144
        manifest = '%s/%s' %('κουβάς', prefix)
2145
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2146

    
2147
        self.assert_object_exists('φάκελος', 'άπαντα')
2148
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
2149
                                                           'άπαντα'))
2150

    
2151
        #wrong manifestation
2152
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2153
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2154

    
2155
    def test_update_from_another_object(self):
2156
        self.client.create_container('κουβάς')
2157
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2158
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2159
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2160
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2161

    
2162
        self.assertEqual(
2163
            self.client.retrieve_object('κουβάς', 'νέο'),
2164
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2165

    
2166
class TestPermissions(BaseTestCase):
2167
    def setUp(self):
2168
        BaseTestCase.setUp(self)
2169

    
2170
        #create a group
2171
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
2172
        groups = {'pithosdev':','.join(self.authorized)}
2173
        self.client.set_account_groups(**groups)
2174

    
2175
        self.container = 'c'
2176
        self.object = 'o'
2177
        self.client.create_container(self.container)
2178
        self.upload_random_data(self.container, self.object)
2179
        self.upload_random_data(self.container, self.object+'/')
2180
        self.upload_random_data(self.container, self.object+'/a')
2181
        self.upload_random_data(self.container, self.object+'a')
2182
        self.upload_random_data(self.container, self.object+'a/')
2183
        self.dir_content_types = ('application/directory', 'application/folder')
2184

    
2185
    def assert_read(self, authorized=[], any=False, depth=0):
2186
        for token, account in OTHER_ACCOUNTS.items():
2187
            cl = Pithos_Client(get_url(), token, account)
2188
            if account in authorized or any:
2189
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2190
                                             self.container, self.object,
2191
                                             account=get_user())
2192
            else:
2193
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
2194
                                         self.container, self.object,
2195
                                         account=get_user())
2196

    
2197
        #check inheritance
2198
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2199
        type = meta['content-type']
2200
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2201
        #exclude the self.object
2202
        del derivatives[derivatives.index(self.object)]
2203
        for o in derivatives:
2204
            for token, account in OTHER_ACCOUNTS.items():
2205
                cl = Pithos_Client(get_url(), token, account)
2206
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2207
                if (account in authorized or any) and \
2208
                (type in self.dir_content_types) and \
2209
                o.startswith(prefix):
2210
                    self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2211
                                             self.container, o, account=get_user())
2212
                else:
2213
                    self.assert_raises_fault(403, cl.retrieve_object_metadata,
2214
                                         self.container, o, account=get_user())
2215

    
2216
    def assert_write(self, authorized=[], any=False):
2217
        o_data = self.client.retrieve_object(self.container, self.object)
2218
        for token, account in OTHER_ACCOUNTS.items():
2219
            cl = Pithos_Client(get_url(), token, account)
2220
            new_data = get_random_data()
2221
            if account in authorized or any:
2222
                # test write access
2223
                self.assert_not_raises_fault(403, cl.update_object,
2224
                                             self.container, self.object, StringIO(new_data),
2225
                                             account=get_user())
2226
                try:
2227
                    # test read access
2228
                    server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2229
                    self.assertEqual(o_data, server_data[:len(o_data)])
2230
                    self.assertEqual(new_data, server_data[len(o_data):])
2231
                    o_data = server_data
2232
                except Fault, f:
2233
                    self.failIf(f.status == 403)
2234
            else:
2235
                self.assert_raises_fault(403, cl.update_object,
2236
                                             self.container, self.object, StringIO(new_data),
2237
                                             account=get_user())
2238
        #check inheritance
2239
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2240
        type = meta['content-type']
2241
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2242
        #exclude the object
2243
        del derivatives[derivatives.index(self.object)]
2244
        for o in derivatives:
2245
            for token, account in OTHER_ACCOUNTS.items():
2246
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2247
                cl = Pithos_Client(get_url(), token, account)
2248
                new_data = get_random_data()
2249
                if (account in authorized or any) and \
2250
                (type in self.dir_content_types) and \
2251
                o.startswith(prefix):
2252
                    # test write access
2253
                    self.assert_not_raises_fault(403, cl.update_object,
2254
                                                 self.container, o,
2255
                                                 StringIO(new_data),
2256
                                                 account=get_user())
2257
                    try:
2258
                        server_data = cl.retrieve_object(self.container, o, account=get_user())
2259
                        self.assertEqual(new_data, server_data[-len(new_data):])
2260
                    except Fault, f:
2261
                        self.failIf(f.status == 403)
2262
                else:
2263
                    self.assert_raises_fault(403, cl.update_object,
2264
                                                 self.container, o,
2265
                                                 StringIO(new_data),
2266
                                                 account=get_user())
2267

    
2268
    def test_group_read(self):
2269
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2270
        self.assert_read(authorized=self.authorized)
2271

    
2272
    def test_read_many(self):
2273
        self.client.share_object(self.container, self.object, self.authorized)
2274
        self.assert_read(authorized=self.authorized)
2275

    
2276
    def test_read_by_everyone(self):
2277
        self.client.share_object(self.container, self.object, ['*'])
2278
        self.assert_read(any=True)
2279

    
2280
    def test_read_directory(self):
2281
        for type in self.dir_content_types:
2282
            #change content type
2283
            self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2284
            self.client.share_object(self.container, self.object, ['*'])
2285
            self.assert_read(any=True)
2286
            self.client.share_object(self.container, self.object, self.authorized)
2287
            self.assert_read(authorized=self.authorized)
2288
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2289
            self.assert_read(authorized=self.authorized)
2290

    
2291
    def test_group_write(self):
2292
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2293
        self.assert_write(authorized=self.authorized)
2294

    
2295
    def test_write_many(self):
2296
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2297
        self.assert_write(authorized=self.authorized)
2298

    
2299
    def test_write_by_everyone(self):
2300
        self.client.share_object(self.container, self.object, ['*'], read=False)
2301
        self.assert_write(any=True)
2302

    
2303
    def test_write_directory(self):
2304
        dir_content_types = ('application/directory', 'application/foler')
2305
        for type in dir_content_types:
2306
            #change content type
2307
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2308
            self.client.share_object(self.container, self.object, ['*'], read=False)
2309
            self.assert_write(any=True)
2310
            self.client.share_object(self.container, self.object, self.authorized, read=False)
2311
            self.assert_write(authorized=self.authorized)
2312
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2313
            self.assert_write(authorized=self.authorized)
2314

    
2315
    def test_shared_listing(self):
2316
        self.client.share_object(self.container, self.object, self.authorized)
2317

    
2318
        my_shared_containers = self.client.list_containers(shared=True)
2319
        self.assertTrue('c' in my_shared_containers)
2320
        my_shared_objects = self.client.list_objects('c', shared=True)
2321
        self.assertEqual(['o'], my_shared_objects)
2322

    
2323
        dir_content_types = ('application/directory', 'application/foler')
2324
        for type in dir_content_types:
2325
            #change content type
2326
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2327
            my_shared_objects = self.client.list_objects('c', shared=True)
2328
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2329

    
2330
        for token, account in OTHER_ACCOUNTS.items():
2331
            if account in self.authorized:
2332
                self.other = Pithos_Client(get_url(), token, account)
2333
                self.assertTrue(get_user() in self.other.list_shared_by_others())
2334

    
2335
class TestPublish(BaseTestCase):
2336
    def test_publish(self):
2337
        self.client.create_container('c')
2338
        o_data = self.upload_random_data('c', 'o')['data']
2339
        self.client.publish_object('c', 'o')
2340
        meta = self.client.retrieve_object_metadata('c', 'o')
2341
        self.assertTrue('x-object-public' in meta)
2342
        url = meta['x-object-public']
2343

    
2344
        p = urlparse(get_url())
2345
        if p.scheme == 'http':
2346
            conn = HTTPConnection(p.netloc)
2347
        elif p.scheme == 'https':
2348
            conn = HTTPSConnection(p.netloc)
2349
        else:
2350
            raise Exception('Unknown URL scheme')
2351

    
2352
        conn.request('GET', url)
2353
        resp = conn.getresponse()
2354
        length = resp.getheader('content-length', None)
2355
        data = resp.read(length)
2356
        self.assertEqual(o_data, data)
2357

    
2358
class TestPolicies(BaseTestCase):
2359
    def test_none_versioning(self):
2360
        self.client.create_container('c', policies={'versioning':'none'})
2361
        o = self.upload_random_data('c', 'o')
2362
        meta = self.client.retrieve_object_metadata('c', 'o')
2363
        v = meta['x-object-version']
2364
        more_data = get_random_data()
2365
        self.client.update_object('c', 'o', StringIO(more_data))
2366
        vlist = self.client.retrieve_object_versionlist('c', 'o')
2367
        self.assert_raises_fault(404, self.client.retrieve_object_version,
2368
                                 'c', 'o', v)
2369
        data = self.client.retrieve_object('c', 'o')
2370
        end = len(o['data'])
2371
        self.assertEqual(data[:end], o['data'])
2372
        self.assertEqual(data[end:], more_data)
2373

    
2374
    def test_quota(self):
2375
        self.client.create_container('c', policies={'quota':'1'})
2376
        meta = self.client.retrieve_container_metadata('c')
2377
        self.assertEqual(meta['x-container-policy-quota'], '1')
2378
        self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2379
                                 length=1024*1024+1)
2380

    
2381
    def test_quota_none(self):
2382
        self.client.create_container('c', policies={'quota':'0'})
2383
        meta = self.client.retrieve_container_metadata('c')
2384
        self.assertEqual(meta['x-container-policy-quota'], '0')
2385
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2386
                                 length=1024*1024+1)
2387

    
2388
class AssertUUidInvariant(object):
2389
    def __init__(self, callable, *args, **kwargs):
2390
        self.callable = callable
2391
        self.args = args
2392
        self.kwargs = kwargs
2393

    
2394
    def __enter__(self):
2395
        self.map = self.callable(*self.args, **self.kwargs)
2396
        assert('x-object-uuid' in self.map)
2397
        self.uuid = self.map['x-object-uuid']
2398
        return self.map
2399

    
2400
    def __exit__(self, type, value, tb):
2401
        map = self.callable(*self.args, **self.kwargs)
2402
        assert('x-object-uuid' in self.map)
2403
        uuid = map['x-object-uuid']
2404
        assert(uuid == self.uuid)
2405

    
2406
class AssertMappingInvariant(object):
2407
    def __init__(self, callable, *args, **kwargs):
2408
        self.callable = callable
2409
        self.args = args
2410
        self.kwargs = kwargs
2411

    
2412
    def __enter__(self):
2413
        self.map = self.callable(*self.args, **self.kwargs)
2414
        return self.map
2415

    
2416
    def __exit__(self, type, value, tb):
2417
        map = self.callable(*self.args, **self.kwargs)
2418
        for k, v in self.map.items():
2419
            if is_date(v):
2420
                continue
2421
            assert(k in map)
2422
            assert v == map[k]
2423

    
2424
class AssertContentInvariant(object):
2425
    def __init__(self, callable, *args, **kwargs):
2426
        self.callable = callable
2427
        self.args = args
2428
        self.kwargs = kwargs
2429

    
2430
    def __enter__(self):
2431
        self.content = self.callable(*self.args, **self.kwargs)[2]
2432
        return self.content
2433

    
2434
    def __exit__(self, type, value, tb):
2435
        content = self.callable(*self.args, **self.kwargs)[2]
2436
        assert self.content == content
2437

    
2438
def get_content_splitted(response):
2439
    if response:
2440
        return response.content.split('\n')
2441

    
2442
def compute_md5_hash(data):
2443
    md5 = hashlib.md5()
2444
    offset = 0
2445
    md5.update(data)
2446
    return md5.hexdigest().lower()
2447

    
2448
def compute_block_hash(data, algorithm):
2449
    h = hashlib.new(algorithm)
2450
    h.update(data.rstrip('\x00'))
2451
    return h.hexdigest()
2452

    
2453
def get_random_data(length=500):
2454
    char_set = string.ascii_uppercase + string.digits
2455
    return ''.join(random.choice(char_set) for x in xrange(length))
2456

    
2457
def is_date(date):
2458
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2459
    __D = r'(?P<day>\d{2})'
2460
    __D2 = r'(?P<day>[ \d]\d)'
2461
    __M = r'(?P<mon>\w{3})'
2462
    __Y = r'(?P<year>\d{4})'
2463
    __Y2 = r'(?P<year>\d{2})'
2464
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2465
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2466
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2467
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2468
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2469
        m = regex.match(date)
2470
        if m is not None:
2471
            return True
2472
    return False
2473

    
2474
def strnextling(prefix):
2475
    """Return the first unicode string
2476
       greater than but not starting with given prefix.
2477
       strnextling('hello') -> 'hellp'
2478
    """
2479
    if not prefix:
2480
        ## all strings start with the null string,
2481
        ## therefore we have to approximate strnextling('')
2482
        ## with the last unicode character supported by python
2483
        ## 0x10ffff for wide (32-bit unicode) python builds
2484
        ## 0x00ffff for narrow (16-bit unicode) python builds
2485
        ## We will not autodetect. 0xffff is safe enough.
2486
        return unichr(0xffff)
2487
    s = prefix[:-1]
2488
    c = ord(prefix[-1])
2489
    if c >= 0xffff:
2490
        raise RuntimeError
2491
    s += unichr(c+1)
2492
    return s
2493

    
2494
o_names = ['kate.jpg',
2495
           'kate_beckinsale.jpg',
2496
           'How To Win Friends And Influence People.pdf',
2497
           'moms_birthday.jpg',
2498
           'poodle_strut.mov',
2499
           'Disturbed - Down With The Sickness.mp3',
2500
           'army_of_darkness.avi',
2501
           'the_mad.avi',
2502
           'photos/animals/dogs/poodle.jpg',
2503
           'photos/animals/dogs/terrier.jpg',
2504
           'photos/animals/cats/persian.jpg',
2505
           'photos/animals/cats/siamese.jpg',
2506
           'photos/plants/fern.jpg',
2507
           'photos/plants/rose.jpg',
2508
           'photos/me.jpg']
2509

    
2510

    
2511
def main():
2512
    if get_user() == 'test':
2513
        unittest.main(module='pithos.tools.test')
2514
    else:
2515
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2516

    
2517

    
2518
if __name__ == "__main__":
2519
    main()
2520