Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ e46b2bcf

History | View | Annotate | Download (103.8 kB)

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

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

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

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

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

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

    
62
OTHER_ACCOUNTS = {
63
    '0001': 'verigak',
64
    '0002': 'chazapis',
65
    '0003': 'gtsouk',
66
    '0004': 'papagian',
67
    '0005': 'louridas',
68
    '0006': 'chstath',
69
    '0007': 'pkanavos',
70
    '0008': 'mvasilak',
71
    '0009': 'διογένης'}
72

    
73
class BaseTestCase(unittest.TestCase):
74
    #TODO unauthorized request
75
    def setUp(self):
76
        self.client = Pithos_Client(get_url(), get_auth(), get_user())
77
        self._clean_account()
78
        self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
79

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

    
83
        #keep track of initial account meta
84
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
85

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
248
            obj['meta'] = args
249

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

    
254
            return obj
255
        except IOError:
256
            return
257

    
258
class AccountHead(BaseTestCase):
259
    def setUp(self):
260
        BaseTestCase.setUp(self)
261
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
262
        for item in self.containers:
263
            self.client.create_container(item)
264

    
265
        meta = {'foo':'bar'}
266
        self.client.update_account_metadata(**meta)
267
        #self.updated_meta = self.initial_meta.update(meta)
268

    
269
    def test_get_account_meta(self):
270
        meta = self.client.retrieve_account_metadata()
271

    
272
        containers = self.client.list_containers()
273
        l = str(len(containers))
274
        self.assertEqual(meta['x-account-container-count'], l)
275
        size = 0
276
        for c in containers:
277
            m = self.client.retrieve_container_metadata(c)
278
            size = size + int(m['x-container-bytes-used'])
279
        self.assertEqual(meta['x-account-bytes-used'], str(size))
280

    
281
    def test_get_account_403(self):
282
        self.assert_raises_fault(403,
283
                                 self.invalid_client.retrieve_account_metadata)
284

    
285
    def test_get_account_meta_until(self):
286
        t = datetime.datetime.utcnow()
287
        past = t - datetime.timedelta(minutes=-15)
288
        past = int(_time.mktime(past.timetuple()))
289

    
290
        meta = {'premium':True}
291
        self.client.update_account_metadata(**meta)
292
        meta = self.client.retrieve_account_metadata(restricted=True,
293
                                                     until=past)
294
        self.assertTrue('premium' not in meta)
295

    
296
        meta = self.client.retrieve_account_metadata(restricted=True)
297
        self.assertTrue('premium' in meta)
298

    
299
    def test_get_account_meta_until_invalid_date(self):
300
        meta = {'premium':True}
301
        self.client.update_account_metadata(**meta)
302
        meta = self.client.retrieve_account_metadata(restricted=True,
303
                                                     until='kshfksfh')
304
        self.assertTrue('premium' in meta)
305

    
306
class AccountGet(BaseTestCase):
307
    def setUp(self):
308
        BaseTestCase.setUp(self)
309
        #create some containers
310
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
311
        for item in self.containers:
312
            self.client.create_container(item)
313

    
314
    def test_list(self):
315
        #list containers
316
        containers = self.client.list_containers()
317
        self.assertEquals(self.containers, containers)
318

    
319
    def test_list_403(self):
320
        self.assert_raises_fault(403, self.invalid_client.list_containers)
321

    
322
    def test_list_with_limit(self):
323
        limit = 2
324
        containers = self.client.list_containers(limit=limit)
325
        self.assertEquals(len(containers), limit)
326
        self.assertEquals(self.containers[:2], containers)
327

    
328
    def test_list_with_marker(self):
329
        l = 2
330
        m = 'bananas'
331
        containers = self.client.list_containers(limit=l, marker=m)
332
        i = self.containers.index(m) + 1
333
        self.assertEquals(self.containers[i:(i+l)], containers)
334

    
335
        m = 'oranges'
336
        containers = self.client.list_containers(limit=l, marker=m)
337
        i = self.containers.index(m) + 1
338
        self.assertEquals(self.containers[i:(i+l)], containers)
339

    
340
    def test_list_json_with_marker(self):
341
        l = 2
342
        m = 'bananas'
343
        containers = self.client.list_containers(limit=l, marker=m, format='json')
344
        self.assert_extended(containers, 'json', 'container', l)
345
        self.assertEqual(containers[0]['name'], 'kiwis')
346
        self.assertEqual(containers[1]['name'], 'oranges')
347

    
348
    def test_list_xml_with_marker(self):
349
        l = 2
350
        m = 'oranges'
351
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
352
        self.assert_extended(xml, 'xml', 'container', l)
353
        nodes = xml.getElementsByTagName('name')
354
        self.assertEqual(len(nodes), 1)
355
        self.assertEqual(nodes[0].childNodes[0].data, 'pears')
356

    
357
    def test_if_modified_since(self):
358
        t = datetime.datetime.utcnow()
359
        t2 = t - datetime.timedelta(minutes=10)
360

    
361
        #add a new container
362
        self.client.create_container('dummy')
363

    
364
        for f in DATE_FORMATS:
365
            past = t2.strftime(f)
366
            try:
367
                c = self.client.list_containers(if_modified_since=past)
368
                self.assertEqual(len(c), len(self.containers) + 1)
369
            except Fault, f:
370
                self.failIf(f.status == 304) #fail if not modified
371
        
372

    
373
    def test_if_modified_since_invalid_date(self):
374
        c = self.client.list_containers(if_modified_since='')
375
        self.assertEqual(len(c), len(self.containers))
376

    
377
    def test_if_not_modified_since(self):
378
        now = datetime.datetime.utcnow()
379
        since = now + datetime.timedelta(1)
380

    
381
        for f in DATE_FORMATS:
382
            args = {'if_modified_since':'%s' %since.strftime(f)}
383

    
384
            #assert not modified
385
            self.assert_raises_fault(304, self.client.list_containers, **args)
386

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

    
391
        for f in DATE_FORMATS:
392
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
393

    
394
            #assert success
395
            self.assertEqual(self.containers, c)
396

    
397
    def test_if_unmodified_since_precondition_failed(self):
398
        t = datetime.datetime.utcnow()
399
        t2 = t - datetime.timedelta(minutes=10)
400

    
401
        #add a new container
402
        self.client.create_container('dummy')
403

    
404
        for f in DATE_FORMATS:
405
            past = t2.strftime(f)
406

    
407
            args = {'if_unmodified_since':'%s' %past}
408

    
409
            #assert precondition failed
410
            self.assert_raises_fault(412, self.client.list_containers, **args)
411

    
412
class AccountPost(BaseTestCase):
413
    def setUp(self):
414
        BaseTestCase.setUp(self)
415
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
416
        for item in self.containers:
417
            self.client.create_container(item)
418

    
419
        meta = {'foo':'bar'}
420
        self.client.update_account_metadata(**meta)
421
        self.updated_meta = self.initial_meta.update(meta)
422

    
423
    def test_update_meta(self):
424
        with AssertMappingInvariant(self.client.retrieve_account_groups):
425
            meta = {'test':'test', 'tost':'tost'}
426
            self.client.update_account_metadata(**meta)
427

    
428
            meta.update(self.initial_meta)
429
            self.assertEqual(meta,
430
                             self.client.retrieve_account_metadata(
431
                                restricted=True))
432

    
433
    def test_invalid_account_update_meta(self):
434
        meta = {'test':'test', 'tost':'tost'}
435
        self.assert_raises_fault(403,
436
                                 self.invalid_client.update_account_metadata,
437
                                 **meta)
438

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

    
444
            meta = {'test':'test33'}
445
            self.client.reset_account_metadata(**meta)
446

    
447
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
448

    
449
    def test_delete_meta(self):
450
        with AssertMappingInvariant(self.client.retrieve_account_groups):
451
            meta = {'test':'test', 'tost':'tost'}
452
            self.client.update_account_metadata(**meta)
453

    
454
            self.client.delete_account_metadata(meta.keys())
455

    
456
            account_meta = self.client.retrieve_account_metadata(restricted=True)
457
            for m in meta:
458
                self.assertTrue(m not in account_meta.keys())
459

    
460
    def test_set_account_groups(self):
461
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
462
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
463
            self.client.set_account_groups(**groups)
464

    
465
            self.assertEqual(set(groups['pithosdev']),
466
                             set(self.client.retrieve_account_groups()['pithosdev']))
467

    
468
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
469
            self.client.set_account_groups(**more_groups)
470

    
471
            groups.update(more_groups)
472
            self.assertEqual(set(groups['clientsdev']),
473
                             set(self.client.retrieve_account_groups()['clientsdev']))
474

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

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

    
486
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
487
            self.client.reset_account_groups(**groups)
488

    
489
            self.assertEqual(set(groups['pithosdev'].split(',')),
490
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
491

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

    
498
            self.client.unset_account_groups(groups.keys())
499

    
500
            self.assertEqual({}, self.client.retrieve_account_groups())
501

    
502
class ContainerHead(BaseTestCase):
503
    def setUp(self):
504
        BaseTestCase.setUp(self)
505
        self.container = 'apples'
506
        self.client.create_container(self.container)
507

    
508
    def test_get_meta(self):
509
        meta = {'trash':'true'}
510
        t1 = datetime.datetime.utcnow()
511
        o = self.upload_random_data(self.container, o_names[0], **meta)
512
        if o:
513
            headers = self.client.retrieve_container_metadata(self.container)
514
            self.assertEqual(headers['x-container-object-count'], '1')
515
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
516
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
517
            delta = (t2 - t1)
518
            threashold = datetime.timedelta(seconds=1)
519
            self.assertTrue(delta < threashold)
520
            self.assertTrue(headers['x-container-object-meta'])
521
            self.assertTrue('Trash' in headers['x-container-object-meta'])
522

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

    
597
    def test_list_objects_containing_slash(self):
598
        self.client.create_container('test')
599
        self.upload_random_data('test', '/objectname')
600

    
601
        objects = self.client.list_objects('test')
602
        self.assertEqual(objects, ['/objectname'])
603

    
604
        objects = self.client.list_objects('test', format='json')
605
        self.assertEqual(objects[0]['name'], '/objectname')
606

    
607
        objects = self.client.list_objects('test', format='xml')
608
        self.assert_extended(objects, 'xml', 'object')
609
        node_name = objects.getElementsByTagName('name')[0]
610
        self.assertEqual(node_name.firstChild.data, '/objectname')
611

    
612
    def test_list_objects_with_limit_marker(self):
613
        objects = self.client.list_objects(self.container[0], limit=2)
614
        l = [elem['name'] for elem in self.obj[:8]]
615
        l.sort()
616
        self.assertEqual(objects, l[:2])
617

    
618
        markers = ['How To Win Friends And Influence People.pdf',
619
                   'moms_birthday.jpg']
620
        limit = 4
621
        for m in markers:
622
            objects = self.client.list_objects(self.container[0], limit=limit,
623
                                               marker=m)
624
            l = [elem['name'] for elem in self.obj[:8]]
625
            l.sort()
626
            start = l.index(m) + 1
627
            end = start + limit
628
            end = end if len(l) >= end else len(l)
629
            self.assertEqual(objects, l[start:end])
630

    
631
    #takes too long
632
    def _test_list_limit_exceeds(self):
633
        self.client.create_container('pithos')
634

    
635
        for i in range(10001):
636
            self.client.create_zero_length_object('pithos', i)
637

    
638
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
639

    
640
    def test_list_empty_params(self):
641
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
642
        if objects:
643
            objects = objects.strip().split('\n')
644
        self.assertEqual(objects,
645
                         self.client.list_objects(self.container[0]))
646

    
647
    def test_list_pseudo_hierarchical_folders(self):
648
        objects = self.client.list_objects(self.container[1], prefix='photos',
649
                                           delimiter='/')
650
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
651
                           'photos/plants/'], objects)
652

    
653
        objects = self.client.list_objects(self.container[1],
654
                                           prefix='photos/animals',
655
                                           delimiter='/')
656
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
657
        self.assertEquals(l, objects)
658

    
659
        objects = self.client.list_objects(self.container[1], path='photos')
660
        self.assertEquals(['photos/me.jpg'], objects)
661

    
662
    def test_extended_list_json(self):
663
        objects = self.client.list_objects(self.container[1], format='json',
664
                                           limit=2, prefix='photos/animals',
665
                                           delimiter='/')
666
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
667
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
668

    
669
    def test_extended_list_xml(self):
670
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
671
                                       prefix='photos', delimiter='/')
672
        self.assert_extended(xml, 'xml', 'object', size=4)
673
        dirs = xml.getElementsByTagName('subdir')
674
        self.assertEqual(len(dirs), 2)
675
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
676
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
677

    
678
        objects = xml.getElementsByTagName('name')
679
        self.assertEqual(len(objects), 1)
680
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
681

    
682
    def test_list_meta_double_matching(self):
683
        meta = {'quality':'aaa', 'stock':'true'}
684
        self.client.update_object_metadata(self.container[0],
685
                                           self.obj[0]['name'], **meta)
686
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
687
        self.assertEqual(len(obj), 1)
688
        self.assertTrue(obj, self.obj[0]['name'])
689

    
690
    def test_list_using_meta(self):
691
        meta = {'quality':'aaa'}
692
        for o in self.obj[:2]:
693
            self.client.update_object_metadata(self.container[0], o['name'],
694
                                               **meta)
695
        meta = {'stock':'true'}
696
        for o in self.obj[3:5]:
697
            self.client.update_object_metadata(self.container[0], o['name'],
698
                                               **meta)
699

    
700
        obj = self.client.list_objects(self.container[0], meta='Quality')
701
        self.assertEqual(len(obj), 2)
702
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
703

    
704
        # test case insensitive
705
        obj = self.client.list_objects(self.container[0], meta='quality')
706
        self.assertEqual(len(obj), 2)
707
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
708

    
709
        # test multiple matches
710
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
711
        self.assertEqual(len(obj), 4)
712
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
713

    
714
        # test non 1-1 multiple match
715
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
716
        self.assertEqual(len(obj), 2)
717
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
718

    
719
    def test_if_modified_since(self):
720
        t = datetime.datetime.utcnow()
721
        t2 = t - datetime.timedelta(minutes=10)
722

    
723
        #add a new object
724
        self.upload_random_data(self.container[0], o_names[0])
725

    
726
        for f in DATE_FORMATS:
727
            past = t2.strftime(f)
728
            try:
729
                o = self.client.list_objects(self.container[0],
730
                                            if_modified_since=past)
731
                self.assertEqual(o,
732
                                 self.client.list_objects(self.container[0]))
733
            except Fault, f:
734
                self.failIf(f.status == 304) #fail if not modified
735

    
736
    def test_if_modified_since_invalid_date(self):
737
        headers = {'if-modified-since':''}
738
        o = self.client.list_objects(self.container[0], if_modified_since='')
739
        self.assertEqual(o, self.client.list_objects(self.container[0]))
740

    
741
    def test_if_not_modified_since(self):
742
        now = datetime.datetime.utcnow()
743
        since = now + datetime.timedelta(1)
744

    
745
        for f in DATE_FORMATS:
746
            args = {'if_modified_since':'%s' %since.strftime(f)}
747

    
748
            #assert not modified
749
            self.assert_raises_fault(304, self.client.list_objects,
750
                                     self.container[0], **args)
751

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

    
756
        for f in DATE_FORMATS:
757
            obj = self.client.list_objects(self.container[0],
758
                                           if_unmodified_since=since.strftime(f))
759

    
760
            #assert unmodified
761
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
762

    
763
    def test_if_unmodified_since_precondition_failed(self):
764
        t = datetime.datetime.utcnow()
765
        t2 = t - datetime.timedelta(minutes=10)
766

    
767
        #add a new container
768
        self.client.create_container('dummy')
769

    
770
        for f in DATE_FORMATS:
771
            past = t2.strftime(f)
772

    
773
            args = {'if_unmodified_since':'%s' %past}
774

    
775
            #assert precondition failed
776
            self.assert_raises_fault(412, self.client.list_objects,
777
                                     self.container[0], **args)
778

    
779
class ContainerPut(BaseTestCase):
780
    def setUp(self):
781
        BaseTestCase.setUp(self)
782
        self.containers = ['c1', 'c2']
783

    
784
    def test_create(self):
785
        self.client.create_container(self.containers[0])
786
        containers = self.client.list_containers()
787
        self.assertTrue(self.containers[0] in containers)
788
        self.assert_container_exists(self.containers[0])
789

    
790
    def test_create_twice(self):
791
        self.client.create_container(self.containers[0])
792
        self.assertTrue(not self.client.create_container(self.containers[0]))
793

    
794
    def test_quota(self):
795
        self.client.create_container(self.containers[0])
796

    
797
        policy = {'quota':100}
798
        self.client.set_container_policies('c1', **policy)
799

    
800
        meta = self.client.retrieve_container_metadata('c1')
801
        self.assertTrue('x-container-policy-quota' in meta)
802
        self.assertEqual(meta['x-container-policy-quota'], '100')
803

    
804
        args = ['c1', 'o1']
805
        kwargs = {'length':101}
806
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
807

    
808
        #reset quota
809
        policy = {'quota':0}
810
        self.client.set_container_policies('c1', **policy)
811

    
812
class ContainerPost(BaseTestCase):
813
    def setUp(self):
814
        BaseTestCase.setUp(self)
815
        self.container = 'apples'
816
        self.client.create_container(self.container)
817

    
818
    def test_update_meta(self):
819
        meta = {'test':'test33',
820
                'tost':'tost22'}
821
        self.client.update_container_metadata(self.container, **meta)
822
        headers = self.client.retrieve_container_metadata(self.container)
823
        for k,v in meta.items():
824
            k = 'x-container-meta-%s' % k
825
            self.assertTrue(headers[k])
826
            self.assertEqual(headers[k], v)
827

    
828
class ContainerDelete(BaseTestCase):
829
    def setUp(self):
830
        BaseTestCase.setUp(self)
831
        self.containers = ['c1', 'c2']
832
        for c in self.containers:
833
            self.client.create_container(c)
834

    
835
    def test_delete(self):
836
        status = self.client.delete_container(self.containers[0])[0]
837
        self.assertEqual(status, 204)
838

    
839
    def test_delete_non_empty(self):
840
        self.upload_random_data(self.containers[1], o_names[0])
841
        self.assert_raises_fault(409, self.client.delete_container,
842
                                 self.containers[1])
843

    
844
    def test_delete_invalid(self):
845
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
846
    
847
    def test_delete_contents(self):
848
        self.client.create_folder(self.containers[0], 'folder-1')
849
        self.upload_random_data(self.containers[1], 'folder-1/%s' % o_names[0])
850
        self.client.create_folder(self.containers[0], 'folder-1/subfolder')
851
        self.client.create_folder(self.containers[0], 'folder-2/%s' % o_names[1])
852
                
853
        objects = self.client.list_objects(self.containers[0])
854
        self.client.delete_container(self.containers[0], delimiter='/')
855
        for o in objects:
856
            self.assert_object_not_exists(self.containers[0], o)
857
        self.assert_container_exists(self.containers[0])
858

    
859
class ObjectGet(BaseTestCase):
860
    def setUp(self):
861
        BaseTestCase.setUp(self)
862
        self.containers = ['c1', 'c2']
863
        #create some containers
864
        for c in self.containers:
865
            self.client.create_container(c)
866

    
867
        #upload a file
868
        names = ('obj1', 'obj2')
869
        self.objects = []
870
        for n in names:
871
            self.objects.append(self.upload_random_data(self.containers[1], n))
872

    
873
    def test_versions(self):
874
        c = self.containers[1]
875
        o = self.objects[0]
876
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
877
        self.assert_versionlist_structure(b)
878

    
879
        #update meta
880
        meta = {'quality':'AAA', 'stock':True}
881
        self.client.update_object_metadata(c, o['name'], **meta)
882

    
883
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
884
        self.assert_versionlist_structure(a)
885
        self.assertEqual(len(b)+1, len(a))
886
        self.assertEqual(b, a[:-1])
887

    
888
        #get exact previous version metadata
889
        v = a[-2][0]
890
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
891
                                                      restricted=True,
892
                                                      version=v)
893
        for k in meta.keys():
894
            self.assertTrue(k not in v_meta)
895

    
896
        #update obejct
897
        data = get_random_data()
898
        self.client.update_object(c, o['name'], StringIO(data))
899

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

    
905
        #get exact previous version
906
        v = aa[-3][0]
907
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
908
        self.assertEqual(o['data'], v_data)
909
        self.assertEqual(self.client.retrieve_object(c, o['name']),
910
                         '%s%s' %(v_data, data))
911

    
912
    def test_get(self):
913
        #perform get
914
        o = self.client.retrieve_object(self.containers[1],
915
                                        self.objects[0]['name'],
916
                                        self.objects[0]['meta'])
917
        self.assertEqual(o, self.objects[0]['data'])
918

    
919
    def test_objects_with_trailing_spaces(self):
920
        self.client.create_container('test')
921
        #create 'a' object
922
        self.upload_random_data('test', 'a')
923
        #look for 'a ' object
924
        self.assert_raises_fault(404, self.client.retrieve_object,
925
                                 'test', 'a ')
926

    
927
        #delete 'a' object
928
        self.client.delete_object('test', 'a')
929
        self.assert_raises_fault(404, self.client.retrieve_object,
930
                                 'test', 'a')
931

    
932
        #create 'a ' object
933
        self.upload_random_data('test', 'a ')
934
        #look for 'a' object
935
        self.assert_raises_fault(404, self.client.retrieve_object,
936
                                 'test', 'a')
937

    
938
    def test_get_invalid(self):
939
        self.assert_raises_fault(404, self.client.retrieve_object,
940
                                 self.containers[0], self.objects[0]['name'])
941

    
942
    def test_get_partial(self):
943
        #perform get with range
944
        status, headers, data = self.client.request_object(self.containers[1],
945
                                                            self.objects[0]['name'],
946
                                                            range='bytes=0-499')
947

    
948
        #assert successful partial content
949
        self.assertEqual(status, 206)
950

    
951
        #assert content-type
952
        self.assertEqual(headers['content-type'],
953
                         self.objects[0]['meta']['content_type'])
954

    
955
        #assert content length
956
        self.assertEqual(int(headers['content-length']), 500)
957

    
958
        #assert content
959
        self.assertEqual(self.objects[0]['data'][:500], data)
960

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

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

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

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

    
978
        #assert content
979
        self.assertTrue(self.objects[0]['data'][-500:], data)
980

    
981
    def test_get_rest(self):
982
        #perform get with range
983
        offset = len(self.objects[0]['data']) - 500
984
        status, headers, data = self.client.request_object(self.containers[1],
985
                                                self.objects[0]['name'],
986
                                                range='bytes=%s-' %offset)
987

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

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

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

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

    
1001
    def test_get_range_not_satisfiable(self):
1002
        #perform get with range
1003
        offset = len(self.objects[0]['data']) + 1
1004

    
1005
        #assert range not satisfiable
1006
        self.assert_raises_fault(416, self.client.retrieve_object,
1007
                                 self.containers[1], self.objects[0]['name'],
1008
                                 range='bytes=0-%s' %offset)
1009

    
1010
    def test_multiple_range(self):
1011
        #perform get with multiple range
1012
        ranges = ['0-499', '-500', '1000-']
1013
        bytes = 'bytes=%s' % ','.join(ranges)
1014
        status, headers, data = self.client.request_object(self.containers[1],
1015
                                                           self.objects[0]['name'],
1016
                                                           range=bytes)
1017

    
1018
        # assert partial content
1019
        self.assertEqual(status, 206)
1020

    
1021
        # assert Content-Type of the reply will be multipart/byteranges
1022
        self.assertTrue(headers['content-type'])
1023
        content_type_parts = headers['content-type'].split()
1024
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1025

    
1026
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1027
        cparts = data.split(boundary)[1:-1]
1028

    
1029
        # assert content parts are exactly 2
1030
        self.assertEqual(len(cparts), len(ranges))
1031

    
1032
        # for each content part assert headers
1033
        i = 0
1034
        for cpart in cparts:
1035
            content = cpart.split('\r\n')
1036
            headers = content[1:3]
1037
            content_range = headers[0].split(': ')
1038
            self.assertEqual(content_range[0], 'Content-Range')
1039

    
1040
            r = ranges[i].split('-')
1041
            if not r[0] and not r[1]:
1042
                pass
1043
            elif not r[0]:
1044
                start = len(self.objects[0]['data']) - int(r[1])
1045
                end = len(self.objects[0]['data'])
1046
            elif not r[1]:
1047
                start = int(r[0])
1048
                end = len(self.objects[0]['data'])
1049
            else:
1050
                start = int(r[0])
1051
                end = int(r[1]) + 1
1052
            fdata = self.objects[0]['data'][start:end]
1053
            sdata = '\r\n'.join(content[4:-1])
1054
            self.assertEqual(len(fdata), len(sdata))
1055
            self.assertEquals(fdata, sdata)
1056
            i+=1
1057

    
1058
    def test_multiple_range_not_satisfiable(self):
1059
        #perform get with multiple range
1060
        out_of_range = len(self.objects[0]['data']) + 1
1061
        ranges = ['0-499', '-500', '%d-' %out_of_range]
1062
        bytes = 'bytes=%s' % ','.join(ranges)
1063

    
1064
        # assert partial content
1065
        self.assert_raises_fault(416, self.client.retrieve_object,
1066
                                 self.containers[1],
1067
                                 self.objects[0]['name'], range=bytes)
1068

    
1069
    def test_get_with_if_match(self):
1070
        #perform get with If-Match
1071
        etag = self.objects[0]['hash']
1072
        status, headers, data = self.client.request_object(self.containers[1],
1073
                                                           self.objects[0]['name'],
1074
                                                           if_match=etag)
1075
        #assert get success
1076
        self.assertEqual(status, 200)
1077

    
1078
        #assert content-type
1079
        self.assertEqual(headers['content-type'],
1080
                         self.objects[0]['meta']['content_type'])
1081

    
1082
        #assert response content
1083
        self.assertEqual(self.objects[0]['data'], data)
1084

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

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

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

    
1101
    def test_get_with_multiple_if_match(self):
1102
        #perform get with If-Match
1103
        etags = [i['hash'] for i in self.objects if i]
1104
        etags = ','.join('"%s"' % etag for etag in etags)
1105
        status, headers, data = self.client.request_object(self.containers[1],
1106
                                                           self.objects[0]['name'],
1107
                                                           if_match=etags)
1108
        #assert get success
1109
        self.assertEqual(status, 200)
1110

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

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

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

    
1122
    def test_if_match_precondition_failed(self):
1123
        #assert precondition failed
1124
        self.assert_raises_fault(412, self.client.retrieve_object,
1125
                                 self.containers[1],
1126
                                 self.objects[0]['name'], if_match='123')
1127

    
1128
    def test_if_none_match(self):
1129
        #perform get with If-None-Match
1130
        status, headers, data = self.client.request_object(self.containers[1],
1131
                                                           self.objects[0]['name'],
1132
                                                           if_none_match='123')
1133

    
1134
        #assert get success
1135
        self.assertEqual(status, 200)
1136

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

    
1141
    def test_if_none_match(self):
1142
        #perform get with If-None-Match * and assert not modified
1143
        self.assert_raises_fault(304, self.client.retrieve_object,
1144
                                 self.containers[1],
1145
                                 self.objects[0]['name'],
1146
                                 if_none_match='*')
1147

    
1148
    def test_if_none_match_not_modified(self):
1149
        #perform get with If-None-Match and assert not modified
1150
        self.assert_raises_fault(304, self.client.retrieve_object,
1151
                                 self.containers[1],
1152
                                 self.objects[0]['name'],
1153
                                 if_none_match=self.objects[0]['hash'])
1154

    
1155
        meta = self.client.retrieve_object_metadata(self.containers[1],
1156
                                                    self.objects[0]['name'])
1157
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1158

    
1159
    def test_if_modified_since(self):
1160
        t = datetime.datetime.utcnow()
1161
        t2 = t - datetime.timedelta(minutes=10)
1162

    
1163
        #modify the object
1164
        self.upload_data(self.containers[1],
1165
                           self.objects[0]['name'],
1166
                           self.objects[0]['data'][:200])
1167

    
1168
        for f in DATE_FORMATS:
1169
            past = t2.strftime(f)
1170

    
1171
            headers = {'if-modified-since':'%s' %past}
1172
            try:
1173
                o = self.client.retrieve_object(self.containers[1],
1174
                                                self.objects[0]['name'],
1175
                                                if_modified_since=past)
1176
                self.assertEqual(o,
1177
                                 self.client.retrieve_object(self.containers[1],
1178
                                                             self.objects[0]['name']))
1179
            except Fault, f:
1180
                self.failIf(f.status == 304)
1181

    
1182
    def test_if_modified_since_invalid_date(self):
1183
        o = self.client.retrieve_object(self.containers[1],
1184
                                        self.objects[0]['name'],
1185
                                        if_modified_since='')
1186
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1187
                                                        self.objects[0]['name']))
1188

    
1189
    def test_if_not_modified_since(self):
1190
        now = datetime.datetime.utcnow()
1191
        since = now + datetime.timedelta(1)
1192

    
1193
        for f in DATE_FORMATS:
1194
            #assert not modified
1195
            self.assert_raises_fault(304, self.client.retrieve_object,
1196
                                     self.containers[1], self.objects[0]['name'],
1197
                                     if_modified_since=since.strftime(f))
1198

    
1199
    def test_if_unmodified_since(self):
1200
        now = datetime.datetime.utcnow()
1201
        since = now + datetime.timedelta(1)
1202

    
1203
        for f in DATE_FORMATS:
1204
            t = since.strftime(f)
1205
            status, headers, data = self.client.request_object(self.containers[1],
1206
                                                               self.objects[0]['name'],
1207
                                                               if_unmodified_since=t)
1208
            #assert success
1209
            self.assertEqual(status, 200)
1210
            self.assertEqual(self.objects[0]['data'], data)
1211

    
1212
            #assert content-type
1213
            self.assertEqual(headers['content-type'],
1214
                             self.objects[0]['meta']['content_type'])
1215

    
1216
    def test_if_unmodified_since_precondition_failed(self):
1217
        t = datetime.datetime.utcnow()
1218
        t2 = t - datetime.timedelta(minutes=10)
1219

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

    
1225
        for f in DATE_FORMATS:
1226
            past = t2.strftime(f)
1227
            #assert precondition failed
1228
            self.assert_raises_fault(412, self.client.retrieve_object,
1229
                                     self.containers[1], self.objects[0]['name'],
1230
                                     if_unmodified_since=past)
1231

    
1232
    def test_hashes(self):
1233
        l = 8388609
1234
        fname = 'largefile'
1235
        o = self.upload_random_data(self.containers[1], fname, l)
1236
        if o:
1237
            body = self.client.retrieve_object(self.containers[1], fname,
1238
                                               format='json')
1239
            hashes = body['hashes']
1240
            block_size = body['block_size']
1241
            block_hash = body['block_hash']
1242
            block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1243
            self.assertTrue(len(hashes), block_num)
1244
            i = 0
1245
            for h in hashes:
1246
                start = i * block_size
1247
                end = (i + 1) * block_size
1248
                hash = compute_block_hash(o['data'][start:end], block_hash)
1249
                self.assertEqual(h, hash)
1250
                i += 1
1251

    
1252
class ObjectPut(BaseTestCase):
1253
    def setUp(self):
1254
        BaseTestCase.setUp(self)
1255
        self.container = 'c1'
1256
        self.client.create_container(self.container)
1257

    
1258
    def test_upload(self):
1259
        name = o_names[0]
1260
        meta = {'test':'test1'}
1261
        o = self.upload_random_data(self.container, name, **meta)
1262

    
1263
        headers = self.client.retrieve_object_metadata(self.container,
1264
                                                       name,
1265
                                                       restricted=True)
1266
        self.assertTrue('test' in headers.keys())
1267
        self.assertEqual(headers['test'], meta['test'])
1268

    
1269
        #assert uploaded content
1270
        status, h, data = self.client.request_object(self.container, name)
1271
        self.assertEqual(len(o['data']), int(h['content-length']))
1272
        self.assertEqual(o['data'], data)
1273

    
1274
        #assert content-type
1275
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1276

    
1277
    def _test_maximum_upload_size_exceeds(self):
1278
        name = o_names[0]
1279
        meta = {'test':'test1'}
1280
        #upload 5GB
1281
        length= 5 * (1024 * 1024 * 1024) + 1
1282
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1283
                                 name, length, **meta)
1284

    
1285
    def test_upload_with_name_containing_slash(self):
1286
        name = '/%s' % o_names[0]
1287
        meta = {'test':'test1'}
1288
        o = self.upload_random_data(self.container, name, **meta)
1289

    
1290
        self.assertEqual(o['data'],
1291
                         self.client.retrieve_object(self.container, name))
1292

    
1293
        self.assertTrue(name in self.client.list_objects(self.container))
1294

    
1295
    def test_create_directory_marker(self):
1296
        self.client.create_directory_marker(self.container, 'foo')
1297
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1298
        self.assertEqual(meta['content-length'], '0')
1299
        self.assertEqual(meta['content-type'], 'application/directory')
1300

    
1301
    def test_upload_unprocessable_entity(self):
1302
        meta={'etag':'123', 'test':'test1'}
1303

    
1304
        #assert unprocessable entity
1305
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1306
                                 o_names[0], **meta)
1307

    
1308
    def test_chunked_transfer(self):
1309
        data = get_random_data()
1310
        objname = 'object'
1311
        self.client.create_object_using_chunks(self.container, objname,
1312
                                               StringIO(data))
1313

    
1314
        uploaded_data = self.client.retrieve_object(self.container, objname)
1315
        self.assertEqual(data, uploaded_data)
1316

    
1317
    def test_manifestation(self):
1318
        prefix = 'myobject/'
1319
        data = ''
1320
        for i in range(5):
1321
            part = '%s%d' %(prefix, i)
1322
            o = self.upload_random_data(self.container, part)
1323
            data += o['data']
1324

    
1325
        manifest = '%s/%s' %(self.container, prefix)
1326
        self.client.create_manifestation(self.container, 'large-object', manifest)
1327

    
1328
        self.assert_object_exists(self.container, 'large-object')
1329
        self.assertEqual(data, self.client.retrieve_object(self.container,
1330
                                                           'large-object'))
1331
        
1332
        r = self.client.retrieve_object_hashmap(self.container,'large-object')
1333
        hashes = r['hashes']
1334
        block_size = int(r['block_size'])
1335
        block_hash = r['block_hash']
1336
        l = len(data)
1337
        block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1338
        self.assertEqual(block_num, len(hashes))
1339
        
1340
        #wrong manifestation
1341
        self.client.create_manifestation(self.container, 'large-object',
1342
                                         '%s/invalid' % self.container)
1343
        self.assertEqual('', self.client.retrieve_object(self.container,
1344
                                                         'large-object'))
1345

    
1346
    def test_create_zero_length_object(self):
1347
        c = self.container
1348
        o = 'object'
1349
        zero = self.client.create_zero_length_object(c, o)
1350
        zero_meta = self.client.retrieve_object_metadata(c, o)
1351
        zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1352
        zero_data = self.client.retrieve_object(c, o)
1353

    
1354
        self.assertEqual(int(zero_meta['content-length']), 0)
1355
        hasher = newhasher('sha256')
1356
        hasher.update("")
1357
        emptyhash = hasher.digest()
1358
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1359
        self.assertEqual(zero_data, '')
1360

    
1361
    def test_create_object_by_hashmap(self):
1362
        c = self.container
1363
        o = 'object'
1364
        self.upload_random_data(c, o)
1365
        hashmap = self.client.retrieve_object(c, o, format='json')
1366
        o2 = 'object-copy'
1367
        self.client.create_object_by_hashmap(c, o2, hashmap)
1368
        self.assertEqual(self.client.retrieve_object(c, o),
1369
                         self.client.retrieve_object(c, o))
1370

    
1371
class ObjectCopy(BaseTestCase):
1372
    def setUp(self):
1373
        BaseTestCase.setUp(self)
1374
        self.containers = ['c1', 'c2']
1375
        for c in self.containers:
1376
            self.client.create_container(c)
1377
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1378

    
1379
    def test_copy(self):
1380
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1381
                             self.containers[0], self.obj['name']):
1382
            #perform copy
1383
            meta = {'test':'testcopy'}
1384
            status = self.client.copy_object(self.containers[0],
1385
                                              self.obj['name'],
1386
                                              self.containers[0],
1387
                                              'testcopy',
1388
                                              meta)[0]
1389

    
1390
            #assert copy success
1391
            self.assertEqual(status, 201)
1392

    
1393
            #assert access the new object
1394
            headers = self.client.retrieve_object_metadata(self.containers[0],
1395
                                                           'testcopy')
1396
            self.assertTrue('x-object-meta-test' in headers.keys())
1397
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1398

    
1399
            #assert etag is the same
1400
            self.assertEqual(headers['etag'], self.obj['hash'])
1401

    
1402
            #assert src object still exists
1403
            self.assert_object_exists(self.containers[0], self.obj['name'])
1404

    
1405
    def test_copy_from_different_container(self):
1406
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1407
                             self.containers[0], self.obj['name']):
1408
            meta = {'test':'testcopy'}
1409
            status = self.client.copy_object(self.containers[0],
1410
                                             self.obj['name'],
1411
                                             self.containers[1],
1412
                                             'testcopy',
1413
                                             meta)[0]
1414
            self.assertEqual(status, 201)
1415

    
1416
            # assert updated metadata
1417
            meta = self.client.retrieve_object_metadata(self.containers[1],
1418
                                                           'testcopy',
1419
                                                           restricted=True)
1420
            self.assertTrue('test' in meta.keys())
1421
            self.assertTrue(meta['test'], 'testcopy')
1422

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

    
1426
    def test_copy_invalid(self):
1427
        #copy from invalid object
1428
        meta = {'test':'testcopy'}
1429
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1430
                                 'test.py', self.containers[1], 'testcopy', meta)
1431

    
1432
        #copy from invalid container
1433
        meta = {'test':'testcopy'}
1434
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1435
                                 self.obj['name'], self.containers[1],
1436
                                 'testcopy', meta)
1437
    
1438
    def test_copy_dir(self):
1439
        self.client.create_folder(self.containers[0], 'dir')
1440
        self.client.create_folder(self.containers[0], 'dir/subdir')
1441
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1442
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1443
        self.client.create_folder(self.containers[0], 'dirs')
1444
        
1445
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1446
        self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1447
        for object in objects[:-1]:
1448
            self.assert_object_exists(self.containers[0], object)
1449
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1450
            meta0 = self.client.retrieve_object_metadata(self.containers[0], object)
1451
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1452
            t = ('content-length', 'x-object-hash', 'content-type')
1453
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1454
        self.assert_object_not_exists(self.containers[1], objects[-1])
1455
        
1456
class ObjectMove(BaseTestCase):
1457
    def setUp(self):
1458
        BaseTestCase.setUp(self)
1459
        self.containers = ['c1', 'c2']
1460
        for c in self.containers:
1461
            self.client.create_container(c)
1462
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1463

    
1464
    def test_move(self):
1465
        meta = self.client.retrieve_object_metadata(self.containers[0],
1466
                                                    self.obj['name'])
1467
        self.assertTrue('x-object-uuid' in meta)
1468
        uuid = meta['x-object-uuid']
1469

    
1470
        #perform move
1471
        meta = {'test':'testcopy'}
1472
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1473
        status = self.client.move_object(self.containers[0], self.obj['name'],
1474
                                         self.containers[0], 'testcopy',
1475
                                         meta)[0]
1476

    
1477
        #assert successful move
1478
        self.assertEqual(status, 201)
1479

    
1480
        #assert updated metadata
1481
        meta = self.client.retrieve_object_metadata(self.containers[0],
1482
                                                    'testcopy')
1483
        self.assertTrue('x-object-meta-test' in meta.keys())
1484
        self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1485

    
1486
        #assert same uuid
1487
        self.assertTrue(meta['x-object-uuid'], uuid)
1488

    
1489
        #assert src object no more exists
1490
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1491
    
1492
    
1493
    def test_move_dir(self):
1494
        meta = {}
1495
        self.client.create_folder(self.containers[0], 'dir')
1496
        meta['dir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir')
1497
        self.client.create_folder(self.containers[0], 'dir/subdir')
1498
        meta['dir/subdir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir')
1499
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1500
        meta['dir/object1.jpg'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/object1.jpg')
1501
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1502
        meta['dir/subdir/object2.pdf'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir/object2.pdf')
1503
        self.client.create_folder(self.containers[0], 'dirs')
1504
        meta['dirs'] = self.client.retrieve_object_metadata(self.containers[0], 'dirs')
1505
        
1506
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1507
        self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1508
        for object in objects[:-1]:
1509
            self.assert_object_not_exists(self.containers[0], object)
1510
            self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1511
            meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1512
            t = ('content-length', 'x-object-hash', 'content-type')
1513
            (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1514
        self.assert_object_exists(self.containers[0], objects[-1])
1515
        self.assert_object_not_exists(self.containers[1], objects[-1])
1516

    
1517
class ObjectPost(BaseTestCase):
1518
    def setUp(self):
1519
        BaseTestCase.setUp(self)
1520
        self.containers = ['c1', 'c2']
1521
        for c in self.containers:
1522
            self.client.create_container(c)
1523
        self.obj = []
1524
        for i in range(2):
1525
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1526

    
1527
    def test_update_meta(self):
1528
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1529
                                 self.containers[0],
1530
                                 self.obj[0]['name']):
1531
            #perform update metadata
1532
            more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1533
            status = self.client.update_object_metadata(self.containers[0],
1534
                                                        self.obj[0]['name'],
1535
                                                        **more)[0]
1536
            #assert request accepted
1537
            self.assertEqual(status, 202)
1538

    
1539
            #assert old metadata are still there
1540
            headers = self.client.retrieve_object_metadata(self.containers[0],
1541
                                                           self.obj[0]['name'],
1542
                                                           restricted=True)
1543
            #assert new metadata have been updated
1544
            for k,v in more.items():
1545
                self.assertTrue(k in headers.keys())
1546
                self.assertTrue(headers[k], v)
1547

    
1548
            #out of limits
1549
            more = {'f' * 114: 'b' * 257}
1550
            self.assert_raises_fault(400, self.client.update_object_metadata,
1551
                                                        self.containers[0],
1552
                                                        self.obj[0]['name'],
1553
                                                        **more)
1554
            
1555
            #perform update metadata
1556
            more = {'α': 'β' * 256}
1557
            status = self.client.update_object_metadata(self.containers[0],
1558
                                                        self.obj[0]['name'],
1559
                                                        **more)[0]
1560
            #assert request accepted
1561
            self.assertEqual(status, 202)
1562
            
1563
            #assert old metadata are still there
1564
            headers = self.client.retrieve_object_metadata(self.containers[0],
1565
                                                           self.obj[0]['name'],
1566
                                                           restricted=True)
1567
            #assert new metadata have been updated
1568
            for k,v in more.items():
1569
                self.assertTrue(k in headers.keys())
1570
                self.assertTrue(headers[k], v)
1571
            
1572
            #out of limits
1573
            more = {'α': 'β' * 257}
1574
            self.assert_raises_fault(400, self.client.update_object_metadata,
1575
                                                        self.containers[0],
1576
                                                        self.obj[0]['name'],
1577
                                                        **more)
1578
    
1579
    def test_update_object(self,
1580
                           first_byte_pos=0,
1581
                           last_byte_pos=499,
1582
                           instance_length = True,
1583
                           content_length = 500):
1584
        with AssertUUidInvariant(self.client.retrieve_object_metadata,
1585
                                 self.containers[0],
1586
                                 self.obj[0]['name']):
1587
            l = len(self.obj[0]['data'])
1588
            range = 'bytes %d-%d/%s' %(first_byte_pos,
1589
                                           last_byte_pos,
1590
                                            l if instance_length else '*')
1591
            partial = last_byte_pos - first_byte_pos + 1
1592
            length = first_byte_pos + partial
1593
            data = get_random_data(partial)
1594
            args = {'content_type':'application/octet-stream',
1595
                    'content_range':'%s' %range}
1596
            if content_length:
1597
                args['content_length'] = content_length
1598

    
1599
            r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1600
                                      StringIO(data), **args)
1601
            status = r[0]
1602
            etag = r[1]['etag']
1603
            if partial < 0 or (instance_length and l <= last_byte_pos):
1604
                self.assertEqual(status, 202)
1605
            else:
1606
                self.assertEqual(status, 204)
1607
                #check modified object
1608
                content = self.client.retrieve_object(self.containers[0],
1609
                                                  self.obj[0]['name'])
1610
                self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1611
                self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1612
                self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1613
                self.assertEqual(etag, compute_md5_hash(content))
1614

    
1615
    def test_update_object_lt_blocksize(self):
1616
        self.test_update_object(10, 20, content_length=None)
1617

    
1618
    def test_update_object_gt_blocksize(self):
1619
        o = self.upload_random_data(self.containers[0], o_names[1],
1620
                                length=4*1024*1024+5)
1621
        c = self.containers[0]
1622
        o_name = o['name']
1623
        o_data = o['data']
1624
        first_byte_pos = 4*1024*1024+1
1625
        last_byte_pos = 4*1024*1024+4
1626
        l = last_byte_pos - first_byte_pos + 1
1627
        data = get_random_data(l)
1628
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1629
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1630
        content = self.client.retrieve_object(c, o_name)
1631
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1632
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1633
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1634

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

    
1652
    def test_update_object_no_content_length(self):
1653
        self.test_update_object(content_length = None)
1654

    
1655
    def test_update_object_invalid_content_length(self):
1656
        with AssertContentInvariant(self.client.retrieve_object,
1657
                                    self.containers[0], self.obj[0]['name']):
1658
            self.assert_raises_fault(400, self.test_update_object,
1659
                                     content_length = 1000)
1660

    
1661
    def test_update_object_invalid_range(self):
1662
        with AssertContentInvariant(self.client.retrieve_object,
1663
                                    self.containers[0], self.obj[0]['name']):
1664
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1665

    
1666
    def test_update_object_invalid_range_and_length(self):
1667
        with AssertContentInvariant(self.client.retrieve_object,
1668
                                    self.containers[0], self.obj[0]['name']):
1669
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1670
                                     -1)
1671

    
1672
    def test_update_object_invalid_range_with_no_content_length(self):
1673
        with AssertContentInvariant(self.client.retrieve_object,
1674
                                    self.containers[0], self.obj[0]['name']):
1675
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1676
                                     content_length = None)
1677

    
1678
    def test_update_object_out_of_limits(self):
1679
        with AssertContentInvariant(self.client.retrieve_object,
1680
                                    self.containers[0], self.obj[0]['name']):
1681
            l = len(self.obj[0]['data'])
1682
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1683

    
1684
    def test_append(self):
1685
        data = get_random_data(500)
1686
        headers = {}
1687
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1688
                                  StringIO(data), content_length=500,
1689
                                  content_type='application/octet-stream')
1690

    
1691
        content = self.client.retrieve_object(self.containers[0],
1692
                                              self.obj[0]['name'])
1693
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1694
        self.assertEqual(content[:-500], self.obj[0]['data'])
1695

    
1696
    def test_update_with_chunked_transfer(self):
1697
        data = get_random_data(500)
1698
        dl = len(data)
1699
        fl = len(self.obj[0]['data'])
1700

    
1701
        self.client.update_object_using_chunks(self.containers[0],
1702
                                               self.obj[0]['name'],
1703
                                               StringIO(data),
1704
                                               offset=0,
1705
                                               content_type='application/octet-stream')
1706

    
1707
        #check modified object
1708
        content = self.client.retrieve_object(self.containers[0],
1709
                                              self.obj[0]['name'])
1710
        self.assertEqual(content[0:dl], data)
1711
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1712

    
1713
    def test_update_from_other_object(self):
1714
        c = self.containers[0]
1715
        src = o_names[0]
1716
        dest = 'object'
1717

    
1718
        source_data = self.client.retrieve_object(c, src)
1719
        source_meta = self.client.retrieve_object_metadata(c, src)
1720
        source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1721

    
1722
        #update zero length object
1723
        self.client.create_zero_length_object(c, dest)
1724
        source_object = '/%s/%s' % (c, src)
1725
        self.client.update_from_other_source(c, dest, source_object)
1726
        dest_data = self.client.retrieve_object(c, src)
1727
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1728
        dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1729
        self.assertEqual(source_data, dest_data)
1730
        self.assertEqual(source_hash, dest_hash)
1731

    
1732
        #test append
1733
        self.client.update_from_other_source(c, dest, source_object)
1734
        content = self.client.retrieve_object(c, dest)
1735
        self.assertEqual(source_data * 2, content)
1736

    
1737
    def test_update_range_from_other_object(self):
1738
        c = self.containers[0]
1739
        dest = 'object'
1740

    
1741
        #test update range
1742
        src = self.obj[1]['name']
1743
        src_data = self.client.retrieve_object(c, src)
1744

    
1745
        #update zero length object
1746
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1747
        source_object = '/%s/%s' % (c, src)
1748
        first_byte_pos = 4*1024*1024+1
1749
        last_byte_pos = 4*1024*1024+4
1750
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1751
        self.client.update_from_other_source(c, dest, source_object,
1752
                                             content_range=range)
1753
        content = self.client.retrieve_object(c, dest)
1754
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1755
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1756
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1757

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

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

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

    
1778

    
1779
    def test_update_zero_length_object(self):
1780
        c = self.containers[0]
1781
        o = 'object'
1782
        other = 'other'
1783
        zero = self.client.create_zero_length_object(c, o)
1784

    
1785
        data = get_random_data()
1786
        self.client.update_object(c, o, StringIO(data))
1787
        self.client.create_object(c, other, StringIO(data))
1788

    
1789
        self.assertEqual(self.client.retrieve_object(c, o),
1790
                         self.client.retrieve_object(c, other))
1791

    
1792
        self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1793
                         self.client.retrieve_object_hashmap(c, other)["hashes"])
1794

    
1795
class ObjectDelete(BaseTestCase):
1796
    def setUp(self):
1797
        BaseTestCase.setUp(self)
1798
        self.containers = ['c1', 'c2']
1799
        for c in self.containers:
1800
            self.client.create_container(c)
1801
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1802

    
1803
    def test_delete(self):
1804
        #perform delete object
1805
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1806

    
1807
    def test_delete_invalid(self):
1808
        #assert item not found
1809
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1810
                                 self.obj['name'])
1811
    
1812
    def test_delete_dir(self):
1813
        self.client.create_folder(self.containers[0], 'dir')
1814
        self.client.create_folder(self.containers[0], 'dir/subdir')
1815
        self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1816
        self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1817
        self.client.create_folder(self.containers[0], 'dirs')
1818
        
1819
        objects = self.client.list_objects(self.containers[0], prefix='dir')
1820
        self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1821
        for object in objects[:-1]:
1822
            self.assert_object_not_exists(self.containers[0], object)
1823
        self.assert_object_exists(self.containers[0], objects[-1])
1824

    
1825
class ListSharing(BaseTestCase):
1826
    def setUp(self):
1827
        BaseTestCase.setUp(self)
1828
        for i in range(2):
1829
            self.client.create_container('c%s' %i)
1830
        self.client.create_container('c')
1831
        for i in range(2):
1832
            self.upload_random_data('c1', 'o%s' %i)
1833
        accounts = OTHER_ACCOUNTS.copy()
1834
        self.o1_sharing_with = accounts.popitem()
1835
        self.o1_sharing = [self.o1_sharing_with[1]]
1836
        self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1837

    
1838
        l = []
1839
        for i in range(2):
1840
            l.append(accounts.popitem())
1841

    
1842
    def test_list_other_shared(self):
1843
        self.other = Pithos_Client(get_url(),
1844
                              self.o1_sharing_with[0],
1845
                              self.o1_sharing_with[1])
1846
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1847

    
1848
    def test_list_my_shared(self):
1849
        my_shared_containers = self.client.list_containers(shared=True)
1850
        self.assertTrue('c1' in my_shared_containers)
1851
        self.assertTrue('c2' not in my_shared_containers)
1852

    
1853
        my_shared_objects = self.client.list_objects('c1', shared=True)
1854
        self.assertTrue('o1' in my_shared_objects)
1855
        self.assertTrue('o2' not in my_shared_objects)
1856

    
1857
class List(BaseTestCase):
1858
    def setUp(self):
1859
        BaseTestCase.setUp(self)
1860
        for i in range(1, 5):
1861
            c = 'c%s' % i
1862
            self.client.create_container(c)
1863
            for j in range(1, 3):
1864
                o = 'o%s' % j
1865
                self.upload_random_data(c, o)
1866
            if i < 3:
1867
                self.client.share_object(c, 'o1', ['papagian'], read=True)
1868
            if i%2 != 0:
1869
                self.client.publish_object(c, 'o2')
1870
    
1871
    def test_shared_public(self):
1872
        func, kwargs = self.client.list_containers, {'shared':True}
1873
        l = func(**kwargs)
1874
        self.assertEqual(l, ['c1', 'c2'])
1875
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1876
        
1877
        func, kwargs = self.client.list_containers, {'public':True}
1878
        l = func(**kwargs)
1879
        self.assertEqual(l, ['c1', 'c3'])
1880
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1881
        
1882
        func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1883
        l = func(**kwargs)
1884
        self.assertEqual(l, ['c1', 'c2', 'c3'])
1885
        self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1886
        
1887
        
1888
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1889
        l = func(*args, **kwargs)
1890
        self.assertEqual(l, ['o1'])
1891
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1892
        
1893
        func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1894
        l = func(*args, **kwargs)
1895
        self.assertEqual(l, ['o2'])
1896
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1897
        
1898
        func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1899
        l = func(*args, **kwargs)
1900
        self.assertEqual(l, ['o1', 'o2'])
1901
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1902
        
1903
        
1904
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1905
        l = func(*args, **kwargs)
1906
        self.assertEqual(l, ['o1'])
1907
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1908
        
1909
        func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1910
        l = func(*args, **kwargs)
1911
        self.assertEqual(l, '')
1912
        self.assertEqual([], func(*args, format='json', **kwargs))
1913
        
1914
        func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True, 'public':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
        
1920
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1921
        l = func(*args, **kwargs)
1922
        self.assertEqual(l, '')
1923
        self.assertEqual([], func(*args, format='json', **kwargs))
1924
        
1925
        func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1926
        l = func(*args, **kwargs)
1927
        self.assertEqual(l, ['o2'])
1928
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1929
        
1930
        func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True, 'public':True}
1931
        l = func(*args, **kwargs)
1932
        self.assertEqual(l, ['o2'])
1933
        self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1934
        
1935
        
1936
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
1937
        l = func(*args, **kwargs)
1938
        self.assertEqual(l, '')
1939
        self.assertEqual([], func(*args, format='json', **kwargs))
1940
        
1941
        func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
1942
        l = func(*args, **kwargs)
1943
        self.assertEqual(l, '')
1944
        self.assertEqual([], func(*args, format='json', **kwargs))
1945
        
1946
        func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
1947
        l = func(*args, **kwargs)
1948
        self.assertEqual(l, '')
1949
        self.assertEqual([], func(*args, format='json', **kwargs))
1950

    
1951
class TestGreek(BaseTestCase):
1952
    def test_create_container(self):
1953
        self.client.create_container('φάκελος')
1954
        self.assert_container_exists('φάκελος')
1955

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

    
1958
    def test_create_object(self):
1959
        self.client.create_container('φάκελος')
1960
        self.upload_random_data('φάκελος', 'αντικείμενο')
1961

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

    
1965
    def test_copy_object(self):
1966
        src_container = 'φάκελος'
1967
        src_object = 'αντικείμενο'
1968
        dest_container = 'αντίγραφα'
1969
        dest_object = 'ασφαλές-αντίγραφο'
1970

    
1971
        self.client.create_container(src_container)
1972
        self.upload_random_data(src_container, src_object)
1973

    
1974
        self.client.create_container(dest_container)
1975
        self.client.copy_object(src_container, src_object, dest_container,
1976
                                dest_object)
1977

    
1978
        self.assert_object_exists(src_container, src_object)
1979
        self.assert_object_exists(dest_container, dest_object)
1980
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1981

    
1982
    def test_move_object(self):
1983
        src_container = 'φάκελος'
1984
        src_object = 'αντικείμενο'
1985
        dest_container = 'αντίγραφα'
1986
        dest_object = 'ασφαλές-αντίγραφο'
1987

    
1988
        self.client.create_container(src_container)
1989
        self.upload_random_data(src_container, src_object)
1990

    
1991
        self.client.create_container(dest_container)
1992
        self.client.move_object(src_container, src_object, dest_container,
1993
                                dest_object)
1994

    
1995
        self.assert_object_not_exists(src_container, src_object)
1996
        self.assert_object_exists(dest_container, dest_object)
1997
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1998

    
1999
    def test_delete_object(self):
2000
        self.client.create_container('φάκελος')
2001
        self.upload_random_data('φάκελος', 'αντικείμενο')
2002
        self.assert_object_exists('φάκελος', 'αντικείμενο')
2003

    
2004
        self.client.delete_object('φάκελος', 'αντικείμενο')
2005
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2006
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2007

    
2008
    def test_delete_container(self):
2009
        self.client.create_container('φάκελος')
2010
        self.assert_container_exists('φάκελος')
2011

    
2012
        self.client.delete_container('φάκελος')
2013
        self.assert_container_not_exists('φάκελος')
2014
        self.assertTrue('φάκελος' not in self.client.list_containers())
2015

    
2016
    def test_account_meta(self):
2017
        meta = {'ποιότητα':'ΑΑΑ'}
2018
        self.client.update_account_metadata(**meta)
2019
        meta = self.client.retrieve_account_metadata(restricted=True)
2020
        self.assertTrue('ποιότητα' in meta.keys())
2021
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2022

    
2023
    def test_container_meta(self):
2024
        meta = {'ποιότητα':'ΑΑΑ'}
2025
        self.client.create_container('φάκελος', meta=meta)
2026

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

    
2031
    def test_object_meta(self):
2032
        self.client.create_container('φάκελος')
2033
        meta = {'ποιότητα':'ΑΑΑ'}
2034
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2035

    
2036
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2037
                                                    restricted=True)
2038
        self.assertTrue('ποιότητα' in meta.keys())
2039
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2040

    
2041
    def test_list_meta_filtering(self):
2042
        self.client.create_container('φάκελος')
2043
        meta = {'ποιότητα':'ΑΑΑ'}
2044
        self.upload_random_data('φάκελος', 'ο1', **meta)
2045
        self.upload_random_data('φάκελος', 'ο2')
2046
        self.upload_random_data('φάκελος', 'ο3')
2047

    
2048
        meta = {'ποσότητα':'μεγάλη'}
2049
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2050
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2051
        self.assertEquals(objects, ['ο1', 'ο2'])
2052

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

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

    
2059
        meta = {'ποιότητα':'ΑΒ'}
2060
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2061
        objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2062
        self.assertEquals(objects, ['ο1'])
2063
        objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2064
        self.assertEquals(objects, ['ο2'])
2065

    
2066
        meta = {'έτος':'2011'}
2067
        self.client.update_object_metadata('φάκελος', 'ο3', **meta)
2068
        meta = {'έτος':'2012'}
2069
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2070
        objects = self.client.list_objects('φάκελος', meta='έτος<2012')
2071
        self.assertEquals(objects, ['ο3'])
2072
        objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
2073
        self.assertEquals(objects, ['ο2', 'ο3'])
2074
        objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
2075
        self.assertEquals(objects, '')
2076

    
2077
    def test_groups(self):
2078
        #create a group
2079
        groups = {'σεφς':'chazapis,διογένης'}
2080
        self.client.set_account_groups(**groups)
2081
        groups.update(self.initial_groups)
2082
        self.assertEqual(groups['σεφς'],
2083
                         self.client.retrieve_account_groups()['σεφς'])
2084

    
2085
        #check read access
2086
        self.client.create_container('φάκελος')
2087
        o = self.upload_random_data('φάκελος', 'ο1')
2088
        self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
2089
        chef = Pithos_Client(get_url(),
2090
                            '0009',
2091
                            'διογένης')
2092
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2093
                                     'φάκελος', 'ο1', account=get_user())
2094

    
2095
        #check write access
2096
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
2097
        new_data = get_random_data()
2098
        self.assert_not_raises_fault(403, chef.update_object,
2099
                                     'φάκελος', 'ο1', StringIO(new_data),
2100
                                     account=get_user())
2101

    
2102
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
2103
        self.assertEqual(server_data[:len(o['data'])], o['data'])
2104
        self.assertEqual(server_data[len(o['data']):], new_data)
2105

    
2106
    def test_manifestation(self):
2107
        self.client.create_container('κουβάς')
2108
        prefix = 'μέρη/'
2109
        data = ''
2110
        for i in range(5):
2111
            part = '%s%d' %(prefix, i)
2112
            o = self.upload_random_data('κουβάς', part)
2113
            data += o['data']
2114

    
2115
        self.client.create_container('φάκελος')
2116
        manifest = '%s/%s' %('κουβάς', prefix)
2117
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2118

    
2119
        self.assert_object_exists('φάκελος', 'άπαντα')
2120
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
2121
                                                           'άπαντα'))
2122

    
2123
        #wrong manifestation
2124
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2125
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2126

    
2127
    def test_update_from_another_object(self):
2128
        self.client.create_container('κουβάς')
2129
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2130
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2131
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2132
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2133

    
2134
        self.assertEqual(
2135
            self.client.retrieve_object('κουβάς', 'νέο'),
2136
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2137

    
2138
class TestPermissions(BaseTestCase):
2139
    def setUp(self):
2140
        BaseTestCase.setUp(self)
2141

    
2142
        #create a group
2143
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
2144
        groups = {'pithosdev':','.join(self.authorized)}
2145
        self.client.set_account_groups(**groups)
2146

    
2147
        self.container = 'c'
2148
        self.object = 'o'
2149
        self.client.create_container(self.container)
2150
        self.upload_random_data(self.container, self.object)
2151
        self.upload_random_data(self.container, self.object+'/')
2152
        self.upload_random_data(self.container, self.object+'/a')
2153
        self.upload_random_data(self.container, self.object+'a')
2154
        self.upload_random_data(self.container, self.object+'a/')
2155
        self.dir_content_types = ('application/directory', 'application/folder')
2156

    
2157
    def assert_read(self, authorized=[], any=False, depth=0):
2158
        for token, account in OTHER_ACCOUNTS.items():
2159
            cl = Pithos_Client(get_url(), token, account)
2160
            if account in authorized or any:
2161
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2162
                                             self.container, self.object,
2163
                                             account=get_user())
2164
            else:
2165
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
2166
                                         self.container, self.object,
2167
                                         account=get_user())
2168

    
2169
        #check inheritance
2170
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2171
        type = meta['content-type']
2172
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2173
        #exclude the self.object
2174
        del derivatives[derivatives.index(self.object)]
2175
        for o in derivatives:
2176
            for token, account in OTHER_ACCOUNTS.items():
2177
                cl = Pithos_Client(get_url(), token, account)
2178
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2179
                if (account in authorized or any) and \
2180
                (type in self.dir_content_types) and \
2181
                o.startswith(prefix):
2182
                    self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2183
                                             self.container, o, account=get_user())
2184
                else:
2185
                    self.assert_raises_fault(403, cl.retrieve_object_metadata,
2186
                                         self.container, o, account=get_user())
2187

    
2188
    def assert_write(self, authorized=[], any=False):
2189
        o_data = self.client.retrieve_object(self.container, self.object)
2190
        for token, account in OTHER_ACCOUNTS.items():
2191
            cl = Pithos_Client(get_url(), token, account)
2192
            new_data = get_random_data()
2193
            if account in authorized or any:
2194
                # test write access
2195
                self.assert_not_raises_fault(403, cl.update_object,
2196
                                             self.container, self.object, StringIO(new_data),
2197
                                             account=get_user())
2198
                try:
2199
                    # test read access
2200
                    server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2201
                    self.assertEqual(o_data, server_data[:len(o_data)])
2202
                    self.assertEqual(new_data, server_data[len(o_data):])
2203
                    o_data = server_data
2204
                except Fault, f:
2205
                    self.failIf(f.status == 403)
2206
            else:
2207
                self.assert_raises_fault(403, cl.update_object,
2208
                                             self.container, self.object, StringIO(new_data),
2209
                                             account=get_user())
2210
        #check inheritance
2211
        meta = self.client.retrieve_object_metadata(self.container, self.object)
2212
        type = meta['content-type']
2213
        derivatives = self.client.list_objects(self.container, prefix=self.object)
2214
        #exclude the object
2215
        del derivatives[derivatives.index(self.object)]
2216
        for o in derivatives:
2217
            for token, account in OTHER_ACCOUNTS.items():
2218
                prefix = self.object if self.object.endswith('/') else self.object+'/'
2219
                cl = Pithos_Client(get_url(), token, account)
2220
                new_data = get_random_data()
2221
                if (account in authorized or any) and \
2222
                (type in self.dir_content_types) and \
2223
                o.startswith(prefix):
2224
                    # test write access
2225
                    self.assert_not_raises_fault(403, cl.update_object,
2226
                                                 self.container, o,
2227
                                                 StringIO(new_data),
2228
                                                 account=get_user())
2229
                    try:
2230
                        server_data = cl.retrieve_object(self.container, o, account=get_user())
2231
                        self.assertEqual(new_data, server_data[-len(new_data):])
2232
                    except Fault, f:
2233
                        self.failIf(f.status == 403)
2234
                else:
2235
                    self.assert_raises_fault(403, cl.update_object,
2236
                                                 self.container, o,
2237
                                                 StringIO(new_data),
2238
                                                 account=get_user())
2239

    
2240
    def test_group_read(self):
2241
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2242
        self.assert_read(authorized=self.authorized)
2243

    
2244
    def test_read_many(self):
2245
        self.client.share_object(self.container, self.object, self.authorized)
2246
        self.assert_read(authorized=self.authorized)
2247

    
2248
    def test_read_by_everyone(self):
2249
        self.client.share_object(self.container, self.object, ['*'])
2250
        self.assert_read(any=True)
2251

    
2252
    def test_read_directory(self):
2253
        for type in self.dir_content_types:
2254
            #change content type
2255
            self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2256
            self.client.share_object(self.container, self.object, ['*'])
2257
            self.assert_read(any=True)
2258
            self.client.share_object(self.container, self.object, self.authorized)
2259
            self.assert_read(authorized=self.authorized)
2260
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2261
            self.assert_read(authorized=self.authorized)
2262

    
2263
    def test_group_write(self):
2264
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2265
        self.assert_write(authorized=self.authorized)
2266

    
2267
    def test_write_many(self):
2268
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2269
        self.assert_write(authorized=self.authorized)
2270

    
2271
    def test_write_by_everyone(self):
2272
        self.client.share_object(self.container, self.object, ['*'], read=False)
2273
        self.assert_write(any=True)
2274

    
2275
    def test_write_directory(self):
2276
        dir_content_types = ('application/directory', 'application/foler')
2277
        for type in dir_content_types:
2278
            #change content type
2279
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2280
            self.client.share_object(self.container, self.object, ['*'], read=False)
2281
            self.assert_write(any=True)
2282
            self.client.share_object(self.container, self.object, self.authorized, read=False)
2283
            self.assert_write(authorized=self.authorized)
2284
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2285
            self.assert_write(authorized=self.authorized)
2286

    
2287
    def test_shared_listing(self):
2288
        self.client.share_object(self.container, self.object, self.authorized)
2289

    
2290
        my_shared_containers = self.client.list_containers(shared=True)
2291
        self.assertEqual(['c'], my_shared_containers)
2292
        my_shared_objects = self.client.list_objects('c', shared=True)
2293
        self.assertEqual(['o'], my_shared_objects)
2294

    
2295
        dir_content_types = ('application/directory', 'application/foler')
2296
        for type in dir_content_types:
2297
            #change content type
2298
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2299
            my_shared_objects = self.client.list_objects('c', shared=True)
2300
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2301

    
2302
        for token, account in OTHER_ACCOUNTS.items():
2303
            if account in self.authorized:
2304
                self.other = Pithos_Client(get_url(), token, account)
2305
                self.assertTrue(get_user() in self.other.list_shared_by_others())
2306

    
2307
class TestPublish(BaseTestCase):
2308
    def test_publish(self):
2309
        self.client.create_container('c')
2310
        o_data = self.upload_random_data('c', 'o')['data']
2311
        self.client.publish_object('c', 'o')
2312
        meta = self.client.retrieve_object_metadata('c', 'o')
2313
        self.assertTrue('x-object-public' in meta)
2314
        url = meta['x-object-public']
2315

    
2316
        p = urlparse(get_url())
2317
        if p.scheme == 'http':
2318
            conn = HTTPConnection(p.netloc)
2319
        elif p.scheme == 'https':
2320
            conn = HTTPSConnection(p.netloc)
2321
        else:
2322
            raise Exception('Unknown URL scheme')
2323

    
2324
        conn.request('GET', url)
2325
        resp = conn.getresponse()
2326
        length = resp.getheader('content-length', None)
2327
        data = resp.read(length)
2328
        self.assertEqual(o_data, data)
2329

    
2330
class TestPolicies(BaseTestCase):
2331
    def test_none_versioning(self):
2332
        self.client.create_container('c', policies={'versioning':'none'})
2333
        o = self.upload_random_data('c', 'o')
2334
        meta = self.client.retrieve_object_metadata('c', 'o')
2335
        v = meta['x-object-version']
2336
        more_data = get_random_data()
2337
        self.client.update_object('c', 'o', StringIO(more_data))
2338
        vlist = self.client.retrieve_object_versionlist('c', 'o')
2339
        self.assert_raises_fault(404, self.client.retrieve_object_version,
2340
                                 'c', 'o', v)
2341
        data = self.client.retrieve_object('c', 'o')
2342
        end = len(o['data'])
2343
        self.assertEqual(data[:end], o['data'])
2344
        self.assertEqual(data[end:], more_data)
2345

    
2346
    def test_quota(self):
2347
        self.client.create_container('c', policies={'quota':'1'})
2348
        meta = self.client.retrieve_container_metadata('c')
2349
        self.assertEqual(meta['x-container-policy-quota'], '1')
2350
        self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2351
                                 length=1024*1024+1)
2352

    
2353
    def test_quota_none(self):
2354
        self.client.create_container('c', policies={'quota':'0'})
2355
        meta = self.client.retrieve_container_metadata('c')
2356
        self.assertEqual(meta['x-container-policy-quota'], '0')
2357
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2358
                                 length=1024*1024+1)
2359

    
2360
class AssertUUidInvariant(object):
2361
    def __init__(self, callable, *args, **kwargs):
2362
        self.callable = callable
2363
        self.args = args
2364
        self.kwargs = kwargs
2365

    
2366
    def __enter__(self):
2367
        self.map = self.callable(*self.args, **self.kwargs)
2368
        assert('x-object-uuid' in self.map)
2369
        self.uuid = self.map['x-object-uuid']
2370
        return self.map
2371

    
2372
    def __exit__(self, type, value, tb):
2373
        map = self.callable(*self.args, **self.kwargs)
2374
        assert('x-object-uuid' in self.map)
2375
        uuid = map['x-object-uuid']
2376
        assert(uuid == self.uuid)
2377

    
2378
class AssertMappingInvariant(object):
2379
    def __init__(self, callable, *args, **kwargs):
2380
        self.callable = callable
2381
        self.args = args
2382
        self.kwargs = kwargs
2383

    
2384
    def __enter__(self):
2385
        self.map = self.callable(*self.args, **self.kwargs)
2386
        return self.map
2387

    
2388
    def __exit__(self, type, value, tb):
2389
        map = self.callable(*self.args, **self.kwargs)
2390
        for k, v in self.map.items():
2391
            if is_date(v):
2392
                continue
2393
            assert(k in map)
2394
            assert v == map[k]
2395

    
2396
class AssertContentInvariant(object):
2397
    def __init__(self, callable, *args, **kwargs):
2398
        self.callable = callable
2399
        self.args = args
2400
        self.kwargs = kwargs
2401

    
2402
    def __enter__(self):
2403
        self.content = self.callable(*self.args, **self.kwargs)[2]
2404
        return self.content
2405

    
2406
    def __exit__(self, type, value, tb):
2407
        content = self.callable(*self.args, **self.kwargs)[2]
2408
        assert self.content == content
2409

    
2410
def get_content_splitted(response):
2411
    if response:
2412
        return response.content.split('\n')
2413

    
2414
def compute_md5_hash(data):
2415
    md5 = hashlib.md5()
2416
    offset = 0
2417
    md5.update(data)
2418
    return md5.hexdigest().lower()
2419

    
2420
def compute_block_hash(data, algorithm):
2421
    h = hashlib.new(algorithm)
2422
    h.update(data.rstrip('\x00'))
2423
    return h.hexdigest()
2424

    
2425
def get_random_data(length=500):
2426
    char_set = string.ascii_uppercase + string.digits
2427
    return ''.join(random.choice(char_set) for x in xrange(length))
2428

    
2429
def is_date(date):
2430
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2431
    __D = r'(?P<day>\d{2})'
2432
    __D2 = r'(?P<day>[ \d]\d)'
2433
    __M = r'(?P<mon>\w{3})'
2434
    __Y = r'(?P<year>\d{4})'
2435
    __Y2 = r'(?P<year>\d{2})'
2436
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2437
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2438
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2439
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2440
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2441
        m = regex.match(date)
2442
        if m is not None:
2443
            return True
2444
    return False
2445

    
2446
def strnextling(prefix):
2447
    """Return the first unicode string
2448
       greater than but not starting with given prefix.
2449
       strnextling('hello') -> 'hellp'
2450
    """
2451
    if not prefix:
2452
        ## all strings start with the null string,
2453
        ## therefore we have to approximate strnextling('')
2454
        ## with the last unicode character supported by python
2455
        ## 0x10ffff for wide (32-bit unicode) python builds
2456
        ## 0x00ffff for narrow (16-bit unicode) python builds
2457
        ## We will not autodetect. 0xffff is safe enough.
2458
        return unichr(0xffff)
2459
    s = prefix[:-1]
2460
    c = ord(prefix[-1])
2461
    if c >= 0xffff:
2462
        raise RuntimeError
2463
    s += unichr(c+1)
2464
    return s
2465

    
2466
o_names = ['kate.jpg',
2467
           'kate_beckinsale.jpg',
2468
           'How To Win Friends And Influence People.pdf',
2469
           'moms_birthday.jpg',
2470
           'poodle_strut.mov',
2471
           'Disturbed - Down With The Sickness.mp3',
2472
           'army_of_darkness.avi',
2473
           'the_mad.avi',
2474
           'photos/animals/dogs/poodle.jpg',
2475
           'photos/animals/dogs/terrier.jpg',
2476
           'photos/animals/cats/persian.jpg',
2477
           'photos/animals/cats/siamese.jpg',
2478
           'photos/plants/fern.jpg',
2479
           'photos/plants/rose.jpg',
2480
           'photos/me.jpg']
2481

    
2482

    
2483
def main():
2484
    if get_user() == 'test':
2485
        unittest.main(module='pithos.tools.test')
2486
    else:
2487
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2488

    
2489

    
2490
if __name__ == "__main__":
2491
    main()
2492