Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 31e1acd3

History | View | Annotate | Download (105.2 kB)

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

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

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

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

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

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

    
62
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
        
81
        self._clean_account()
82
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
83

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
241
            args = {}
242
            args['etag'] = etag if etag else obj['hash']
243

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

    
253
            obj['meta'] = args
254

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

    
259
            return obj
260
        except IOError:
261
            return
262

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

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

    
276
    def test_get_account_meta(self):
277
        meta = self.client.retrieve_account_metadata()
278

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

    
293
    def test_get_account_403(self):
294
        self.assert_raises_fault(403,
295
                                 self.invalid_client.retrieve_account_metadata)
296

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

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

    
308
        meta = self.client.retrieve_account_metadata(restricted=True)
309
        self.assertTrue('premium' in meta)
310

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

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

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

    
333
    def test_list_403(self):
334
        self.assert_raises_fault(403, self.invalid_client.list_containers)
335

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

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

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

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

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

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

    
376
        #add a new container
377
        self.client.create_container('dummy')
378

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

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

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

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

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

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

    
408
            #assert success
409
            self.assertEqual(self.containers, c)
410

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

    
415
        #add a new container
416
        self.client.create_container('dummy')
417

    
418
        for f in DATE_FORMATS:
419
            past = t2.strftime(f)
420

    
421
            args = {'if_unmodified_since':'%s' %past}
422

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

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

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

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

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

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

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

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

    
463
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
464

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

    
470
            self.client.delete_account_metadata(meta.keys())
471

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

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

    
481
            self.assertEqual(set(groups['pithosdev']),
482
                             set(self.client.retrieve_account_groups()['pithosdev']))
483

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

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

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

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

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

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

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

    
514
            self.client.unset_account_groups(groups.keys())
515

    
516
            self.assertEqual({}, self.client.retrieve_account_groups())
517

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

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

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

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

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

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

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

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

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

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

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

    
654
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
655

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
783
        #add a new container
784
        self.client.create_container('dummy')
785

    
786
        for f in DATE_FORMATS:
787
            past = t2.strftime(f)
788

    
789
            args = {'if_unmodified_since':'%s' %past}
790

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

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

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

    
811
    def test_quota(self):
812
        self.client.create_container(self.containers[0])
813

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
969
        #assert successful partial content
970
        self.assertEqual(status, 206)
971

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

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

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

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

    
989
        #assert successful partial content
990
        self.assertEqual(status, 206)
991

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

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

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

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

    
1009
        #assert successful partial content
1010
        self.assertEqual(status, 206)
1011

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

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

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

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

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

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

    
1039
        # assert partial content
1040
        self.assertEqual(status, 206)
1041

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1155
        #assert get success
1156
        self.assertEqual(status, 200)
1157

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

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

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

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

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

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

    
1189
        for f in DATE_FORMATS:
1190
            past = t2.strftime(f)
1191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1314
        self.assertTrue(name in self.client.list_objects(self.container))
1315

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

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

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

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

    
1335
        uploaded_data = self.client.retrieve_object(self.container, objname)
1336
        self.assertEqual(data, uploaded_data)
1337

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

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

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

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

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

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

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

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

    
1413
            #assert copy success
1414
            self.assertEqual(status, 201)
1415

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

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

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

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

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

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

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

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

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

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

    
1502
        #assert successful move
1503
        self.assertEqual(status, 201)
1504

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

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

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

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

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

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

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

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

    
1642
    def test_update_object_lt_blocksize(self):
1643
        self.test_update_object(10, 20, content_length=None)
1644

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

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

    
1679
    def test_update_object_no_content_length(self):
1680
        self.test_update_object(content_length = None)
1681

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1805

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

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

    
1816
        self.assertEqual(self.client.retrieve_object(c, o),
1817
                         self.client.retrieve_object(c, other))
1818

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2002
        self.client.create_container(src_container)
2003
        self.upload_random_data(src_container, src_object)
2004

    
2005
        self.client.create_container(dest_container)
2006
        self.client.copy_object(src_container, src_object, dest_container,
2007
                                dest_object)
2008

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

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

    
2019
        self.client.create_container(src_container)
2020
        self.upload_random_data(src_container, src_object)
2021

    
2022
        self.client.create_container(dest_container)
2023
        self.client.move_object(src_container, src_object, dest_container,
2024
                                dest_object)
2025

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2169
class TestPermissions(BaseTestCase):
2170
    def setUp(self):
2171
        BaseTestCase.setUp(self)
2172

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2318
    def test_shared_listing(self):
2319
        self.client.share_object(self.container, self.object, self.authorized)
2320

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2513

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

    
2520

    
2521
if __name__ == "__main__":
2522
    main()
2523