Statistics
| Branch: | Tag: | Revision:

root / contrib / snf-pithos-tools / pithos / tools / test.py @ e5acddf8

History | View | Annotate | Download (143.4 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
        self.client.publish_object(self.container[0], self.obj[0]['name'])
569
        objs = self.client.list_objects(self.container[0], public=True)
570
        self.assertEqual(objs, [self.obj[0]['name']])
571

    
572
        # create child object
573
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
574
        objs = self.client.list_objects(self.container[0], public=True)
575
        self.assertEqual(objs, [self.obj[0]['name']])
576

    
577
        # test inheritance
578
        self.client.create_folder(self.container[1], 'folder')
579
        self.client.publish_object(self.container[1], 'folder')
580
        self.upload_random_data(self.container[1], 'folder/object')
581
        objs = self.client.list_objects(self.container[1], public=True)
582
        self.assertEqual(objs, ['folder'])
583

    
584
    def test_list_shared_public(self):
585
        self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
586
        self.client.publish_object(self.container[0], self.obj[1]['name'])
587
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
588
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
589

    
590
        # create child object
591
        self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
592
        self.upload_random_data(self.container[0], strnextling(self.obj[1]['name']))
593
        objs = self.client.list_objects(self.container[0], shared=True, public=True)
594
        self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
595

    
596
        # test inheritance
597
        self.client.create_folder(self.container[1], 'folder1')
598
        self.client.share_object(self.container[1], 'folder1', ('*',))
599
        self.upload_random_data(self.container[1], 'folder1/object')
600
        self.client.create_folder(self.container[1], 'folder2')
601
        self.client.publish_object(self.container[1], 'folder2')
602
        o = self.upload_random_data(self.container[1], 'folder2/object')
603
        objs = self.client.list_objects(self.container[1], shared=True, public=True)
604
        self.assertEqual(objs, ['folder1', 'folder1/object', 'folder2'])
605

    
606
    def test_list_objects(self):
607
        objects = self.client.list_objects(self.container[0])
608
        l = [elem['name'] for elem in self.obj[:8]]
609
        l.sort()
610
        self.assertEqual(objects, l)
611

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
794
class ContainerPut(BaseTestCase):
795
    def setUp(self):
796
        BaseTestCase.setUp(self)
797
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
798
        self.containers.sort()
799

    
800
    def test_create(self):
801
        self.client.create_container(self.containers[0])
802
        containers = self.client.list_containers()
803
        self.assertTrue(self.containers[0] in containers)
804
        self.assert_container_exists(self.containers[0])
805

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

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

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

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

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

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

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

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

    
844
class ContainerDelete(BaseTestCase):
845
    def setUp(self):
846
        BaseTestCase.setUp(self)
847
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
848
        self.containers.sort()
849

    
850
        for c in self.containers:
851
            self.client.create_container(c)
852

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

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

    
862
    def test_delete_invalid(self):
863
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
864

    
865
    def test_delete_contents(self):
866
        self.client.create_folder(self.containers[0], 'folder-1')
867
        self.upload_random_data(self.containers[1], 'folder-1/%s' % o_names[0])
868
        self.client.create_folder(self.containers[0], 'folder-1/subfolder')
869
        self.client.create_folder(self.containers[0], 'folder-2/%s' % o_names[1])
870

    
871
        objects = self.client.list_objects(self.containers[0])
872
        self.client.delete_container(self.containers[0], delimiter='/')
873
        for o in objects:
874
            self.assert_object_not_exists(self.containers[0], o)
875
        self.assert_container_exists(self.containers[0])
876

    
877
class ObjectGet(BaseTestCase):
878
    def setUp(self):
879
        BaseTestCase.setUp(self)
880
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
881
        self.containers.sort()
882

    
883
        #create some containers
884
        for c in self.containers:
885
            self.client.create_container(c)
886

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
967
        #assert successful partial content
968
        self.assertEqual(status, 206)
969

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

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

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

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

    
987
        #assert successful partial content
988
        self.assertEqual(status, 206)
989

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

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

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

    
1000
    def test_get_rest(self):
1001
        #perform get with range
1002
        offset = len(self.objects[0]['data']) - 500
1003
        status, headers, data = self.client.request_object(self.containers[1],
1004
                                                self.objects[0]['name'],
1005
                                                range='bytes=%s-' %offset)
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.assertTrue(self.objects[0]['data'][-500:], data)
1019

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

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

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

    
1037
        # assert partial content
1038
        self.assertEqual(status, 206)
1039

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1153
        #assert get success
1154
        self.assertEqual(status, 200)
1155

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

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

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

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

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

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

    
1187
        for f in DATE_FORMATS:
1188
            past = t2.strftime(f)
1189

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1312
        self.assertTrue(name in self.client.list_objects(self.container))
1313

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

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

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

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

    
1333
        uploaded_data = self.client.retrieve_object(self.container, objname)
1334
        self.assertEqual(data, uploaded_data)
1335

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

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

    
1347
        self.assert_object_exists(self.container, 'large-object')
1348
        self.assertEqual(data, self.client.retrieve_object(self.container,
1349
                                                           'large-object'))
1350

    
1351
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1352
        hashes = r['hashes']
1353
        block_size = int(r['block_size'])
1354
        block_hash = r['block_hash']
1355
        l = len(data)
1356
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1357
        self.assertEqual(block_num, len(hashes))
1358

    
1359
        #wrong manifestation
1360
        self.client.create_manifestation(self.container, 'large-object',
1361
                                         '%s/invalid' % self.container)
1362
        self.assertEqual('', self.client.retrieve_object(self.container,
1363
                                                         'large-object'))
1364

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

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

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

    
1390
class ObjectCopy(BaseTestCase):
1391
    def setUp(self):
1392
        BaseTestCase.setUp(self)
1393
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1394
        self.containers.sort()
1395

    
1396
        for c in self.containers:
1397
            self.client.create_container(c)
1398
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1399

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

    
1411
            #assert copy success
1412
            self.assertEqual(status, 201)
1413

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

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

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

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

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

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

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

    
1453
        #copy from invalid container
1454
        meta = {'test':'testcopy'}
1455
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1456
                                 self.obj['name'], self.containers[1],
1457
                                 'testcopy', meta)
1458

    
1459
    def test_copy_dir(self):
1460
        self.client.create_folder(self.containers[0], 'dir')
1461
        self.client.create_folder(self.containers[0], 'dir/subdir')
1462
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1463
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1464
        self.client.create_folder(self.containers[0], 'dirs')
1465

    
1466
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1467
        self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1468
        for object in objects[:-1]:
1469
            self.assert_object_exists(self.containers[0], object)
1470
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1471
            meta0 = self.client.retrieve_object_metadata(self.containers[0], object)
1472
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1473
            t = ('content-length', 'x-object-hash', 'content-type')
1474
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1475
        self.assert_object_not_exists(self.containers[1], objects[-1])
1476

    
1477
class ObjectMove(BaseTestCase):
1478
    def setUp(self):
1479
        BaseTestCase.setUp(self)
1480
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1481
        self.containers.sort()
1482

    
1483
        for c in self.containers:
1484
            self.client.create_container(c)
1485
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1486

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

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

    
1500
        #assert successful move
1501
        self.assertEqual(status, 201)
1502

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

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

    
1512
        #assert src object no more exists
1513
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1514

    
1515

    
1516
    def test_move_dir(self):
1517
        meta = {}
1518
        self.client.create_folder(self.containers[0], 'dir')
1519
        meta['dir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir')
1520
        self.client.create_folder(self.containers[0], 'dir/subdir')
1521
        meta['dir/subdir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir')
1522
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1523
        meta['dir/object1.jpg'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/object1.jpg')
1524
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1525
        meta['dir/subdir/object2.pdf'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir/object2.pdf')
1526
        self.client.create_folder(self.containers[0], 'dirs')
1527
        meta['dirs'] = self.client.retrieve_object_metadata(self.containers[0], 'dirs')
1528

    
1529
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1530
        self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1531
        for object in objects[:-1]:
1532
            self.assert_object_not_exists(self.containers[0], object)
1533
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1534
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1535
            t = ('content-length', 'x-object-hash', 'content-type')
1536
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1537
        self.assert_object_exists(self.containers[0], objects[-1])
1538
        self.assert_object_not_exists(self.containers[1], objects[-1])
1539

    
1540
class ObjectPost(BaseTestCase):
1541
    def setUp(self):
1542
        BaseTestCase.setUp(self)
1543
        self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1544
        self.containers.sort()
1545

    
1546
        for c in self.containers:
1547
            self.client.create_container(c)
1548
        self.obj = []
1549
        for i in range(2):
1550
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1551

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

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

    
1573
            #out of limits
1574
            more = {'f' * 114: 'b' * 257}
1575
            self.assert_raises_fault(400, self.client.update_object_metadata,
1576
                                                        self.containers[0],
1577
                                                        self.obj[0]['name'],
1578
                                                        **more)
1579

    
1580
            #perform update metadata
1581
            more = {'α': 'β' * 256}
1582
            status = self.client.update_object_metadata(self.containers[0],
1583
                                                        self.obj[0]['name'],
1584
                                                        **more)[0]
1585
            #assert request accepted
1586
            self.assertEqual(status, 202)
1587

    
1588
            #assert old metadata are still there
1589
            headers = self.client.retrieve_object_metadata(self.containers[0],
1590
                                                           self.obj[0]['name'],
1591
                                                           restricted=True)
1592
            #assert new metadata have been updated
1593
            for k,v in more.items():
1594
                self.assertTrue(k in headers.keys())
1595
                self.assertTrue(headers[k], v)
1596

    
1597
            #out of limits
1598
            more = {'α': 'β' * 257}
1599
            self.assert_raises_fault(400, self.client.update_object_metadata,
1600
                                                        self.containers[0],
1601
                                                        self.obj[0]['name'],
1602
                                                        **more)
1603

    
1604
    def test_update_object(self,
1605
                           first_byte_pos=0,
1606
                           last_byte_pos=499,
1607
                           instance_length = True,
1608
                           content_length = 500):
1609
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1610
                                 self.containers[0],
1611
                                 self.obj[0]['name']):
1612
            l = len(self.obj[0]['data'])
1613
            range = 'bytes %d-%d/%s' %(first_byte_pos,
1614
                                           last_byte_pos,
1615
                                            l if instance_length else '*')
1616
            partial = last_byte_pos - first_byte_pos + 1
1617
            length = first_byte_pos + partial
1618
            data = get_random_data(partial)
1619
            args = {'content_type':'application/octet-stream',
1620
                    'content_range':'%s' %range}
1621
            if content_length:
1622
                args['content_length'] = content_length
1623

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

    
1640
    def test_update_object_lt_blocksize(self):
1641
        self.test_update_object(10, 20, content_length=None)
1642

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

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

    
1677
    def test_update_object_no_content_length(self):
1678
        self.test_update_object(content_length = None)
1679

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1803

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

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

    
1814
        self.assertEqual(self.client.retrieve_object(c, o),
1815
                         self.client.retrieve_object(c, other))
1816

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

    
1820
class ObjectDelete(BaseTestCase):
1821
    def setUp(self):
1822
        BaseTestCase.setUp(self)
1823
        self.containers = ['c1', 'c2']
1824
        self.containers.extend(self.initial_containers)
1825

    
1826
        for c in self.containers:
1827
            self.client.create_container(c)
1828
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1829

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

    
1834
    def test_delete_invalid(self):
1835
        #assert item not found
1836
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1837
                                 self.obj['name'])
1838

    
1839
    def test_delete_dir(self):
1840
        self.client.create_folder(self.containers[0], 'dir')
1841
        self.client.create_folder(self.containers[0], 'dir/subdir')
1842
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1843
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1844
        self.client.create_folder(self.containers[0], 'dirs')
1845

    
1846
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1847
        self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1848
        for object in objects[:-1]:
1849
            self.assert_object_not_exists(self.containers[0], object)
1850
        self.assert_object_exists(self.containers[0], objects[-1])
1851

    
1852
class ListSharing(BaseTestCase):
1853
    def setUp(self):
1854
        BaseTestCase.setUp(self)
1855
        for i in range(2):
1856
            self.client.create_container('c%s' %i)
1857
        self.client.create_container('c')
1858
        for i in range(2):
1859
            self.upload_random_data('c1', 'o%s' %i)
1860
        if not OTHER_ACCOUNTS:
1861
            raise Warning('No other accounts avalaible for running this test.')
1862
        for token, account in OTHER_ACCOUNTS.items():
1863
            self.o1_sharing = token, account
1864
            self.client.share_object('c1', 'o1', (account,), read=True)
1865
            break
1866

    
1867
    def test_list_other_shared(self):
1868
        self.other = Pithos_Client(get_url(),
1869
                              self.o1_sharing[0],
1870
                              self.o1_sharing[1])
1871
        self.assertTrue(get_user() in self.other.list_shared_with_me())
1872

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

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

    
1882
class List(BaseTestCase):
1883
    def setUp(self):
1884
        BaseTestCase.setUp(self)
1885
        for i in range(1, 5):
1886
            c = 'c%s' % i
1887
            self.client.create_container(c)
1888
            for j in range(1, 3):
1889
                o = 'o%s' % j
1890
                self.upload_random_data(c, o)
1891
            if i < 3:
1892
                self.client.share_object(c, 'o1', ['papagian'], read=True)
1893
            if i%2 != 0:
1894
                self.client.publish_object(c, 'o2')
1895

    
1896
    def test_shared_public(self):
1897
        diff = lambda l: set(l) - set(self.initial_containers)
1898

    
1899
        func, kwargs = self.client.list_containers, {'shared':True}
1900
        l = func(**kwargs)
1901
        self.assertEqual(set(['c1', 'c2']), diff(l))
1902
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1903

    
1904
        func, kwargs = self.client.list_containers, {'public':True}
1905
        l = func(**kwargs)
1906
        self.assertEqual(set(['c1', 'c3']), diff(l))
1907
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1908

    
1909
        func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1910
        l = func(**kwargs)
1911
        self.assertEqual(set(['c1', 'c2', 'c3']), diff(l))
1912
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1913

    
1914
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1915
        l = func(*args, **kwargs)
1916
        self.assertEqual(l, ['o1'])
1917
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1918

    
1919
        func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1920
        l = func(*args, **kwargs)
1921
        self.assertEqual(l, ['o2'])
1922
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1923

    
1924
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1925
        l = func(*args, **kwargs)
1926
        self.assertEqual(l, ['o1', 'o2'])
1927
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1928

    
1929
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1930
        l = func(*args, **kwargs)
1931
        self.assertEqual(l, ['o1'])
1932
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1933

    
1934
        func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1935
        l = func(*args, **kwargs)
1936
        self.assertEqual(l, '')
1937
        self.assertEqual([], func(*args, format='json', **kwargs))
1938

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

    
1944
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1945
        l = func(*args, **kwargs)
1946
        self.assertEqual(l, '')
1947
        self.assertEqual([], func(*args, format='json', **kwargs))
1948

    
1949
        func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1950
        l = func(*args, **kwargs)
1951
        self.assertEqual(l, ['o2'])
1952
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1953

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

    
1959
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
1960
        l = func(*args, **kwargs)
1961
        self.assertEqual(l, '')
1962
        self.assertEqual([], func(*args, format='json', **kwargs))
1963

    
1964
        func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
1965
        l = func(*args, **kwargs)
1966
        self.assertEqual(l, '')
1967
        self.assertEqual([], func(*args, format='json', **kwargs))
1968

    
1969
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
1970
        l = func(*args, **kwargs)
1971
        self.assertEqual(l, '')
1972
        self.assertEqual([], func(*args, format='json', **kwargs))
1973

    
1974
class TestUTF8(BaseTestCase):
1975
    def test_create_container(self):
1976
        self.client.create_container('φάκελος')
1977
        self.assert_container_exists('φάκελος')
1978

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

    
1981
    def test_create_object(self):
1982
        self.client.create_container('φάκελος')
1983
        self.upload_random_data('φάκελος', 'αντικείμενο')
1984

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

    
1988
    def test_copy_object(self):
1989
        src_container = 'φάκελος'
1990
        src_object = 'αντικείμενο'
1991
        dest_container = 'αντίγραφα'
1992
        dest_object = 'ασφαλές-αντίγραφο'
1993

    
1994
        self.client.create_container(src_container)
1995
        self.upload_random_data(src_container, src_object)
1996

    
1997
        self.client.create_container(dest_container)
1998
        self.client.copy_object(src_container, src_object, dest_container,
1999
                                dest_object)
2000

    
2001
        self.assert_object_exists(src_container, src_object)
2002
        self.assert_object_exists(dest_container, dest_object)
2003
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2004

    
2005
    def test_move_object(self):
2006
        src_container = 'φάκελος'
2007
        src_object = 'αντικείμενο'
2008
        dest_container = 'αντίγραφα'
2009
        dest_object = 'ασφαλές-αντίγραφο'
2010

    
2011
        self.client.create_container(src_container)
2012
        self.upload_random_data(src_container, src_object)
2013

    
2014
        self.client.create_container(dest_container)
2015
        self.client.move_object(src_container, src_object, dest_container,
2016
                                dest_object)
2017

    
2018
        self.assert_object_not_exists(src_container, src_object)
2019
        self.assert_object_exists(dest_container, dest_object)
2020
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
2021

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

    
2027
        self.client.delete_object('φάκελος', 'αντικείμενο')
2028
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2029
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2030

    
2031
    def test_delete_container(self):
2032
        self.client.create_container('φάκελος')
2033
        self.assert_container_exists('φάκελος')
2034

    
2035
        self.client.delete_container('φάκελος')
2036
        self.assert_container_not_exists('φάκελος')
2037
        self.assertTrue('φάκελος' not in self.client.list_containers())
2038

    
2039
    def test_account_meta(self):
2040
        meta = {'ποιότητα':'ΑΑΑ'}
2041
        self.client.update_account_metadata(**meta)
2042
        meta = self.client.retrieve_account_metadata(restricted=True)
2043
        self.assertTrue('ποιότητα' in meta.keys())
2044
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2045

    
2046
    def test_container_meta(self):
2047
        meta = {'ποιότητα':'ΑΑΑ'}
2048
        self.client.create_container('φάκελος', meta=meta)
2049

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

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

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

    
2064
    def test_list_meta_filtering(self):
2065
        self.client.create_container('φάκελος')
2066
        meta = {'ποιότητα':'ΑΑΑ'}
2067
        self.upload_random_data('φάκελος', 'ο1', **meta)
2068
        self.upload_random_data('φάκελος', 'ο2')
2069
        self.upload_random_data('φάκελος', 'ο3')
2070

    
2071
        meta = {'ποσότητα':'μεγάλη'}
2072
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2073
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2074
        self.assertEquals(objects, ['ο1', 'ο2'])
2075

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

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

    
2082
        meta = {'ποιότητα':'ΑΒ'}
2083
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2084
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2085
        self.assertEquals(objects, ['ο1'])
2086
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2087
        self.assertEquals(objects, ['ο2'])
2088

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

    
2100
    def test_groups(self):
2101
        #create a group
2102
        groups = {'γκρουπ':'chazapis,διογένης'}
2103
        self.client.set_account_groups(**groups)
2104
        groups.update(self.initial_groups)
2105
        self.assertEqual(groups['γκρουπ'],
2106
                         self.client.retrieve_account_groups()['γκρουπ'])
2107

    
2108
        #check read access
2109
        self.client.create_container('φάκελος')
2110
        o = self.upload_random_data('φάκελος', 'ο1')
2111
        self.client.share_object('φάκελος', 'ο1', ['%s:γκρουπ' % get_user()])
2112
        if 'διογένης' not in OTHER_ACCOUNTS.values():
2113
            raise Warning('No such an account exists for running this test.')
2114
        chef = Pithos_Client(get_url(),
2115
                            '0009',
2116
                            'διογένης')
2117
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2118
                                     'φάκελος', 'ο1', account=get_user())
2119

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

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

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

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

    
2144
        self.assert_object_exists('φάκελος', 'άπαντα')
2145
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
2146
                                                           'άπαντα'))
2147

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

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

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

    
2163
class TestPermissions(BaseTestCase):
2164
    def setUp(self):
2165
        BaseTestCase.setUp(self)
2166

    
2167
        if not OTHER_ACCOUNTS:
2168
            raise Warning('No other accounts avalaible for running this test.')
2169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2390
class TestUsageFreeVersioningAutoContainerPolicy(BaseTestCase):
2391
    """ Challenge free version accounting
2392
       in a container with auto versioning policy
2393

2394
       In case of unknown server version accounting policy or
2395
       debit version accounting policy
2396
       enforce all test cases to return immediately
2397
    """
2398
    def setUp(self):
2399
        BaseTestCase.setUp(self)
2400

    
2401
        # TODO Only works if tests are running in the same host with the server. Add support for remote server
2402
        self.stop_execution = False
2403
        try:
2404
            from pithos.api.settings import BACKEND_FREE_VERSIONING
2405
        except ImportError:
2406
            print 'Unable to execute the test: unknown version accounting policy'
2407
            self.stop_execution = True
2408
            return
2409
        else:
2410
            if not BACKEND_FREE_VERSIONING:
2411
                print 'Unable to execute the test: no free version accounting policy'
2412
                self.stop_execution = True
2413
                return
2414

    
2415
        self.create_container('container')
2416
        meta = self.client.retrieve_account_metadata()
2417
        self.initial_usage = int(meta['x-account-bytes-used'])
2418
        print 'Current account usage: %d' % self.initial_usage
2419

    
2420
        self.usage = self.initial_usage
2421
        #self.mtime = {}
2422
        l =  (50, 100, 150, 70, 80)
2423
        self.curr_acc_usage = self.initial_usage
2424
        for i, length in list(enumerate(l)):
2425
            self.upload_random_data('container', 'object', length=length)
2426
            meta = self.client.retrieve_account_metadata()
2427
            self.usage = int(meta['x-account-bytes-used'])
2428
            print 'Current account usage: %d' % self.usage
2429
            self.curr_acc_usage = self.initial_usage + length
2430
            self.assertEqual(self.usage, self.curr_acc_usage)
2431

    
2432
            #t = datetime.datetime.utcnow()
2433
            #self.mtime[i] = int(_time.mktime(t.timetuple()))
2434
            _time.sleep(1)
2435

    
2436
        versions = self.client.retrieve_object_versionlist(
2437
            'container', 'object'
2438
        )['versions']
2439
        self.mtime = [int(i[1]) for i in versions]
2440

    
2441
    def create_container(self, cname):
2442
        self.client.create_container(cname)
2443

    
2444
    def _test_delete_object_container(self):
2445
        """
2446
            assume account usage = 0
2447
            scenario:
2448
                upload object (length=50)
2449
                update object (length=100)
2450
                update object (length=150)
2451
                update object (length=70)
2452
                update object (length=80)
2453
                delete object
2454
                delete container
2455
            account usage sequel: 50|100|150|70|80|0|0
2456
        """
2457
        if self.stop_execution:
2458
            return
2459

    
2460
        self.client.delete_object('container', 'object')
2461
        meta = self.client.retrieve_account_metadata()
2462
        self.usage = int(meta['x-account-bytes-used'])
2463
        print 'Current account usage: %d' % self.usage
2464
        self.assertEqual(self.usage, self.initial_usage)
2465

    
2466
        self.client.delete_container('container')
2467
        meta = self.client.retrieve_account_metadata()
2468
        self.usage = int(meta['x-account-bytes-used'])
2469
        print 'Current account usage: %d' % self.usage
2470
        self.assertEqual(self.usage, self.initial_usage)
2471

    
2472
    def _test_purge_delete_object(self):
2473
        """
2474
            assume account usage = 0
2475
            scenario:
2476
                upload object (length=50)
2477
                update object (length=100)
2478
                update object (length=150)
2479
                update object (length=70)
2480
                update object (length=80)
2481
                delete object history
2482
                delete object
2483
                delete container
2484
            account usage sequel: 50|100|150|70|80|80|0|0
2485
        """
2486
        if self.stop_execution:
2487
            return
2488

    
2489
        # purge some object history
2490
        i = random.randrange(len(self.mtime))
2491
        self.client.delete_object('container', 'object', until=self.mtime[i])
2492
        meta = self.client.retrieve_account_metadata()
2493
        self.usage = int(meta['x-account-bytes-used'])
2494
        print 'Current account usage: %d' % self.usage
2495
        self.assertEqual(self.usage, self.curr_acc_usage)
2496

    
2497
        self.client.delete_object('container', 'object')
2498
        meta = self.client.retrieve_account_metadata()
2499
        self.usage = int(meta['x-account-bytes-used'])
2500
        print 'Current account usage: %d' % self.usage
2501
        self.assertEqual(self.usage, self.initial_usage)
2502

    
2503
        self.client.delete_container('container')
2504
        meta = self.client.retrieve_account_metadata()
2505
        self.usage = int(meta['x-account-bytes-used'])
2506
        print 'Current account usage: %d' % self.usage
2507
        self.assertEqual(self.usage, self.initial_usage)
2508

    
2509
    def _test_delete_object_purge_container_history(self):
2510
        """
2511
            assume account usage = 0
2512
            scenario:
2513
                upload object (length=50)
2514
                update object (length=100)
2515
                update object (length=150)
2516
                update object (length=70)
2517
                update object (length=80)
2518
                delete object
2519
                delete container history
2520
                delete container
2521
            account usage sequel: 50|100|150|70|80|0|0|0
2522
        """
2523
        if self.stop_execution:
2524
            return
2525

    
2526
        self.client.delete_object('container', 'object')
2527
        meta = self.client.retrieve_account_metadata()
2528
        self.usage = int(meta['x-account-bytes-used'])
2529
        print 'Current account usage: %d' % self.usage
2530
        self.assertEqual(self.usage,
2531
                         self.initial_usage)
2532

    
2533
        # purge some container history
2534
        i = random.randrange(len(self.mtime))
2535
        self.client.delete_container('container', until=self.mtime[i])
2536
        meta = self.client.retrieve_account_metadata()
2537
        self.usage = int(meta['x-account-bytes-used'])
2538
        print 'Current account usage: %d' % self.usage
2539
        self.assertEqual(self.usage, self.initial_usage)
2540

    
2541
        self.client.delete_container('container')
2542
        meta = self.client.retrieve_account_metadata()
2543
        self.usage = int(meta['x-account-bytes-used'])
2544
        print 'Current account usage: %d' % self.usage
2545
        self.assertEqual(self.usage, self.initial_usage)
2546

    
2547
    def _test_purge_container_delete_object(self):
2548
        """
2549
            assume account usage = 0
2550
            scenario:
2551
                upload object (length=50)
2552
                update object (length=100)
2553
                update object (length=150)
2554
                update object (length=70)
2555
                update object (length=80)
2556
                delete container history
2557
                delete object
2558
                delete container
2559
            account usage sequel: 50|100|150|70|80|80|0|0
2560
        """
2561
        if self.stop_execution:
2562
            return
2563

    
2564
        # purge some container history
2565
        i = random.randrange(len(self.mtime))
2566
        self.client.delete_container('container', until=self.mtime[i])
2567
        meta = self.client.retrieve_account_metadata()
2568
        self.usage = int(meta['x-account-bytes-used'])
2569
        print 'Current account usage: %d' % self.usage
2570
        self.assertEqual( self.usage, self.curr_acc_usage)
2571

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

    
2578
        self.client.delete_container('container')
2579
        meta = self.client.retrieve_account_metadata()
2580
        self.usage = int(meta['x-account-bytes-used'])
2581
        print 'Current account usage: %d' % self.usage
2582
        self.assertEqual(self.usage, self.initial_usage)
2583

    
2584
    def _test_successive_purging(self):
2585
        """
2586
            assume account usage = 0
2587
            scenario:
2588
                upload object (length=50)
2589
                update object (length=100)
2590
                update object (length=150)
2591
                update object (length=70)
2592
                update object (length=80)
2593
                delete earlier 2 object history versions
2594
                delete next earlier object history version
2595
                delete rest object history and current version
2596
                delete object (404 status)
2597
                delete container
2598
            account usage sequel: 50|100|150|70|80|80|80|0|0|0
2599
        """
2600
        if self.stop_execution:
2601
            return
2602

    
2603
        # purge some object history
2604
        i = random.randrange(len(self.mtime))
2605
        self.client.delete_object('container', 'object', until=self.mtime[i])
2606
        meta = self.client.retrieve_account_metadata()
2607
        self.usage = int(meta['x-account-bytes-used'])
2608
        print 'Current account usage: %d' % self.usage
2609
        self.assertEqual(self.usage, self.curr_acc_usage)
2610

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

    
2619
        # purge some more object history
2620
        i = random.randrange(len(self.mtime))
2621
        self.client.delete_object('container', 'object', until=self.mtime[i])
2622
        meta = self.client.retrieve_account_metadata()
2623
        self.usage = int(meta['x-account-bytes-used'])
2624
        print 'Current account usage: %d' % self.usage
2625
        self.assertEqual(self.usage, self.curr_acc_usage)
2626

    
2627
        # purge some object history and current
2628
        t = datetime.datetime.utcnow()
2629
        now = int(_time.mktime(t.timetuple()))
2630
        self.client.delete_object('container', 'object', until=now)
2631
        meta = self.client.retrieve_account_metadata()
2632
        self.usage = int(meta['x-account-bytes-used'])
2633
        print 'Current account usage: %d' % self.usage
2634
        self.assertEqual(self.usage, self.initial_usage)
2635

    
2636
        # try to delete object and assert object is not found
2637
        self.assert_raises_fault(
2638
            404, self.client.delete_object, 'container', 'object'
2639
        )
2640

    
2641
        meta = self.client.retrieve_account_metadata()
2642
        self.usage = int(meta['x-account-bytes-used'])
2643
        print 'Current account usage: %d' % self.usage
2644
        self.assertEqual(self.usage, self.initial_usage)
2645

    
2646
        self.client.delete_container('container')
2647
        meta = self.client.retrieve_account_metadata()
2648
        self.usage = int(meta['x-account-bytes-used'])
2649
        print 'Current account usage: %d' % self.usage
2650
        self.assertEqual(self.usage, self.initial_usage)
2651

    
2652
    def _test_delete_object_empty_container_content(self):
2653
        """
2654
            assume account usage = 0
2655
            scenario:
2656
                upload object (length=50)
2657
                update object (length=100)
2658
                update object (length=150)
2659
                update object (length=70)
2660
                update object (length=80)
2661
                delete object
2662
                delete container contents
2663
                delete container
2664
            account usage sequel: 50|100|150|70|80|0|0|0
2665
        """
2666
        if self.stop_execution:
2667
            return
2668

    
2669
        self.client.delete_object('container', 'object')
2670

    
2671
        meta = self.client.retrieve_account_metadata()
2672
        self.usage = int(meta['x-account-bytes-used'])
2673
        print 'Current account usage: %d' % self.usage
2674
        self.assertEqual(self.usage, self.initial_usage)
2675

    
2676
        self.client.delete_container('container', delimiter='/')
2677
        meta = self.client.retrieve_account_metadata()
2678
        self.usage = int(meta['x-account-bytes-used'])
2679
        print 'Current account usage: %d' % self.usage
2680
        self.assertEqual(self.usage, self.initial_usage)
2681

    
2682
        self.client.delete_container('container')
2683
        meta = self.client.retrieve_account_metadata()
2684
        self.usage = int(meta['x-account-bytes-used'])
2685
        print 'Current account usage: %d' % self.usage
2686
        self.assertEqual(self.usage, self.initial_usage)
2687

    
2688
    def _test_delete_container_content(self):
2689
        """
2690
            assume account usage = 0
2691
            scenario:
2692
                upload object (length=50)
2693
                update object (length=100)
2694
                update object (length=150)
2695
                update object (length=70)
2696
                update object (length=80)
2697
                delete container contents
2698
                delete container
2699
            account usage sequel: 50|100|150|70|80|0|0
2700
        """
2701
        if self.stop_execution:
2702
            return
2703

    
2704
        self.client.delete_container('container', delimiter='/')
2705
        meta = self.client.retrieve_account_metadata()
2706
        self.usage = int(meta['x-account-bytes-used'])
2707
        print 'Current account usage: %d' % self.usage
2708
        self.assertEqual(self.usage, self.initial_usage)
2709

    
2710
        self.client.delete_container('container')
2711
        meta = self.client.retrieve_account_metadata()
2712
        self.usage = int(meta['x-account-bytes-used'])
2713
        print 'Current account usage: %d' % self.usage
2714
        self.assertEqual(self.usage, self.initial_usage)
2715

    
2716

    
2717
class TestUsageFreeVersioningNoneContainerPolicy(
2718
    TestUsageFreeVersioningAutoContainerPolicy):
2719
    """ Challenge free version accounting
2720
       in a container with none versioning policy
2721

2722
       In case of unknown server version accounting policy or
2723
       debit version accounting policy
2724
       enforce all test cases to return immediately
2725
    """
2726
    def create_container(self, cname):
2727
        self.client.create_container(cname,
2728
                                     policies={'versioning':'none'})
2729

    
2730
class TestUsageDebitVersioningAutoContainerPolicy(BaseTestCase):
2731
    """ Challenge debit version accounting
2732
       in a container with auto versioning policy
2733

2734
       In case of unknown server version accounting policy or
2735
       free version accounting policy
2736
       enforce all test cases to return immediately
2737
    """
2738
    def setUp(self):
2739
        BaseTestCase.setUp(self)
2740

    
2741
        self.stop_execution = False
2742
        try:
2743
            from pithos.api.settings import BACKEND_FREE_VERSIONING
2744
        except ImportError:
2745
            print 'Unable to execute the test: unknown version accounting policy'
2746
            self.stop_execution = True
2747
            return
2748
        else:
2749
            if BACKEND_FREE_VERSIONING:
2750
                print 'Unable to execute the test: free version accounting policy'
2751
                self.stop_execution = True
2752
                return
2753

    
2754
        self.create_container('container')
2755
        meta = self.client.retrieve_account_metadata()
2756
        self.initial_usage = int(meta['x-account-bytes-used'])
2757
        print 'Current account usage: %d' % self.initial_usage
2758

    
2759
        self.usage = self.initial_usage
2760
        self.l = (50, 100, 150, 70, 80)
2761
        self.curr_acc_usage = self.initial_usage
2762
        for i, length in list(enumerate(self.l)):
2763
            self.upload_random_data('container', 'object', length=length)
2764
            meta = self.client.retrieve_account_metadata()
2765
            self.usage = int(meta['x-account-bytes-used'])
2766
            print 'Current account usage: %d' % self.usage
2767
            self.curr_acc_usage += length
2768
            self.assertEqual(self.usage, self.curr_acc_usage)
2769
            _time.sleep(1)
2770

    
2771
        versions = self.client.retrieve_object_versionlist(
2772
            'container', 'object'
2773
        )['versions']
2774
        self.mtime = [int(i[1]) for i in versions]
2775

    
2776
    def create_container(self, cname):
2777
        self.client.create_container(cname)
2778

    
2779
    def _test_delete_object_container(self):
2780
        """
2781
            assume account usage = 0
2782
            scenario:
2783
                upload object (length=50)
2784
                update object (length=100)
2785
                update object (length=150)
2786
                update object (length=70)
2787
                update object (length=80)
2788
                delete object
2789
                delete container
2790
            account usage sequel: 50|150|300|370|450|450|0
2791
        """
2792
        if self.stop_execution:
2793
            return
2794

    
2795
        self.client.delete_object('container', 'object')
2796
        meta = self.client.retrieve_account_metadata()
2797
        self.usage = int(meta['x-account-bytes-used'])
2798
        print 'Current account usage: %d' % self.usage
2799
        self.assertEqual(self.usage, self.initial_usage + sum(self.l))
2800

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

    
2807
    def _test_purge_delete_object(self):
2808
        """
2809
            assume account usage = 0
2810
            scenario:
2811
                upload object (length=50)
2812
                update object (length=100)
2813
                update object (length=150)
2814
                update object (length=70)
2815
                update object (length=80)
2816
                delete 3 earlier object versions
2817
                delete object
2818
                delete container
2819
            account usage sequel: 50|150|300|370|450|150|150|0
2820
        """
2821
        if self.stop_execution:
2822
            return
2823

    
2824
        # purge some object history
2825
        i = 3
2826
        self.client.delete_object('container', 'object', until=self.mtime[i])
2827
        meta = self.client.retrieve_account_metadata()
2828
        self.usage = int(meta['x-account-bytes-used'])
2829
        print 'Current account usage: %d' % self.usage
2830
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2831

    
2832
        self.client.delete_object('container', 'object')
2833
        meta = self.client.retrieve_account_metadata()
2834
        self.usage = int(meta['x-account-bytes-used'])
2835
        print 'Current account usage: %d' % self.usage
2836
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2837

    
2838
        self.client.delete_container('container')
2839
        meta = self.client.retrieve_account_metadata()
2840
        self.usage = int(meta['x-account-bytes-used'])
2841
        print 'Current account usage: %d' % self.usage
2842
        self.assertEqual(self.usage, self.initial_usage)
2843

    
2844
    def _test_delete_object_purge_container_history(self):
2845
        """
2846
            assume account usage = 0
2847
            scenario:
2848
                upload object (length=50)
2849
                update object (length=100)
2850
                update object (length=150)
2851
                update object (length=70)
2852
                update object (length=80)
2853
                delete object
2854
                delete container history (3 earlier object versions)
2855
                delete container
2856
            account usage sequel: 50|150|300|370|450|450|150|0
2857
        """
2858
        if self.stop_execution:
2859
            return
2860

    
2861
        self.client.delete_object('container', 'object')
2862
        meta = self.client.retrieve_account_metadata()
2863
        self.usage = int(meta['x-account-bytes-used'])
2864
        print 'Current account usage: %d' % self.usage
2865
        self.assertEqual(self.usage, self.curr_acc_usage)
2866

    
2867
        # purge some container history
2868
        i = 3
2869
        self.client.delete_container('container', until=self.mtime[i])
2870
        meta = self.client.retrieve_account_metadata()
2871
        self.usage = int(meta['x-account-bytes-used'])
2872
        print 'Current account usage: %d' % self.usage
2873
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2874

    
2875
        self.client.delete_container('container')
2876
        meta = self.client.retrieve_account_metadata()
2877
        self.usage = int(meta['x-account-bytes-used'])
2878
        print 'Current account usage: %d' % self.usage
2879
        self.assertEqual(self.usage, self.initial_usage)
2880

    
2881
    def _test_purge_container_delete_object(self):
2882
        """
2883
            assume account usage = 0
2884
            scenario:
2885
                upload object (length=50)
2886
                update object (length=100)
2887
                update object (length=150)
2888
                update object (length=70)
2889
                update object (length=80)
2890
                delete container history
2891
                delete object
2892
                delete container
2893
            account usage sequel: 50|150|300|370|450|150|150|0
2894
        """
2895
        if self.stop_execution:
2896
            return
2897

    
2898
        versions = self.client.retrieve_object_versionlist(
2899
            'container', 'object'
2900
        )['versions']
2901
        mtime = [i[1] for i in versions]
2902

    
2903
        # purge some container history
2904
        i = 3
2905
        until = int(mtime[i])
2906
        self.client.delete_container('container', until=until)
2907
        meta = self.client.retrieve_account_metadata()
2908
        self.usage = int(meta['x-account-bytes-used'])
2909
        print 'Current account usage: %d' % self.usage
2910
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2911

    
2912
        self.client.delete_object('container', 'object')
2913
        meta = self.client.retrieve_account_metadata()
2914
        self.usage = int(meta['x-account-bytes-used'])
2915
        print 'Current account usage: %d' % self.usage
2916
        self.assertEqual(self.usage, self.curr_acc_usage - sum(self.l[:i]))
2917

    
2918

    
2919
        self.client.delete_container('container')
2920
        meta = self.client.retrieve_account_metadata()
2921
        self.usage = int(meta['x-account-bytes-used'])
2922
        print 'Current account usage: %d' % self.usage
2923
        self.assertEqual(self.usage, self.initial_usage)
2924

    
2925
    def _test_successive_purging(self):
2926
        """
2927
            assume account usage = 0
2928
            scenario:
2929
                upload object (length=50)
2930
                update object (length=100)
2931
                update object (length=150)
2932
                update object (length=70)
2933
                update object (length=80)
2934
                delete earlier 2 object history versions
2935
                delete next earlier object history version
2936
                delete rest object history and current version
2937
                delete object (404 status)
2938
                delete container
2939
            account usage sequel: 50|150|300|370|450|400|300|150|0|0
2940
        """
2941
        if self.stop_execution:
2942
            return
2943

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

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

    
2959
        # purge some container history
2960
        j = 2
2961
        until = int(mtime[j])
2962
        self.client.delete_container('container', until=until)
2963
        meta = self.client.retrieve_account_metadata()
2964
        self.usage = int(meta['x-account-bytes-used'])
2965
        print 'Current account usage: %d' % self.usage
2966
        self.curr_acc_usage = self.curr_acc_usage - sum(self.l[i:j])
2967
        self.assertEqual(self.usage, self.curr_acc_usage)
2968

    
2969
        # purge some more object history
2970
        k = 3
2971
        until = int(mtime[k])
2972
        self.client.delete_object('container', 'object', until=until)
2973
        meta = self.client.retrieve_account_metadata()
2974
        self.usage = int(meta['x-account-bytes-used'])
2975
        print 'Current account usage: %d' % self.usage
2976
        self.curr_acc_usage = self.curr_acc_usage - sum(self.l[j:k])
2977
        self.assertEqual(self.usage, self.curr_acc_usage)
2978

    
2979
        # purge some object history and current
2980
        t = datetime.datetime.utcnow()
2981
        now = int(_time.mktime(t.timetuple()))
2982
        self.client.delete_object('container', 'object', until=now)
2983
        meta = self.client.retrieve_account_metadata()
2984
        self.usage = int(meta['x-account-bytes-used'])
2985
        print 'Current account usage: %d' % self.usage
2986
        self.assertEqual(self.usage, self.initial_usage)
2987

    
2988
        # try to delete object and assert object is not found
2989
        self.assert_raises_fault(
2990
            404, self.client.delete_object, 'container', 'object'
2991
        )
2992

    
2993
        meta = self.client.retrieve_account_metadata()
2994
        self.usage = int(meta['x-account-bytes-used'])
2995
        print 'Current account usage: %d' % self.usage
2996
        self.assertEqual(self.usage, self.initial_usage)
2997

    
2998
        self.client.delete_container('container')
2999
        meta = self.client.retrieve_account_metadata()
3000
        self.usage = int(meta['x-account-bytes-used'])
3001
        print 'Current account usage: %d' % self.usage
3002
        self.assertEqual(self.usage, self.initial_usage)
3003

    
3004
    def _test_delete_object_empty_container_content(self):
3005
        """
3006
            assume account usage = 0
3007
            scenario:
3008
                upload object (length=50)
3009
                update object (length=100)
3010
                update object (length=150)
3011
                update object (length=70)
3012
                update object (length=80)
3013
                delete object
3014
                delete container contents
3015
                delete container
3016
            account usage sequel: 50|150|300|370|450|450|450|0
3017
        """
3018
        if self.stop_execution:
3019
            return
3020

    
3021
        self.client.delete_object('container', 'object')
3022

    
3023
        meta = self.client.retrieve_account_metadata()
3024
        self.usage = int(meta['x-account-bytes-used'])
3025
        print 'Current account usage: %d' % self.usage
3026
        self.assertEqual(self.usage, self.curr_acc_usage)
3027

    
3028
        self.client.delete_container('container', delimiter='/')
3029
        meta = self.client.retrieve_account_metadata()
3030
        self.usage = int(meta['x-account-bytes-used'])
3031
        print 'Current account usage: %d' % self.usage
3032
        self.assertEqual(self.usage, self.curr_acc_usage)
3033

    
3034
        self.client.delete_container('container')
3035
        meta = self.client.retrieve_account_metadata()
3036
        self.usage = int(meta['x-account-bytes-used'])
3037
        print 'Current account usage: %d' % self.usage
3038
        self.assertEqual(self.usage, self.initial_usage)
3039

    
3040
    def _test_delete_container_content(self):
3041
        """
3042
            assume account usage = 0
3043
            scenario:
3044
                upload object (length=50)
3045
                update object (length=100)
3046
                update object (length=150)
3047
                update object (length=70)
3048
                update object (length=80)
3049
                delete container contents
3050
                delete container
3051
            account usage sequel: 50|150|300|370|450|450|0
3052
        """
3053
        if self.stop_execution:
3054
            return
3055

    
3056
        self.client.delete_container('container', delimiter='/')
3057
        meta = self.client.retrieve_account_metadata()
3058
        self.usage = int(meta['x-account-bytes-used'])
3059
        print 'Current account usage: %d' % self.usage
3060
        self.assertEqual(self.usage, self.curr_acc_usage)
3061

    
3062
        self.client.delete_container('container')
3063
        meta = self.client.retrieve_account_metadata()
3064
        self.usage = int(meta['x-account-bytes-used'])
3065
        print 'Current account usage: %d' % self.usage
3066
        self.assertEqual(self.usage, self.initial_usage)
3067

    
3068
class TestUsageDebitVersioningNoneContainerPolicy(BaseTestCase):
3069
    """ Challenge debit version accounting
3070
       in a container with none versioning policy
3071

3072
       In case of unknown server version accounting policy or
3073
       free version accounting policy
3074
       enforce all test cases to return immediately
3075
    """
3076
    def setUp(self):
3077
        BaseTestCase.setUp(self)
3078

    
3079
        self.stop_execution = False
3080
        try:
3081
            from pithos.api.settings import BACKEND_FREE_VERSIONING
3082
        except ImportError:
3083
            print 'Unable to execute the test: unknown version accounting policy'
3084
            self.stop_execution = True
3085
            return
3086
        else:
3087
            if BACKEND_FREE_VERSIONING:
3088
                print 'Unable to execute the test: free version accounting policy'
3089
                self.stop_execution = True
3090
                return
3091

    
3092
        self.create_container('container')
3093
        meta = self.client.retrieve_account_metadata()
3094
        self.initial_usage = int(meta['x-account-bytes-used'])
3095
        print 'Current account usage: %d' % self.initial_usage
3096

    
3097
        l =  (50, 100, 150, 70, 80)
3098
        self.curr_acc_usage = self.initial_usage
3099
        for i, length in list(enumerate(l)):
3100
            self.upload_random_data('container', 'object', length=length)
3101
            meta = self.client.retrieve_account_metadata()
3102
            self.usage = int(meta['x-account-bytes-used'])
3103
            print 'Current account usage: %d' % self.usage
3104
            self.curr_acc_usage = self.initial_usage + length
3105
            self.assertEqual(self.usage, self.curr_acc_usage)
3106
            _time.sleep(1)
3107

    
3108
        versions = self.client.retrieve_object_versionlist(
3109
            'container', 'object'
3110
        )['versions']
3111
        self.mtime = [int(i[1]) for i in versions]
3112

    
3113
    def create_container(self, cname):
3114
        self.client.create_container(cname,
3115
                                     policies={'versioning':'none'})
3116

    
3117
    def _test_delete_object_container(self):
3118
        """
3119
            assume account usage = 0
3120
            scenario:
3121
                upload object (length=50)
3122
                update object (length=100)
3123
                update object (length=150)
3124
                update object (length=70)
3125
                update object (length=80)
3126
                delete object
3127
                delete container
3128
            account usage sequel: 50|100|150|70|80|0|0
3129
        """
3130
        if self.stop_execution:
3131
            return
3132

    
3133
        self.client.delete_object('container', 'object')
3134
        meta = self.client.retrieve_account_metadata()
3135
        self.usage = int(meta['x-account-bytes-used'])
3136
        print 'Current account usage: %d' % self.usage
3137
        self.assertEqual(self.usage, self.initial_usage)
3138

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

    
3145
    def _test_purge_delete_object(self):
3146
        """
3147
            assume account usage = 0
3148
            scenario:
3149
                upload object (length=50)
3150
                update object (length=100)
3151
                update object (length=150)
3152
                update object (length=70)
3153
                update object (length=80)
3154
                delete object history
3155
                delete object
3156
                delete container
3157
            account usage sequel: 50|100|150|70|80|80|0|0
3158
        """
3159
        if self.stop_execution:
3160
            return
3161

    
3162
        # purge some object history
3163
        self.client.delete_object('container', 'object', until=self.mtime[0])
3164
        meta = self.client.retrieve_account_metadata()
3165
        self.usage = int(meta['x-account-bytes-used'])
3166
        print 'Current account usage: %d' % self.usage
3167
        self.assertEqual(self.usage, self.curr_acc_usage)
3168

    
3169
        self.client.delete_object('container', 'object')
3170
        meta = self.client.retrieve_account_metadata()
3171
        self.usage = int(meta['x-account-bytes-used'])
3172
        print 'Current account usage: %d' % self.usage
3173
        self.assertEqual(self.usage, self.initial_usage)
3174

    
3175
        self.client.delete_container('container')
3176
        meta = self.client.retrieve_account_metadata()
3177
        self.usage = int(meta['x-account-bytes-used'])
3178
        print 'Current account usage: %d' % self.usage
3179
        self.assertEqual(self.usage, self.initial_usage)
3180

    
3181
    def _test_delete_object_purge_container_history(self):
3182
        """
3183
            assume account usage = 0
3184
            scenario:
3185
                upload object (length=50)
3186
                update object (length=100)
3187
                update object (length=150)
3188
                update object (length=70)
3189
                update object (length=80)
3190
                delete object
3191
                delete container history
3192
                delete container
3193
            account usage sequel: 50|100|150|70|80|0|0|0
3194
        """
3195
        if self.stop_execution:
3196
            return
3197

    
3198
        self.client.delete_object('container', 'object')
3199
        meta = self.client.retrieve_account_metadata()
3200
        self.usage = int(meta['x-account-bytes-used'])
3201
        print 'Current account usage: %d' % self.usage
3202
        self.assertEqual(self.usage, self.initial_usage)
3203

    
3204
        # purge some container history
3205
        self.client.delete_container('container', until=self.mtime[0])
3206
        meta = self.client.retrieve_account_metadata()
3207
        self.usage = int(meta['x-account-bytes-used'])
3208
        print 'Current account usage: %d' % self.usage
3209
        self.assertEqual(self.usage, self.initial_usage)
3210

    
3211
        self.client.delete_container('container')
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.initial_usage)
3216

    
3217
    def _test_purge_container_delete_object(self):
3218
        """
3219
            assume account usage = 0
3220
            scenario:
3221
                upload object (length=50)
3222
                update object (length=100)
3223
                update object (length=150)
3224
                update object (length=70)
3225
                update object (length=80)
3226
                delete container history
3227
                delete object
3228
                delete container
3229
            account usage sequel: 50|100|150|70|80|80|0|0
3230
        """
3231
        if self.stop_execution:
3232
            return
3233

    
3234
        # purge some container history
3235
        self.client.delete_container('container', until=self.mtime[0])
3236
        meta = self.client.retrieve_account_metadata()
3237
        self.usage = int(meta['x-account-bytes-used'])
3238
        print 'Current account usage: %d' % self.usage
3239
        self.assertEqual(self.usage, self.curr_acc_usage)
3240

    
3241
        self.client.delete_object('container', 'object')
3242
        meta = self.client.retrieve_account_metadata()
3243
        self.usage = int(meta['x-account-bytes-used'])
3244
        print 'Current account usage: %d' % self.usage
3245
        self.assertEqual(self.usage, self.initial_usage)
3246

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

    
3253
    def _test_successive_purging(self):
3254
        """
3255
            assume account usage = 0
3256
            scenario:
3257
                upload object (length=50)
3258
                update object (length=100)
3259
                update object (length=150)
3260
                update object (length=70)
3261
                update object (length=80)
3262
                delete earlier 2 object history versions
3263
                delete next earlier object history version
3264
                delete rest object history and current version
3265
                delete object (404 status)
3266
                delete container
3267
            account usage sequel: 50|100|150|70|80|80|80|80|0|0|0
3268
        """
3269
        if self.stop_execution:
3270
            return
3271

    
3272
        # purge some object history
3273
        i = random.randrange(len(self.mtime))
3274
        self.client.delete_object('container', 'object', until=self.mtime[i])
3275
        meta = self.client.retrieve_account_metadata()
3276
        self.usage = int(meta['x-account-bytes-used'])
3277
        print 'Current account usage: %d' % self.usage
3278
        self.assertEqual(self.usage, self.curr_acc_usage)
3279

    
3280
        # purge some object history and current
3281
        t = datetime.datetime.utcnow()
3282
        now = int(_time.mktime(t.timetuple()))
3283
        self.client.delete_object('container', 'object', until=now)
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.initial_usage)
3288

    
3289
        # try to delete object and assert object is not found
3290
        self.assert_raises_fault(
3291
            404, self.client.delete_object, 'container', 'object'
3292
        )
3293

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

    
3299
        self.client.delete_container('container')
3300
        meta = self.client.retrieve_account_metadata()
3301
        self.usage = int(meta['x-account-bytes-used'])
3302
        print 'Current account usage: %d' % self.usage
3303
        self.assertEqual(self.usage, self.initial_usage)
3304

    
3305
    def _test_delete_object_empty_container_content(self):
3306
        """
3307
            assume account usage = 0
3308
            scenario:
3309
                upload object (length=50)
3310
                update object (length=100)
3311
                update object (length=150)
3312
                update object (length=70)
3313
                update object (length=80)
3314
                delete object
3315
                delete container contents
3316
                delete container
3317
            account usage sequel: 50|100|150|70|80|0|0|0
3318
        """
3319
        if self.stop_execution:
3320
            return
3321

    
3322
        self.client.delete_object('container', 'object')
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.initial_usage)
3327

    
3328
        self.client.delete_container('container', delimiter='/')
3329
        meta = self.client.retrieve_account_metadata()
3330
        self.usage = int(meta['x-account-bytes-used'])
3331
        print 'Current account usage: %d' % self.usage
3332
        self.assertEqual(self.usage, self.initial_usage)
3333

    
3334
        self.client.delete_container('container')
3335
        meta = self.client.retrieve_account_metadata()
3336
        self.usage = int(meta['x-account-bytes-used'])
3337
        print 'Current account usage: %d' % self.usage
3338
        self.assertEqual(self.usage, self.initial_usage)
3339

    
3340
    def _test_delete_container_content(self):
3341
        """
3342
            assume account usage = 0
3343
            scenario:
3344
                upload object (length=50)
3345
                update object (length=100)
3346
                update object (length=150)
3347
                update object (length=70)
3348
                update object (length=80)
3349
                delete container contents
3350
                delete container
3351
            account usage sequel: 50|100|150|70|80|0|0
3352
        """
3353
        if self.stop_execution:
3354
            return
3355

    
3356
        self.client.delete_container('container', delimiter='/')
3357
        meta = self.client.retrieve_account_metadata()
3358
        self.usage = int(meta['x-account-bytes-used'])
3359
        print 'Current account usage: %d' % self.usage
3360
        self.assertEqual(self.usage, self.initial_usage)
3361

    
3362
        self.client.delete_container('container')
3363
        meta = self.client.retrieve_account_metadata()
3364
        self.usage = int(meta['x-account-bytes-used'])
3365
        print 'Current account usage: %d' % self.usage
3366
        self.assertEqual(self.usage, self.initial_usage)
3367

    
3368
class AssertUUidInvariant(object):
3369
    def __init__(self, callable, *args, **kwargs):
3370
        self.callable = callable
3371
        self.args = args
3372
        self.kwargs = kwargs
3373

    
3374
    def __enter__(self):
3375
        self.map = self.callable(*self.args, **self.kwargs)
3376
        assert('x-object-uuid' in self.map)
3377
        self.uuid = self.map['x-object-uuid']
3378
        return self.map
3379

    
3380
    def __exit__(self, type, value, tb):
3381
        map = self.callable(*self.args, **self.kwargs)
3382
        assert('x-object-uuid' in self.map)
3383
        uuid = map['x-object-uuid']
3384
        assert(uuid == self.uuid)
3385

    
3386
class AssertMappingInvariant(object):
3387
    def __init__(self, callable, *args, **kwargs):
3388
        self.callable = callable
3389
        self.args = args
3390
        self.kwargs = kwargs
3391

    
3392
    def __enter__(self):
3393
        self.map = self.callable(*self.args, **self.kwargs)
3394
        return self.map
3395

    
3396
    def __exit__(self, type, value, tb):
3397
        map = self.callable(*self.args, **self.kwargs)
3398
        for k, v in self.map.items():
3399
            if is_date(v):
3400
                continue
3401
            assert(k in map)
3402
            assert v == map[k]
3403

    
3404
class AssertContentInvariant(object):
3405
    def __init__(self, callable, *args, **kwargs):
3406
        self.callable = callable
3407
        self.args = args
3408
        self.kwargs = kwargs
3409

    
3410
    def __enter__(self):
3411
        self.content = self.callable(*self.args, **self.kwargs)[2]
3412
        return self.content
3413

    
3414
    def __exit__(self, type, value, tb):
3415
        content = self.callable(*self.args, **self.kwargs)[2]
3416
        assert self.content == content
3417

    
3418
def get_content_splitted(response):
3419
    if response:
3420
        return response.content.split('\n')
3421

    
3422
def compute_md5_hash(data):
3423
    md5 = hashlib.md5()
3424
    offset = 0
3425
    md5.update(data)
3426
    return md5.hexdigest().lower()
3427

    
3428
def compute_block_hash(data, algorithm):
3429
    h = hashlib.new(algorithm)
3430
    h.update(data.rstrip('\x00'))
3431
    return h.hexdigest()
3432

    
3433
def get_random_data(length=500):
3434
    char_set = string.ascii_uppercase + string.digits
3435
    return ''.join(random.choice(char_set) for x in xrange(length))
3436

    
3437
def is_date(date):
3438
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
3439
    __D = r'(?P<day>\d{2})'
3440
    __D2 = r'(?P<day>[ \d]\d)'
3441
    __M = r'(?P<mon>\w{3})'
3442
    __Y = r'(?P<year>\d{4})'
3443
    __Y2 = r'(?P<year>\d{2})'
3444
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
3445
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
3446
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
3447
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
3448
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
3449
        m = regex.match(date)
3450
        if m is not None:
3451
            return True
3452
    return False
3453

    
3454
def strnextling(prefix):
3455
    """Return the first unicode string
3456
       greater than but not starting with given prefix.
3457
       strnextling('hello') -> 'hellp'
3458
    """
3459
    if not prefix:
3460
        ## all strings start with the null string,
3461
        ## therefore we have to approximate strnextling('')
3462
        ## with the last unicode character supported by python
3463
        ## 0x10ffff for wide (32-bit unicode) python builds
3464
        ## 0x00ffff for narrow (16-bit unicode) python builds
3465
        ## We will not autodetect. 0xffff is safe enough.
3466
        return unichr(0xffff)
3467
    s = prefix[:-1]
3468
    c = ord(prefix[-1])
3469
    if c >= 0xffff:
3470
        raise RuntimeError
3471
    s += unichr(c+1)
3472
    return s
3473

    
3474
o_names = ['kate.jpg',
3475
           'kate_beckinsale.jpg',
3476
           'How To Win Friends And Influence People.pdf',
3477
           'moms_birthday.jpg',
3478
           'poodle_strut.mov',
3479
           'Disturbed - Down With The Sickness.mp3',
3480
           'army_of_darkness.avi',
3481
           'the_mad.avi',
3482
           'photos/animals/dogs/poodle.jpg',
3483
           'photos/animals/dogs/terrier.jpg',
3484
           'photos/animals/cats/persian.jpg',
3485
           'photos/animals/cats/siamese.jpg',
3486
           'photos/plants/fern.jpg',
3487
           'photos/plants/rose.jpg',
3488
           'photos/me.jpg']
3489

    
3490

    
3491
def main():
3492
    if get_user() == 'test':
3493
        unittest.main(module='pithos.tools.test')
3494
    else:
3495
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
3496

    
3497

    
3498
if __name__ == "__main__":
3499
    main()
3500