Statistics
| Branch: | Tag: | Revision:

root / contrib / snf-pithos-tools / pithos / tools / test.py @ 6fe32fb6

History | View | Annotate | Download (145.2 kB)

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

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

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

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

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

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

    
62
from pithos.api.settings import AUTHENTICATION_USERS
63
AUTHENTICATION_USERS = AUTHENTICATION_USERS or {}
64
OTHER_ACCOUNTS = AUTHENTICATION_USERS.copy()
65
try:
66
    OTHER_ACCOUNTS.pop(get_auth())
67
except:
68
    pass
69

    
70
class BaseTestCase(unittest.TestCase):
71
    #TODO unauthorized request
72
    def setUp(self):
73
        self.client = Pithos_Client(get_url(), get_auth(), get_user())
74

    
75
        #keep track of initial containers
76
        self.initial_containers = self.client.list_containers()
77
        if self.initial_containers == '':
78
            self.initial_containers = []
79

    
80
        self._clean_account()
81
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
82

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

    
86
        #keep track of initial account meta
87
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
88

    
89
        self.extended = {
90
            'container':(
91
                'name',
92
                'count',
93
                'bytes',
94
                'last_modified',
95
                'x_container_policy'),
96
            'object':(
97
                'name',
98
                'hash',
99
                'bytes',
100
                'content_type',
101
                'content_encoding',
102
                'last_modified',)}
103
        self.return_codes = (400, 401, 403, 404, 503,)
104

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

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

    
121
    def _clean_account(self):
122
        for c in self.client.list_containers():
123
#             if c not in self.initial_containers:
124
                self.client.delete_container(c, delimiter='/')
125
                self.client.delete_container(c)
126

    
127
    def assert_status(self, status, codes):
128
        l = [elem for elem in self.return_codes]
129
        if type(codes) == types.ListType:
130
            l.extend(codes)
131
        else:
132
            l.append(codes)
133
        self.assertTrue(status in l)
134

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
252
            obj['meta'] = args
253

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

    
258
            return obj
259
        except IOError:
260
            return
261

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

    
268
        for item in self.containers:
269
            self.client.create_container(item)
270

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

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

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

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

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

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

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

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

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

    
324
        for item in self.containers:
325
            self.client.create_container(item)
326

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

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

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

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

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

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

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

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

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

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

    
386
    def test_if_modified_since_invalid_date(self):
387
        c = self.client.list_containers(if_modified_since='')
388
        self.assertEqual(len(c), len(self.containers))
389

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

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

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

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

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

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

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

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

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

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

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

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

    
431
        for item in self.containers:
432
            self.client.create_container(item)
433

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
538
class ContainerGet(BaseTestCase):
539
    def setUp(self):
540
        BaseTestCase.setUp(self)
541
        self.container = ['pears', 'apples']
542
        for c in self.container:
543
            self.client.create_container(c)
544
        self.obj = []
545
        for o in o_names[:8]:
546
            self.obj.append(self.upload_random_data(self.container[0], o))
547
        for o in o_names[8:]:
548
            self.obj.append(self.upload_random_data(self.container[1], o))
549

    
550
    def test_list_shared(self):
551
        self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
552
        objs = self.client.list_objects(self.container[0], shared=True)
553
        self.assertEqual(objs, [self.obj[0]['name']])
554

    
555
        # create child object
556
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
557
        objs = self.client.list_objects(self.container[0], shared=True)
558
        self.assertEqual(objs, [self.obj[0]['name']])
559

    
560
        # test inheritance
561
        self.client.create_folder(self.container[1], 'folder')
562
        self.client.share_object(self.container[1], 'folder', ('*',))
563
        self.upload_random_data(self.container[1], 'folder/object')
564
        objs = self.client.list_objects(self.container[1], shared=True)
565
        self.assertEqual(objs, ['folder', 'folder/object'])
566

    
567
    def test_list_public(self):
568
        token = OTHER_ACCOUNTS.keys()[0]
569
        account = OTHER_ACCOUNTS[token]
570
        cl = Pithos_Client(get_url(), token, account)
571

    
572
        self.client.publish_object(self.container[0], self.obj[0]['name'])
573
        objs = self.client.list_objects(self.container[0], public=True)
574
        self.assertEqual(objs, [self.obj[0]['name']])
575
        self.assert_raises_fault(
576
            403, cl.list_objects, self.container[0], public=True,
577
            account=get_user()
578
        )
579
        self.client.share_object(
580
            self.container[0], self.obj[1]['name'], [account]
581
        )
582
        objs = cl.list_objects(
583
            self.container[0], public=True, account=get_user()
584
        )
585
        self.assertTrue(self.obj[0]['name'] not in objs)
586

    
587
        # create child object
588
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
589
        objs = self.client.list_objects(self.container[0], public=True)
590
        self.assertEqual(objs, [self.obj[0]['name']])
591
        objs = cl.list_objects(
592
            self.container[0], public=True, account=get_user()
593
        )
594
        self.assertTrue(self.obj[0]['name'] not in objs)
595

    
596
        # test inheritance
597
        self.client.create_folder(self.container[1], 'folder')
598
        self.client.publish_object(self.container[1], 'folder')
599
        self.upload_random_data(self.container[1], 'folder/object')
600
        objs = self.client.list_objects(self.container[1], public=True)
601
        self.assertEqual(objs, ['folder'])
602
        self.assert_raises_fault(
603
            403, cl.list_objects, self.container[1], public=True,
604
            account=get_user()
605
        )
606

    
607

    
608
    def test_list_shared_public(self):
609
        token = OTHER_ACCOUNTS.keys()[0]
610
        account = OTHER_ACCOUNTS[token]
611
        cl = Pithos_Client(get_url(), token, account)
612

    
613
        self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
614
        self.client.publish_object(self.container[0], self.obj[1]['name'])
615
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
616
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
617
        objs = cl.list_objects(
618
            self.container[0], shared=True, public=True, account=get_user()
619
        )
620
        self.assertEqual(objs, [self.obj[0]['name']])
621

    
622
        # create child object
623
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
624
        self.upload_random_data(self.container[0], strnextling(self.obj[1]['name']))
625
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
626
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
627
        objs = cl.list_objects(
628
            self.container[0], shared=True, public=True, account=get_user()
629
        )
630
        self.assertEqual(objs, [self.obj[0]['name']])
631

    
632
        # test inheritance
633
        self.client.create_folder(self.container[1], 'folder1')
634
        self.client.share_object(self.container[1], 'folder1', ('*',))
635
        self.upload_random_data(self.container[1], 'folder1/object')
636
        self.client.create_folder(self.container[1], 'folder2')
637
        self.client.publish_object(self.container[1], 'folder2')
638
        o = self.upload_random_data(self.container[1], 'folder2/object')
639
        objs = self.client.list_objects(self.container[1], shared=True, public=True)
640
        self.assertEqual(objs, ['folder1', 'folder1/object', 'folder2'])
641
        objs = cl.list_objects(
642
            self.container[1], shared=True, public=True, account=get_user()
643
        )
644
        self.assertEqual(objs, ['folder1', 'folder1/object'])
645

    
646
    def test_list_objects(self):
647
        objects = self.client.list_objects(self.container[0])
648
        l = [elem['name'] for elem in self.obj[:8]]
649
        l.sort()
650
        self.assertEqual(objects, l)
651

    
652
    def test_list_objects_containing_slash(self):
653
        self.client.create_container('test')
654
        self.upload_random_data('test', '/objectname')
655

    
656
        objects = self.client.list_objects('test')
657
        self.assertEqual(objects, ['/objectname'])
658

    
659
        objects = self.client.list_objects('test', format='json')
660
        self.assertEqual(objects[0]['name'], '/objectname')
661

    
662
        objects = self.client.list_objects('test', format='xml')
663
        self.assert_extended(objects, 'xml', 'object')
664
        node_name = objects.getElementsByTagName('name')[0]
665
        self.assertEqual(node_name.firstChild.data, '/objectname')
666

    
667
    def test_list_objects_with_limit_marker(self):
668
        objects = self.client.list_objects(self.container[0], limit=2)
669
        l = [elem['name'] for elem in self.obj[:8]]
670
        l.sort()
671
        self.assertEqual(objects, l[:2])
672

    
673
        markers = ['How To Win Friends And Influence People.pdf',
674
                   'moms_birthday.jpg']
675
        limit = 4
676
        for m in markers:
677
            objects = self.client.list_objects(self.container[0], limit=limit,
678
                                               marker=m)
679
            l = [elem['name'] for elem in self.obj[:8]]
680
            l.sort()
681
            start = l.index(m) + 1
682
            end = start + limit
683
            end = end if len(l) >= end else len(l)
684
            self.assertEqual(objects, l[start:end])
685

    
686
    #takes too long
687
    def _test_list_limit_exceeds(self):
688
        self.client.create_container('pithos')
689

    
690
        for i in range(10001):
691
            self.client.create_zero_length_object('pithos', i)
692

    
693
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
694

    
695
    def test_list_empty_params(self):
696
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
697
        if objects:
698
            objects = objects.strip().split('\n')
699
        self.assertEqual(objects,
700
                         self.client.list_objects(self.container[0]))
701

    
702
    def test_list_pseudo_hierarchical_folders(self):
703
        objects = self.client.list_objects(self.container[1], prefix='photos',
704
                                           delimiter='/')
705
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
706
                           'photos/plants/'], objects)
707

    
708
        objects = self.client.list_objects(self.container[1],
709
                                           prefix='photos/animals',
710
                                           delimiter='/')
711
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
712
        self.assertEquals(l, objects)
713

    
714
        objects = self.client.list_objects(self.container[1], path='photos')
715
        self.assertEquals(['photos/me.jpg'], objects)
716

    
717
    def test_extended_list_json(self):
718
        objects = self.client.list_objects(self.container[1], format='json',
719
                                           limit=2, prefix='photos/animals',
720
                                           delimiter='/')
721
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
722
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
723

    
724
    def test_extended_list_xml(self):
725
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
726
                                       prefix='photos', delimiter='/')
727
        self.assert_extended(xml, 'xml', 'object', size=4)
728
        dirs = xml.getElementsByTagName('subdir')
729
        self.assertEqual(len(dirs), 2)
730
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
731
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
732

    
733
        objects = xml.getElementsByTagName('name')
734
        self.assertEqual(len(objects), 1)
735
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
736

    
737
    def test_list_meta_double_matching(self):
738
        meta = {'quality':'aaa', 'stock':'true'}
739
        self.client.update_object_metadata(self.container[0],
740
                                           self.obj[0]['name'], **meta)
741
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
742
        self.assertEqual(len(obj), 1)
743
        self.assertTrue(obj, self.obj[0]['name'])
744

    
745
    def test_list_using_meta(self):
746
        meta = {'quality':'aaa'}
747
        for o in self.obj[:2]:
748
            self.client.update_object_metadata(self.container[0], o['name'],
749
                                               **meta)
750
        meta = {'stock':'true'}
751
        for o in self.obj[3:5]:
752
            self.client.update_object_metadata(self.container[0], o['name'],
753
                                               **meta)
754

    
755
        obj = self.client.list_objects(self.container[0], meta='Quality')
756
        self.assertEqual(len(obj), 2)
757
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
758

    
759
        # test case insensitive
760
        obj = self.client.list_objects(self.container[0], meta='quality')
761
        self.assertEqual(len(obj), 2)
762
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
763

    
764
        # test multiple matches
765
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
766
        self.assertEqual(len(obj), 4)
767
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
768

    
769
        # test non 1-1 multiple match
770
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
771
        self.assertEqual(len(obj), 2)
772
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
773

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

    
778
        #add a new object
779
        self.upload_random_data(self.container[0], o_names[0])
780

    
781
        for f in DATE_FORMATS:
782
            past = t2.strftime(f)
783
            try:
784
                o = self.client.list_objects(self.container[0],
785
                                            if_modified_since=past)
786
                self.assertEqual(o,
787
                                 self.client.list_objects(self.container[0]))
788
            except Fault, f:
789
                self.failIf(f.status == 304) #fail if not modified
790

    
791
    def test_if_modified_since_invalid_date(self):
792
        headers = {'if-modified-since':''}
793
        o = self.client.list_objects(self.container[0], if_modified_since='')
794
        self.assertEqual(o, self.client.list_objects(self.container[0]))
795

    
796
    def test_if_not_modified_since(self):
797
        now = datetime.datetime.utcnow()
798
        since = now + datetime.timedelta(1)
799

    
800
        for f in DATE_FORMATS:
801
            args = {'if_modified_since':'%s' %since.strftime(f)}
802

    
803
            #assert not modified
804
            self.assert_raises_fault(304, self.client.list_objects,
805
                                     self.container[0], **args)
806

    
807
    def test_if_unmodified_since(self):
808
        now = datetime.datetime.utcnow()
809
        since = now + datetime.timedelta(1)
810

    
811
        for f in DATE_FORMATS:
812
            obj = self.client.list_objects(self.container[0],
813
                                           if_unmodified_since=since.strftime(f))
814

    
815
            #assert unmodified
816
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
817

    
818
    def test_if_unmodified_since_precondition_failed(self):
819
        t = datetime.datetime.utcnow()
820
        t2 = t - datetime.timedelta(minutes=10)
821

    
822
        #add a new container
823
        self.client.create_container('dummy')
824

    
825
        for f in DATE_FORMATS:
826
            past = t2.strftime(f)
827

    
828
            args = {'if_unmodified_since':'%s' %past}
829

    
830
            #assert precondition failed
831
            self.assert_raises_fault(412, self.client.list_objects,
832
                                     self.container[0], **args)
833

    
834
class ContainerPut(BaseTestCase):
835
    def setUp(self):
836
        BaseTestCase.setUp(self)
837
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
838
        self.containers.sort()
839

    
840
    def test_create(self):
841
        self.client.create_container(self.containers[0])
842
        containers = self.client.list_containers()
843
        self.assertTrue(self.containers[0] in containers)
844
        self.assert_container_exists(self.containers[0])
845

    
846
    def test_create_twice(self):
847
        self.client.create_container(self.containers[0])
848
        self.assertTrue(not self.client.create_container(self.containers[0]))
849

    
850
    def test_quota(self):
851
        self.client.create_container(self.containers[0])
852

    
853
        policy = {'quota':100}
854
        self.client.set_container_policies(self.containers[0], **policy)
855

    
856
        meta = self.client.retrieve_container_metadata(self.containers[0])
857
        self.assertTrue('x-container-policy-quota' in meta)
858
        self.assertEqual(meta['x-container-policy-quota'], '100')
859

    
860
        args = [self.containers[0], 'o1']
861
        kwargs = {'length':101}
862
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
863

    
864
        #reset quota
865
        policy = {'quota':0}
866
        self.client.set_container_policies(self.containers[0], **policy)
867

    
868
class ContainerPost(BaseTestCase):
869
    def setUp(self):
870
        BaseTestCase.setUp(self)
871
        self.container = 'apples'
872
        self.client.create_container(self.container)
873

    
874
    def test_update_meta(self):
875
        meta = {'test':'test33',
876
                'tost':'tost22'}
877
        self.client.update_container_metadata(self.container, **meta)
878
        headers = self.client.retrieve_container_metadata(self.container)
879
        for k,v in meta.items():
880
            k = 'x-container-meta-%s' % k
881
            self.assertTrue(headers[k])
882
            self.assertEqual(headers[k], v)
883

    
884
class ContainerDelete(BaseTestCase):
885
    def setUp(self):
886
        BaseTestCase.setUp(self)
887
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
888
        self.containers.sort()
889

    
890
        for c in self.containers:
891
            self.client.create_container(c)
892

    
893
    def test_delete(self):
894
        status = self.client.delete_container(self.containers[0])[0]
895
        self.assertEqual(status, 204)
896

    
897
    def test_delete_non_empty(self):
898
        self.upload_random_data(self.containers[1], o_names[0])
899
        self.assert_raises_fault(409, self.client.delete_container,
900
                                 self.containers[1])
901

    
902
    def test_delete_invalid(self):
903
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
904

    
905
    def test_delete_contents(self):
906
        self.client.create_folder(self.containers[0], 'folder-1')
907
        self.upload_random_data(self.containers[1], 'folder-1/%s' % o_names[0])
908
        self.client.create_folder(self.containers[0], 'folder-1/subfolder')
909
        self.client.create_folder(self.containers[0], 'folder-2/%s' % o_names[1])
910

    
911
        objects = self.client.list_objects(self.containers[0])
912
        self.client.delete_container(self.containers[0], delimiter='/')
913
        for o in objects:
914
            self.assert_object_not_exists(self.containers[0], o)
915
        self.assert_container_exists(self.containers[0])
916

    
917
class ObjectGet(BaseTestCase):
918
    def setUp(self):
919
        BaseTestCase.setUp(self)
920
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
921
        self.containers.sort()
922

    
923
        #create some containers
924
        for c in self.containers:
925
            self.client.create_container(c)
926

    
927
        #upload a file
928
        names = ('obj1', 'obj2')
929
        self.objects = []
930
        for n in names:
931
            self.objects.append(self.upload_random_data(self.containers[1], n))
932

    
933
    def test_versions(self):
934
        c = self.containers[1]
935
        o = self.objects[0]
936
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
937
        self.assert_versionlist_structure(b)
938

    
939
        #update meta
940
        meta = {'quality':'AAA', 'stock':True}
941
        self.client.update_object_metadata(c, o['name'], **meta)
942

    
943
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
944
        self.assert_versionlist_structure(a)
945
        self.assertEqual(len(b)+1, len(a))
946
        self.assertEqual(b, a[:-1])
947

    
948
        #get exact previous version metadata
949
        v = a[-2][0]
950
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
951
                                                      restricted=True,
952
                                                      version=v)
953
        (self.assertTrue(k not in v_meta) for k in meta.keys())
954

    
955
        #update obejct
956
        data = get_random_data()
957
        self.client.update_object(c, o['name'], StringIO(data))
958

    
959
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
960
        self.assert_versionlist_structure(aa)
961
        self.assertEqual(len(a)+1, len(aa))
962
        self.assertEqual(a, aa[:-1])
963

    
964
        #get exact previous version
965
        v = aa[-3][0]
966
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
967
        self.assertEqual(o['data'], v_data)
968
        self.assertEqual(self.client.retrieve_object(c, o['name']),
969
                         '%s%s' %(v_data, data))
970

    
971
    def test_get(self):
972
        #perform get
973
        o = self.client.retrieve_object(self.containers[1],
974
                                        self.objects[0]['name'],
975
                                        self.objects[0]['meta'])
976
        self.assertEqual(o, self.objects[0]['data'])
977

    
978
    def test_objects_with_trailing_spaces(self):
979
        self.client.create_container('test')
980
        #create 'a' object
981
        self.upload_random_data('test', 'a')
982
        #look for 'a ' object
983
        self.assert_raises_fault(404, self.client.retrieve_object,
984
                                 'test', 'a ')
985

    
986
        #delete 'a' object
987
        self.client.delete_object('test', 'a')
988
        self.assert_raises_fault(404, self.client.retrieve_object,
989
                                 'test', 'a')
990

    
991
        #create 'a ' object
992
        self.upload_random_data('test', 'a ')
993
        #look for 'a' object
994
        self.assert_raises_fault(404, self.client.retrieve_object,
995
                                 'test', 'a')
996

    
997
    def test_get_invalid(self):
998
        self.assert_raises_fault(404, self.client.retrieve_object,
999
                                 self.containers[0], self.objects[0]['name'])
1000

    
1001
    def test_get_partial(self):
1002
        #perform get with range
1003
        status, headers, data = self.client.request_object(self.containers[1],
1004
                                                            self.objects[0]['name'],
1005
                                                            range='bytes=0-499')
1006

    
1007
        #assert successful partial content
1008
        self.assertEqual(status, 206)
1009

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

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

    
1017
        #assert content
1018
        self.assertEqual(self.objects[0]['data'][:500], data)
1019

    
1020
    def test_get_final_500(self):
1021
        #perform get with range
1022
        headers = {'range':'bytes=-500'}
1023
        status, headers, data = self.client.request_object(self.containers[1],
1024
                                                            self.objects[0]['name'],
1025
                                                            range='bytes=-500')
1026

    
1027
        #assert successful partial content
1028
        self.assertEqual(status, 206)
1029

    
1030
        #assert content-type
1031
        self.assertEqual(headers['content-type'],
1032
                         self.objects[0]['meta']['content_type'])
1033

    
1034
        #assert content length
1035
        self.assertEqual(int(headers['content-length']), 500)
1036

    
1037
        #assert content
1038
        self.assertTrue(self.objects[0]['data'][-500:], data)
1039

    
1040
    def test_get_rest(self):
1041
        #perform get with range
1042
        offset = len(self.objects[0]['data']) - 500
1043
        status, headers, data = self.client.request_object(self.containers[1],
1044
                                                self.objects[0]['name'],
1045
                                                range='bytes=%s-' %offset)
1046

    
1047
        #assert successful partial content
1048
        self.assertEqual(status, 206)
1049

    
1050
        #assert content-type
1051
        self.assertEqual(headers['content-type'],
1052
                         self.objects[0]['meta']['content_type'])
1053

    
1054
        #assert content length
1055
        self.assertEqual(int(headers['content-length']), 500)
1056

    
1057
        #assert content
1058
        self.assertTrue(self.objects[0]['data'][-500:], data)
1059

    
1060
    def test_get_range_not_satisfiable(self):
1061
        #perform get with range
1062
        offset = len(self.objects[0]['data']) + 1
1063

    
1064
        #assert range not satisfiable
1065
        self.assert_raises_fault(416, self.client.retrieve_object,
1066
                                 self.containers[1], self.objects[0]['name'],
1067
                                 range='bytes=0-%s' %offset)
1068

    
1069
    def test_multiple_range(self):
1070
        #perform get with multiple range
1071
        ranges = ['0-499', '-500', '1000-']
1072
        bytes = 'bytes=%s' % ','.join(ranges)
1073
        status, headers, data = self.client.request_object(self.containers[1],
1074
                                                           self.objects[0]['name'],
1075
                                                           range=bytes)
1076

    
1077
        # assert partial content
1078
        self.assertEqual(status, 206)
1079

    
1080
        # assert Content-Type of the reply will be multipart/byteranges
1081
        self.assertTrue(headers['content-type'])
1082
        content_type_parts = headers['content-type'].split()
1083
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1084

    
1085
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1086
        cparts = data.split(boundary)[1:-1]
1087

    
1088
        # assert content parts are exactly 2
1089
        self.assertEqual(len(cparts), len(ranges))
1090

    
1091
        # for each content part assert headers
1092
        i = 0
1093
        for cpart in cparts:
1094
            content = cpart.split('\r\n')
1095
            headers = content[1:3]
1096
            content_range = headers[0].split(': ')
1097
            self.assertEqual(content_range[0], 'Content-Range')
1098

    
1099
            r = ranges[i].split('-')
1100
            if not r[0] and not r[1]:
1101
                pass
1102
            elif not r[0]:
1103
                start = len(self.objects[0]['data']) - int(r[1])
1104
                end = len(self.objects[0]['data'])
1105
            elif not r[1]:
1106
                start = int(r[0])
1107
                end = len(self.objects[0]['data'])
1108
            else:
1109
                start = int(r[0])
1110
                end = int(r[1]) + 1
1111
            fdata = self.objects[0]['data'][start:end]
1112
            sdata = '\r\n'.join(content[4:-1])
1113
            self.assertEqual(len(fdata), len(sdata))
1114
            self.assertEquals(fdata, sdata)
1115
            i+=1
1116

    
1117
    def test_multiple_range_not_satisfiable(self):
1118
        #perform get with multiple range
1119
        out_of_range = len(self.objects[0]['data']) + 1
1120
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1121
        bytes = 'bytes=%s' % ','.join(ranges)
1122

    
1123
        # assert partial content
1124
        self.assert_raises_fault(416, self.client.retrieve_object,
1125
                                 self.containers[1],
1126
                                 self.objects[0]['name'], range=bytes)
1127

    
1128
    def test_get_with_if_match(self):
1129
        #perform get with If-Match
1130
        etag = self.objects[0]['hash']
1131
        status, headers, data = self.client.request_object(self.containers[1],
1132
                                                           self.objects[0]['name'],
1133
                                                           if_match=etag)
1134
        #assert get success
1135
        self.assertEqual(status, 200)
1136

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

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

    
1144
    def test_get_with_if_match_star(self):
1145
        #perform get with If-Match *
1146
        headers = {'if-match':'*'}
1147
        status, headers, data = self.client.request_object(self.containers[1],
1148
                                                self.objects[0]['name'],
1149
                                                **headers)
1150
        #assert get success
1151
        self.assertEqual(status, 200)
1152

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

    
1157
        #assert response content
1158
        self.assertEqual(self.objects[0]['data'], data)
1159

    
1160
    def test_get_with_multiple_if_match(self):
1161
        #perform get with If-Match
1162
        etags = [i['hash'] for i in self.objects if i]
1163
        etags = ','.join('"%s"' % etag for etag in etags)
1164
        status, headers, data = self.client.request_object(self.containers[1],
1165
                                                           self.objects[0]['name'],
1166
                                                           if_match=etags)
1167
        #assert get success
1168
        self.assertEqual(status, 200)
1169

    
1170
        #assert content-type
1171
        self.assertEqual(headers['content-type'],
1172
                         self.objects[0]['meta']['content_type'])
1173

    
1174
        #assert content-type
1175
        self.assertEqual(headers['content-type'],
1176
                         self.objects[0]['meta']['content_type'])
1177

    
1178
        #assert response content
1179
        self.assertEqual(self.objects[0]['data'], data)
1180

    
1181
    def test_if_match_precondition_failed(self):
1182
        #assert precondition failed
1183
        self.assert_raises_fault(412, self.client.retrieve_object,
1184
                                 self.containers[1],
1185
                                 self.objects[0]['name'], if_match='123')
1186

    
1187
    def test_if_none_match(self):
1188
        #perform get with If-None-Match
1189
        status, headers, data = self.client.request_object(self.containers[1],
1190
                                                           self.objects[0]['name'],
1191
                                                           if_none_match='123')
1192

    
1193
        #assert get success
1194
        self.assertEqual(status, 200)
1195

    
1196
        #assert content-type
1197
        self.assertEqual(headers['content_type'],
1198
                         self.objects[0]['meta']['content_type'])
1199

    
1200
    def test_if_none_match(self):
1201
        #perform get with If-None-Match * and assert not modified
1202
        self.assert_raises_fault(304, self.client.retrieve_object,
1203
                                 self.containers[1],
1204
                                 self.objects[0]['name'],
1205
                                 if_none_match='*')
1206

    
1207
    def test_if_none_match_not_modified(self):
1208
        #perform get with If-None-Match and assert not modified
1209
        self.assert_raises_fault(304, self.client.retrieve_object,
1210
                                 self.containers[1],
1211
                                 self.objects[0]['name'],
1212
                                 if_none_match=self.objects[0]['hash'])
1213

    
1214
        meta = self.client.retrieve_object_metadata(self.containers[1],
1215
                                                    self.objects[0]['name'])
1216
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1217

    
1218
    def test_if_modified_since(self):
1219
        t = datetime.datetime.utcnow()
1220
        t2 = t - datetime.timedelta(minutes=10)
1221

    
1222
        #modify the object
1223
        self.upload_data(self.containers[1],
1224
                           self.objects[0]['name'],
1225
                           self.objects[0]['data'][:200])
1226

    
1227
        for f in DATE_FORMATS:
1228
            past = t2.strftime(f)
1229

    
1230
            headers = {'if-modified-since':'%s' %past}
1231
            try:
1232
                o = self.client.retrieve_object(self.containers[1],
1233
                                                self.objects[0]['name'],
1234
                                                if_modified_since=past)
1235
                self.assertEqual(o,
1236
                                 self.client.retrieve_object(self.containers[1],
1237
                                                             self.objects[0]['name']))
1238
            except Fault, f:
1239
                self.failIf(f.status == 304)
1240

    
1241
    def test_if_modified_since_invalid_date(self):
1242
        o = self.client.retrieve_object(self.containers[1],
1243
                                        self.objects[0]['name'],
1244
                                        if_modified_since='')
1245
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1246
                                                        self.objects[0]['name']))
1247

    
1248
    def test_if_not_modified_since(self):
1249
        now = datetime.datetime.utcnow()
1250
        since = now + datetime.timedelta(1)
1251

    
1252
        for f in DATE_FORMATS:
1253
            #assert not modified
1254
            self.assert_raises_fault(304, self.client.retrieve_object,
1255
                                     self.containers[1], self.objects[0]['name'],
1256
                                     if_modified_since=since.strftime(f))
1257

    
1258
    def test_if_unmodified_since(self):
1259
        now = datetime.datetime.utcnow()
1260
        since = now + datetime.timedelta(1)
1261

    
1262
        for f in DATE_FORMATS:
1263
            t = since.strftime(f)
1264
            status, headers, data = self.client.request_object(self.containers[1],
1265
                                                               self.objects[0]['name'],
1266
                                                               if_unmodified_since=t)
1267
            #assert success
1268
            self.assertEqual(status, 200)
1269
            self.assertEqual(self.objects[0]['data'], data)
1270

    
1271
            #assert content-type
1272
            self.assertEqual(headers['content-type'],
1273
                             self.objects[0]['meta']['content_type'])
1274

    
1275
    def test_if_unmodified_since_precondition_failed(self):
1276
        t = datetime.datetime.utcnow()
1277
        t2 = t - datetime.timedelta(minutes=10)
1278

    
1279
        #modify the object
1280
        self.upload_data(self.containers[1],
1281
                           self.objects[0]['name'],
1282
                           self.objects[0]['data'][:200])
1283

    
1284
        for f in DATE_FORMATS:
1285
            past = t2.strftime(f)
1286
            #assert precondition failed
1287
            self.assert_raises_fault(412, self.client.retrieve_object,
1288
                                     self.containers[1], self.objects[0]['name'],
1289
                                     if_unmodified_since=past)
1290

    
1291
    def test_hashes(self):
1292
        l = 8388609
1293
        fname = 'largefile'
1294
        o = self.upload_random_data(self.containers[1], fname, l)
1295
        if o:
1296
            body = self.client.retrieve_object(self.containers[1], fname,
1297
                                               format='json')
1298
            hashes = body['hashes']
1299
            block_size = body['block_size']
1300
            block_hash = body['block_hash']
1301
            block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1302
            self.assertTrue(len(hashes), block_num)
1303
            i = 0
1304
            for h in hashes:
1305
                start = i * block_size
1306
                end = (i + 1) * block_size
1307
                hash = compute_block_hash(o['data'][start:end], block_hash)
1308
                self.assertEqual(h, hash)
1309
                i += 1
1310

    
1311
class ObjectPut(BaseTestCase):
1312
    def setUp(self):
1313
        BaseTestCase.setUp(self)
1314
        self.container = 'c1'
1315
        self.client.create_container(self.container)
1316

    
1317
    def test_upload(self):
1318
        name = o_names[0]
1319
        meta = {'test':'test1'}
1320
        o = self.upload_random_data(self.container, name, **meta)
1321

    
1322
        headers = self.client.retrieve_object_metadata(self.container,
1323
                                                       name,
1324
                                                       restricted=True)
1325
        self.assertTrue('test' in headers.keys())
1326
        self.assertEqual(headers['test'], meta['test'])
1327

    
1328
        #assert uploaded content
1329
        status, h, data = self.client.request_object(self.container, name)
1330
        self.assertEqual(len(o['data']), int(h['content-length']))
1331
        self.assertEqual(o['data'], data)
1332

    
1333
        #assert content-type
1334
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1335

    
1336
    def _test_maximum_upload_size_exceeds(self):
1337
        name = o_names[0]
1338
        meta = {'test':'test1'}
1339
        #upload 5GB
1340
        length= 5 * (1024 * 1024 * 1024) + 1
1341
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1342
                                 name, length, **meta)
1343

    
1344
    def test_upload_with_name_containing_slash(self):
1345
        name = '/%s' % o_names[0]
1346
        meta = {'test':'test1'}
1347
        o = self.upload_random_data(self.container, name, **meta)
1348

    
1349
        self.assertEqual(o['data'],
1350
                         self.client.retrieve_object(self.container, name))
1351

    
1352
        self.assertTrue(name in self.client.list_objects(self.container))
1353

    
1354
    def test_create_directory_marker(self):
1355
        self.client.create_directory_marker(self.container, 'foo')
1356
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1357
        self.assertEqual(meta['content-length'], '0')
1358
        self.assertEqual(meta['content-type'], 'application/directory')
1359

    
1360
    def test_upload_unprocessable_entity(self):
1361
        meta={'etag':'123', 'test':'test1'}
1362

    
1363
        #assert unprocessable entity
1364
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1365
                                 o_names[0], **meta)
1366

    
1367
    def test_chunked_transfer(self):
1368
        data = get_random_data()
1369
        objname = 'object'
1370
        self.client.create_object_using_chunks(self.container, objname,
1371
                                               StringIO(data))
1372

    
1373
        uploaded_data = self.client.retrieve_object(self.container, objname)
1374
        self.assertEqual(data, uploaded_data)
1375

    
1376
    def test_manifestation(self):
1377
        prefix = 'myobject/'
1378
        data = ''
1379
        for i in range(5):
1380
            part = '%s%d' %(prefix, i)
1381
            o = self.upload_random_data(self.container, part)
1382
            data += o['data']
1383

    
1384
        manifest = '%s/%s' %(self.container, prefix)
1385
        self.client.create_manifestation(self.container, 'large-object', manifest)
1386

    
1387
        self.assert_object_exists(self.container, 'large-object')
1388
        self.assertEqual(data, self.client.retrieve_object(self.container,
1389
                                                           'large-object'))
1390

    
1391
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1392
        hashes = r['hashes']
1393
        block_size = int(r['block_size'])
1394
        block_hash = r['block_hash']
1395
        l = len(data)
1396
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1397
        self.assertEqual(block_num, len(hashes))
1398

    
1399
        #wrong manifestation
1400
        self.client.create_manifestation(self.container, 'large-object',
1401
                                         '%s/invalid' % self.container)
1402
        self.assertEqual('', self.client.retrieve_object(self.container,
1403
                                                         'large-object'))
1404

    
1405
    def test_create_zero_length_object(self):
1406
        c = self.container
1407
        o = 'object'
1408
        zero = self.client.create_zero_length_object(c, o)
1409
        zero_meta = self.client.retrieve_object_metadata(c, o)
1410
        zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1411
        zero_data = self.client.retrieve_object(c, o)
1412

    
1413
        self.assertEqual(int(zero_meta['content-length']), 0)
1414
        hasher = newhasher('sha256')
1415
        hasher.update("")
1416
        emptyhash = hasher.digest()
1417
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1418
        self.assertEqual(zero_data, '')
1419

    
1420
    def test_create_object_by_hashmap(self):
1421
        c = self.container
1422
        o = 'object'
1423
        self.upload_random_data(c, o)
1424
        hashmap = self.client.retrieve_object(c, o, format='json')
1425
        o2 = 'object-copy'
1426
        self.client.create_object_by_hashmap(c, o2, hashmap)
1427
        self.assertEqual(self.client.retrieve_object(c, o),
1428
                         self.client.retrieve_object(c, o))
1429

    
1430
class ObjectCopy(BaseTestCase):
1431
    def setUp(self):
1432
        BaseTestCase.setUp(self)
1433
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1434
        self.containers.sort()
1435

    
1436
        for c in self.containers:
1437
            self.client.create_container(c)
1438
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1439

    
1440
    def test_copy(self):
1441
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1442
                             self.containers[0], self.obj['name']):
1443
            #perform copy
1444
            meta = {'test':'testcopy'}
1445
            status = self.client.copy_object(self.containers[0],
1446
                                              self.obj['name'],
1447
                                              self.containers[0],
1448
                                              'testcopy',
1449
                                              meta)[0]
1450

    
1451
            #assert copy success
1452
            self.assertEqual(status, 201)
1453

    
1454
            #assert access the new object
1455
            headers = self.client.retrieve_object_metadata(self.containers[0],
1456
                                                           'testcopy')
1457
            self.assertTrue('x-object-meta-test' in headers.keys())
1458
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1459

    
1460
            #assert etag is the same
1461
            self.assertEqual(headers['etag'], self.obj['hash'])
1462

    
1463
            #assert src object still exists
1464
            self.assert_object_exists(self.containers[0], self.obj['name'])
1465

    
1466
    def test_copy_from_different_container(self):
1467
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1468
                             self.containers[0], self.obj['name']):
1469
            meta = {'test':'testcopy'}
1470
            status = self.client.copy_object(self.containers[0],
1471
                                             self.obj['name'],
1472
                                             self.containers[1],
1473
                                             'testcopy',
1474
                                             meta)[0]
1475
            self.assertEqual(status, 201)
1476

    
1477
            # assert updated metadata
1478
            meta = self.client.retrieve_object_metadata(self.containers[1],
1479
                                                           'testcopy',
1480
                                                           restricted=True)
1481
            self.assertTrue('test' in meta.keys())
1482
            self.assertTrue(meta['test'], 'testcopy')
1483

    
1484
            #assert src object still exists
1485
            self.assert_object_exists(self.containers[0], self.obj['name'])
1486

    
1487
    def test_copy_invalid(self):
1488
        #copy from invalid object
1489
        meta = {'test':'testcopy'}
1490
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1491
                                 'test.py', self.containers[1], 'testcopy', meta)
1492

    
1493
        #copy from invalid container
1494
        meta = {'test':'testcopy'}
1495
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1496
                                 self.obj['name'], self.containers[1],
1497
                                 'testcopy', meta)
1498

    
1499
    def test_copy_dir(self):
1500
        self.client.create_folder(self.containers[0], 'dir')
1501
        self.client.create_folder(self.containers[0], 'dir/subdir')
1502
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1503
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1504
        self.client.create_folder(self.containers[0], 'dirs')
1505

    
1506
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1507
        self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1508
        for object in objects[:-1]:
1509
            self.assert_object_exists(self.containers[0], object)
1510
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1511
            meta0 = self.client.retrieve_object_metadata(self.containers[0], object)
1512
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1513
            t = ('content-length', 'x-object-hash', 'content-type')
1514
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1515
        self.assert_object_not_exists(self.containers[1], objects[-1])
1516

    
1517
class ObjectMove(BaseTestCase):
1518
    def setUp(self):
1519
        BaseTestCase.setUp(self)
1520
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1521
        self.containers.sort()
1522

    
1523
        for c in self.containers:
1524
            self.client.create_container(c)
1525
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1526

    
1527
    def test_move(self):
1528
        meta = self.client.retrieve_object_metadata(self.containers[0],
1529
                                                    self.obj['name'])
1530
        self.assertTrue('x-object-uuid' in meta)
1531
        uuid = meta['x-object-uuid']
1532

    
1533
        #perform move
1534
        meta = {'test':'testcopy'}
1535
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1536
        status = self.client.move_object(self.containers[0], self.obj['name'],
1537
                                         self.containers[0], 'testcopy',
1538
                                         meta)[0]
1539

    
1540
        #assert successful move
1541
        self.assertEqual(status, 201)
1542

    
1543
        #assert updated metadata
1544
        meta = self.client.retrieve_object_metadata(self.containers[0],
1545
                                                    'testcopy')
1546
        self.assertTrue('x-object-meta-test' in meta.keys())
1547
        self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1548

    
1549
        #assert same uuid
1550
        self.assertTrue(meta['x-object-uuid'], uuid)
1551

    
1552
        #assert src object no more exists
1553
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1554

    
1555

    
1556
    def test_move_dir(self):
1557
        meta = {}
1558
        self.client.create_folder(self.containers[0], 'dir')
1559
        meta['dir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir')
1560
        self.client.create_folder(self.containers[0], 'dir/subdir')
1561
        meta['dir/subdir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir')
1562
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1563
        meta['dir/object1.jpg'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/object1.jpg')
1564
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1565
        meta['dir/subdir/object2.pdf'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir/object2.pdf')
1566
        self.client.create_folder(self.containers[0], 'dirs')
1567
        meta['dirs'] = self.client.retrieve_object_metadata(self.containers[0], 'dirs')
1568

    
1569
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1570
        self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1571
        for object in objects[:-1]:
1572
            self.assert_object_not_exists(self.containers[0], object)
1573
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1574
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1575
            t = ('content-length', 'x-object-hash', 'content-type')
1576
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1577
        self.assert_object_exists(self.containers[0], objects[-1])
1578
        self.assert_object_not_exists(self.containers[1], objects[-1])
1579

    
1580
class ObjectPost(BaseTestCase):
1581
    def setUp(self):
1582
        BaseTestCase.setUp(self)
1583
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1584
        self.containers.sort()
1585

    
1586
        for c in self.containers:
1587
            self.client.create_container(c)
1588
        self.obj = []
1589
        for i in range(2):
1590
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1591

    
1592
    def test_update_meta(self):
1593
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1594
                                 self.containers[0],
1595
                                 self.obj[0]['name']):
1596
            #perform update metadata
1597
            more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1598
            status = self.client.update_object_metadata(self.containers[0],
1599
                                                        self.obj[0]['name'],
1600
                                                        **more)[0]
1601
            #assert request accepted
1602
            self.assertEqual(status, 202)
1603

    
1604
            #assert old metadata are still there
1605
            headers = self.client.retrieve_object_metadata(self.containers[0],
1606
                                                           self.obj[0]['name'],
1607
                                                           restricted=True)
1608
            #assert new metadata have been updated
1609
            for k,v in more.items():
1610
                self.assertTrue(k in headers.keys())
1611
                self.assertTrue(headers[k], v)
1612

    
1613
            #out of limits
1614
            more = {'f' * 114: 'b' * 257}
1615
            self.assert_raises_fault(400, self.client.update_object_metadata,
1616
                                                        self.containers[0],
1617
                                                        self.obj[0]['name'],
1618
                                                        **more)
1619

    
1620
            #perform update metadata
1621
            more = {'α': 'β' * 256}
1622
            status = self.client.update_object_metadata(self.containers[0],
1623
                                                        self.obj[0]['name'],
1624
                                                        **more)[0]
1625
            #assert request accepted
1626
            self.assertEqual(status, 202)
1627

    
1628
            #assert old metadata are still there
1629
            headers = self.client.retrieve_object_metadata(self.containers[0],
1630
                                                           self.obj[0]['name'],
1631
                                                           restricted=True)
1632
            #assert new metadata have been updated
1633
            for k,v in more.items():
1634
                self.assertTrue(k in headers.keys())
1635
                self.assertTrue(headers[k], v)
1636

    
1637
            #out of limits
1638
            more = {'α': 'β' * 257}
1639
            self.assert_raises_fault(400, self.client.update_object_metadata,
1640
                                                        self.containers[0],
1641
                                                        self.obj[0]['name'],
1642
                                                        **more)
1643

    
1644
    def test_update_object(self,
1645
                           first_byte_pos=0,
1646
                           last_byte_pos=499,
1647
                           instance_length = True,
1648
                           content_length = 500):
1649
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1650
                                 self.containers[0],
1651
                                 self.obj[0]['name']):
1652
            l = len(self.obj[0]['data'])
1653
            range = 'bytes %d-%d/%s' %(first_byte_pos,
1654
                                           last_byte_pos,
1655
                                            l if instance_length else '*')
1656
            partial = last_byte_pos - first_byte_pos + 1
1657
            length = first_byte_pos + partial
1658
            data = get_random_data(partial)
1659
            args = {'content_type':'application/octet-stream',
1660
                    'content_range':'%s' %range}
1661
            if content_length:
1662
                args['content_length'] = content_length
1663

    
1664
            r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1665
                                      StringIO(data), **args)
1666
            status = r[0]
1667
            etag = r[1]['etag']
1668
            if partial < 0 or (instance_length and l <= last_byte_pos):
1669
                self.assertEqual(status, 202)
1670
            else:
1671
                self.assertEqual(status, 204)
1672
                #check modified object
1673
                content = self.client.retrieve_object(self.containers[0],
1674
                                                  self.obj[0]['name'])
1675
                self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1676
                self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1677
                self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1678
                self.assertEqual(etag, compute_md5_hash(content))
1679

    
1680
    def test_update_object_lt_blocksize(self):
1681
        self.test_update_object(10, 20, content_length=None)
1682

    
1683
    def test_update_object_gt_blocksize(self):
1684
        o = self.upload_random_data(self.containers[0], o_names[1],
1685
                                length=4*1024*1024+5)
1686
        c = self.containers[0]
1687
        o_name = o['name']
1688
        o_data = o['data']
1689
        first_byte_pos = 4*1024*1024+1
1690
        last_byte_pos = 4*1024*1024+4
1691
        l = last_byte_pos - first_byte_pos + 1
1692
        data = get_random_data(l)
1693
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1694
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1695
        content = self.client.retrieve_object(c, o_name)
1696
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1697
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1698
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1699

    
1700
    def test_update_object_divided_by_blocksize(self):
1701
        o = self.upload_random_data(self.containers[0], o_names[1],
1702
                                length=4*1024*1024+5)
1703
        c = self.containers[0]
1704
        o_name = o['name']
1705
        o_data = o['data']
1706
        first_byte_pos = 4*1024*1024
1707
        last_byte_pos = 5*1024*1024
1708
        l = last_byte_pos - first_byte_pos + 1
1709
        data = get_random_data(l)
1710
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1711
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1712
        content = self.client.retrieve_object(c, o_name)
1713
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1714
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1715
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1716

    
1717
    def test_update_object_no_content_length(self):
1718
        self.test_update_object(content_length = None)
1719

    
1720
    def test_update_object_invalid_content_length(self):
1721
        with AssertContentInvariant(self.client.retrieve_object,
1722
                                    self.containers[0], self.obj[0]['name']):
1723
            self.assert_raises_fault(400, self.test_update_object,
1724
                                     content_length = 1000)
1725

    
1726
    def test_update_object_invalid_range(self):
1727
        with AssertContentInvariant(self.client.retrieve_object,
1728
                                    self.containers[0], self.obj[0]['name']):
1729
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1730

    
1731
    def test_update_object_invalid_range_and_length(self):
1732
        with AssertContentInvariant(self.client.retrieve_object,
1733
                                    self.containers[0], self.obj[0]['name']):
1734
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1735
                                     -1)
1736

    
1737
    def test_update_object_invalid_range_with_no_content_length(self):
1738
        with AssertContentInvariant(self.client.retrieve_object,
1739
                                    self.containers[0], self.obj[0]['name']):
1740
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1741
                                     content_length = None)
1742

    
1743
    def test_update_object_out_of_limits(self):
1744
        with AssertContentInvariant(self.client.retrieve_object,
1745
                                    self.containers[0], self.obj[0]['name']):
1746
            l = len(self.obj[0]['data'])
1747
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1748

    
1749
    def test_append(self):
1750
        data = get_random_data(500)
1751
        headers = {}
1752
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1753
                                  StringIO(data), content_length=500,
1754
                                  content_type='application/octet-stream')
1755

    
1756
        content = self.client.retrieve_object(self.containers[0],
1757
                                              self.obj[0]['name'])
1758
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1759
        self.assertEqual(content[:-500], self.obj[0]['data'])
1760

    
1761
    def test_update_with_chunked_transfer(self):
1762
        data = get_random_data(500)
1763
        dl = len(data)
1764
        fl = len(self.obj[0]['data'])
1765

    
1766
        self.client.update_object_using_chunks(self.containers[0],
1767
                                               self.obj[0]['name'],
1768
                                               StringIO(data),
1769
                                               offset=0,
1770
                                               content_type='application/octet-stream')
1771

    
1772
        #check modified object
1773
        content = self.client.retrieve_object(self.containers[0],
1774
                                              self.obj[0]['name'])
1775
        self.assertEqual(content[0:dl], data)
1776
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1777

    
1778
    def test_update_from_other_object(self):
1779
        c = self.containers[0]
1780
        src = o_names[0]
1781
        dest = 'object'
1782

    
1783
        source_data = self.client.retrieve_object(c, src)
1784
        source_meta = self.client.retrieve_object_metadata(c, src)
1785
        source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1786

    
1787
        #update zero length object
1788
        self.client.create_zero_length_object(c, dest)
1789
        source_object = '/%s/%s' % (c, src)
1790
        self.client.update_from_other_source(c, dest, source_object)
1791
        dest_data = self.client.retrieve_object(c, src)
1792
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1793
        dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1794
        self.assertEqual(source_data, dest_data)
1795
        self.assertEqual(source_hash, dest_hash)
1796

    
1797
        #test append
1798
        self.client.update_from_other_source(c, dest, source_object)
1799
        content = self.client.retrieve_object(c, dest)
1800
        self.assertEqual(source_data * 2, content)
1801

    
1802
    def test_update_range_from_other_object(self):
1803
        c = self.containers[0]
1804
        dest = 'object'
1805

    
1806
        #test update range
1807
        src = self.obj[1]['name']
1808
        src_data = self.client.retrieve_object(c, src)
1809

    
1810
        #update zero length object
1811
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1812
        source_object = '/%s/%s' % (c, src)
1813
        first_byte_pos = 4*1024*1024+1
1814
        last_byte_pos = 4*1024*1024+4
1815
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1816
        self.client.update_from_other_source(c, dest, source_object,
1817
                                             content_range=range)
1818
        content = self.client.retrieve_object(c, dest)
1819
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1820
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1821
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1822

    
1823
    def test_update_hashes_from_other_object(self):
1824
        c = self.containers[0]
1825
        dest = 'object'
1826

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

    
1830
        #update zero length object
1831
        prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1832
        source_object = '/%s/%s' % (c, o_names[0])
1833
        first_byte_pos = 4*1024*1024
1834
        last_byte_pos = 5*1024*1024
1835
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1836
        self.client.update_from_other_source(c, dest, source_object,
1837
                                             content_range=range)
1838
        content = self.client.retrieve_object(c, dest)
1839
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1840
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1841
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1842

    
1843

    
1844
    def test_update_zero_length_object(self):
1845
        c = self.containers[0]
1846
        o = 'object'
1847
        other = 'other'
1848
        zero = self.client.create_zero_length_object(c, o)
1849

    
1850
        data = get_random_data()
1851
        self.client.update_object(c, o, StringIO(data))
1852
        self.client.create_object(c, other, StringIO(data))
1853

    
1854
        self.assertEqual(self.client.retrieve_object(c, o),
1855
                         self.client.retrieve_object(c, other))
1856

    
1857
        self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1858
                         self.client.retrieve_object_hashmap(c, other)["hashes"])
1859

    
1860
class ObjectDelete(BaseTestCase):
1861
    def setUp(self):
1862
        BaseTestCase.setUp(self)
1863
        self.containers = ['c1', 'c2']
1864
        self.containers.extend(self.initial_containers)
1865

    
1866
        for c in self.containers:
1867
            self.client.create_container(c)
1868
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1869

    
1870
    def test_delete(self):
1871
        #perform delete object
1872
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1873

    
1874
    def test_delete_invalid(self):
1875
        #assert item not found
1876
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1877
                                 self.obj['name'])
1878

    
1879
    def test_delete_dir(self):
1880
        self.client.create_folder(self.containers[0], 'dir')
1881
        self.client.create_folder(self.containers[0], 'dir/subdir')
1882
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1883
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1884
        self.client.create_folder(self.containers[0], 'dirs')
1885

    
1886
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1887
        self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1888
        for object in objects[:-1]:
1889
            self.assert_object_not_exists(self.containers[0], object)
1890
        self.assert_object_exists(self.containers[0], objects[-1])
1891

    
1892
class ListSharing(BaseTestCase):
1893
    def setUp(self):
1894
        BaseTestCase.setUp(self)
1895
        for i in range(2):
1896
            self.client.create_container('c%s' %i)
1897
        self.client.create_container('c')
1898
        for i in range(2):
1899
            self.upload_random_data('c1', 'o%s' %i)
1900
        if not OTHER_ACCOUNTS:
1901
            raise Warning('No other accounts avalaible for running this test.')
1902
        for token, account in OTHER_ACCOUNTS.items():
1903
            self.o1_sharing = token, account
1904
            self.client.share_object('c1', 'o1', (account,), read=True)
1905
            break
1906

    
1907
    def test_list_other_shared(self):
1908
        self.other = Pithos_Client(get_url(),
1909
                              self.o1_sharing[0],
1910
                              self.o1_sharing[1])
1911
        self.assertTrue(get_user() in self.other.list_shared_with_me())
1912

    
1913
    def test_list_my_shared(self):
1914
        my_shared_containers = self.client.list_containers(shared=True)
1915
        self.assertTrue('c1' in my_shared_containers)
1916
        self.assertTrue('c2' not in my_shared_containers)
1917

    
1918
        my_shared_objects = self.client.list_objects('c1', shared=True)
1919
        self.assertTrue('o1' in my_shared_objects)
1920
        self.assertTrue('o2' not in my_shared_objects)
1921

    
1922
class List(BaseTestCase):
1923
    def setUp(self):
1924
        BaseTestCase.setUp(self)
1925
        for i in range(1, 5):
1926
            c = 'c%s' % i
1927
            self.client.create_container(c)
1928
            for j in range(1, 3):
1929
                o = 'o%s' % j
1930
                self.upload_random_data(c, o)
1931
            if i < 3:
1932
                self.client.share_object(c, 'o1', ['papagian'], read=True)
1933
            if i%2 != 0:
1934
                self.client.publish_object(c, 'o2')
1935

    
1936
    def test_shared_public(self):
1937
        diff = lambda l: set(l) - set(self.initial_containers)
1938

    
1939
        func, kwargs = self.client.list_containers, {'shared':True}
1940
        l = func(**kwargs)
1941
        self.assertEqual(set(['c1', 'c2']), diff(l))
1942
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1943

    
1944
        func, kwargs = self.client.list_containers, {'public':True}
1945
        l = func(**kwargs)
1946
        self.assertEqual(set(['c1', 'c3']), diff(l))
1947
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1948

    
1949
        func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1950
        l = func(**kwargs)
1951
        self.assertEqual(set(['c1', 'c2', 'c3']), diff(l))
1952
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1953

    
1954
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1955
        l = func(*args, **kwargs)
1956
        self.assertEqual(l, ['o1'])
1957
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1958

    
1959
        func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1960
        l = func(*args, **kwargs)
1961
        self.assertEqual(l, ['o2'])
1962
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1963

    
1964
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1965
        l = func(*args, **kwargs)
1966
        self.assertEqual(l, ['o1', 'o2'])
1967
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1968

    
1969
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1970
        l = func(*args, **kwargs)
1971
        self.assertEqual(l, ['o1'])
1972
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1973

    
1974
        func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1975
        l = func(*args, **kwargs)
1976
        self.assertEqual(l, '')
1977
        self.assertEqual([], func(*args, format='json', **kwargs))
1978

    
1979
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True, 'public':True}
1980
        l = func(*args, **kwargs)
1981
        self.assertEqual(l, ['o1'])
1982
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1983

    
1984
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1985
        l = func(*args, **kwargs)
1986
        self.assertEqual(l, '')
1987
        self.assertEqual([], func(*args, format='json', **kwargs))
1988

    
1989
        func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1990
        l = func(*args, **kwargs)
1991
        self.assertEqual(l, ['o2'])
1992
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1993

    
1994
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True, 'public':True}
1995
        l = func(*args, **kwargs)
1996
        self.assertEqual(l, ['o2'])
1997
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1998

    
1999
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
2000
        l = func(*args, **kwargs)
2001
        self.assertEqual(l, '')
2002
        self.assertEqual([], func(*args, format='json', **kwargs))
2003

    
2004
        func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
2005
        l = func(*args, **kwargs)
2006
        self.assertEqual(l, '')
2007
        self.assertEqual([], func(*args, format='json', **kwargs))
2008

    
2009
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
2010
        l = func(*args, **kwargs)
2011
        self.assertEqual(l, '')
2012
        self.assertEqual([], func(*args, format='json', **kwargs))
2013

    
2014
class TestUTF8(BaseTestCase):
2015
    def test_create_container(self):
2016
        self.client.create_container('φάκελος')
2017
        self.assert_container_exists('φάκελος')
2018

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

    
2021
    def test_create_object(self):
2022
        self.client.create_container('φάκελος')
2023
        self.upload_random_data('φάκελος', 'αντικείμενο')
2024

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

    
2028
    def test_copy_object(self):
2029
        src_container = 'φάκελος'
2030
        src_object = 'αντικείμενο'
2031
        dest_container = 'αντίγραφα'
2032
        dest_object = 'ασφαλές-αντίγραφο'
2033

    
2034
        self.client.create_container(src_container)
2035
        self.upload_random_data(src_container, src_object)
2036

    
2037
        self.client.create_container(dest_container)
2038
        self.client.copy_object(src_container, src_object, dest_container,
2039
                                dest_object)
2040

    
2041
        self.assert_object_exists(src_container, src_object)
2042
        self.assert_object_exists(dest_container, dest_object)
2043
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2044

    
2045
    def test_move_object(self):
2046
        src_container = 'φάκελος'
2047
        src_object = 'αντικείμενο'
2048
        dest_container = 'αντίγραφα'
2049
        dest_object = 'ασφαλές-αντίγραφο'
2050

    
2051
        self.client.create_container(src_container)
2052
        self.upload_random_data(src_container, src_object)
2053

    
2054
        self.client.create_container(dest_container)
2055
        self.client.move_object(src_container, src_object, dest_container,
2056
                                dest_object)
2057

    
2058
        self.assert_object_not_exists(src_container, src_object)
2059
        self.assert_object_exists(dest_container, dest_object)
2060
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2061

    
2062
    def test_delete_object(self):
2063
        self.client.create_container('φάκελος')
2064
        self.upload_random_data('φάκελος', 'αντικείμενο')
2065
        self.assert_object_exists('φάκελος', 'αντικείμενο')
2066

    
2067
        self.client.delete_object('φάκελος', 'αντικείμενο')
2068
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2069
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2070

    
2071
    def test_delete_container(self):
2072
        self.client.create_container('φάκελος')
2073
        self.assert_container_exists('φάκελος')
2074

    
2075
        self.client.delete_container('φάκελος')
2076
        self.assert_container_not_exists('φάκελος')
2077
        self.assertTrue('φάκελος' not in self.client.list_containers())
2078

    
2079
    def test_account_meta(self):
2080
        meta = {'ποιότητα':'ΑΑΑ'}
2081
        self.client.update_account_metadata(**meta)
2082
        meta = self.client.retrieve_account_metadata(restricted=True)
2083
        self.assertTrue('ποιότητα' in meta.keys())
2084
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2085

    
2086
    def test_container_meta(self):
2087
        meta = {'ποιότητα':'ΑΑΑ'}
2088
        self.client.create_container('φάκελος', meta=meta)
2089

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

    
2094
    def test_object_meta(self):
2095
        self.client.create_container('φάκελος')
2096
        meta = {'ποιότητα':'ΑΑΑ'}
2097
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2098

    
2099
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2100
                                                    restricted=True)
2101
        self.assertTrue('ποιότητα' in meta.keys())
2102
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2103

    
2104
    def test_list_meta_filtering(self):
2105
        self.client.create_container('φάκελος')
2106
        meta = {'ποιότητα':'ΑΑΑ'}
2107
        self.upload_random_data('φάκελος', 'ο1', **meta)
2108
        self.upload_random_data('φάκελος', 'ο2')
2109
        self.upload_random_data('φάκελος', 'ο3')
2110

    
2111
        meta = {'ποσότητα':'μεγάλη'}
2112
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2113
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2114
        self.assertEquals(objects, ['ο1', 'ο2'])
2115

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

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

    
2122
        meta = {'ποιότητα':'ΑΒ'}
2123
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2124
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2125
        self.assertEquals(objects, ['ο1'])
2126
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2127
        self.assertEquals(objects, ['ο2'])
2128

    
2129
        meta = {'έτος':'2011'}
2130
        self.client.update_object_metadata('φάκελος', 'ο3', **meta)
2131
        meta = {'έτος':'2012'}
2132
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2133
        objects = self.client.list_objects('φάκελος', meta='έτος<2012')
2134
        self.assertEquals(objects, ['ο3'])
2135
        objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
2136
        self.assertEquals(objects, ['ο2', 'ο3'])
2137
        objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
2138
        self.assertEquals(objects, '')
2139

    
2140
    def test_groups(self):
2141
        #create a group
2142
        groups = {'γκρουπ':'chazapis,διογένης'}
2143
        self.client.set_account_groups(**groups)
2144
        groups.update(self.initial_groups)
2145
        self.assertEqual(groups['γκρουπ'],
2146
                         self.client.retrieve_account_groups()['γκρουπ'])
2147

    
2148
        #check read access
2149
        self.client.create_container('φάκελος')
2150
        o = self.upload_random_data('φάκελος', 'ο1')
2151
        self.client.share_object('φάκελος', 'ο1', ['%s:γκρουπ' % get_user()])
2152
        if 'διογένης' not in OTHER_ACCOUNTS.values():
2153
            raise Warning('No such an account exists for running this test.')
2154
        chef = Pithos_Client(get_url(),
2155
                            '0009',
2156
                            'διογένης')
2157
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2158
                                     'φάκελος', 'ο1', account=get_user())
2159

    
2160
        #check write access
2161
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
2162
        new_data = get_random_data()
2163
        self.assert_not_raises_fault(403, chef.update_object,
2164
                                     'φάκελος', 'ο1', StringIO(new_data),
2165
                                     account=get_user())
2166

    
2167
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
2168
        self.assertEqual(server_data[:len(o['data'])], o['data'])
2169
        self.assertEqual(server_data[len(o['data']):], new_data)
2170

    
2171
    def test_manifestation(self):
2172
        self.client.create_container('κουβάς')
2173
        prefix = 'μέρη/'
2174
        data = ''
2175
        for i in range(5):
2176
            part = '%s%d' %(prefix, i)
2177
            o = self.upload_random_data('κουβάς', part)
2178
            data += o['data']
2179

    
2180
        self.client.create_container('φάκελος')
2181
        manifest = '%s/%s' %('κουβάς', prefix)
2182
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2183

    
2184
        self.assert_object_exists('φάκελος', 'άπαντα')
2185
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
2186
                                                           'άπαντα'))
2187

    
2188
        #wrong manifestation
2189
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2190
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2191

    
2192
    def test_update_from_another_object(self):
2193
        self.client.create_container('κουβάς')
2194
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2195
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2196
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2197
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2198

    
2199
        self.assertEqual(
2200
            self.client.retrieve_object('κουβάς', 'νέο'),
2201
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2202

    
2203
class TestPermissions(BaseTestCase):
2204
    def setUp(self):
2205
        BaseTestCase.setUp(self)
2206

    
2207
        if not OTHER_ACCOUNTS:
2208
            raise Warning('No other accounts avalaible for running this test.')
2209

    
2210
        #create a group
2211
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
2212
        groups = {'pithosdev':','.join(self.authorized)}
2213
        self.client.set_account_groups(**groups)
2214

    
2215
        self.container = 'c'
2216
        self.object = 'o'
2217
        self.client.create_container(self.container)
2218
        self.upload_random_data(self.container, self.object)
2219
        self.upload_random_data(self.container, self.object+'/')
2220
        self.upload_random_data(self.container, self.object+'/a')
2221
        self.upload_random_data(self.container, self.object+'a')
2222
        self.upload_random_data(self.container, self.object+'a/')
2223
        self.dir_content_types = ('application/directory', 'application/folder')
2224

    
2225
    def assert_read(self, authorized=None, any=False, depth=0):
2226
        authorized = authorized or []
2227
        for token, account in OTHER_ACCOUNTS.items():
2228
            cl = Pithos_Client(get_url(), token, account)
2229
            if account in authorized or any:
2230
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2231
                                             self.container, self.object,
2232
                                             account=get_user())
2233
            else:
2234
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
2235
                                         self.container, self.object,
2236
                                         account=get_user())
2237

    
2238
        #check inheritance
2239
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2240
        type = meta['content-type']
2241
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2242
        #exclude the self.object
2243
        del derivatives[derivatives.index(self.object)]
2244
        for o in derivatives:
2245
            for token, account in OTHER_ACCOUNTS.items():
2246
                cl = Pithos_Client(get_url(), token, account)
2247
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2248
                if (account in authorized or any) and \
2249
                (type in self.dir_content_types) and \
2250
                o.startswith(prefix):
2251
                    self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2252
                                             self.container, o, account=get_user())
2253
                else:
2254
                    self.assert_raises_fault(403, cl.retrieve_object_metadata,
2255
                                         self.container, o, account=get_user())
2256

    
2257
    def assert_write(self, authorized=None, any=False):
2258
        authorized = authorized or []
2259
        o_data = self.client.retrieve_object(self.container, self.object)
2260
        for token, account in OTHER_ACCOUNTS.items():
2261
            cl = Pithos_Client(get_url(), token, account)
2262
            new_data = get_random_data()
2263
            if account in authorized or any:
2264
                # test write access
2265
                self.assert_not_raises_fault(403, cl.update_object,
2266
                                             self.container, self.object, StringIO(new_data),
2267
                                             account=get_user())
2268
                try:
2269
                    # test read access
2270
                    server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2271
                    self.assertEqual(o_data, server_data[:len(o_data)])
2272
                    self.assertEqual(new_data, server_data[len(o_data):])
2273
                    o_data = server_data
2274
                except Fault, f:
2275
                    self.failIf(f.status == 403)
2276
            else:
2277
                self.assert_raises_fault(403, cl.update_object,
2278
                                             self.container, self.object, StringIO(new_data),
2279
                                             account=get_user())
2280
        #check inheritance
2281
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2282
        type = meta['content-type']
2283
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2284
        #exclude the object
2285
        del derivatives[derivatives.index(self.object)]
2286
        for o in derivatives:
2287
            for token, account in OTHER_ACCOUNTS.items():
2288
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2289
                cl = Pithos_Client(get_url(), token, account)
2290
                new_data = get_random_data()
2291
                if (account in authorized or any) and \
2292
                (type in self.dir_content_types) and \
2293
                o.startswith(prefix):
2294
                    # test write access
2295
                    self.assert_not_raises_fault(403, cl.update_object,
2296
                                                 self.container, o,
2297
                                                 StringIO(new_data),
2298
                                                 account=get_user())
2299
                    try:
2300
                        server_data = cl.retrieve_object(self.container, o, account=get_user())
2301
                        self.assertEqual(new_data, server_data[-len(new_data):])
2302
                    except Fault, f:
2303
                        self.failIf(f.status == 403)
2304
                else:
2305
                    self.assert_raises_fault(403, cl.update_object,
2306
                                                 self.container, o,
2307
                                                 StringIO(new_data),
2308
                                                 account=get_user())
2309

    
2310
    def test_group_read(self):
2311
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2312
        self.assert_read(authorized=self.authorized)
2313

    
2314
    def test_read_many(self):
2315
        self.client.share_object(self.container, self.object, self.authorized)
2316
        self.assert_read(authorized=self.authorized)
2317

    
2318
    def test_read_by_everyone(self):
2319
        self.client.share_object(self.container, self.object, ['*'])
2320
        self.assert_read(any=True)
2321

    
2322
    def test_read_directory(self):
2323
        for type in self.dir_content_types:
2324
            #change content type
2325
            self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2326
            self.client.share_object(self.container, self.object, ['*'])
2327
            self.assert_read(any=True)
2328
            self.client.share_object(self.container, self.object, self.authorized)
2329
            self.assert_read(authorized=self.authorized)
2330
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2331
            self.assert_read(authorized=self.authorized)
2332

    
2333
    def test_group_write(self):
2334
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2335
        self.assert_write(authorized=self.authorized)
2336

    
2337
    def test_write_many(self):
2338
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2339
        self.assert_write(authorized=self.authorized)
2340

    
2341
    def test_write_by_everyone(self):
2342
        self.client.share_object(self.container, self.object, ['*'], read=False)
2343
        self.assert_write(any=True)
2344

    
2345
    def test_write_directory(self):
2346
        dir_content_types = ('application/directory', 'application/foler')
2347
        for type in dir_content_types:
2348
            #change content type
2349
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2350
            self.client.share_object(self.container, self.object, ['*'], read=False)
2351
            self.assert_write(any=True)
2352
            self.client.share_object(self.container, self.object, self.authorized, read=False)
2353
            self.assert_write(authorized=self.authorized)
2354
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2355
            self.assert_write(authorized=self.authorized)
2356

    
2357
    def test_shared_listing(self):
2358
        self.client.share_object(self.container, self.object, self.authorized)
2359

    
2360
        my_shared_containers = self.client.list_containers(shared=True)
2361
        self.assertTrue('c' in my_shared_containers)
2362
        my_shared_objects = self.client.list_objects('c', shared=True)
2363
        self.assertEqual(['o'], my_shared_objects)
2364

    
2365
        dir_content_types = ('application/directory', 'application/foler')
2366
        for type in dir_content_types:
2367
            #change content type
2368
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2369
            my_shared_objects = self.client.list_objects('c', shared=True)
2370
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2371

    
2372
        for token, account in OTHER_ACCOUNTS.items():
2373
            if account in self.authorized:
2374
                self.other = Pithos_Client(get_url(), token, account)
2375
                self.assertTrue(get_user() in self.other.list_shared_with_me())
2376

    
2377
class TestPublish(BaseTestCase):
2378
    def test_publish(self):
2379
        self.client.create_container('c')
2380
        o_data = self.upload_random_data('c', 'o')['data']
2381
        self.client.publish_object('c', 'o')
2382
        meta = self.client.retrieve_object_metadata('c', 'o')
2383
        self.assertTrue('x-object-public' in meta)
2384
        url = meta['x-object-public']
2385

    
2386
        p = urlparse(get_url())
2387
        if p.scheme == 'http':
2388
            conn = HTTPConnection(p.netloc)
2389
        elif p.scheme == 'https':
2390
            conn = HTTPSConnection(p.netloc)
2391
        else:
2392
            raise Exception('Unknown URL scheme')
2393

    
2394
        conn.request('GET', url)
2395
        resp = conn.getresponse()
2396
        length = resp.getheader('content-length', None)
2397
        data = resp.read(length)
2398
        self.assertEqual(o_data, data)
2399

    
2400
        token = OTHER_ACCOUNTS.keys()[0]
2401
        account = OTHER_ACCOUNTS[token]
2402
        cl = Pithos_Client(get_url(), token, account)
2403

    
2404
        self.client.share_object('c', 'o', (account,))
2405
        meta = cl.retrieve_object_metadata('c', 'o', account=get_user())
2406
        self.assertTrue('x-object-public' not in meta)
2407

    
2408
class TestPolicies(BaseTestCase):
2409
    def test_none_versioning(self):
2410
        self.client.create_container('c', policies={'versioning':'none'})
2411
        o = self.upload_random_data('c', 'o')
2412
        meta = self.client.retrieve_object_metadata('c', 'o')
2413
        v = meta['x-object-version']
2414
        more_data = get_random_data()
2415
        self.client.update_object('c', 'o', StringIO(more_data))
2416
        vlist = self.client.retrieve_object_versionlist('c', 'o')
2417
        self.assert_raises_fault(404, self.client.retrieve_object_version,
2418
                                 'c', 'o', v)
2419
        data = self.client.retrieve_object('c', 'o')
2420
        end = len(o['data'])
2421
        self.assertEqual(data[:end], o['data'])
2422
        self.assertEqual(data[end:], more_data)
2423

    
2424
    def test_quota(self):
2425
        self.client.create_container('c', policies={'quota':'1'})
2426
        meta = self.client.retrieve_container_metadata('c')
2427
        self.assertEqual(meta['x-container-policy-quota'], '1')
2428
        self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2429
                                 length=1024*1024+1)
2430

    
2431
    def test_quota_none(self):
2432
        self.client.create_container('c', policies={'quota':'0'})
2433
        meta = self.client.retrieve_container_metadata('c')
2434
        self.assertEqual(meta['x-container-policy-quota'], '0')
2435
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2436
                                 length=1024*1024+1)
2437

    
2438
class TestUsageFreeVersioningAutoContainerPolicy(BaseTestCase):
2439
    """ Challenge free version accounting
2440
       in a container with auto versioning policy
2441

2442
       In case of unknown server version accounting policy or
2443
       debit version accounting policy
2444
       enforce all test cases to return immediately
2445
    """
2446
    def setUp(self):
2447
        BaseTestCase.setUp(self)
2448

    
2449
        # TODO Only works if tests are running in the same host with the server. Add support for remote server
2450
        self.stop_execution = False
2451
        try:
2452
            from pithos.api.settings import BACKEND_FREE_VERSIONING
2453
        except ImportError:
2454
            print 'Unable to execute the test: unknown version accounting policy'
2455
            self.stop_execution = True
2456
            return
2457
        else:
2458
            if not BACKEND_FREE_VERSIONING:
2459
                print 'Unable to execute the test: no free version accounting policy'
2460
                self.stop_execution = True
2461
                return
2462

    
2463
        self.create_container('container')
2464
        meta = self.client.retrieve_account_metadata()
2465
        self.initial_usage = int(meta['x-account-bytes-used'])
2466
        print 'Current account usage: %d' % self.initial_usage
2467

    
2468
        self.usage = self.initial_usage
2469
        #self.mtime = {}
2470
        l =  (50, 100, 150, 70, 80)
2471
        self.curr_acc_usage = self.initial_usage
2472
        for i, length in list(enumerate(l)):
2473
            self.upload_random_data('container', 'object', length=length)
2474
            meta = self.client.retrieve_account_metadata()
2475
            self.usage = int(meta['x-account-bytes-used'])
2476
            print 'Current account usage: %d' % self.usage
2477
            self.curr_acc_usage = self.initial_usage + length
2478
            self.assertEqual(self.usage, self.curr_acc_usage)
2479

    
2480
            #t = datetime.datetime.utcnow()
2481
            #self.mtime[i] = int(_time.mktime(t.timetuple()))
2482
            _time.sleep(1)
2483

    
2484
        versions = self.client.retrieve_object_versionlist(
2485
            'container', 'object'
2486
        )['versions']
2487
        self.mtime = [int(i[1]) for i in versions]
2488

    
2489
    def create_container(self, cname):
2490
        self.client.create_container(cname)
2491

    
2492
    def _test_delete_object_container(self):
2493
        """
2494
            assume account usage = 0
2495
            scenario:
2496
                upload object (length=50)
2497
                update object (length=100)
2498
                update object (length=150)
2499
                update object (length=70)
2500
                update object (length=80)
2501
                delete object
2502
                delete container
2503
            account usage sequel: 50|100|150|70|80|0|0
2504
        """
2505
        if self.stop_execution:
2506
            return
2507

    
2508
        self.client.delete_object('container', 'object')
2509
        meta = self.client.retrieve_account_metadata()
2510
        self.usage = int(meta['x-account-bytes-used'])
2511
        print 'Current account usage: %d' % self.usage
2512
        self.assertEqual(self.usage, self.initial_usage)
2513

    
2514
        self.client.delete_container('container')
2515
        meta = self.client.retrieve_account_metadata()
2516
        self.usage = int(meta['x-account-bytes-used'])
2517
        print 'Current account usage: %d' % self.usage
2518
        self.assertEqual(self.usage, self.initial_usage)
2519

    
2520
    def _test_purge_delete_object(self):
2521
        """
2522
            assume account usage = 0
2523
            scenario:
2524
                upload object (length=50)
2525
                update object (length=100)
2526
                update object (length=150)
2527
                update object (length=70)
2528
                update object (length=80)
2529
                delete object history
2530
                delete object
2531
                delete container
2532
            account usage sequel: 50|100|150|70|80|80|0|0
2533
        """
2534
        if self.stop_execution:
2535
            return
2536

    
2537
        # purge some object history
2538
        i = random.randrange(len(self.mtime))
2539
        self.client.delete_object('container', 'object', until=self.mtime[i])
2540
        meta = self.client.retrieve_account_metadata()
2541
        self.usage = int(meta['x-account-bytes-used'])
2542
        print 'Current account usage: %d' % self.usage
2543
        self.assertEqual(self.usage, self.curr_acc_usage)
2544

    
2545
        self.client.delete_object('container', 'object')
2546
        meta = self.client.retrieve_account_metadata()
2547
        self.usage = int(meta['x-account-bytes-used'])
2548
        print 'Current account usage: %d' % self.usage
2549
        self.assertEqual(self.usage, self.initial_usage)
2550

    
2551
        self.client.delete_container('container')
2552
        meta = self.client.retrieve_account_metadata()
2553
        self.usage = int(meta['x-account-bytes-used'])
2554
        print 'Current account usage: %d' % self.usage
2555
        self.assertEqual(self.usage, self.initial_usage)
2556

    
2557
    def _test_delete_object_purge_container_history(self):
2558
        """
2559
            assume account usage = 0
2560
            scenario:
2561
                upload object (length=50)
2562
                update object (length=100)
2563
                update object (length=150)
2564
                update object (length=70)
2565
                update object (length=80)
2566
                delete object
2567
                delete container history
2568
                delete container
2569
            account usage sequel: 50|100|150|70|80|0|0|0
2570
        """
2571
        if self.stop_execution:
2572
            return
2573

    
2574
        self.client.delete_object('container', 'object')
2575
        meta = self.client.retrieve_account_metadata()
2576
        self.usage = int(meta['x-account-bytes-used'])
2577
        print 'Current account usage: %d' % self.usage
2578
        self.assertEqual(self.usage,
2579
                         self.initial_usage)
2580

    
2581
        # purge some container history
2582
        i = random.randrange(len(self.mtime))
2583
        self.client.delete_container('container', until=self.mtime[i])
2584
        meta = self.client.retrieve_account_metadata()
2585
        self.usage = int(meta['x-account-bytes-used'])
2586
        print 'Current account usage: %d' % self.usage
2587
        self.assertEqual(self.usage, self.initial_usage)
2588

    
2589
        self.client.delete_container('container')
2590
        meta = self.client.retrieve_account_metadata()
2591
        self.usage = int(meta['x-account-bytes-used'])
2592
        print 'Current account usage: %d' % self.usage
2593
        self.assertEqual(self.usage, self.initial_usage)
2594

    
2595
    def _test_purge_container_delete_object(self):
2596
        """
2597
            assume account usage = 0
2598
            scenario:
2599
                upload object (length=50)
2600
                update object (length=100)
2601
                update object (length=150)
2602
                update object (length=70)
2603
                update object (length=80)
2604
                delete container history
2605
                delete object
2606
                delete container
2607
            account usage sequel: 50|100|150|70|80|80|0|0
2608
        """
2609
        if self.stop_execution:
2610
            return
2611

    
2612
        # purge some container history
2613
        i = random.randrange(len(self.mtime))
2614
        self.client.delete_container('container', until=self.mtime[i])
2615
        meta = self.client.retrieve_account_metadata()
2616
        self.usage = int(meta['x-account-bytes-used'])
2617
        print 'Current account usage: %d' % self.usage
2618
        self.assertEqual( self.usage, self.curr_acc_usage)
2619

    
2620
        self.client.delete_object('container', 'object')
2621
        meta = self.client.retrieve_account_metadata()
2622
        self.usage = int(meta['x-account-bytes-used'])
2623
        print 'Current account usage: %d' % self.usage
2624
        self.assertEqual(self.usage, self.initial_usage)
2625

    
2626
        self.client.delete_container('container')
2627
        meta = self.client.retrieve_account_metadata()
2628
        self.usage = int(meta['x-account-bytes-used'])
2629
        print 'Current account usage: %d' % self.usage
2630
        self.assertEqual(self.usage, self.initial_usage)
2631

    
2632
    def _test_successive_purging(self):
2633
        """
2634
            assume account usage = 0
2635
            scenario:
2636
                upload object (length=50)
2637
                update object (length=100)
2638
                update object (length=150)
2639
                update object (length=70)
2640
                update object (length=80)
2641
                delete earlier 2 object history versions
2642
                delete next earlier object history version
2643
                delete rest object history and current version
2644
                delete object (404 status)
2645
                delete container
2646
            account usage sequel: 50|100|150|70|80|80|80|0|0|0
2647
        """
2648
        if self.stop_execution:
2649
            return
2650

    
2651
        # purge some object history
2652
        i = random.randrange(len(self.mtime))
2653
        self.client.delete_object('container', 'object', until=self.mtime[i])
2654
        meta = self.client.retrieve_account_metadata()
2655
        self.usage = int(meta['x-account-bytes-used'])
2656
        print 'Current account usage: %d' % self.usage
2657
        self.assertEqual(self.usage, self.curr_acc_usage)
2658

    
2659
        # purge some container history
2660
        i = random.randrange(len(self.mtime))
2661
        self.client.delete_container('container', until=self.mtime[i])
2662
        meta = self.client.retrieve_account_metadata()
2663
        self.usage = int(meta['x-account-bytes-used'])
2664
        print 'Current account usage: %d' % self.usage
2665
        self.assertEqual(self.usage, self.curr_acc_usage)
2666

    
2667
        # purge some more object history
2668
        i = random.randrange(len(self.mtime))
2669
        self.client.delete_object('container', 'object', until=self.mtime[i])
2670
        meta = self.client.retrieve_account_metadata()
2671
        self.usage = int(meta['x-account-bytes-used'])
2672
        print 'Current account usage: %d' % self.usage
2673
        self.assertEqual(self.usage, self.curr_acc_usage)
2674

    
2675
        # purge some object history and current
2676
        t = datetime.datetime.utcnow()
2677
        now = int(_time.mktime(t.timetuple()))
2678
        self.client.delete_object('container', 'object', until=now)
2679
        meta = self.client.retrieve_account_metadata()
2680
        self.usage = int(meta['x-account-bytes-used'])
2681
        print 'Current account usage: %d' % self.usage
2682
        self.assertEqual(self.usage, self.initial_usage)
2683

    
2684
        # try to delete object and assert object is not found
2685
        self.assert_raises_fault(
2686
            404, self.client.delete_object, 'container', 'object'
2687
        )
2688

    
2689
        meta = self.client.retrieve_account_metadata()
2690
        self.usage = int(meta['x-account-bytes-used'])
2691
        print 'Current account usage: %d' % self.usage
2692
        self.assertEqual(self.usage, self.initial_usage)
2693

    
2694
        self.client.delete_container('container')
2695
        meta = self.client.retrieve_account_metadata()
2696
        self.usage = int(meta['x-account-bytes-used'])
2697
        print 'Current account usage: %d' % self.usage
2698
        self.assertEqual(self.usage, self.initial_usage)
2699

    
2700
    def _test_delete_object_empty_container_content(self):
2701
        """
2702
            assume account usage = 0
2703
            scenario:
2704
                upload object (length=50)
2705
                update object (length=100)
2706
                update object (length=150)
2707
                update object (length=70)
2708
                update object (length=80)
2709
                delete object
2710
                delete container contents
2711
                delete container
2712
            account usage sequel: 50|100|150|70|80|0|0|0
2713
        """
2714
        if self.stop_execution:
2715
            return
2716

    
2717
        self.client.delete_object('container', 'object')
2718

    
2719
        meta = self.client.retrieve_account_metadata()
2720
        self.usage = int(meta['x-account-bytes-used'])
2721
        print 'Current account usage: %d' % self.usage
2722
        self.assertEqual(self.usage, self.initial_usage)
2723

    
2724
        self.client.delete_container('container', delimiter='/')
2725
        meta = self.client.retrieve_account_metadata()
2726
        self.usage = int(meta['x-account-bytes-used'])
2727
        print 'Current account usage: %d' % self.usage
2728
        self.assertEqual(self.usage, self.initial_usage)
2729

    
2730
        self.client.delete_container('container')
2731
        meta = self.client.retrieve_account_metadata()
2732
        self.usage = int(meta['x-account-bytes-used'])
2733
        print 'Current account usage: %d' % self.usage
2734
        self.assertEqual(self.usage, self.initial_usage)
2735

    
2736
    def _test_delete_container_content(self):
2737
        """
2738
            assume account usage = 0
2739
            scenario:
2740
                upload object (length=50)
2741
                update object (length=100)
2742
                update object (length=150)
2743
                update object (length=70)
2744
                update object (length=80)
2745
                delete container contents
2746
                delete container
2747
            account usage sequel: 50|100|150|70|80|0|0
2748
        """
2749
        if self.stop_execution:
2750
            return
2751

    
2752
        self.client.delete_container('container', delimiter='/')
2753
        meta = self.client.retrieve_account_metadata()
2754
        self.usage = int(meta['x-account-bytes-used'])
2755
        print 'Current account usage: %d' % self.usage
2756
        self.assertEqual(self.usage, self.initial_usage)
2757

    
2758
        self.client.delete_container('container')
2759
        meta = self.client.retrieve_account_metadata()
2760
        self.usage = int(meta['x-account-bytes-used'])
2761
        print 'Current account usage: %d' % self.usage
2762
        self.assertEqual(self.usage, self.initial_usage)
2763

    
2764

    
2765
class TestUsageFreeVersioningNoneContainerPolicy(
2766
    TestUsageFreeVersioningAutoContainerPolicy):
2767
    """ Challenge free version accounting
2768
       in a container with none versioning policy
2769

2770
       In case of unknown server version accounting policy or
2771
       debit version accounting policy
2772
       enforce all test cases to return immediately
2773
    """
2774
    def create_container(self, cname):
2775
        self.client.create_container(cname,
2776
                                     policies={'versioning':'none'})
2777

    
2778
class TestUsageDebitVersioningAutoContainerPolicy(BaseTestCase):
2779
    """ Challenge debit version accounting
2780
       in a container with auto versioning policy
2781

2782
       In case of unknown server version accounting policy or
2783
       free version accounting policy
2784
       enforce all test cases to return immediately
2785
    """
2786
    def setUp(self):
2787
        BaseTestCase.setUp(self)
2788

    
2789
        self.stop_execution = False
2790
        try:
2791
            from pithos.api.settings import BACKEND_FREE_VERSIONING
2792
        except ImportError:
2793
            print 'Unable to execute the test: unknown version accounting policy'
2794
            self.stop_execution = True
2795
            return
2796
        else:
2797
            if BACKEND_FREE_VERSIONING:
2798
                print 'Unable to execute the test: free version accounting policy'
2799
                self.stop_execution = True
2800
                return
2801

    
2802
        self.create_container('container')
2803
        meta = self.client.retrieve_account_metadata()
2804
        self.initial_usage = int(meta['x-account-bytes-used'])
2805
        print 'Current account usage: %d' % self.initial_usage
2806

    
2807
        self.usage = self.initial_usage
2808
        self.l = (50, 100, 150, 70, 80)
2809
        self.curr_acc_usage = self.initial_usage
2810
        for i, length in list(enumerate(self.l)):
2811
            self.upload_random_data('container', 'object', length=length)
2812
            meta = self.client.retrieve_account_metadata()
2813
            self.usage = int(meta['x-account-bytes-used'])
2814
            print 'Current account usage: %d' % self.usage
2815
            self.curr_acc_usage += length
2816
            self.assertEqual(self.usage, self.curr_acc_usage)
2817
            _time.sleep(1)
2818

    
2819
        versions = self.client.retrieve_object_versionlist(
2820
            'container', 'object'
2821
        )['versions']
2822
        self.mtime = [int(i[1]) for i in versions]
2823

    
2824
    def create_container(self, cname):
2825
        self.client.create_container(cname)
2826

    
2827
    def _test_delete_object_container(self):
2828
        """
2829
            assume account usage = 0
2830
            scenario:
2831
                upload object (length=50)
2832
                update object (length=100)
2833
                update object (length=150)
2834
                update object (length=70)
2835
                update object (length=80)
2836
                delete object
2837
                delete container
2838
            account usage sequel: 50|150|300|370|450|450|0
2839
        """
2840
        if self.stop_execution:
2841
            return
2842

    
2843
        self.client.delete_object('container', 'object')
2844
        meta = self.client.retrieve_account_metadata()
2845
        self.usage = int(meta['x-account-bytes-used'])
2846
        print 'Current account usage: %d' % self.usage
2847
        self.assertEqual(self.usage, self.initial_usage + sum(self.l))
2848

    
2849
        self.client.delete_container('container')
2850
        meta = self.client.retrieve_account_metadata()
2851
        self.usage = int(meta['x-account-bytes-used'])
2852
        print 'Current account usage: %d' % self.usage
2853
        self.assertEqual(self.usage, self.initial_usage)
2854

    
2855
    def _test_purge_delete_object(self):
2856
        """
2857
            assume account usage = 0
2858
            scenario:
2859
                upload object (length=50)
2860
                update object (length=100)
2861
                update object (length=150)
2862
                update object (length=70)
2863
                update object (length=80)
2864
                delete 3 earlier object versions
2865
                delete object
2866
                delete container
2867
            account usage sequel: 50|150|300|370|450|150|150|0
2868
        """
2869
        if self.stop_execution:
2870
            return
2871

    
2872
        # purge some object history
2873
        i = 3
2874
        self.client.delete_object('container', 'object', until=self.mtime[i])
2875
        meta = self.client.retrieve_account_metadata()
2876
        self.usage = int(meta['x-account-bytes-used'])
2877
        print 'Current account usage: %d' % self.usage
2878
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2879

    
2880
        self.client.delete_object('container', 'object')
2881
        meta = self.client.retrieve_account_metadata()
2882
        self.usage = int(meta['x-account-bytes-used'])
2883
        print 'Current account usage: %d' % self.usage
2884
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2885

    
2886
        self.client.delete_container('container')
2887
        meta = self.client.retrieve_account_metadata()
2888
        self.usage = int(meta['x-account-bytes-used'])
2889
        print 'Current account usage: %d' % self.usage
2890
        self.assertEqual(self.usage, self.initial_usage)
2891

    
2892
    def _test_delete_object_purge_container_history(self):
2893
        """
2894
            assume account usage = 0
2895
            scenario:
2896
                upload object (length=50)
2897
                update object (length=100)
2898
                update object (length=150)
2899
                update object (length=70)
2900
                update object (length=80)
2901
                delete object
2902
                delete container history (3 earlier object versions)
2903
                delete container
2904
            account usage sequel: 50|150|300|370|450|450|150|0
2905
        """
2906
        if self.stop_execution:
2907
            return
2908

    
2909
        self.client.delete_object('container', 'object')
2910
        meta = self.client.retrieve_account_metadata()
2911
        self.usage = int(meta['x-account-bytes-used'])
2912
        print 'Current account usage: %d' % self.usage
2913
        self.assertEqual(self.usage, self.curr_acc_usage)
2914

    
2915
        # purge some container history
2916
        i = 3
2917
        self.client.delete_container('container', until=self.mtime[i])
2918
        meta = self.client.retrieve_account_metadata()
2919
        self.usage = int(meta['x-account-bytes-used'])
2920
        print 'Current account usage: %d' % self.usage
2921
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2922

    
2923
        self.client.delete_container('container')
2924
        meta = self.client.retrieve_account_metadata()
2925
        self.usage = int(meta['x-account-bytes-used'])
2926
        print 'Current account usage: %d' % self.usage
2927
        self.assertEqual(self.usage, self.initial_usage)
2928

    
2929
    def _test_purge_container_delete_object(self):
2930
        """
2931
            assume account usage = 0
2932
            scenario:
2933
                upload object (length=50)
2934
                update object (length=100)
2935
                update object (length=150)
2936
                update object (length=70)
2937
                update object (length=80)
2938
                delete container history
2939
                delete object
2940
                delete container
2941
            account usage sequel: 50|150|300|370|450|150|150|0
2942
        """
2943
        if self.stop_execution:
2944
            return
2945

    
2946
        versions = self.client.retrieve_object_versionlist(
2947
            'container', 'object'
2948
        )['versions']
2949
        mtime = [i[1] for i in versions]
2950

    
2951
        # purge some container history
2952
        i = 3
2953
        until = int(mtime[i])
2954
        self.client.delete_container('container', until=until)
2955
        meta = self.client.retrieve_account_metadata()
2956
        self.usage = int(meta['x-account-bytes-used'])
2957
        print 'Current account usage: %d' % self.usage
2958
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2959

    
2960
        self.client.delete_object('container', 'object')
2961
        meta = self.client.retrieve_account_metadata()
2962
        self.usage = int(meta['x-account-bytes-used'])
2963
        print 'Current account usage: %d' % self.usage
2964
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2965

    
2966

    
2967
        self.client.delete_container('container')
2968
        meta = self.client.retrieve_account_metadata()
2969
        self.usage = int(meta['x-account-bytes-used'])
2970
        print 'Current account usage: %d' % self.usage
2971
        self.assertEqual(self.usage, self.initial_usage)
2972

    
2973
    def _test_successive_purging(self):
2974
        """
2975
            assume account usage = 0
2976
            scenario:
2977
                upload object (length=50)
2978
                update object (length=100)
2979
                update object (length=150)
2980
                update object (length=70)
2981
                update object (length=80)
2982
                delete earlier 2 object history versions
2983
                delete next earlier object history version
2984
                delete rest object history and current version
2985
                delete object (404 status)
2986
                delete container
2987
            account usage sequel: 50|150|300|370|450|400|300|150|0|0
2988
        """
2989
        if self.stop_execution:
2990
            return
2991

    
2992
        versions = self.client.retrieve_object_versionlist(
2993
            'container', 'object'
2994
        )['versions']
2995
        mtime = [i[1] for i in versions]
2996

    
2997
        # purge some object history
2998
        i = 1
2999
        until = int(mtime[i])
3000
        self.client.delete_object('container', 'object', until=until)
3001
        meta = self.client.retrieve_account_metadata()
3002
        self.usage = int(meta['x-account-bytes-used'])
3003
        print 'Current account usage: %d' % self.usage
3004
        self.curr_acc_usage = self.curr_acc_usage - sum(self.l[:i])
3005
        self.assertEqual(self.usage, self.curr_acc_usage)
3006

    
3007
        # purge some container history
3008
        j = 2
3009
        until = int(mtime[j])
3010
        self.client.delete_container('container', until=until)
3011
        meta = self.client.retrieve_account_metadata()
3012
        self.usage = int(meta['x-account-bytes-used'])
3013
        print 'Current account usage: %d' % self.usage
3014
        self.curr_acc_usage = self.curr_acc_usage - sum(self.l[i:j])
3015
        self.assertEqual(self.usage, self.curr_acc_usage)
3016

    
3017
        # purge some more object history
3018
        k = 3
3019
        until = int(mtime[k])
3020
        self.client.delete_object('container', 'object', until=until)
3021
        meta = self.client.retrieve_account_metadata()
3022
        self.usage = int(meta['x-account-bytes-used'])
3023
        print 'Current account usage: %d' % self.usage
3024
        self.curr_acc_usage = self.curr_acc_usage - sum(self.l[j:k])
3025
        self.assertEqual(self.usage, self.curr_acc_usage)
3026

    
3027
        # purge some object history and current
3028
        t = datetime.datetime.utcnow()
3029
        now = int(_time.mktime(t.timetuple()))
3030
        self.client.delete_object('container', 'object', until=now)
3031
        meta = self.client.retrieve_account_metadata()
3032
        self.usage = int(meta['x-account-bytes-used'])
3033
        print 'Current account usage: %d' % self.usage
3034
        self.assertEqual(self.usage, self.initial_usage)
3035

    
3036
        # try to delete object and assert object is not found
3037
        self.assert_raises_fault(
3038
            404, self.client.delete_object, 'container', 'object'
3039
        )
3040

    
3041
        meta = self.client.retrieve_account_metadata()
3042
        self.usage = int(meta['x-account-bytes-used'])
3043
        print 'Current account usage: %d' % self.usage
3044
        self.assertEqual(self.usage, self.initial_usage)
3045

    
3046
        self.client.delete_container('container')
3047
        meta = self.client.retrieve_account_metadata()
3048
        self.usage = int(meta['x-account-bytes-used'])
3049
        print 'Current account usage: %d' % self.usage
3050
        self.assertEqual(self.usage, self.initial_usage)
3051

    
3052
    def _test_delete_object_empty_container_content(self):
3053
        """
3054
            assume account usage = 0
3055
            scenario:
3056
                upload object (length=50)
3057
                update object (length=100)
3058
                update object (length=150)
3059
                update object (length=70)
3060
                update object (length=80)
3061
                delete object
3062
                delete container contents
3063
                delete container
3064
            account usage sequel: 50|150|300|370|450|450|450|0
3065
        """
3066
        if self.stop_execution:
3067
            return
3068

    
3069
        self.client.delete_object('container', 'object')
3070

    
3071
        meta = self.client.retrieve_account_metadata()
3072
        self.usage = int(meta['x-account-bytes-used'])
3073
        print 'Current account usage: %d' % self.usage
3074
        self.assertEqual(self.usage, self.curr_acc_usage)
3075

    
3076
        self.client.delete_container('container', delimiter='/')
3077
        meta = self.client.retrieve_account_metadata()
3078
        self.usage = int(meta['x-account-bytes-used'])
3079
        print 'Current account usage: %d' % self.usage
3080
        self.assertEqual(self.usage, self.curr_acc_usage)
3081

    
3082
        self.client.delete_container('container')
3083
        meta = self.client.retrieve_account_metadata()
3084
        self.usage = int(meta['x-account-bytes-used'])
3085
        print 'Current account usage: %d' % self.usage
3086
        self.assertEqual(self.usage, self.initial_usage)
3087

    
3088
    def _test_delete_container_content(self):
3089
        """
3090
            assume account usage = 0
3091
            scenario:
3092
                upload object (length=50)
3093
                update object (length=100)
3094
                update object (length=150)
3095
                update object (length=70)
3096
                update object (length=80)
3097
                delete container contents
3098
                delete container
3099
            account usage sequel: 50|150|300|370|450|450|0
3100
        """
3101
        if self.stop_execution:
3102
            return
3103

    
3104
        self.client.delete_container('container', delimiter='/')
3105
        meta = self.client.retrieve_account_metadata()
3106
        self.usage = int(meta['x-account-bytes-used'])
3107
        print 'Current account usage: %d' % self.usage
3108
        self.assertEqual(self.usage, self.curr_acc_usage)
3109

    
3110
        self.client.delete_container('container')
3111
        meta = self.client.retrieve_account_metadata()
3112
        self.usage = int(meta['x-account-bytes-used'])
3113
        print 'Current account usage: %d' % self.usage
3114
        self.assertEqual(self.usage, self.initial_usage)
3115

    
3116
class TestUsageDebitVersioningNoneContainerPolicy(BaseTestCase):
3117
    """ Challenge debit version accounting
3118
       in a container with none versioning policy
3119

3120
       In case of unknown server version accounting policy or
3121
       free version accounting policy
3122
       enforce all test cases to return immediately
3123
    """
3124
    def setUp(self):
3125
        BaseTestCase.setUp(self)
3126

    
3127
        self.stop_execution = False
3128
        try:
3129
            from pithos.api.settings import BACKEND_FREE_VERSIONING
3130
        except ImportError:
3131
            print 'Unable to execute the test: unknown version accounting policy'
3132
            self.stop_execution = True
3133
            return
3134
        else:
3135
            if BACKEND_FREE_VERSIONING:
3136
                print 'Unable to execute the test: free version accounting policy'
3137
                self.stop_execution = True
3138
                return
3139

    
3140
        self.create_container('container')
3141
        meta = self.client.retrieve_account_metadata()
3142
        self.initial_usage = int(meta['x-account-bytes-used'])
3143
        print 'Current account usage: %d' % self.initial_usage
3144

    
3145
        l =  (50, 100, 150, 70, 80)
3146
        self.curr_acc_usage = self.initial_usage
3147
        for i, length in list(enumerate(l)):
3148
            self.upload_random_data('container', 'object', length=length)
3149
            meta = self.client.retrieve_account_metadata()
3150
            self.usage = int(meta['x-account-bytes-used'])
3151
            print 'Current account usage: %d' % self.usage
3152
            self.curr_acc_usage = self.initial_usage + length
3153
            self.assertEqual(self.usage, self.curr_acc_usage)
3154
            _time.sleep(1)
3155

    
3156
        versions = self.client.retrieve_object_versionlist(
3157
            'container', 'object'
3158
        )['versions']
3159
        self.mtime = [int(i[1]) for i in versions]
3160

    
3161
    def create_container(self, cname):
3162
        self.client.create_container(cname,
3163
                                     policies={'versioning':'none'})
3164

    
3165
    def _test_delete_object_container(self):
3166
        """
3167
            assume account usage = 0
3168
            scenario:
3169
                upload object (length=50)
3170
                update object (length=100)
3171
                update object (length=150)
3172
                update object (length=70)
3173
                update object (length=80)
3174
                delete object
3175
                delete container
3176
            account usage sequel: 50|100|150|70|80|0|0
3177
        """
3178
        if self.stop_execution:
3179
            return
3180

    
3181
        self.client.delete_object('container', 'object')
3182
        meta = self.client.retrieve_account_metadata()
3183
        self.usage = int(meta['x-account-bytes-used'])
3184
        print 'Current account usage: %d' % self.usage
3185
        self.assertEqual(self.usage, self.initial_usage)
3186

    
3187
        self.client.delete_container('container')
3188
        meta = self.client.retrieve_account_metadata()
3189
        self.usage = int(meta['x-account-bytes-used'])
3190
        print 'Current account usage: %d' % self.usage
3191
        self.assertEqual(self.usage, self.initial_usage)
3192

    
3193
    def _test_purge_delete_object(self):
3194
        """
3195
            assume account usage = 0
3196
            scenario:
3197
                upload object (length=50)
3198
                update object (length=100)
3199
                update object (length=150)
3200
                update object (length=70)
3201
                update object (length=80)
3202
                delete object history
3203
                delete object
3204
                delete container
3205
            account usage sequel: 50|100|150|70|80|80|0|0
3206
        """
3207
        if self.stop_execution:
3208
            return
3209

    
3210
        # purge some object history
3211
        self.client.delete_object('container', 'object', until=self.mtime[0])
3212
        meta = self.client.retrieve_account_metadata()
3213
        self.usage = int(meta['x-account-bytes-used'])
3214
        print 'Current account usage: %d' % self.usage
3215
        self.assertEqual(self.usage, self.curr_acc_usage)
3216

    
3217
        self.client.delete_object('container', 'object')
3218
        meta = self.client.retrieve_account_metadata()
3219
        self.usage = int(meta['x-account-bytes-used'])
3220
        print 'Current account usage: %d' % self.usage
3221
        self.assertEqual(self.usage, self.initial_usage)
3222

    
3223
        self.client.delete_container('container')
3224
        meta = self.client.retrieve_account_metadata()
3225
        self.usage = int(meta['x-account-bytes-used'])
3226
        print 'Current account usage: %d' % self.usage
3227
        self.assertEqual(self.usage, self.initial_usage)
3228

    
3229
    def _test_delete_object_purge_container_history(self):
3230
        """
3231
            assume account usage = 0
3232
            scenario:
3233
                upload object (length=50)
3234
                update object (length=100)
3235
                update object (length=150)
3236
                update object (length=70)
3237
                update object (length=80)
3238
                delete object
3239
                delete container history
3240
                delete container
3241
            account usage sequel: 50|100|150|70|80|0|0|0
3242
        """
3243
        if self.stop_execution:
3244
            return
3245

    
3246
        self.client.delete_object('container', 'object')
3247
        meta = self.client.retrieve_account_metadata()
3248
        self.usage = int(meta['x-account-bytes-used'])
3249
        print 'Current account usage: %d' % self.usage
3250
        self.assertEqual(self.usage, self.initial_usage)
3251

    
3252
        # purge some container history
3253
        self.client.delete_container('container', until=self.mtime[0])
3254
        meta = self.client.retrieve_account_metadata()
3255
        self.usage = int(meta['x-account-bytes-used'])
3256
        print 'Current account usage: %d' % self.usage
3257
        self.assertEqual(self.usage, self.initial_usage)
3258

    
3259
        self.client.delete_container('container')
3260
        meta = self.client.retrieve_account_metadata()
3261
        self.usage = int(meta['x-account-bytes-used'])
3262
        print 'Current account usage: %d' % self.usage
3263
        self.assertEqual(self.usage, self.initial_usage)
3264

    
3265
    def _test_purge_container_delete_object(self):
3266
        """
3267
            assume account usage = 0
3268
            scenario:
3269
                upload object (length=50)
3270
                update object (length=100)
3271
                update object (length=150)
3272
                update object (length=70)
3273
                update object (length=80)
3274
                delete container history
3275
                delete object
3276
                delete container
3277
            account usage sequel: 50|100|150|70|80|80|0|0
3278
        """
3279
        if self.stop_execution:
3280
            return
3281

    
3282
        # purge some container history
3283
        self.client.delete_container('container', until=self.mtime[0])
3284
        meta = self.client.retrieve_account_metadata()
3285
        self.usage = int(meta['x-account-bytes-used'])
3286
        print 'Current account usage: %d' % self.usage
3287
        self.assertEqual(self.usage, self.curr_acc_usage)
3288

    
3289
        self.client.delete_object('container', 'object')
3290
        meta = self.client.retrieve_account_metadata()
3291
        self.usage = int(meta['x-account-bytes-used'])
3292
        print 'Current account usage: %d' % self.usage
3293
        self.assertEqual(self.usage, self.initial_usage)
3294

    
3295
        self.client.delete_container('container')
3296
        meta = self.client.retrieve_account_metadata()
3297
        self.usage = int(meta['x-account-bytes-used'])
3298
        print 'Current account usage: %d' % self.usage
3299
        self.assertEqual(self.usage, self.initial_usage)
3300

    
3301
    def _test_successive_purging(self):
3302
        """
3303
            assume account usage = 0
3304
            scenario:
3305
                upload object (length=50)
3306
                update object (length=100)
3307
                update object (length=150)
3308
                update object (length=70)
3309
                update object (length=80)
3310
                delete earlier 2 object history versions
3311
                delete next earlier object history version
3312
                delete rest object history and current version
3313
                delete object (404 status)
3314
                delete container
3315
            account usage sequel: 50|100|150|70|80|80|80|80|0|0|0
3316
        """
3317
        if self.stop_execution:
3318
            return
3319

    
3320
        # purge some object history
3321
        i = random.randrange(len(self.mtime))
3322
        self.client.delete_object('container', 'object', until=self.mtime[i])
3323
        meta = self.client.retrieve_account_metadata()
3324
        self.usage = int(meta['x-account-bytes-used'])
3325
        print 'Current account usage: %d' % self.usage
3326
        self.assertEqual(self.usage, self.curr_acc_usage)
3327

    
3328
        # purge some object history and current
3329
        t = datetime.datetime.utcnow()
3330
        now = int(_time.mktime(t.timetuple()))
3331
        self.client.delete_object('container', 'object', until=now)
3332
        meta = self.client.retrieve_account_metadata()
3333
        self.usage = int(meta['x-account-bytes-used'])
3334
        print 'Current account usage: %d' % self.usage
3335
        self.assertEqual(self.usage, self.initial_usage)
3336

    
3337
        # try to delete object and assert object is not found
3338
        self.assert_raises_fault(
3339
            404, self.client.delete_object, 'container', 'object'
3340
        )
3341

    
3342
        meta = self.client.retrieve_account_metadata()
3343
        self.usage = int(meta['x-account-bytes-used'])
3344
        print 'Current account usage: %d' % self.usage
3345
        self.assertEqual(self.usage, self.initial_usage)
3346

    
3347
        self.client.delete_container('container')
3348
        meta = self.client.retrieve_account_metadata()
3349
        self.usage = int(meta['x-account-bytes-used'])
3350
        print 'Current account usage: %d' % self.usage
3351
        self.assertEqual(self.usage, self.initial_usage)
3352

    
3353
    def _test_delete_object_empty_container_content(self):
3354
        """
3355
            assume account usage = 0
3356
            scenario:
3357
                upload object (length=50)
3358
                update object (length=100)
3359
                update object (length=150)
3360
                update object (length=70)
3361
                update object (length=80)
3362
                delete object
3363
                delete container contents
3364
                delete container
3365
            account usage sequel: 50|100|150|70|80|0|0|0
3366
        """
3367
        if self.stop_execution:
3368
            return
3369

    
3370
        self.client.delete_object('container', 'object')
3371
        meta = self.client.retrieve_account_metadata()
3372
        self.usage = int(meta['x-account-bytes-used'])
3373
        print 'Current account usage: %d' % self.usage
3374
        self.assertEqual(self.usage, self.initial_usage)
3375

    
3376
        self.client.delete_container('container', delimiter='/')
3377
        meta = self.client.retrieve_account_metadata()
3378
        self.usage = int(meta['x-account-bytes-used'])
3379
        print 'Current account usage: %d' % self.usage
3380
        self.assertEqual(self.usage, self.initial_usage)
3381

    
3382
        self.client.delete_container('container')
3383
        meta = self.client.retrieve_account_metadata()
3384
        self.usage = int(meta['x-account-bytes-used'])
3385
        print 'Current account usage: %d' % self.usage
3386
        self.assertEqual(self.usage, self.initial_usage)
3387

    
3388
    def _test_delete_container_content(self):
3389
        """
3390
            assume account usage = 0
3391
            scenario:
3392
                upload object (length=50)
3393
                update object (length=100)
3394
                update object (length=150)
3395
                update object (length=70)
3396
                update object (length=80)
3397
                delete container contents
3398
                delete container
3399
            account usage sequel: 50|100|150|70|80|0|0
3400
        """
3401
        if self.stop_execution:
3402
            return
3403

    
3404
        self.client.delete_container('container', delimiter='/')
3405
        meta = self.client.retrieve_account_metadata()
3406
        self.usage = int(meta['x-account-bytes-used'])
3407
        print 'Current account usage: %d' % self.usage
3408
        self.assertEqual(self.usage, self.initial_usage)
3409

    
3410
        self.client.delete_container('container')
3411
        meta = self.client.retrieve_account_metadata()
3412
        self.usage = int(meta['x-account-bytes-used'])
3413
        print 'Current account usage: %d' % self.usage
3414
        self.assertEqual(self.usage, self.initial_usage)
3415

    
3416
class AssertUUidInvariant(object):
3417
    def __init__(self, callable, *args, **kwargs):
3418
        self.callable = callable
3419
        self.args = args
3420
        self.kwargs = kwargs
3421

    
3422
    def __enter__(self):
3423
        self.map = self.callable(*self.args, **self.kwargs)
3424
        assert('x-object-uuid' in self.map)
3425
        self.uuid = self.map['x-object-uuid']
3426
        return self.map
3427

    
3428
    def __exit__(self, type, value, tb):
3429
        map = self.callable(*self.args, **self.kwargs)
3430
        assert('x-object-uuid' in self.map)
3431
        uuid = map['x-object-uuid']
3432
        assert(uuid == self.uuid)
3433

    
3434
class AssertMappingInvariant(object):
3435
    def __init__(self, callable, *args, **kwargs):
3436
        self.callable = callable
3437
        self.args = args
3438
        self.kwargs = kwargs
3439

    
3440
    def __enter__(self):
3441
        self.map = self.callable(*self.args, **self.kwargs)
3442
        return self.map
3443

    
3444
    def __exit__(self, type, value, tb):
3445
        map = self.callable(*self.args, **self.kwargs)
3446
        for k, v in self.map.items():
3447
            if is_date(v):
3448
                continue
3449
            assert(k in map)
3450
            assert v == map[k]
3451

    
3452
class AssertContentInvariant(object):
3453
    def __init__(self, callable, *args, **kwargs):
3454
        self.callable = callable
3455
        self.args = args
3456
        self.kwargs = kwargs
3457

    
3458
    def __enter__(self):
3459
        self.content = self.callable(*self.args, **self.kwargs)[2]
3460
        return self.content
3461

    
3462
    def __exit__(self, type, value, tb):
3463
        content = self.callable(*self.args, **self.kwargs)[2]
3464
        assert self.content == content
3465

    
3466
def get_content_splitted(response):
3467
    if response:
3468
        return response.content.split('\n')
3469

    
3470
def compute_md5_hash(data):
3471
    md5 = hashlib.md5()
3472
    offset = 0
3473
    md5.update(data)
3474
    return md5.hexdigest().lower()
3475

    
3476
def compute_block_hash(data, algorithm):
3477
    h = hashlib.new(algorithm)
3478
    h.update(data.rstrip('\x00'))
3479
    return h.hexdigest()
3480

    
3481
def get_random_data(length=500):
3482
    char_set = string.ascii_uppercase + string.digits
3483
    return ''.join(random.choice(char_set) for x in xrange(length))
3484

    
3485
def is_date(date):
3486
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
3487
    __D = r'(?P<day>\d{2})'
3488
    __D2 = r'(?P<day>[ \d]\d)'
3489
    __M = r'(?P<mon>\w{3})'
3490
    __Y = r'(?P<year>\d{4})'
3491
    __Y2 = r'(?P<year>\d{2})'
3492
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
3493
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
3494
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
3495
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
3496
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
3497
        m = regex.match(date)
3498
        if m is not None:
3499
            return True
3500
    return False
3501

    
3502
def strnextling(prefix):
3503
    """Return the first unicode string
3504
       greater than but not starting with given prefix.
3505
       strnextling('hello') -> 'hellp'
3506
    """
3507
    if not prefix:
3508
        ## all strings start with the null string,
3509
        ## therefore we have to approximate strnextling('')
3510
        ## with the last unicode character supported by python
3511
        ## 0x10ffff for wide (32-bit unicode) python builds
3512
        ## 0x00ffff for narrow (16-bit unicode) python builds
3513
        ## We will not autodetect. 0xffff is safe enough.
3514
        return unichr(0xffff)
3515
    s = prefix[:-1]
3516
    c = ord(prefix[-1])
3517
    if c >= 0xffff:
3518
        raise RuntimeError
3519
    s += unichr(c+1)
3520
    return s
3521

    
3522
o_names = ['kate.jpg',
3523
           'kate_beckinsale.jpg',
3524
           'How To Win Friends And Influence People.pdf',
3525
           'moms_birthday.jpg',
3526
           'poodle_strut.mov',
3527
           'Disturbed - Down With The Sickness.mp3',
3528
           'army_of_darkness.avi',
3529
           'the_mad.avi',
3530
           'photos/animals/dogs/poodle.jpg',
3531
           'photos/animals/dogs/terrier.jpg',
3532
           'photos/animals/cats/persian.jpg',
3533
           'photos/animals/cats/siamese.jpg',
3534
           'photos/plants/fern.jpg',
3535
           'photos/plants/rose.jpg',
3536
           'photos/me.jpg']
3537

    
3538

    
3539
def main():
3540
    if get_user() == 'test':
3541
        unittest.main(module='pithos.tools.test')
3542
    else:
3543
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
3544

    
3545

    
3546
if __name__ == "__main__":
3547
    main()
3548