Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 94a83ed6

History | View | Annotate | Download (105.6 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
252
            obj['meta'] = args
253

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

    
258
            return obj
259
        except IOError:
260
            return
261

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
908
        #get exact previous version metadata
909
        v = a[-2][0]
910
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
911
                                                      restricted=True,
912
                                                      version=v)
913
        (self.assertTrue(k not in v_meta) for k in meta.keys())
914
        
915
        #update obejct
916
        data = get_random_data()
917
        self.client.update_object(c, o['name'], StringIO(data))
918
        
919
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
920
        self.assert_versionlist_structure(aa)
921
        self.assertEqual(len(a)+1, len(aa))
922
        self.assertEqual(a, aa[:-1])
923

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1803

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

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

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

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

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

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

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

    
1852
class ListSharing(BaseTestCase):
1853
    def setUp(self):
1854
        BaseTestCase.setUp(self)
1855
        for i in range(2):
1856
            self.client.create_container('c%s' %i)
1857
        self.client.create_container('c')
1858
        for i in range(2):
1859
            self.upload_random_data('c1', 'o%s' %i)
1860
        if not OTHER_ACCOUNTS:
1861
            raise Warning('No other accounts avalaible for running this test.')
1862
        for token, account in OTHER_ACCOUNTS.items():
1863
            self.o1_sharing = token, account
1864
            self.client.share_object('c1', 'o1', (account,), read=True)
1865
            break
1866
        
1867
    def tearDown(self):
1868
        pass
1869
    
1870
    def test_list_other_shared(self):
1871
        self.other = Pithos_Client(get_url(),
1872
                              self.o1_sharing[0],
1873
                              self.o1_sharing[1])
1874
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1875

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2170
class TestPermissions(BaseTestCase):
2171
    def setUp(self):
2172
        BaseTestCase.setUp(self)
2173
        
2174
        if not OTHER_ACCOUNTS:
2175
            raise Warning('No other accounts avalaible for running this test.')
2176
        
2177
        #create a group
2178
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
2179
        groups = {'pithosdev':','.join(self.authorized)}
2180
        self.client.set_account_groups(**groups)
2181

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

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

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

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

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

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

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

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

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

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

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

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

    
2322
    def test_shared_listing(self):
2323
        self.client.share_object(self.container, self.object, self.authorized)
2324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2517

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

    
2524

    
2525
if __name__ == "__main__":
2526
    main()
2527