Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 594e5b33

History | View | Annotate | Download (105.3 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
OTHER_ACCOUNTS = {
63
    '0001': 'verigak',
64
    '0002': 'chazapis',
65
    '0003': 'gtsouk',
66
    '0004': 'papagian',
67
    '0005': 'louridas',
68
    '0006': 'chstath',
69
    '0007': 'pkanavos',
70
    '0008': 'mvasilak',
71
    '0009': 'διογένης'}
72

    
73
class BaseTestCase(unittest.TestCase):
74
    #TODO unauthorized request
75
    def setUp(self):
76
        self.client = Pithos_Client(get_url(), get_auth(), get_user())
77
        
78
        #keep track of initial containers
79
        self.initial_containers = self.client.list_containers()
80
        if self.initial_containers == '':
81
            self.initial_containers = []
82
        
83
        self._clean_account()
84
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
85

    
86
        #keep track of initial account groups
87
        self.initial_groups = self.client.retrieve_account_groups()
88

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

    
108
    def tearDown(self):
109
        #delete additionally created meta
110
        l = []
111
        for m in self.client.retrieve_account_metadata(restricted=True):
112
            if m not in self.initial_meta:
113
                l.append(m)
114
        self.client.delete_account_metadata(l)
115

    
116
        #delete additionally created groups
117
        l = []
118
        for g in self.client.retrieve_account_groups():
119
            if g not in self.initial_groups:
120
                l.append(g)
121
        self.client.unset_account_groups(l)
122
        self._clean_account()
123

    
124
    def _clean_account(self):
125
        for c in self.client.list_containers():
126
            if c not in self.initial_containers:
127
                self.client.delete_container(c, delimiter='/')
128
                self.client.delete_container(c)
129
    
130
    def assert_status(self, status, codes):
131
        l = [elem for elem in self.return_codes]
132
        if type(codes) == types.ListType:
133
            l.extend(codes)
134
        else:
135
            l.append(codes)
136
        self.assertTrue(status in l)
137

    
138
    def assert_extended(self, data, format, type, size=10000):
139
        if format == 'xml':
140
            self._assert_xml(data, type, size)
141
        elif format == 'json':
142
            self._assert_json(data, type, size)
143

    
144
    def _assert_json(self, data, type, size):
145
        convert = lambda s: s.lower()
146
        info = [convert(elem) for elem in self.extended[type]]
147
        self.assertTrue(len(data) <= size)
148
        for item in info:
149
            for i in data:
150
                if 'subdir' in i.keys():
151
                    continue
152
                self.assertTrue(item in i.keys())
153

    
154
    def _assert_xml(self, data, type, size):
155
        convert = lambda s: s.lower()
156
        info = [convert(elem) for elem in self.extended[type]]
157
        try:
158
            info.remove('content_encoding')
159
        except ValueError:
160
            pass
161
        xml = data
162
        entities = xml.getElementsByTagName(type)
163
        self.assertTrue(len(entities) <= size)
164
        for e in entities:
165
            for item in info:
166
                self.assertTrue(e.getElementsByTagName(item))
167

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

    
182
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
183
        """
184
        asserts that a Fault with a specific status is not raised
185
        when callableObj is called with the specific arguments
186
        """
187
        try:
188
            r = callableObj(*args, **kwargs)
189
        except Fault, f:
190
            self.failIfEqual(f.status, status)
191

    
192
    def assert_container_exists(self, container):
193
        """
194
        asserts the existence of a container
195
        """
196
        try:
197
            self.client.retrieve_container_metadata(container)
198
        except Fault, f:
199
            self.failIf(f.status == 404)
200

    
201
    def assert_container_not_exists(self, container):
202
        """
203
        asserts there is no such a container
204
        """
205
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
206
                                 container)
207

    
208
    def assert_object_exists(self, container, object):
209
        """
210
        asserts the existence of an object
211
        """
212
        try:
213
            self.client.retrieve_object_metadata(container, object)
214
        except Fault, f:
215
            self.failIf(f.status == 404)
216

    
217
    def assert_object_not_exists(self, container, object):
218
        """
219
        asserts there is no such an object
220
        """
221
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
222
                                 container, object)
223

    
224
    def assert_versionlist_structure(self, versionlist):
225
        self.assertTrue(type(versionlist) == types.ListType)
226
        for elem in versionlist:
227
            self.assertTrue(type(elem) == types.ListType)
228
            self.assertEqual(len(elem), 2)
229

    
230
    def upload_random_data(self, container, name, length=1024, type=None,
231
                           enc=None, **meta):
232
        data = get_random_data(length)
233
        return self.upload_data(container, name, data, type, enc, **meta)
234

    
235
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
236
                    **meta):
237
        obj = {}
238
        obj['name'] = name
239
        try:
240
            obj['data'] = data
241
            obj['hash'] = compute_md5_hash(obj['data'])
242

    
243
            args = {}
244
            args['etag'] = etag if etag else obj['hash']
245

    
246
            try:
247
                guess = mimetypes.guess_type(name)
248
                type = type if type else guess[0]
249
                enc = enc if enc else guess[1]
250
            except:
251
                pass
252
            args['content_type'] = type if type else 'plain/text'
253
            args['content_encoding'] = enc if enc else None
254

    
255
            obj['meta'] = args
256

    
257
            path = '/%s/%s' % (container, name)
258
            self.client.create_object(container, name, f=StringIO(obj['data']),
259
                                      meta=meta, **args)
260

    
261
            return obj
262
        except IOError:
263
            return
264

    
265
class AccountHead(BaseTestCase):
266
    def setUp(self):
267
        BaseTestCase.setUp(self)
268
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
269
        self.containers.sort()
270
        
271
        for item in self.containers:
272
            self.client.create_container(item)
273

    
274
        meta = {'foo':'bar'}
275
        self.client.update_account_metadata(**meta)
276
        #self.updated_meta = self.initial_meta.update(meta)
277

    
278
    def test_get_account_meta(self):
279
        meta = self.client.retrieve_account_metadata()
280

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

    
295
    def test_get_account_403(self):
296
        self.assert_raises_fault(403,
297
                                 self.invalid_client.retrieve_account_metadata)
298

    
299
    def test_get_account_meta_until(self):
300
        t = datetime.datetime.utcnow()
301
        past = t - datetime.timedelta(minutes=-15)
302
        past = int(_time.mktime(past.timetuple()))
303

    
304
        meta = {'premium':True}
305
        self.client.update_account_metadata(**meta)
306
        meta = self.client.retrieve_account_metadata(restricted=True,
307
                                                     until=past)
308
        self.assertTrue('premium' not in meta)
309

    
310
        meta = self.client.retrieve_account_metadata(restricted=True)
311
        self.assertTrue('premium' in meta)
312

    
313
    def test_get_account_meta_until_invalid_date(self):
314
        meta = {'premium':True}
315
        self.client.update_account_metadata(**meta)
316
        meta = self.client.retrieve_account_metadata(restricted=True,
317
                                                     until='kshfksfh')
318
        self.assertTrue('premium' in meta)
319

    
320
class AccountGet(BaseTestCase):
321
    def setUp(self):
322
        BaseTestCase.setUp(self)
323
        #create some containers
324
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
325
        self.containers.sort()
326
        
327
        for item in self.containers:
328
            self.client.create_container(item)
329

    
330
    def test_list(self):
331
        #list containers
332
        containers = self.client.list_containers()
333
        self.assertEquals(self.containers, containers)
334

    
335
    def test_list_403(self):
336
        self.assert_raises_fault(403, self.invalid_client.list_containers)
337

    
338
    def test_list_with_limit(self):
339
        limit = 2
340
        containers = self.client.list_containers(limit=limit)
341
        self.assertEquals(len(containers), limit)
342
        self.assertEquals(self.containers[:2], containers)
343

    
344
    def test_list_with_marker(self):
345
        l = 2
346
        m = 'bananas'
347
        containers = self.client.list_containers(limit=l, marker=m)
348
        i = self.containers.index(m) + 1
349
        self.assertEquals(self.containers[i:(i+l)], containers)
350

    
351
        m = 'oranges'
352
        containers = self.client.list_containers(limit=l, marker=m)
353
        i = self.containers.index(m) + 1
354
        self.assertEquals(self.containers[i:(i+l)], containers)
355

    
356
    def test_list_json_with_marker(self):
357
        l = 2
358
        m = 'bananas'
359
        containers = self.client.list_containers(limit=l, marker=m, format='json')
360
        self.assert_extended(containers, 'json', 'container', l)
361
        self.assertEqual(containers[0]['name'], 'kiwis')
362
        self.assertEqual(containers[1]['name'], 'oranges')
363

    
364
    def test_list_xml_with_marker(self):
365
        l = 2
366
        m = 'oranges'
367
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
368
        self.assert_extended(xml, 'xml', 'container', l)
369
        nodes = xml.getElementsByTagName('name')
370
        self.assertTrue(len(nodes) <= l)
371
        names = [n.childNodes[0].data for n in nodes]
372
        self.assertTrue('pears' in names or 'pears' > name for name in names)
373

    
374
    def test_if_modified_since(self):
375
        t = datetime.datetime.utcnow()
376
        t2 = t - datetime.timedelta(minutes=10)
377

    
378
        #add a new container
379
        self.client.create_container('dummy')
380

    
381
        for f in DATE_FORMATS:
382
            past = t2.strftime(f)
383
            try:
384
                c = self.client.list_containers(if_modified_since=past)
385
                self.assertEqual(len(c), len(self.containers) + 1)
386
            except Fault, f:
387
                self.failIf(f.status == 304) #fail if not modified
388
    
389
    def test_if_modified_since_invalid_date(self):
390
        c = self.client.list_containers(if_modified_since='')
391
        self.assertEqual(len(c), len(self.containers))
392

    
393
    def test_if_not_modified_since(self):
394
        now = datetime.datetime.utcnow()
395
        since = now + datetime.timedelta(1)
396

    
397
        for f in DATE_FORMATS:
398
            args = {'if_modified_since':'%s' %since.strftime(f)}
399

    
400
            #assert not modified
401
            self.assert_raises_fault(304, self.client.list_containers, **args)
402

    
403
    def test_if_unmodified_since(self):
404
        now = datetime.datetime.utcnow()
405
        since = now + datetime.timedelta(1)
406

    
407
        for f in DATE_FORMATS:
408
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
409

    
410
            #assert success
411
            self.assertEqual(self.containers, c)
412

    
413
    def test_if_unmodified_since_precondition_failed(self):
414
        t = datetime.datetime.utcnow()
415
        t2 = t - datetime.timedelta(minutes=10)
416

    
417
        #add a new container
418
        self.client.create_container('dummy')
419

    
420
        for f in DATE_FORMATS:
421
            past = t2.strftime(f)
422

    
423
            args = {'if_unmodified_since':'%s' %past}
424

    
425
            #assert precondition failed
426
            self.assert_raises_fault(412, self.client.list_containers, **args)
427

    
428
class AccountPost(BaseTestCase):
429
    def setUp(self):
430
        BaseTestCase.setUp(self)
431
        self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
432
        self.containers.sort()
433
        
434
        for item in self.containers:
435
            self.client.create_container(item)
436

    
437
        meta = {'foo':'bar'}
438
        self.client.update_account_metadata(**meta)
439
        self.updated_meta = self.initial_meta.update(meta)
440

    
441
    def test_update_meta(self):
442
        with AssertMappingInvariant(self.client.retrieve_account_groups):
443
            meta = {'test':'test', 'tost':'tost'}
444
            self.client.update_account_metadata(**meta)
445

    
446
            meta.update(self.initial_meta)
447
            self.assertEqual(meta,
448
                             self.client.retrieve_account_metadata(
449
                                restricted=True))
450

    
451
    def test_invalid_account_update_meta(self):
452
        meta = {'test':'test', 'tost':'tost'}
453
        self.assert_raises_fault(403,
454
                                 self.invalid_client.update_account_metadata,
455
                                 **meta)
456

    
457
    def test_reset_meta(self):
458
        with AssertMappingInvariant(self.client.retrieve_account_groups):
459
            meta = {'test':'test', 'tost':'tost'}
460
            self.client.update_account_metadata(**meta)
461

    
462
            meta = {'test':'test33'}
463
            self.client.reset_account_metadata(**meta)
464

    
465
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
466

    
467
    def test_delete_meta(self):
468
        with AssertMappingInvariant(self.client.retrieve_account_groups):
469
            meta = {'test':'test', 'tost':'tost'}
470
            self.client.update_account_metadata(**meta)
471

    
472
            self.client.delete_account_metadata(meta.keys())
473

    
474
            account_meta = self.client.retrieve_account_metadata(restricted=True)
475
            for m in meta:
476
                self.assertTrue(m not in account_meta.keys())
477

    
478
    def test_set_account_groups(self):
479
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
480
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
481
            self.client.set_account_groups(**groups)
482

    
483
            self.assertEqual(set(groups['pithosdev']),
484
                             set(self.client.retrieve_account_groups()['pithosdev']))
485

    
486
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
487
            self.client.set_account_groups(**more_groups)
488

    
489
            groups.update(more_groups)
490
            self.assertEqual(set(groups['clientsdev']),
491
                             set(self.client.retrieve_account_groups()['clientsdev']))
492

    
493
    def test_reset_account_groups(self):
494
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
495
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
496
                      'clientsdev':'pkanavos,mvasilak'}
497
            self.client.set_account_groups(**groups)
498

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

    
504
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
505
            self.client.reset_account_groups(**groups)
506

    
507
            self.assertEqual(set(groups['pithosdev'].split(',')),
508
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
509

    
510
    def test_delete_account_groups(self):
511
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
512
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
513
                      'clientsdev':'pkanavos,mvasilak'}
514
            self.client.set_account_groups(**groups)
515

    
516
            self.client.unset_account_groups(groups.keys())
517

    
518
            self.assertEqual({}, self.client.retrieve_account_groups())
519

    
520
class ContainerHead(BaseTestCase):
521
    def setUp(self):
522
        BaseTestCase.setUp(self)
523
        self.container = 'apples'
524
        self.client.create_container(self.container)
525

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

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

    
615
    def test_list_objects_containing_slash(self):
616
        self.client.create_container('test')
617
        self.upload_random_data('test', '/objectname')
618

    
619
        objects = self.client.list_objects('test')
620
        self.assertEqual(objects, ['/objectname'])
621

    
622
        objects = self.client.list_objects('test', format='json')
623
        self.assertEqual(objects[0]['name'], '/objectname')
624

    
625
        objects = self.client.list_objects('test', format='xml')
626
        self.assert_extended(objects, 'xml', 'object')
627
        node_name = objects.getElementsByTagName('name')[0]
628
        self.assertEqual(node_name.firstChild.data, '/objectname')
629

    
630
    def test_list_objects_with_limit_marker(self):
631
        objects = self.client.list_objects(self.container[0], limit=2)
632
        l = [elem['name'] for elem in self.obj[:8]]
633
        l.sort()
634
        self.assertEqual(objects, l[:2])
635

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

    
649
    #takes too long
650
    def _test_list_limit_exceeds(self):
651
        self.client.create_container('pithos')
652

    
653
        for i in range(10001):
654
            self.client.create_zero_length_object('pithos', i)
655

    
656
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
657

    
658
    def test_list_empty_params(self):
659
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
660
        if objects:
661
            objects = objects.strip().split('\n')
662
        self.assertEqual(objects,
663
                         self.client.list_objects(self.container[0]))
664

    
665
    def test_list_pseudo_hierarchical_folders(self):
666
        objects = self.client.list_objects(self.container[1], prefix='photos',
667
                                           delimiter='/')
668
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
669
                           'photos/plants/'], objects)
670

    
671
        objects = self.client.list_objects(self.container[1],
672
                                           prefix='photos/animals',
673
                                           delimiter='/')
674
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
675
        self.assertEquals(l, objects)
676

    
677
        objects = self.client.list_objects(self.container[1], path='photos')
678
        self.assertEquals(['photos/me.jpg'], objects)
679

    
680
    def test_extended_list_json(self):
681
        objects = self.client.list_objects(self.container[1], format='json',
682
                                           limit=2, prefix='photos/animals',
683
                                           delimiter='/')
684
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
685
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
686

    
687
    def test_extended_list_xml(self):
688
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
689
                                       prefix='photos', delimiter='/')
690
        self.assert_extended(xml, 'xml', 'object', size=4)
691
        dirs = xml.getElementsByTagName('subdir')
692
        self.assertEqual(len(dirs), 2)
693
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
694
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
695

    
696
        objects = xml.getElementsByTagName('name')
697
        self.assertEqual(len(objects), 1)
698
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
699

    
700
    def test_list_meta_double_matching(self):
701
        meta = {'quality':'aaa', 'stock':'true'}
702
        self.client.update_object_metadata(self.container[0],
703
                                           self.obj[0]['name'], **meta)
704
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
705
        self.assertEqual(len(obj), 1)
706
        self.assertTrue(obj, self.obj[0]['name'])
707

    
708
    def test_list_using_meta(self):
709
        meta = {'quality':'aaa'}
710
        for o in self.obj[:2]:
711
            self.client.update_object_metadata(self.container[0], o['name'],
712
                                               **meta)
713
        meta = {'stock':'true'}
714
        for o in self.obj[3:5]:
715
            self.client.update_object_metadata(self.container[0], o['name'],
716
                                               **meta)
717

    
718
        obj = self.client.list_objects(self.container[0], meta='Quality')
719
        self.assertEqual(len(obj), 2)
720
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
721

    
722
        # test case insensitive
723
        obj = self.client.list_objects(self.container[0], meta='quality')
724
        self.assertEqual(len(obj), 2)
725
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
726

    
727
        # test multiple matches
728
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
729
        self.assertEqual(len(obj), 4)
730
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
731

    
732
        # test non 1-1 multiple match
733
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
734
        self.assertEqual(len(obj), 2)
735
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
736

    
737
    def test_if_modified_since(self):
738
        t = datetime.datetime.utcnow()
739
        t2 = t - datetime.timedelta(minutes=10)
740

    
741
        #add a new object
742
        self.upload_random_data(self.container[0], o_names[0])
743

    
744
        for f in DATE_FORMATS:
745
            past = t2.strftime(f)
746
            try:
747
                o = self.client.list_objects(self.container[0],
748
                                            if_modified_since=past)
749
                self.assertEqual(o,
750
                                 self.client.list_objects(self.container[0]))
751
            except Fault, f:
752
                self.failIf(f.status == 304) #fail if not modified
753

    
754
    def test_if_modified_since_invalid_date(self):
755
        headers = {'if-modified-since':''}
756
        o = self.client.list_objects(self.container[0], if_modified_since='')
757
        self.assertEqual(o, self.client.list_objects(self.container[0]))
758

    
759
    def test_if_not_modified_since(self):
760
        now = datetime.datetime.utcnow()
761
        since = now + datetime.timedelta(1)
762

    
763
        for f in DATE_FORMATS:
764
            args = {'if_modified_since':'%s' %since.strftime(f)}
765

    
766
            #assert not modified
767
            self.assert_raises_fault(304, self.client.list_objects,
768
                                     self.container[0], **args)
769

    
770
    def test_if_unmodified_since(self):
771
        now = datetime.datetime.utcnow()
772
        since = now + datetime.timedelta(1)
773

    
774
        for f in DATE_FORMATS:
775
            obj = self.client.list_objects(self.container[0],
776
                                           if_unmodified_since=since.strftime(f))
777

    
778
            #assert unmodified
779
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
780

    
781
    def test_if_unmodified_since_precondition_failed(self):
782
        t = datetime.datetime.utcnow()
783
        t2 = t - datetime.timedelta(minutes=10)
784

    
785
        #add a new container
786
        self.client.create_container('dummy')
787

    
788
        for f in DATE_FORMATS:
789
            past = t2.strftime(f)
790

    
791
            args = {'if_unmodified_since':'%s' %past}
792

    
793
            #assert precondition failed
794
            self.assert_raises_fault(412, self.client.list_objects,
795
                                     self.container[0], **args)
796

    
797
class ContainerPut(BaseTestCase):
798
    def setUp(self):
799
        BaseTestCase.setUp(self)
800
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
801
        self.containers.sort()
802
    
803
    def test_create(self):
804
        self.client.create_container(self.containers[0])
805
        containers = self.client.list_containers()
806
        self.assertTrue(self.containers[0] in containers)
807
        self.assert_container_exists(self.containers[0])
808

    
809
    def test_create_twice(self):
810
        self.client.create_container(self.containers[0])
811
        self.assertTrue(not self.client.create_container(self.containers[0]))
812

    
813
    def test_quota(self):
814
        self.client.create_container(self.containers[0])
815

    
816
        policy = {'quota':100}
817
        self.client.set_container_policies(self.containers[0], **policy)
818

    
819
        meta = self.client.retrieve_container_metadata(self.containers[0])
820
        self.assertTrue('x-container-policy-quota' in meta)
821
        self.assertEqual(meta['x-container-policy-quota'], '100')
822

    
823
        args = [self.containers[0], 'o1']
824
        kwargs = {'length':101}
825
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
826

    
827
        #reset quota
828
        policy = {'quota':0}
829
        self.client.set_container_policies(self.containers[0], **policy)
830

    
831
class ContainerPost(BaseTestCase):
832
    def setUp(self):
833
        BaseTestCase.setUp(self)
834
        self.container = 'apples'
835
        self.client.create_container(self.container)
836

    
837
    def test_update_meta(self):
838
        meta = {'test':'test33',
839
                'tost':'tost22'}
840
        self.client.update_container_metadata(self.container, **meta)
841
        headers = self.client.retrieve_container_metadata(self.container)
842
        for k,v in meta.items():
843
            k = 'x-container-meta-%s' % k
844
            self.assertTrue(headers[k])
845
            self.assertEqual(headers[k], v)
846

    
847
class ContainerDelete(BaseTestCase):
848
    def setUp(self):
849
        BaseTestCase.setUp(self)
850
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
851
        self.containers.sort()
852
        
853
        for c in self.containers:
854
            self.client.create_container(c)
855

    
856
    def test_delete(self):
857
        status = self.client.delete_container(self.containers[0])[0]
858
        self.assertEqual(status, 204)
859

    
860
    def test_delete_non_empty(self):
861
        self.upload_random_data(self.containers[1], o_names[0])
862
        self.assert_raises_fault(409, self.client.delete_container,
863
                                 self.containers[1])
864

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

    
880
class ObjectGet(BaseTestCase):
881
    def setUp(self):
882
        BaseTestCase.setUp(self)
883
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
884
        self.containers.sort()
885
        
886
        #create some containers
887
        for c in self.containers:
888
            self.client.create_container(c)
889

    
890
        #upload a file
891
        names = ('obj1', 'obj2')
892
        self.objects = []
893
        for n in names:
894
            self.objects.append(self.upload_random_data(self.containers[1], n))
895

    
896
    def test_versions(self):
897
        c = self.containers[1]
898
        o = self.objects[0]
899
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
900
        self.assert_versionlist_structure(b)
901

    
902
        #update meta
903
        meta = {'quality':'AAA', 'stock':True}
904
        self.client.update_object_metadata(c, o['name'], **meta)
905

    
906
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
907
        self.assert_versionlist_structure(a)
908
        self.assertEqual(len(b)+1, len(a))
909
        self.assertEqual(b, a[:-1])
910

    
911
        #get exact previous version metadata
912
        v = a[-2][0]
913
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
914
                                                      restricted=True,
915
                                                      version=v)
916
        for k in meta.keys():
917
            self.assertTrue(k not in v_meta)
918

    
919
        #update obejct
920
        data = get_random_data()
921
        self.client.update_object(c, o['name'], StringIO(data))
922

    
923
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
924
        self.assert_versionlist_structure(aa)
925
        self.assertEqual(len(a)+1, len(aa))
926
        self.assertEqual(a, aa[:-1])
927

    
928
        #get exact previous version
929
        v = aa[-3][0]
930
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
931
        self.assertEqual(o['data'], v_data)
932
        self.assertEqual(self.client.retrieve_object(c, o['name']),
933
                         '%s%s' %(v_data, data))
934

    
935
    def test_get(self):
936
        #perform get
937
        o = self.client.retrieve_object(self.containers[1],
938
                                        self.objects[0]['name'],
939
                                        self.objects[0]['meta'])
940
        self.assertEqual(o, self.objects[0]['data'])
941

    
942
    def test_objects_with_trailing_spaces(self):
943
        self.client.create_container('test')
944
        #create 'a' object
945
        self.upload_random_data('test', 'a')
946
        #look for 'a ' object
947
        self.assert_raises_fault(404, self.client.retrieve_object,
948
                                 'test', 'a ')
949

    
950
        #delete 'a' object
951
        self.client.delete_object('test', 'a')
952
        self.assert_raises_fault(404, self.client.retrieve_object,
953
                                 'test', 'a')
954

    
955
        #create 'a ' object
956
        self.upload_random_data('test', 'a ')
957
        #look for 'a' object
958
        self.assert_raises_fault(404, self.client.retrieve_object,
959
                                 'test', 'a')
960

    
961
    def test_get_invalid(self):
962
        self.assert_raises_fault(404, self.client.retrieve_object,
963
                                 self.containers[0], self.objects[0]['name'])
964

    
965
    def test_get_partial(self):
966
        #perform get with range
967
        status, headers, data = self.client.request_object(self.containers[1],
968
                                                            self.objects[0]['name'],
969
                                                            range='bytes=0-499')
970

    
971
        #assert successful partial content
972
        self.assertEqual(status, 206)
973

    
974
        #assert content-type
975
        self.assertEqual(headers['content-type'],
976
                         self.objects[0]['meta']['content_type'])
977

    
978
        #assert content length
979
        self.assertEqual(int(headers['content-length']), 500)
980

    
981
        #assert content
982
        self.assertEqual(self.objects[0]['data'][:500], data)
983

    
984
    def test_get_final_500(self):
985
        #perform get with range
986
        headers = {'range':'bytes=-500'}
987
        status, headers, data = self.client.request_object(self.containers[1],
988
                                                            self.objects[0]['name'],
989
                                                            range='bytes=-500')
990

    
991
        #assert successful partial content
992
        self.assertEqual(status, 206)
993

    
994
        #assert content-type
995
        self.assertEqual(headers['content-type'],
996
                         self.objects[0]['meta']['content_type'])
997

    
998
        #assert content length
999
        self.assertEqual(int(headers['content-length']), 500)
1000

    
1001
        #assert content
1002
        self.assertTrue(self.objects[0]['data'][-500:], data)
1003

    
1004
    def test_get_rest(self):
1005
        #perform get with range
1006
        offset = len(self.objects[0]['data']) - 500
1007
        status, headers, data = self.client.request_object(self.containers[1],
1008
                                                self.objects[0]['name'],
1009
                                                range='bytes=%s-' %offset)
1010

    
1011
        #assert successful partial content
1012
        self.assertEqual(status, 206)
1013

    
1014
        #assert content-type
1015
        self.assertEqual(headers['content-type'],
1016
                         self.objects[0]['meta']['content_type'])
1017

    
1018
        #assert content length
1019
        self.assertEqual(int(headers['content-length']), 500)
1020

    
1021
        #assert content
1022
        self.assertTrue(self.objects[0]['data'][-500:], data)
1023

    
1024
    def test_get_range_not_satisfiable(self):
1025
        #perform get with range
1026
        offset = len(self.objects[0]['data']) + 1
1027

    
1028
        #assert range not satisfiable
1029
        self.assert_raises_fault(416, self.client.retrieve_object,
1030
                                 self.containers[1], self.objects[0]['name'],
1031
                                 range='bytes=0-%s' %offset)
1032

    
1033
    def test_multiple_range(self):
1034
        #perform get with multiple range
1035
        ranges = ['0-499', '-500', '1000-']
1036
        bytes = 'bytes=%s' % ','.join(ranges)
1037
        status, headers, data = self.client.request_object(self.containers[1],
1038
                                                           self.objects[0]['name'],
1039
                                                           range=bytes)
1040

    
1041
        # assert partial content
1042
        self.assertEqual(status, 206)
1043

    
1044
        # assert Content-Type of the reply will be multipart/byteranges
1045
        self.assertTrue(headers['content-type'])
1046
        content_type_parts = headers['content-type'].split()
1047
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1048

    
1049
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1050
        cparts = data.split(boundary)[1:-1]
1051

    
1052
        # assert content parts are exactly 2
1053
        self.assertEqual(len(cparts), len(ranges))
1054

    
1055
        # for each content part assert headers
1056
        i = 0
1057
        for cpart in cparts:
1058
            content = cpart.split('\r\n')
1059
            headers = content[1:3]
1060
            content_range = headers[0].split(': ')
1061
            self.assertEqual(content_range[0], 'Content-Range')
1062

    
1063
            r = ranges[i].split('-')
1064
            if not r[0] and not r[1]:
1065
                pass
1066
            elif not r[0]:
1067
                start = len(self.objects[0]['data']) - int(r[1])
1068
                end = len(self.objects[0]['data'])
1069
            elif not r[1]:
1070
                start = int(r[0])
1071
                end = len(self.objects[0]['data'])
1072
            else:
1073
                start = int(r[0])
1074
                end = int(r[1]) + 1
1075
            fdata = self.objects[0]['data'][start:end]
1076
            sdata = '\r\n'.join(content[4:-1])
1077
            self.assertEqual(len(fdata), len(sdata))
1078
            self.assertEquals(fdata, sdata)
1079
            i+=1
1080

    
1081
    def test_multiple_range_not_satisfiable(self):
1082
        #perform get with multiple range
1083
        out_of_range = len(self.objects[0]['data']) + 1
1084
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1085
        bytes = 'bytes=%s' % ','.join(ranges)
1086

    
1087
        # assert partial content
1088
        self.assert_raises_fault(416, self.client.retrieve_object,
1089
                                 self.containers[1],
1090
                                 self.objects[0]['name'], range=bytes)
1091

    
1092
    def test_get_with_if_match(self):
1093
        #perform get with If-Match
1094
        etag = self.objects[0]['hash']
1095
        status, headers, data = self.client.request_object(self.containers[1],
1096
                                                           self.objects[0]['name'],
1097
                                                           if_match=etag)
1098
        #assert get success
1099
        self.assertEqual(status, 200)
1100

    
1101
        #assert content-type
1102
        self.assertEqual(headers['content-type'],
1103
                         self.objects[0]['meta']['content_type'])
1104

    
1105
        #assert response content
1106
        self.assertEqual(self.objects[0]['data'], data)
1107

    
1108
    def test_get_with_if_match_star(self):
1109
        #perform get with If-Match *
1110
        headers = {'if-match':'*'}
1111
        status, headers, data = self.client.request_object(self.containers[1],
1112
                                                self.objects[0]['name'],
1113
                                                **headers)
1114
        #assert get success
1115
        self.assertEqual(status, 200)
1116

    
1117
        #assert content-type
1118
        self.assertEqual(headers['content-type'],
1119
                         self.objects[0]['meta']['content_type'])
1120

    
1121
        #assert response content
1122
        self.assertEqual(self.objects[0]['data'], data)
1123

    
1124
    def test_get_with_multiple_if_match(self):
1125
        #perform get with If-Match
1126
        etags = [i['hash'] for i in self.objects if i]
1127
        etags = ','.join('"%s"' % etag for etag in etags)
1128
        status, headers, data = self.client.request_object(self.containers[1],
1129
                                                           self.objects[0]['name'],
1130
                                                           if_match=etags)
1131
        #assert get success
1132
        self.assertEqual(status, 200)
1133

    
1134
        #assert content-type
1135
        self.assertEqual(headers['content-type'],
1136
                         self.objects[0]['meta']['content_type'])
1137

    
1138
        #assert content-type
1139
        self.assertEqual(headers['content-type'],
1140
                         self.objects[0]['meta']['content_type'])
1141

    
1142
        #assert response content
1143
        self.assertEqual(self.objects[0]['data'], data)
1144

    
1145
    def test_if_match_precondition_failed(self):
1146
        #assert precondition failed
1147
        self.assert_raises_fault(412, self.client.retrieve_object,
1148
                                 self.containers[1],
1149
                                 self.objects[0]['name'], if_match='123')
1150

    
1151
    def test_if_none_match(self):
1152
        #perform get with If-None-Match
1153
        status, headers, data = self.client.request_object(self.containers[1],
1154
                                                           self.objects[0]['name'],
1155
                                                           if_none_match='123')
1156

    
1157
        #assert get success
1158
        self.assertEqual(status, 200)
1159

    
1160
        #assert content-type
1161
        self.assertEqual(headers['content_type'],
1162
                         self.objects[0]['meta']['content_type'])
1163

    
1164
    def test_if_none_match(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='*')
1170

    
1171
    def test_if_none_match_not_modified(self):
1172
        #perform get with If-None-Match and assert not modified
1173
        self.assert_raises_fault(304, self.client.retrieve_object,
1174
                                 self.containers[1],
1175
                                 self.objects[0]['name'],
1176
                                 if_none_match=self.objects[0]['hash'])
1177

    
1178
        meta = self.client.retrieve_object_metadata(self.containers[1],
1179
                                                    self.objects[0]['name'])
1180
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1181

    
1182
    def test_if_modified_since(self):
1183
        t = datetime.datetime.utcnow()
1184
        t2 = t - datetime.timedelta(minutes=10)
1185

    
1186
        #modify the object
1187
        self.upload_data(self.containers[1],
1188
                           self.objects[0]['name'],
1189
                           self.objects[0]['data'][:200])
1190

    
1191
        for f in DATE_FORMATS:
1192
            past = t2.strftime(f)
1193

    
1194
            headers = {'if-modified-since':'%s' %past}
1195
            try:
1196
                o = self.client.retrieve_object(self.containers[1],
1197
                                                self.objects[0]['name'],
1198
                                                if_modified_since=past)
1199
                self.assertEqual(o,
1200
                                 self.client.retrieve_object(self.containers[1],
1201
                                                             self.objects[0]['name']))
1202
            except Fault, f:
1203
                self.failIf(f.status == 304)
1204

    
1205
    def test_if_modified_since_invalid_date(self):
1206
        o = self.client.retrieve_object(self.containers[1],
1207
                                        self.objects[0]['name'],
1208
                                        if_modified_since='')
1209
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1210
                                                        self.objects[0]['name']))
1211

    
1212
    def test_if_not_modified_since(self):
1213
        now = datetime.datetime.utcnow()
1214
        since = now + datetime.timedelta(1)
1215

    
1216
        for f in DATE_FORMATS:
1217
            #assert not modified
1218
            self.assert_raises_fault(304, self.client.retrieve_object,
1219
                                     self.containers[1], self.objects[0]['name'],
1220
                                     if_modified_since=since.strftime(f))
1221

    
1222
    def test_if_unmodified_since(self):
1223
        now = datetime.datetime.utcnow()
1224
        since = now + datetime.timedelta(1)
1225

    
1226
        for f in DATE_FORMATS:
1227
            t = since.strftime(f)
1228
            status, headers, data = self.client.request_object(self.containers[1],
1229
                                                               self.objects[0]['name'],
1230
                                                               if_unmodified_since=t)
1231
            #assert success
1232
            self.assertEqual(status, 200)
1233
            self.assertEqual(self.objects[0]['data'], data)
1234

    
1235
            #assert content-type
1236
            self.assertEqual(headers['content-type'],
1237
                             self.objects[0]['meta']['content_type'])
1238

    
1239
    def test_if_unmodified_since_precondition_failed(self):
1240
        t = datetime.datetime.utcnow()
1241
        t2 = t - datetime.timedelta(minutes=10)
1242

    
1243
        #modify the object
1244
        self.upload_data(self.containers[1],
1245
                           self.objects[0]['name'],
1246
                           self.objects[0]['data'][:200])
1247

    
1248
        for f in DATE_FORMATS:
1249
            past = t2.strftime(f)
1250
            #assert precondition failed
1251
            self.assert_raises_fault(412, self.client.retrieve_object,
1252
                                     self.containers[1], self.objects[0]['name'],
1253
                                     if_unmodified_since=past)
1254

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

    
1275
class ObjectPut(BaseTestCase):
1276
    def setUp(self):
1277
        BaseTestCase.setUp(self)
1278
        self.container = 'c1'
1279
        self.client.create_container(self.container)
1280

    
1281
    def test_upload(self):
1282
        name = o_names[0]
1283
        meta = {'test':'test1'}
1284
        o = self.upload_random_data(self.container, name, **meta)
1285

    
1286
        headers = self.client.retrieve_object_metadata(self.container,
1287
                                                       name,
1288
                                                       restricted=True)
1289
        self.assertTrue('test' in headers.keys())
1290
        self.assertEqual(headers['test'], meta['test'])
1291

    
1292
        #assert uploaded content
1293
        status, h, data = self.client.request_object(self.container, name)
1294
        self.assertEqual(len(o['data']), int(h['content-length']))
1295
        self.assertEqual(o['data'], data)
1296

    
1297
        #assert content-type
1298
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1299

    
1300
    def _test_maximum_upload_size_exceeds(self):
1301
        name = o_names[0]
1302
        meta = {'test':'test1'}
1303
        #upload 5GB
1304
        length= 5 * (1024 * 1024 * 1024) + 1
1305
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1306
                                 name, length, **meta)
1307

    
1308
    def test_upload_with_name_containing_slash(self):
1309
        name = '/%s' % o_names[0]
1310
        meta = {'test':'test1'}
1311
        o = self.upload_random_data(self.container, name, **meta)
1312

    
1313
        self.assertEqual(o['data'],
1314
                         self.client.retrieve_object(self.container, name))
1315

    
1316
        self.assertTrue(name in self.client.list_objects(self.container))
1317

    
1318
    def test_create_directory_marker(self):
1319
        self.client.create_directory_marker(self.container, 'foo')
1320
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1321
        self.assertEqual(meta['content-length'], '0')
1322
        self.assertEqual(meta['content-type'], 'application/directory')
1323

    
1324
    def test_upload_unprocessable_entity(self):
1325
        meta={'etag':'123', 'test':'test1'}
1326

    
1327
        #assert unprocessable entity
1328
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1329
                                 o_names[0], **meta)
1330

    
1331
    def test_chunked_transfer(self):
1332
        data = get_random_data()
1333
        objname = 'object'
1334
        self.client.create_object_using_chunks(self.container, objname,
1335
                                               StringIO(data))
1336

    
1337
        uploaded_data = self.client.retrieve_object(self.container, objname)
1338
        self.assertEqual(data, uploaded_data)
1339

    
1340
    def test_manifestation(self):
1341
        prefix = 'myobject/'
1342
        data = ''
1343
        for i in range(5):
1344
            part = '%s%d' %(prefix, i)
1345
            o = self.upload_random_data(self.container, part)
1346
            data += o['data']
1347

    
1348
        manifest = '%s/%s' %(self.container, prefix)
1349
        self.client.create_manifestation(self.container, 'large-object', manifest)
1350

    
1351
        self.assert_object_exists(self.container, 'large-object')
1352
        self.assertEqual(data, self.client.retrieve_object(self.container,
1353
                                                           'large-object'))
1354
        
1355
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1356
        hashes = r['hashes']
1357
        block_size = int(r['block_size'])
1358
        block_hash = r['block_hash']
1359
        l = len(data)
1360
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1361
        self.assertEqual(block_num, len(hashes))
1362
        
1363
        #wrong manifestation
1364
        self.client.create_manifestation(self.container, 'large-object',
1365
                                         '%s/invalid' % self.container)
1366
        self.assertEqual('', self.client.retrieve_object(self.container,
1367
                                                         'large-object'))
1368

    
1369
    def test_create_zero_length_object(self):
1370
        c = self.container
1371
        o = 'object'
1372
        zero = self.client.create_zero_length_object(c, o)
1373
        zero_meta = self.client.retrieve_object_metadata(c, o)
1374
        zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1375
        zero_data = self.client.retrieve_object(c, o)
1376

    
1377
        self.assertEqual(int(zero_meta['content-length']), 0)
1378
        hasher = newhasher('sha256')
1379
        hasher.update("")
1380
        emptyhash = hasher.digest()
1381
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1382
        self.assertEqual(zero_data, '')
1383

    
1384
    def test_create_object_by_hashmap(self):
1385
        c = self.container
1386
        o = 'object'
1387
        self.upload_random_data(c, o)
1388
        hashmap = self.client.retrieve_object(c, o, format='json')
1389
        o2 = 'object-copy'
1390
        self.client.create_object_by_hashmap(c, o2, hashmap)
1391
        self.assertEqual(self.client.retrieve_object(c, o),
1392
                         self.client.retrieve_object(c, o))
1393

    
1394
class ObjectCopy(BaseTestCase):
1395
    def setUp(self):
1396
        BaseTestCase.setUp(self)
1397
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1398
        self.containers.sort()
1399
        
1400
        for c in self.containers:
1401
            self.client.create_container(c)
1402
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1403

    
1404
    def test_copy(self):
1405
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1406
                             self.containers[0], self.obj['name']):
1407
            #perform copy
1408
            meta = {'test':'testcopy'}
1409
            status = self.client.copy_object(self.containers[0],
1410
                                              self.obj['name'],
1411
                                              self.containers[0],
1412
                                              'testcopy',
1413
                                              meta)[0]
1414

    
1415
            #assert copy success
1416
            self.assertEqual(status, 201)
1417

    
1418
            #assert access the new object
1419
            headers = self.client.retrieve_object_metadata(self.containers[0],
1420
                                                           'testcopy')
1421
            self.assertTrue('x-object-meta-test' in headers.keys())
1422
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1423

    
1424
            #assert etag is the same
1425
            self.assertEqual(headers['etag'], self.obj['hash'])
1426

    
1427
            #assert src object still exists
1428
            self.assert_object_exists(self.containers[0], self.obj['name'])
1429

    
1430
    def test_copy_from_different_container(self):
1431
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1432
                             self.containers[0], self.obj['name']):
1433
            meta = {'test':'testcopy'}
1434
            status = self.client.copy_object(self.containers[0],
1435
                                             self.obj['name'],
1436
                                             self.containers[1],
1437
                                             'testcopy',
1438
                                             meta)[0]
1439
            self.assertEqual(status, 201)
1440

    
1441
            # assert updated metadata
1442
            meta = self.client.retrieve_object_metadata(self.containers[1],
1443
                                                           'testcopy',
1444
                                                           restricted=True)
1445
            self.assertTrue('test' in meta.keys())
1446
            self.assertTrue(meta['test'], 'testcopy')
1447

    
1448
            #assert src object still exists
1449
            self.assert_object_exists(self.containers[0], self.obj['name'])
1450

    
1451
    def test_copy_invalid(self):
1452
        #copy from invalid object
1453
        meta = {'test':'testcopy'}
1454
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1455
                                 'test.py', self.containers[1], 'testcopy', meta)
1456

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

    
1491
    def test_move(self):
1492
        meta = self.client.retrieve_object_metadata(self.containers[0],
1493
                                                    self.obj['name'])
1494
        self.assertTrue('x-object-uuid' in meta)
1495
        uuid = meta['x-object-uuid']
1496

    
1497
        #perform move
1498
        meta = {'test':'testcopy'}
1499
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1500
        status = self.client.move_object(self.containers[0], self.obj['name'],
1501
                                         self.containers[0], 'testcopy',
1502
                                         meta)[0]
1503

    
1504
        #assert successful move
1505
        self.assertEqual(status, 201)
1506

    
1507
        #assert updated metadata
1508
        meta = self.client.retrieve_object_metadata(self.containers[0],
1509
                                                    'testcopy')
1510
        self.assertTrue('x-object-meta-test' in meta.keys())
1511
        self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1512

    
1513
        #assert same uuid
1514
        self.assertTrue(meta['x-object-uuid'], uuid)
1515

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

    
1544
class ObjectPost(BaseTestCase):
1545
    def setUp(self):
1546
        BaseTestCase.setUp(self)
1547
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1548
        self.containers.sort()
1549
        
1550
        for c in self.containers:
1551
            self.client.create_container(c)
1552
        self.obj = []
1553
        for i in range(2):
1554
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1555

    
1556
    def test_update_meta(self):
1557
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1558
                                 self.containers[0],
1559
                                 self.obj[0]['name']):
1560
            #perform update metadata
1561
            more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1562
            status = self.client.update_object_metadata(self.containers[0],
1563
                                                        self.obj[0]['name'],
1564
                                                        **more)[0]
1565
            #assert request accepted
1566
            self.assertEqual(status, 202)
1567

    
1568
            #assert old metadata are still there
1569
            headers = self.client.retrieve_object_metadata(self.containers[0],
1570
                                                           self.obj[0]['name'],
1571
                                                           restricted=True)
1572
            #assert new metadata have been updated
1573
            for k,v in more.items():
1574
                self.assertTrue(k in headers.keys())
1575
                self.assertTrue(headers[k], v)
1576

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

    
1628
            r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1629
                                      StringIO(data), **args)
1630
            status = r[0]
1631
            etag = r[1]['etag']
1632
            if partial < 0 or (instance_length and l <= last_byte_pos):
1633
                self.assertEqual(status, 202)
1634
            else:
1635
                self.assertEqual(status, 204)
1636
                #check modified object
1637
                content = self.client.retrieve_object(self.containers[0],
1638
                                                  self.obj[0]['name'])
1639
                self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1640
                self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1641
                self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1642
                self.assertEqual(etag, compute_md5_hash(content))
1643

    
1644
    def test_update_object_lt_blocksize(self):
1645
        self.test_update_object(10, 20, content_length=None)
1646

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

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

    
1681
    def test_update_object_no_content_length(self):
1682
        self.test_update_object(content_length = None)
1683

    
1684
    def test_update_object_invalid_content_length(self):
1685
        with AssertContentInvariant(self.client.retrieve_object,
1686
                                    self.containers[0], self.obj[0]['name']):
1687
            self.assert_raises_fault(400, self.test_update_object,
1688
                                     content_length = 1000)
1689

    
1690
    def test_update_object_invalid_range(self):
1691
        with AssertContentInvariant(self.client.retrieve_object,
1692
                                    self.containers[0], self.obj[0]['name']):
1693
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1694

    
1695
    def test_update_object_invalid_range_and_length(self):
1696
        with AssertContentInvariant(self.client.retrieve_object,
1697
                                    self.containers[0], self.obj[0]['name']):
1698
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1699
                                     -1)
1700

    
1701
    def test_update_object_invalid_range_with_no_content_length(self):
1702
        with AssertContentInvariant(self.client.retrieve_object,
1703
                                    self.containers[0], self.obj[0]['name']):
1704
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1705
                                     content_length = None)
1706

    
1707
    def test_update_object_out_of_limits(self):
1708
        with AssertContentInvariant(self.client.retrieve_object,
1709
                                    self.containers[0], self.obj[0]['name']):
1710
            l = len(self.obj[0]['data'])
1711
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1712

    
1713
    def test_append(self):
1714
        data = get_random_data(500)
1715
        headers = {}
1716
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1717
                                  StringIO(data), content_length=500,
1718
                                  content_type='application/octet-stream')
1719

    
1720
        content = self.client.retrieve_object(self.containers[0],
1721
                                              self.obj[0]['name'])
1722
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1723
        self.assertEqual(content[:-500], self.obj[0]['data'])
1724

    
1725
    def test_update_with_chunked_transfer(self):
1726
        data = get_random_data(500)
1727
        dl = len(data)
1728
        fl = len(self.obj[0]['data'])
1729

    
1730
        self.client.update_object_using_chunks(self.containers[0],
1731
                                               self.obj[0]['name'],
1732
                                               StringIO(data),
1733
                                               offset=0,
1734
                                               content_type='application/octet-stream')
1735

    
1736
        #check modified object
1737
        content = self.client.retrieve_object(self.containers[0],
1738
                                              self.obj[0]['name'])
1739
        self.assertEqual(content[0:dl], data)
1740
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1741

    
1742
    def test_update_from_other_object(self):
1743
        c = self.containers[0]
1744
        src = o_names[0]
1745
        dest = 'object'
1746

    
1747
        source_data = self.client.retrieve_object(c, src)
1748
        source_meta = self.client.retrieve_object_metadata(c, src)
1749
        source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1750

    
1751
        #update zero length object
1752
        self.client.create_zero_length_object(c, dest)
1753
        source_object = '/%s/%s' % (c, src)
1754
        self.client.update_from_other_source(c, dest, source_object)
1755
        dest_data = self.client.retrieve_object(c, src)
1756
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1757
        dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1758
        self.assertEqual(source_data, dest_data)
1759
        self.assertEqual(source_hash, dest_hash)
1760

    
1761
        #test append
1762
        self.client.update_from_other_source(c, dest, source_object)
1763
        content = self.client.retrieve_object(c, dest)
1764
        self.assertEqual(source_data * 2, content)
1765

    
1766
    def test_update_range_from_other_object(self):
1767
        c = self.containers[0]
1768
        dest = 'object'
1769

    
1770
        #test update range
1771
        src = self.obj[1]['name']
1772
        src_data = self.client.retrieve_object(c, src)
1773

    
1774
        #update zero length object
1775
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1776
        source_object = '/%s/%s' % (c, src)
1777
        first_byte_pos = 4*1024*1024+1
1778
        last_byte_pos = 4*1024*1024+4
1779
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1780
        self.client.update_from_other_source(c, dest, source_object,
1781
                                             content_range=range)
1782
        content = self.client.retrieve_object(c, dest)
1783
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1784
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1785
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1786

    
1787
    def test_update_hashes_from_other_object(self):
1788
        c = self.containers[0]
1789
        dest = 'object'
1790

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

    
1794
        #update zero length object
1795
        prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1796
        source_object = '/%s/%s' % (c, o_names[0])
1797
        first_byte_pos = 4*1024*1024
1798
        last_byte_pos = 5*1024*1024
1799
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1800
        self.client.update_from_other_source(c, dest, source_object,
1801
                                             content_range=range)
1802
        content = self.client.retrieve_object(c, dest)
1803
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1804
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1805
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1806

    
1807

    
1808
    def test_update_zero_length_object(self):
1809
        c = self.containers[0]
1810
        o = 'object'
1811
        other = 'other'
1812
        zero = self.client.create_zero_length_object(c, o)
1813

    
1814
        data = get_random_data()
1815
        self.client.update_object(c, o, StringIO(data))
1816
        self.client.create_object(c, other, StringIO(data))
1817

    
1818
        self.assertEqual(self.client.retrieve_object(c, o),
1819
                         self.client.retrieve_object(c, other))
1820

    
1821
        self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1822
                         self.client.retrieve_object_hashmap(c, other)["hashes"])
1823

    
1824
class ObjectDelete(BaseTestCase):
1825
    def setUp(self):
1826
        BaseTestCase.setUp(self)
1827
        self.containers = ['c1', 'c2']
1828
        self.containers.extend(self.initial_containers)
1829
        
1830
        for c in self.containers:
1831
            self.client.create_container(c)
1832
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1833

    
1834
    def test_delete(self):
1835
        #perform delete object
1836
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1837

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

    
1856
class ListSharing(BaseTestCase):
1857
    def setUp(self):
1858
        BaseTestCase.setUp(self)
1859
        for i in range(2):
1860
            self.client.create_container('c%s' %i)
1861
        self.client.create_container('c')
1862
        for i in range(2):
1863
            self.upload_random_data('c1', 'o%s' %i)
1864
        accounts = OTHER_ACCOUNTS.copy()
1865
        self.o1_sharing_with = accounts.popitem()
1866
        self.o1_sharing = [self.o1_sharing_with[1]]
1867
        self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1868

    
1869
        l = []
1870
        for i in range(2):
1871
            l.append(accounts.popitem())
1872

    
1873
    def test_list_other_shared(self):
1874
        self.other = Pithos_Client(get_url(),
1875
                              self.o1_sharing_with[0],
1876
                              self.o1_sharing_with[1])
1877
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1878

    
1879
    def test_list_my_shared(self):
1880
        my_shared_containers = self.client.list_containers(shared=True)
1881
        self.assertTrue('c1' in my_shared_containers)
1882
        self.assertTrue('c2' not in my_shared_containers)
1883

    
1884
        my_shared_objects = self.client.list_objects('c1', shared=True)
1885
        self.assertTrue('o1' in my_shared_objects)
1886
        self.assertTrue('o2' not in my_shared_objects)
1887

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

    
1984
class TestGreek(BaseTestCase):
1985
    def test_create_container(self):
1986
        self.client.create_container('φάκελος')
1987
        self.assert_container_exists('φάκελος')
1988

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

    
1991
    def test_create_object(self):
1992
        self.client.create_container('φάκελος')
1993
        self.upload_random_data('φάκελος', 'αντικείμενο')
1994

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

    
1998
    def test_copy_object(self):
1999
        src_container = 'φάκελος'
2000
        src_object = 'αντικείμενο'
2001
        dest_container = 'αντίγραφα'
2002
        dest_object = 'ασφαλές-αντίγραφο'
2003

    
2004
        self.client.create_container(src_container)
2005
        self.upload_random_data(src_container, src_object)
2006

    
2007
        self.client.create_container(dest_container)
2008
        self.client.copy_object(src_container, src_object, dest_container,
2009
                                dest_object)
2010

    
2011
        self.assert_object_exists(src_container, src_object)
2012
        self.assert_object_exists(dest_container, dest_object)
2013
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2014

    
2015
    def test_move_object(self):
2016
        src_container = 'φάκελος'
2017
        src_object = 'αντικείμενο'
2018
        dest_container = 'αντίγραφα'
2019
        dest_object = 'ασφαλές-αντίγραφο'
2020

    
2021
        self.client.create_container(src_container)
2022
        self.upload_random_data(src_container, src_object)
2023

    
2024
        self.client.create_container(dest_container)
2025
        self.client.move_object(src_container, src_object, dest_container,
2026
                                dest_object)
2027

    
2028
        self.assert_object_not_exists(src_container, src_object)
2029
        self.assert_object_exists(dest_container, dest_object)
2030
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2031

    
2032
    def test_delete_object(self):
2033
        self.client.create_container('φάκελος')
2034
        self.upload_random_data('φάκελος', 'αντικείμενο')
2035
        self.assert_object_exists('φάκελος', 'αντικείμενο')
2036

    
2037
        self.client.delete_object('φάκελος', 'αντικείμενο')
2038
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2039
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2040

    
2041
    def test_delete_container(self):
2042
        self.client.create_container('φάκελος')
2043
        self.assert_container_exists('φάκελος')
2044

    
2045
        self.client.delete_container('φάκελος')
2046
        self.assert_container_not_exists('φάκελος')
2047
        self.assertTrue('φάκελος' not in self.client.list_containers())
2048

    
2049
    def test_account_meta(self):
2050
        meta = {'ποιότητα':'ΑΑΑ'}
2051
        self.client.update_account_metadata(**meta)
2052
        meta = self.client.retrieve_account_metadata(restricted=True)
2053
        self.assertTrue('ποιότητα' in meta.keys())
2054
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2055

    
2056
    def test_container_meta(self):
2057
        meta = {'ποιότητα':'ΑΑΑ'}
2058
        self.client.create_container('φάκελος', meta=meta)
2059

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

    
2064
    def test_object_meta(self):
2065
        self.client.create_container('φάκελος')
2066
        meta = {'ποιότητα':'ΑΑΑ'}
2067
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2068

    
2069
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2070
                                                    restricted=True)
2071
        self.assertTrue('ποιότητα' in meta.keys())
2072
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2073

    
2074
    def test_list_meta_filtering(self):
2075
        self.client.create_container('φάκελος')
2076
        meta = {'ποιότητα':'ΑΑΑ'}
2077
        self.upload_random_data('φάκελος', 'ο1', **meta)
2078
        self.upload_random_data('φάκελος', 'ο2')
2079
        self.upload_random_data('φάκελος', 'ο3')
2080

    
2081
        meta = {'ποσότητα':'μεγάλη'}
2082
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2083
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2084
        self.assertEquals(objects, ['ο1', 'ο2'])
2085

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

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

    
2092
        meta = {'ποιότητα':'ΑΒ'}
2093
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2094
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2095
        self.assertEquals(objects, ['ο1'])
2096
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2097
        self.assertEquals(objects, ['ο2'])
2098

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

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

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

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

    
2135
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
2136
        self.assertEqual(server_data[:len(o['data'])], o['data'])
2137
        self.assertEqual(server_data[len(o['data']):], new_data)
2138

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

    
2148
        self.client.create_container('φάκελος')
2149
        manifest = '%s/%s' %('κουβάς', prefix)
2150
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2151

    
2152
        self.assert_object_exists('φάκελος', 'άπαντα')
2153
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
2154
                                                           'άπαντα'))
2155

    
2156
        #wrong manifestation
2157
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2158
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2159

    
2160
    def test_update_from_another_object(self):
2161
        self.client.create_container('κουβάς')
2162
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2163
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2164
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2165
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2166

    
2167
        self.assertEqual(
2168
            self.client.retrieve_object('κουβάς', 'νέο'),
2169
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2170

    
2171
class TestPermissions(BaseTestCase):
2172
    def setUp(self):
2173
        BaseTestCase.setUp(self)
2174

    
2175
        #create a group
2176
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
2177
        groups = {'pithosdev':','.join(self.authorized)}
2178
        self.client.set_account_groups(**groups)
2179

    
2180
        self.container = 'c'
2181
        self.object = 'o'
2182
        self.client.create_container(self.container)
2183
        self.upload_random_data(self.container, self.object)
2184
        self.upload_random_data(self.container, self.object+'/')
2185
        self.upload_random_data(self.container, self.object+'/a')
2186
        self.upload_random_data(self.container, self.object+'a')
2187
        self.upload_random_data(self.container, self.object+'a/')
2188
        self.dir_content_types = ('application/directory', 'application/folder')
2189

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

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

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

    
2273
    def test_group_read(self):
2274
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2275
        self.assert_read(authorized=self.authorized)
2276

    
2277
    def test_read_many(self):
2278
        self.client.share_object(self.container, self.object, self.authorized)
2279
        self.assert_read(authorized=self.authorized)
2280

    
2281
    def test_read_by_everyone(self):
2282
        self.client.share_object(self.container, self.object, ['*'])
2283
        self.assert_read(any=True)
2284

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

    
2296
    def test_group_write(self):
2297
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2298
        self.assert_write(authorized=self.authorized)
2299

    
2300
    def test_write_many(self):
2301
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2302
        self.assert_write(authorized=self.authorized)
2303

    
2304
    def test_write_by_everyone(self):
2305
        self.client.share_object(self.container, self.object, ['*'], read=False)
2306
        self.assert_write(any=True)
2307

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

    
2320
    def test_shared_listing(self):
2321
        self.client.share_object(self.container, self.object, self.authorized)
2322

    
2323
        my_shared_containers = self.client.list_containers(shared=True)
2324
        self.assertTrue('c' in my_shared_containers)
2325
        my_shared_objects = self.client.list_objects('c', shared=True)
2326
        self.assertEqual(['o'], my_shared_objects)
2327

    
2328
        dir_content_types = ('application/directory', 'application/foler')
2329
        for type in dir_content_types:
2330
            #change content type
2331
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2332
            my_shared_objects = self.client.list_objects('c', shared=True)
2333
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2334

    
2335
        for token, account in OTHER_ACCOUNTS.items():
2336
            if account in self.authorized:
2337
                self.other = Pithos_Client(get_url(), token, account)
2338
                self.assertTrue(get_user() in self.other.list_shared_by_others())
2339

    
2340
class TestPublish(BaseTestCase):
2341
    def test_publish(self):
2342
        self.client.create_container('c')
2343
        o_data = self.upload_random_data('c', 'o')['data']
2344
        self.client.publish_object('c', 'o')
2345
        meta = self.client.retrieve_object_metadata('c', 'o')
2346
        self.assertTrue('x-object-public' in meta)
2347
        url = meta['x-object-public']
2348

    
2349
        p = urlparse(get_url())
2350
        if p.scheme == 'http':
2351
            conn = HTTPConnection(p.netloc)
2352
        elif p.scheme == 'https':
2353
            conn = HTTPSConnection(p.netloc)
2354
        else:
2355
            raise Exception('Unknown URL scheme')
2356

    
2357
        conn.request('GET', url)
2358
        resp = conn.getresponse()
2359
        length = resp.getheader('content-length', None)
2360
        data = resp.read(length)
2361
        self.assertEqual(o_data, data)
2362

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

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

    
2386
    def test_quota_none(self):
2387
        self.client.create_container('c', policies={'quota':'0'})
2388
        meta = self.client.retrieve_container_metadata('c')
2389
        self.assertEqual(meta['x-container-policy-quota'], '0')
2390
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2391
                                 length=1024*1024+1)
2392

    
2393
class AssertUUidInvariant(object):
2394
    def __init__(self, callable, *args, **kwargs):
2395
        self.callable = callable
2396
        self.args = args
2397
        self.kwargs = kwargs
2398

    
2399
    def __enter__(self):
2400
        self.map = self.callable(*self.args, **self.kwargs)
2401
        assert('x-object-uuid' in self.map)
2402
        self.uuid = self.map['x-object-uuid']
2403
        return self.map
2404

    
2405
    def __exit__(self, type, value, tb):
2406
        map = self.callable(*self.args, **self.kwargs)
2407
        assert('x-object-uuid' in self.map)
2408
        uuid = map['x-object-uuid']
2409
        assert(uuid == self.uuid)
2410

    
2411
class AssertMappingInvariant(object):
2412
    def __init__(self, callable, *args, **kwargs):
2413
        self.callable = callable
2414
        self.args = args
2415
        self.kwargs = kwargs
2416

    
2417
    def __enter__(self):
2418
        self.map = self.callable(*self.args, **self.kwargs)
2419
        return self.map
2420

    
2421
    def __exit__(self, type, value, tb):
2422
        map = self.callable(*self.args, **self.kwargs)
2423
        for k, v in self.map.items():
2424
            if is_date(v):
2425
                continue
2426
            assert(k in map)
2427
            assert v == map[k]
2428

    
2429
class AssertContentInvariant(object):
2430
    def __init__(self, callable, *args, **kwargs):
2431
        self.callable = callable
2432
        self.args = args
2433
        self.kwargs = kwargs
2434

    
2435
    def __enter__(self):
2436
        self.content = self.callable(*self.args, **self.kwargs)[2]
2437
        return self.content
2438

    
2439
    def __exit__(self, type, value, tb):
2440
        content = self.callable(*self.args, **self.kwargs)[2]
2441
        assert self.content == content
2442

    
2443
def get_content_splitted(response):
2444
    if response:
2445
        return response.content.split('\n')
2446

    
2447
def compute_md5_hash(data):
2448
    md5 = hashlib.md5()
2449
    offset = 0
2450
    md5.update(data)
2451
    return md5.hexdigest().lower()
2452

    
2453
def compute_block_hash(data, algorithm):
2454
    h = hashlib.new(algorithm)
2455
    h.update(data.rstrip('\x00'))
2456
    return h.hexdigest()
2457

    
2458
def get_random_data(length=500):
2459
    char_set = string.ascii_uppercase + string.digits
2460
    return ''.join(random.choice(char_set) for x in xrange(length))
2461

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

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

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

    
2515

    
2516
def main():
2517
    if get_user() == 'test':
2518
        unittest.main(module='pithos.tools.test')
2519
    else:
2520
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2521

    
2522

    
2523
if __name__ == "__main__":
2524
    main()
2525