Statistics
| Branch: | Tag: | Revision:

root / tools / test @ ce9e59cf

History | View | Annotate | Download (84.6 kB)

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

    
4
# Copyright 2011 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 lib.client import Pithos_Client, Fault
38
from lib.util import get_user, get_auth, get_server, get_api
39
from xml.dom import minidom
40
from StringIO import StringIO
41
from hashlib import new as newhasher
42
from binascii import hexlify
43

    
44
import json
45
import unittest
46
import time as _time
47
import types
48
import hashlib
49
import mimetypes
50
import random
51
import datetime
52
import string
53
import re
54

    
55
DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
56
                "%A, %d-%b-%y %H:%M:%S GMT",
57
                "%a, %d %b %Y %H:%M:%S GMT"]
58

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

    
70
class BaseTestCase(unittest.TestCase):
71
    #TODO unauthorized request
72
    def setUp(self):
73
        self.client = Pithos_Client(get_server(), get_auth(), get_user(),
74
                                    get_api())
75
        self._clean_account()
76
        self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
77
                                            get_api())
78
        
79
        #keep track of initial account groups
80
        self.initial_groups = self.client.retrieve_account_groups()
81
        
82
        #keep track of initial account meta
83
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
84
        
85
        self.extended = {
86
            'container':(
87
                'name',
88
                'count',
89
                'bytes',
90
                'last_modified',
91
                'x_container_policy'),
92
            'object':(
93
                'name',
94
                'hash',
95
                'bytes',
96
                'content_type',
97
                'content_encoding',
98
                'last_modified',)}
99
        self.return_codes = (400, 401, 403, 404, 503,)
100
    
101
    def tearDown(self):
102
        #delete additionally created meta
103
        l = []
104
        for m in self.client.retrieve_account_metadata(restricted=True):
105
            if m not in self.initial_meta:
106
                l.append(m)
107
        self.client.delete_account_metadata(l)
108
        
109
        #delete additionally created groups
110
        l = []
111
        for g in self.client.retrieve_account_groups():
112
            if g not in self.initial_groups:
113
                l.append(g)
114
        self.client.unset_account_groups(l)
115
        self._clean_account()
116
    
117
    def _clean_account(self):
118
        for c in self.client.list_containers():
119
            while True:
120
                #list objects returns at most 10000 objects
121
                #so repeat until there are no more objects
122
                objects = self.client.list_objects(c)
123
                if not objects:
124
                    break
125
                for o in objects:
126
                    self.client.delete_object(c, o)
127
            self.client.delete_container(c)
128
    
129
    def assert_status(self, status, codes):
130
        l = [elem for elem in self.return_codes]
131
        if type(codes) == types.ListType:
132
            l.extend(codes)
133
        else:
134
            l.append(codes)
135
        self.assertTrue(status in l)
136
    
137
    def assert_extended(self, data, format, type, size=10000):
138
        if format == 'xml':
139
            self._assert_xml(data, type, size)
140
        elif format == 'json':
141
            self._assert_json(data, type, size)
142
    
143
    def _assert_json(self, data, type, size):
144
        convert = lambda s: s.lower()
145
        info = [convert(elem) for elem in self.extended[type]]
146
        self.assertTrue(len(data) <= size)
147
        for item in info:
148
            for i in data:
149
                if 'subdir' in i.keys():
150
                    continue
151
                self.assertTrue(item in i.keys())
152
    
153
    def _assert_xml(self, data, type, size):
154
        convert = lambda s: s.lower()
155
        info = [convert(elem) for elem in self.extended[type]]
156
        try:
157
            info.remove('content_encoding')
158
        except ValueError:
159
            pass
160
        xml = data
161
        entities = xml.getElementsByTagName(type)
162
        self.assertTrue(len(entities) <= size)
163
        for e in entities:
164
            for item in info:
165
                self.assertTrue(e.getElementsByTagName(item))
166
    
167
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
168
        """
169
        asserts that a Fault with a specific status is raised
170
        when callableObj is called with the specific arguments
171
        """
172
        try:
173
            r = callableObj(*args, **kwargs)
174
            self.fail('Should never reach here')
175
        except Fault, f:
176
            if type(status) == types.ListType:
177
                self.failUnless(f.status in status)
178
            else:
179
                self.failUnless(f.status == status)
180
    
181
    def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
182
        """
183
        asserts that a Fault with a specific status is not raised
184
        when callableObj is called with the specific arguments
185
        """
186
        try:
187
            r = callableObj(*args, **kwargs)
188
        except Fault, f:
189
            self.failIfEqual(f.status, status)
190
    
191
    def assert_container_exists(self, container):
192
        """
193
        asserts the existence of a container
194
        """
195
        try:
196
            self.client.retrieve_container_metadata(container)
197
        except Fault, f:
198
            self.failIf(f.status == 404)
199
    
200
    def assert_container_not_exists(self, container):
201
        """
202
        asserts there is no such a container
203
        """
204
        self.assert_raises_fault(404, self.client.retrieve_container_metadata,
205
                                 container)
206
    
207
    def assert_object_exists(self, container, object):
208
        """
209
        asserts the existence of an object
210
        """
211
        try:
212
            self.client.retrieve_object_metadata(container, object)
213
        except Fault, f:
214
            self.failIf(f.status == 404)
215
    
216
    def assert_object_not_exists(self, container, object):
217
        """
218
        asserts there is no such an object
219
        """
220
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
221
                                 container, object)
222
    
223
    def assert_versionlist_structure(self, versionlist):
224
        self.assertTrue(type(versionlist) == types.ListType)
225
        for elem in versionlist:
226
            self.assertTrue(type(elem) == types.ListType)
227
            self.assertEqual(len(elem), 2)
228
    
229
    def upload_random_data(self, container, name, length=1024, type=None,
230
                           enc=None, **meta):
231
        data = get_random_data(length)
232
        return self.upload_data(container, name, data, type, enc, **meta)
233
    
234
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
235
                    **meta):
236
        obj = {}
237
        obj['name'] = name
238
        try:
239
            obj['data'] = data
240
            obj['hash'] = compute_md5_hash(obj['data'])
241
            
242
            args = {}
243
            args['etag'] = etag if etag else obj['hash']
244
            
245
            try:
246
                guess = mimetypes.guess_type(name)
247
                type = type if type else guess[0]
248
                enc = enc if enc else guess[1]
249
            except:
250
                pass
251
            args['content_type'] = type if type else 'plain/text'
252
            args['content_encoding'] = enc if enc else None
253
            
254
            obj['meta'] = args
255
            
256
            path = '/%s/%s' % (container, name)
257
            self.client.create_object(container, name, f=StringIO(obj['data']),
258
                                      meta=meta, **args)
259
            
260
            return obj
261
        except IOError:
262
            return
263

    
264
class AccountHead(BaseTestCase):
265
    def setUp(self):
266
        BaseTestCase.setUp(self)
267
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
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
        size = 0
282
        for c in containers:
283
            m = self.client.retrieve_container_metadata(c)
284
            size = size + int(m['x-container-bytes-used'])
285
        self.assertEqual(meta['x-account-bytes-used'], str(size))
286
    
287
    def test_get_account_403(self):
288
        self.assert_raises_fault(403,
289
                                 self.invalid_client.retrieve_account_metadata)
290
    
291
    def test_get_account_meta_until(self):
292
        t = datetime.datetime.utcnow()
293
        past = t - datetime.timedelta(minutes=-15)
294
        past = int(_time.mktime(past.timetuple()))
295
        
296
        meta = {'premium':True}
297
        self.client.update_account_metadata(**meta)
298
        meta = self.client.retrieve_account_metadata(restricted=True,
299
                                                     until=past)
300
        self.assertTrue('premium' not in meta)
301
        
302
        meta = self.client.retrieve_account_metadata(restricted=True)
303
        self.assertTrue('premium' in meta)
304
    
305
    def test_get_account_meta_until_invalid_date(self):
306
        meta = {'premium':True}
307
        self.client.update_account_metadata(**meta)
308
        meta = self.client.retrieve_account_metadata(restricted=True,
309
                                                     until='kshfksfh')
310
        self.assertTrue('premium' in meta)
311
    
312
class AccountGet(BaseTestCase):
313
    def setUp(self):
314
        BaseTestCase.setUp(self)
315
        #create some containers
316
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
317
        for item in self.containers:
318
            self.client.create_container(item)
319
    
320
    def test_list(self):
321
        #list containers
322
        containers = self.client.list_containers()
323
        self.assertEquals(self.containers, containers)
324
    
325
    def test_list_403(self):
326
        self.assert_raises_fault(403, self.invalid_client.list_containers)
327
    
328
    def test_list_with_limit(self):
329
        limit = 2
330
        containers = self.client.list_containers(limit=limit)
331
        self.assertEquals(len(containers), limit)
332
        self.assertEquals(self.containers[:2], containers)
333
    
334
    def test_list_with_marker(self):
335
        l = 2
336
        m = 'bananas'
337
        containers = self.client.list_containers(limit=l, marker=m)
338
        i = self.containers.index(m) + 1
339
        self.assertEquals(self.containers[i:(i+l)], containers)
340
        
341
        m = 'oranges'
342
        containers = self.client.list_containers(limit=l, marker=m)
343
        i = self.containers.index(m) + 1
344
        self.assertEquals(self.containers[i:(i+l)], containers)
345
    
346
    def test_list_json_with_marker(self):
347
        l = 2
348
        m = 'bananas'
349
        containers = self.client.list_containers(limit=l, marker=m, format='json')
350
        self.assert_extended(containers, 'json', 'container', l)
351
        self.assertEqual(containers[0]['name'], 'kiwis')
352
        self.assertEqual(containers[1]['name'], 'oranges')
353
    
354
    def test_list_xml_with_marker(self):
355
        l = 2
356
        m = 'oranges'
357
        xml = self.client.list_containers(limit=l, marker=m, format='xml')
358
        self.assert_extended(xml, 'xml', 'container', l)
359
        nodes = xml.getElementsByTagName('name')
360
        self.assertEqual(len(nodes), 1)
361
        self.assertEqual(nodes[0].childNodes[0].data, 'pears')
362
    
363
    def test_if_modified_since(self):
364
        t = datetime.datetime.utcnow()
365
        t2 = t - datetime.timedelta(minutes=10)
366
        
367
        #add a new container
368
        self.client.create_container('dummy')
369
        
370
        for f in DATE_FORMATS:
371
            past = t2.strftime(f)
372
            try:
373
                c = self.client.list_containers(if_modified_since=past)
374
                self.assertEqual(len(c), len(self.containers) + 1)
375
            except Fault, f:
376
                self.failIf(f.status == 304) #fail if not modified
377
    
378
    def test_if_modified_since_invalid_date(self):
379
        c = self.client.list_containers(if_modified_since='')
380
        self.assertEqual(len(c), len(self.containers))
381
    
382
    def test_if_not_modified_since(self):
383
        now = datetime.datetime.utcnow()
384
        since = now + datetime.timedelta(1)
385
        
386
        for f in DATE_FORMATS:
387
            args = {'if_modified_since':'%s' %since.strftime(f)}
388
            
389
            #assert not modified
390
            self.assert_raises_fault(304, self.client.list_containers, **args)
391
    
392
    def test_if_unmodified_since(self):
393
        now = datetime.datetime.utcnow()
394
        since = now + datetime.timedelta(1)
395
        
396
        for f in DATE_FORMATS:
397
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
398
            
399
            #assert success
400
            self.assertEqual(self.containers, c)
401
    
402
    def test_if_unmodified_since_precondition_failed(self):
403
        t = datetime.datetime.utcnow()
404
        t2 = t - datetime.timedelta(minutes=10)
405
        
406
        #add a new container
407
        self.client.create_container('dummy')
408
        
409
        for f in DATE_FORMATS:
410
            past = t2.strftime(f)
411
            
412
            args = {'if_unmodified_since':'%s' %past}
413
            
414
            #assert precondition failed
415
            self.assert_raises_fault(412, self.client.list_containers, **args)
416
    
417
class AccountPost(BaseTestCase):
418
    def setUp(self):
419
        BaseTestCase.setUp(self)
420
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
421
        for item in self.containers:
422
            self.client.create_container(item)
423
        
424
        meta = {'foo':'bar'}
425
        self.client.update_account_metadata(**meta)
426
        self.updated_meta = self.initial_meta.update(meta)
427
    
428
    def test_update_meta(self):
429
        with AssertMappingInvariant(self.client.retrieve_account_groups):
430
            meta = {'test':'test', 'tost':'tost'}
431
            self.client.update_account_metadata(**meta)
432
            
433
            meta.update(self.initial_meta)
434
            self.assertEqual(meta,
435
                             self.client.retrieve_account_metadata(
436
                                restricted=True))
437
        
438
    def test_invalid_account_update_meta(self):
439
        meta = {'test':'test', 'tost':'tost'}
440
        self.assert_raises_fault(403,
441
                                 self.invalid_client.update_account_metadata,
442
                                 **meta)
443
    
444
    def test_reset_meta(self):
445
        with AssertMappingInvariant(self.client.retrieve_account_groups):
446
            meta = {'test':'test', 'tost':'tost'}
447
            self.client.update_account_metadata(**meta)
448
            
449
            meta = {'test':'test33'}
450
            self.client.reset_account_metadata(**meta)
451
            
452
            self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
453
    
454
    def test_delete_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
            self.client.delete_account_metadata(meta.keys())
460
            
461
            account_meta = self.client.retrieve_account_metadata(restricted=True)
462
            for m in meta:
463
                self.assertTrue(m not in account_meta.keys())
464
    
465
    def test_set_account_groups(self):
466
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
467
            groups = {'pithosdev':'verigak,gtsouk,chazapis'}
468
            self.client.set_account_groups(**groups)
469
            
470
            self.assertEqual(set(groups['pithosdev']),
471
                             set(self.client.retrieve_account_groups()['pithosdev']))
472
            
473
            more_groups = {'clientsdev':'pkanavos,mvasilak'}
474
            self.client.set_account_groups(**more_groups)
475
            
476
            groups.update(more_groups)
477
            self.assertEqual(set(groups['clientsdev']),
478
                             set(self.client.retrieve_account_groups()['clientsdev']))
479
    
480
    def test_reset_account_groups(self):
481
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
482
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
483
                      'clientsdev':'pkanavos,mvasilak'}
484
            self.client.set_account_groups(**groups)
485
            
486
            self.assertEqual(set(groups['pithosdev'].split(',')),
487
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
488
            self.assertEqual(set(groups['clientsdev'].split(',')),
489
                             set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
490
            
491
            groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
492
            self.client.reset_account_groups(**groups)
493
            
494
            self.assertEqual(set(groups['pithosdev'].split(',')),
495
                             set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
496
    
497
    def test_delete_account_groups(self):
498
        with AssertMappingInvariant(self.client.retrieve_account_metadata):
499
            groups = {'pithosdev':'verigak,gtsouk,chazapis',
500
                      'clientsdev':'pkanavos,mvasilak'}
501
            self.client.set_account_groups(**groups)
502
            
503
            self.client.unset_account_groups(groups.keys())
504
            
505
            self.assertEqual({}, self.client.retrieve_account_groups())
506
    
507
class ContainerHead(BaseTestCase):
508
    def setUp(self):
509
        BaseTestCase.setUp(self)
510
        self.container = 'apples'
511
        self.client.create_container(self.container)
512
    
513
    def test_get_meta(self):
514
        meta = {'trash':'true'}
515
        t1 = datetime.datetime.utcnow()
516
        o = self.upload_random_data(self.container, o_names[0], **meta)
517
        if o:
518
            headers = self.client.retrieve_container_metadata(self.container)
519
            self.assertEqual(headers['x-container-object-count'], '1')
520
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
521
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
522
            delta = (t2 - t1)
523
            threashold = datetime.timedelta(seconds=1) 
524
            self.assertTrue(delta < threashold)
525
            self.assertTrue(headers['x-container-object-meta'])
526
            self.assertTrue('Trash' in headers['x-container-object-meta'])
527

    
528
class ContainerGet(BaseTestCase):
529
    def setUp(self):
530
        BaseTestCase.setUp(self)
531
        self.container = ['pears', 'apples']
532
        for c in self.container:
533
            self.client.create_container(c)
534
        self.obj = []
535
        for o in o_names[:8]:
536
            self.obj.append(self.upload_random_data(self.container[0], o))
537
        for o in o_names[8:]:
538
            self.obj.append(self.upload_random_data(self.container[1], o))
539
    
540
    def test_list_objects(self):
541
        objects = self.client.list_objects(self.container[0])
542
        l = [elem['name'] for elem in self.obj[:8]]
543
        l.sort()
544
        self.assertEqual(objects, l)
545
    
546
    def test_list_objects_containing_slash(self):
547
        self.client.create_container('test')
548
        self.upload_random_data('test', '/objectname')
549
        
550
        objects = self.client.list_objects('test')
551
        self.assertEqual(objects, ['/objectname'])
552
        
553
        objects = self.client.list_objects('test', format='json')
554
        self.assertEqual(objects[0]['name'], '/objectname')
555
        
556
        objects = self.client.list_objects('test', format='xml')
557
        self.assert_extended(objects, 'xml', 'object')
558
        node_name = objects.getElementsByTagName('name')[0]
559
        self.assertEqual(node_name.firstChild.data, '/objectname')
560

    
561
    def test_list_objects_with_limit_marker(self):
562
        objects = self.client.list_objects(self.container[0], limit=2)
563
        l = [elem['name'] for elem in self.obj[:8]]
564
        l.sort()
565
        self.assertEqual(objects, l[:2])
566
        
567
        markers = ['How To Win Friends And Influence People.pdf',
568
                   'moms_birthday.jpg']
569
        limit = 4
570
        for m in markers:
571
            objects = self.client.list_objects(self.container[0], limit=limit,
572
                                               marker=m)
573
            l = [elem['name'] for elem in self.obj[:8]]
574
            l.sort()
575
            start = l.index(m) + 1
576
            end = start + limit
577
            end = len(l) >= end and end or len(l)
578
            self.assertEqual(objects, l[start:end])
579
    
580
    #takes too long
581
    def _test_list_limit_exceeds(self):
582
        self.client.create_container('pithos')
583
        
584
        for i in range(10001):
585
            self.client.create_zero_length_object('pithos', i)
586
        
587
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
588
    
589
    def test_list_empty_params(self):
590
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
591
        if objects:
592
            objects = objects.strip().split('\n')
593
        self.assertEqual(objects,
594
                         self.client.list_objects(self.container[0]))
595
    
596
    def test_list_pseudo_hierarchical_folders(self):
597
        objects = self.client.list_objects(self.container[1], prefix='photos',
598
                                           delimiter='/')
599
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
600
                           'photos/plants/'], objects)
601
        
602
        objects = self.client.list_objects(self.container[1],
603
                                           prefix='photos/animals',
604
                                           delimiter='/')
605
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
606
        self.assertEquals(l, objects)
607
        
608
        objects = self.client.list_objects(self.container[1], path='photos')
609
        self.assertEquals(['photos/me.jpg'], objects)
610
    
611
    def test_extended_list_json(self):
612
        objects = self.client.list_objects(self.container[1], format='json',
613
                                           limit=2, prefix='photos/animals',
614
                                           delimiter='/')
615
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
616
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
617
    
618
    def test_extended_list_xml(self):
619
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
620
                                       prefix='photos', delimiter='/')
621
        self.assert_extended(xml, 'xml', 'object', size=4)
622
        dirs = xml.getElementsByTagName('subdir')
623
        self.assertEqual(len(dirs), 2)
624
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
625
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
626
        
627
        objects = xml.getElementsByTagName('name')
628
        self.assertEqual(len(objects), 1)
629
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
630
    
631
    def test_list_meta_double_matching(self):
632
        meta = {'quality':'aaa', 'stock':'true'}
633
        self.client.update_object_metadata(self.container[0],
634
                                           self.obj[0]['name'], **meta)
635
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
636
        self.assertEqual(len(obj), 1)
637
        self.assertTrue(obj, self.obj[0]['name'])
638
    
639
    def test_list_using_meta(self):
640
        meta = {'quality':'aaa'}
641
        for o in self.obj[:2]:
642
            self.client.update_object_metadata(self.container[0], o['name'],
643
                                               **meta)
644
        meta = {'stock':'true'}
645
        for o in self.obj[3:5]:
646
            self.client.update_object_metadata(self.container[0], o['name'],
647
                                               **meta)
648
        
649
        obj = self.client.list_objects(self.container[0], meta='Quality')
650
        self.assertEqual(len(obj), 2)
651
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
652
        
653
        # test case insensitive
654
        obj = self.client.list_objects(self.container[0], meta='quality')
655
        self.assertEqual(len(obj), 2)
656
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
657
        
658
        # test multiple matches
659
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
660
        self.assertEqual(len(obj), 4)
661
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
662
        
663
        # test non 1-1 multiple match
664
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
665
        self.assertEqual(len(obj), 2)
666
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
667
    
668
    def test_if_modified_since(self):
669
        t = datetime.datetime.utcnow()
670
        t2 = t - datetime.timedelta(minutes=10)
671
        
672
        #add a new object
673
        self.upload_random_data(self.container[0], o_names[0])
674
        
675
        for f in DATE_FORMATS:
676
            past = t2.strftime(f)
677
            try:
678
                o = self.client.list_objects(self.container[0],
679
                                            if_modified_since=past)
680
                self.assertEqual(o,
681
                                 self.client.list_objects(self.container[0]))
682
            except Fault, f:
683
                self.failIf(f.status == 304) #fail if not modified
684
    
685
    def test_if_modified_since_invalid_date(self):
686
        headers = {'if-modified-since':''}
687
        o = self.client.list_objects(self.container[0], if_modified_since='')
688
        self.assertEqual(o, self.client.list_objects(self.container[0]))
689
    
690
    def test_if_not_modified_since(self):
691
        now = datetime.datetime.utcnow()
692
        since = now + datetime.timedelta(1)
693
        
694
        for f in DATE_FORMATS:
695
            args = {'if_modified_since':'%s' %since.strftime(f)}
696
            
697
            #assert not modified
698
            self.assert_raises_fault(304, self.client.list_objects,
699
                                     self.container[0], **args)
700
    
701
    def test_if_unmodified_since(self):
702
        now = datetime.datetime.utcnow()
703
        since = now + datetime.timedelta(1)
704
        
705
        for f in DATE_FORMATS:
706
            obj = self.client.list_objects(self.container[0],
707
                                           if_unmodified_since=since.strftime(f))
708
            
709
            #assert unmodified
710
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
711
    
712
    def test_if_unmodified_since_precondition_failed(self):
713
        t = datetime.datetime.utcnow()
714
        t2 = t - datetime.timedelta(minutes=10)
715
        
716
        #add a new container
717
        self.client.create_container('dummy')
718
        
719
        for f in DATE_FORMATS:
720
            past = t2.strftime(f)
721
            
722
            args = {'if_unmodified_since':'%s' %past}
723
            
724
            #assert precondition failed
725
            self.assert_raises_fault(412, self.client.list_objects,
726
                                     self.container[0], **args)
727

    
728
class ContainerPut(BaseTestCase):
729
    def setUp(self):
730
        BaseTestCase.setUp(self)
731
        self.containers = ['c1', 'c2']
732
    
733
    def test_create(self):
734
        self.client.create_container(self.containers[0])
735
        containers = self.client.list_containers()
736
        self.assertTrue(self.containers[0] in containers)
737
        self.assert_container_exists(self.containers[0])
738
    
739
    def test_create_twice(self):
740
        self.client.create_container(self.containers[0])
741
        self.assertTrue(not self.client.create_container(self.containers[0]))
742
    
743
    def test_quota(self):
744
        self.client.create_container(self.containers[0])
745
        
746
        policy = {'quota':100}
747
        self.client.set_container_policies('c1', **policy)
748
        
749
        meta = self.client.retrieve_container_metadata('c1')
750
        self.assertTrue('x-container-policy-quota' in meta)
751
        self.assertEqual(meta['x-container-policy-quota'], '100')
752
        
753
        args = ['c1', 'o1']
754
        kwargs = {'length':101}
755
        self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
756
        
757
        #reset quota
758
        policy = {'quota':0}
759
        self.client.set_container_policies('c1', **policy)
760
    
761
class ContainerPost(BaseTestCase):
762
    def setUp(self):
763
        BaseTestCase.setUp(self)
764
        self.container = 'apples'
765
        self.client.create_container(self.container)
766
    
767
    def test_update_meta(self):
768
        meta = {'test':'test33',
769
                'tost':'tost22'}
770
        self.client.update_container_metadata(self.container, **meta)
771
        headers = self.client.retrieve_container_metadata(self.container)
772
        for k,v in meta.items():
773
            k = 'x-container-meta-%s' % k
774
            self.assertTrue(headers[k])
775
            self.assertEqual(headers[k], v)
776

    
777
class ContainerDelete(BaseTestCase):
778
    def setUp(self):
779
        BaseTestCase.setUp(self)
780
        self.containers = ['c1', 'c2']
781
        for c in self.containers:
782
            self.client.create_container(c)
783
    
784
    def test_delete(self):
785
        status = self.client.delete_container(self.containers[0])[0]
786
        self.assertEqual(status, 204)
787
    
788
    def test_delete_non_empty(self):
789
        self.upload_random_data(self.containers[1], o_names[0])
790
        self.assert_raises_fault(409, self.client.delete_container,
791
                                 self.containers[1])
792
    
793
    def test_delete_invalid(self):
794
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
795

    
796
class ObjectGet(BaseTestCase):
797
    def setUp(self):
798
        BaseTestCase.setUp(self)
799
        self.containers = ['c1', 'c2']
800
        #create some containers
801
        for c in self.containers:
802
            self.client.create_container(c)
803
        
804
        #upload a file
805
        names = ('obj1', 'obj2')
806
        self.objects = []
807
        for n in names:
808
            self.objects.append(self.upload_random_data(self.containers[1], n))
809
    
810
    def test_versions(self):
811
        c = self.containers[1]
812
        o = self.objects[0]
813
        b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
814
        self.assert_versionlist_structure(b)
815
        
816
        #update meta
817
        meta = {'quality':'AAA', 'stock':True}
818
        self.client.update_object_metadata(c, o['name'], **meta)
819
        
820
        a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
821
        self.assert_versionlist_structure(a)
822
        self.assertEqual(len(b)+1, len(a))
823
        self.assertEqual(b, a[:-1])
824
        
825
        #get exact previous version metadata
826
        v = a[-2][0]
827
        v_meta = self.client.retrieve_object_metadata(c, o['name'],
828
                                                      restricted=True,
829
                                                      version=v)
830
        for k in meta.keys():
831
            self.assertTrue(k not in v_meta)
832
        
833
        #update obejct
834
        data = get_random_data()
835
        self.client.update_object(c, o['name'], StringIO(data))
836
        
837
        aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
838
        self.assert_versionlist_structure(aa)
839
        self.assertEqual(len(a)+1, len(aa))
840
        self.assertEqual(a, aa[:-1])
841
        
842
        #get exact previous version
843
        v = aa[-3][0]
844
        v_data = self.client.retrieve_object_version(c, o['name'], version=v)
845
        self.assertEqual(o['data'], v_data)
846
        self.assertEqual(self.client.retrieve_object(c, o['name']),
847
                         '%s%s' %(v_data, data))
848
    
849
    def test_get(self):
850
        #perform get
851
        o = self.client.retrieve_object(self.containers[1],
852
                                        self.objects[0]['name'],
853
                                        self.objects[0]['meta'])
854
        self.assertEqual(o, self.objects[0]['data'])
855
    
856
    def test_objects_with_trailing_spaces(self):
857
        self.client.create_container('test')
858
        #create 'a' object
859
        self.upload_random_data('test', 'a')
860
        #look for 'a ' object
861
        self.assert_raises_fault(404, self.client.retrieve_object,
862
                                 'test', 'a ')
863
        
864
        #delete 'a' object
865
        self.client.delete_object('test', 'a')
866
        self.assert_raises_fault(404, self.client.retrieve_object,
867
                                 'test', 'a')
868
        
869
        #create 'a ' object
870
        self.upload_random_data('test', 'a ')
871
        #look for 'a' object
872
        self.assert_raises_fault(404, self.client.retrieve_object,
873
                                 'test', 'a')
874
    
875
    def test_get_invalid(self):
876
        self.assert_raises_fault(404, self.client.retrieve_object,
877
                                 self.containers[0], self.objects[0]['name'])
878
    
879
    def test_get_partial(self):
880
        #perform get with range
881
        status, headers, data = self.client.request_object(self.containers[1],
882
                                                            self.objects[0]['name'],
883
                                                            range='bytes=0-499')
884
        
885
        #assert successful partial content
886
        self.assertEqual(status, 206)
887
        
888
        #assert content-type
889
        self.assertEqual(headers['content-type'],
890
                         self.objects[0]['meta']['content_type'])
891
        
892
        #assert content length
893
        self.assertEqual(int(headers['content-length']), 500)
894
        
895
        #assert content
896
        self.assertEqual(self.objects[0]['data'][:500], data)
897
    
898
    def test_get_final_500(self):
899
        #perform get with range
900
        headers = {'range':'bytes=-500'}
901
        status, headers, data = self.client.request_object(self.containers[1],
902
                                                            self.objects[0]['name'],
903
                                                            range='bytes=-500')
904
        
905
        #assert successful partial content
906
        self.assertEqual(status, 206)
907
        
908
        #assert content-type
909
        self.assertEqual(headers['content-type'],
910
                         self.objects[0]['meta']['content_type'])
911
        
912
        #assert content length
913
        self.assertEqual(int(headers['content-length']), 500)
914
        
915
        #assert content
916
        self.assertTrue(self.objects[0]['data'][-500:], data)
917
    
918
    def test_get_rest(self):
919
        #perform get with range
920
        offset = len(self.objects[0]['data']) - 500
921
        status, headers, data = self.client.request_object(self.containers[1],
922
                                                self.objects[0]['name'],
923
                                                range='bytes=%s-' %offset)
924
        
925
        #assert successful partial content
926
        self.assertEqual(status, 206)
927
        
928
        #assert content-type
929
        self.assertEqual(headers['content-type'],
930
                         self.objects[0]['meta']['content_type'])
931
        
932
        #assert content length
933
        self.assertEqual(int(headers['content-length']), 500)
934
        
935
        #assert content
936
        self.assertTrue(self.objects[0]['data'][-500:], data)
937
    
938
    def test_get_range_not_satisfiable(self):
939
        #perform get with range
940
        offset = len(self.objects[0]['data']) + 1
941
        
942
        #assert range not satisfiable
943
        self.assert_raises_fault(416, self.client.retrieve_object,
944
                                 self.containers[1], self.objects[0]['name'],
945
                                 range='bytes=0-%s' %offset)
946
    
947
    def test_multiple_range(self):
948
        #perform get with multiple range
949
        ranges = ['0-499', '-500', '1000-']
950
        bytes = 'bytes=%s' % ','.join(ranges)
951
        status, headers, data = self.client.request_object(self.containers[1],
952
                                                           self.objects[0]['name'],
953
                                                           range=bytes)
954
        
955
        # assert partial content
956
        self.assertEqual(status, 206)
957
        
958
        # assert Content-Type of the reply will be multipart/byteranges
959
        self.assertTrue(headers['content-type'])
960
        content_type_parts = headers['content-type'].split()
961
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
962
        
963
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
964
        cparts = data.split(boundary)[1:-1]
965
        
966
        # assert content parts are exactly 2
967
        self.assertEqual(len(cparts), len(ranges))
968
        
969
        # for each content part assert headers
970
        i = 0
971
        for cpart in cparts:
972
            content = cpart.split('\r\n')
973
            headers = content[1:3]
974
            content_range = headers[0].split(': ')
975
            self.assertEqual(content_range[0], 'Content-Range')
976
            
977
            r = ranges[i].split('-')
978
            if not r[0] and not r[1]:
979
                pass
980
            elif not r[0]:
981
                start = len(self.objects[0]['data']) - int(r[1])
982
                end = len(self.objects[0]['data'])
983
            elif not r[1]:
984
                start = int(r[0])
985
                end = len(self.objects[0]['data'])
986
            else:
987
                start = int(r[0])
988
                end = int(r[1]) + 1
989
            fdata = self.objects[0]['data'][start:end]
990
            sdata = '\r\n'.join(content[4:-1])
991
            self.assertEqual(len(fdata), len(sdata))
992
            self.assertEquals(fdata, sdata)
993
            i+=1
994
    
995
    def test_multiple_range_not_satisfiable(self):
996
        #perform get with multiple range
997
        out_of_range = len(self.objects[0]['data']) + 1
998
        ranges = ['0-499', '-500', '%d-' %out_of_range]
999
        bytes = 'bytes=%s' % ','.join(ranges)
1000
        
1001
        # assert partial content
1002
        self.assert_raises_fault(416, self.client.retrieve_object,
1003
                                 self.containers[1],
1004
                                 self.objects[0]['name'], range=bytes)
1005
    
1006
    def test_get_with_if_match(self):
1007
        #perform get with If-Match
1008
        etag = self.objects[0]['hash']
1009
        status, headers, data = self.client.request_object(self.containers[1],
1010
                                                           self.objects[0]['name'],
1011
                                                           if_match=etag)
1012
        #assert get success
1013
        self.assertEqual(status, 200)
1014
        
1015
        #assert content-type
1016
        self.assertEqual(headers['content-type'],
1017
                         self.objects[0]['meta']['content_type'])
1018
        
1019
        #assert response content
1020
        self.assertEqual(self.objects[0]['data'], data)
1021
    
1022
    def test_get_with_if_match_star(self):
1023
        #perform get with If-Match *
1024
        headers = {'if-match':'*'}
1025
        status, headers, data = self.client.request_object(self.containers[1],
1026
                                                self.objects[0]['name'],
1027
                                                **headers)
1028
        #assert get success
1029
        self.assertEqual(status, 200)
1030
        
1031
        #assert content-type
1032
        self.assertEqual(headers['content-type'],
1033
                         self.objects[0]['meta']['content_type'])
1034
        
1035
        #assert response content
1036
        self.assertEqual(self.objects[0]['data'], data)
1037
    
1038
    def test_get_with_multiple_if_match(self):
1039
        #perform get with If-Match
1040
        etags = [i['hash'] for i in self.objects if i]
1041
        etags = ','.join('"%s"' % etag for etag in etags)
1042
        status, headers, data = self.client.request_object(self.containers[1],
1043
                                                           self.objects[0]['name'],
1044
                                                           if_match=etags)
1045
        #assert get success
1046
        self.assertEqual(status, 200)
1047
        
1048
        #assert content-type
1049
        self.assertEqual(headers['content-type'],
1050
                         self.objects[0]['meta']['content_type'])
1051
        
1052
        #assert content-type
1053
        self.assertEqual(headers['content-type'],
1054
                         self.objects[0]['meta']['content_type'])
1055
        
1056
        #assert response content
1057
        self.assertEqual(self.objects[0]['data'], data)
1058
    
1059
    def test_if_match_precondition_failed(self):
1060
        #assert precondition failed
1061
        self.assert_raises_fault(412, self.client.retrieve_object,
1062
                                 self.containers[1],
1063
                                 self.objects[0]['name'], if_match='123')
1064
    
1065
    def test_if_none_match(self):
1066
        #perform get with If-None-Match
1067
        status, headers, data = self.client.request_object(self.containers[1],
1068
                                                           self.objects[0]['name'],
1069
                                                           if_none_match='123')
1070
        
1071
        #assert get success
1072
        self.assertEqual(status, 200)
1073
        
1074
        #assert content-type
1075
        self.assertEqual(headers['content_type'],
1076
                         self.objects[0]['meta']['content_type'])
1077
    
1078
    def test_if_none_match(self):
1079
        #perform get with If-None-Match * and assert not modified
1080
        self.assert_raises_fault(304, self.client.retrieve_object,
1081
                                 self.containers[1],
1082
                                 self.objects[0]['name'],
1083
                                 if_none_match='*')
1084
    
1085
    def test_if_none_match_not_modified(self):
1086
        #perform get with If-None-Match and assert not modified
1087
        self.assert_raises_fault(304, self.client.retrieve_object,
1088
                                 self.containers[1],
1089
                                 self.objects[0]['name'],
1090
                                 if_none_match=self.objects[0]['hash'])
1091
        
1092
        meta = self.client.retrieve_object_metadata(self.containers[1],
1093
                                                    self.objects[0]['name'])
1094
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
1095
    
1096
    def test_if_modified_since(self):
1097
        t = datetime.datetime.utcnow()
1098
        t2 = t - datetime.timedelta(minutes=10)
1099
        
1100
        #modify the object
1101
        self.upload_data(self.containers[1],
1102
                           self.objects[0]['name'],
1103
                           self.objects[0]['data'][:200])
1104
        
1105
        for f in DATE_FORMATS:
1106
            past = t2.strftime(f)
1107
            
1108
            headers = {'if-modified-since':'%s' %past}
1109
            try:
1110
                o = self.client.retrieve_object(self.containers[1],
1111
                                                self.objects[0]['name'],
1112
                                                if_modified_since=past)
1113
                self.assertEqual(o,
1114
                                 self.client.retrieve_object(self.containers[1],
1115
                                                             self.objects[0]['name']))
1116
            except Fault, f:
1117
                self.failIf(f.status == 304)
1118
    
1119
    def test_if_modified_since_invalid_date(self):
1120
        o = self.client.retrieve_object(self.containers[1],
1121
                                        self.objects[0]['name'],
1122
                                        if_modified_since='')
1123
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1124
                                                        self.objects[0]['name']))
1125
            
1126
    def test_if_not_modified_since(self):
1127
        now = datetime.datetime.utcnow()
1128
        since = now + datetime.timedelta(1)
1129
        
1130
        for f in DATE_FORMATS:
1131
            #assert not modified
1132
            self.assert_raises_fault(304, self.client.retrieve_object,
1133
                                     self.containers[1], self.objects[0]['name'],
1134
                                     if_modified_since=since.strftime(f))
1135
    
1136
    def test_if_unmodified_since(self):
1137
        now = datetime.datetime.utcnow()
1138
        since = now + datetime.timedelta(1)
1139
        
1140
        for f in DATE_FORMATS:
1141
            t = since.strftime(f)
1142
            status, headers, data = self.client.request_object(self.containers[1],
1143
                                                               self.objects[0]['name'],
1144
                                                               if_unmodified_since=t)
1145
            #assert success
1146
            self.assertEqual(status, 200)
1147
            self.assertEqual(self.objects[0]['data'], data)
1148
            
1149
            #assert content-type
1150
            self.assertEqual(headers['content-type'],
1151
                             self.objects[0]['meta']['content_type'])
1152
    
1153
    def test_if_unmodified_since_precondition_failed(self):
1154
        t = datetime.datetime.utcnow()
1155
        t2 = t - datetime.timedelta(minutes=10)
1156
        
1157
        #modify the object
1158
        self.upload_data(self.containers[1],
1159
                           self.objects[0]['name'],
1160
                           self.objects[0]['data'][:200])
1161
        
1162
        for f in DATE_FORMATS:
1163
            past = t2.strftime(f)
1164
            #assert precondition failed
1165
            self.assert_raises_fault(412, self.client.retrieve_object,
1166
                                     self.containers[1], self.objects[0]['name'],
1167
                                     if_unmodified_since=past)
1168
    
1169
    def test_hashes(self):
1170
        l = 8388609
1171
        fname = 'largefile'
1172
        o = self.upload_random_data(self.containers[1], fname, l)
1173
        if o:
1174
            body = self.client.retrieve_object(self.containers[1], fname,
1175
                                               format='json')
1176
            hashes = body['hashes']
1177
            block_size = body['block_size']
1178
            block_hash = body['block_hash']
1179
            block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1180
            self.assertTrue(len(hashes), block_num)
1181
            i = 0
1182
            for h in hashes:
1183
                start = i * block_size
1184
                end = (i + 1) * block_size
1185
                hash = compute_block_hash(o['data'][start:end], block_hash)
1186
                self.assertEqual(h, hash)
1187
                i += 1
1188

    
1189
class ObjectPut(BaseTestCase):
1190
    def setUp(self):
1191
        BaseTestCase.setUp(self)
1192
        self.container = 'c1'
1193
        self.client.create_container(self.container)
1194
    
1195
    def test_upload(self):
1196
        name = o_names[0]
1197
        meta = {'test':'test1'}
1198
        o = self.upload_random_data(self.container, name, **meta)
1199
        
1200
        headers = self.client.retrieve_object_metadata(self.container,
1201
                                                       name,
1202
                                                       restricted=True)
1203
        self.assertTrue('test' in headers.keys())
1204
        self.assertEqual(headers['test'], meta['test'])
1205
        
1206
        #assert uploaded content
1207
        status, h, data = self.client.request_object(self.container, name)
1208
        self.assertEqual(len(o['data']), int(h['content-length']))
1209
        self.assertEqual(o['data'], data)
1210
        
1211
        #assert content-type
1212
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1213
    
1214
    def _test_maximum_upload_size_exceeds(self):
1215
        name = o_names[0]
1216
        meta = {'test':'test1'}
1217
        #upload 100MB
1218
        length=1024*1024*100
1219
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1220
                                 name, length, **meta)
1221
    
1222
    def test_upload_with_name_containing_slash(self):
1223
        name = '/%s' % o_names[0]
1224
        meta = {'test':'test1'}
1225
        o = self.upload_random_data(self.container, name, **meta)
1226
        
1227
        self.assertEqual(o['data'],
1228
                         self.client.retrieve_object(self.container, name))
1229
        
1230
        self.assertTrue(name in self.client.list_objects(self.container))
1231
    
1232
    def test_create_directory_marker(self):
1233
        self.client.create_directory_marker(self.container, 'foo')
1234
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1235
        self.assertEqual(meta['content-length'], '0')
1236
        self.assertEqual(meta['content-type'], 'application/directory')
1237

    
1238
    def test_upload_unprocessable_entity(self):
1239
        meta={'etag':'123', 'test':'test1'}
1240
        
1241
        #assert unprocessable entity
1242
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1243
                                 o_names[0], **meta)
1244
    
1245
    def test_chunked_transfer(self):
1246
        data = get_random_data()
1247
        objname = 'object'
1248
        self.client.create_object_using_chunks(self.container, objname,
1249
                                               StringIO(data))
1250
        
1251
        uploaded_data = self.client.retrieve_object(self.container, objname)
1252
        self.assertEqual(data, uploaded_data)
1253
    
1254
    def test_manifestation(self):
1255
        prefix = 'myobject/'
1256
        data = ''
1257
        for i in range(5):
1258
            part = '%s%d' %(prefix, i)
1259
            o = self.upload_random_data(self.container, part)
1260
            data += o['data']
1261
        
1262
        manifest = '%s/%s' %(self.container, prefix)
1263
        self.client.create_manifestation(self.container, 'large-object', manifest)
1264
        
1265
        self.assert_object_exists(self.container, 'large-object')
1266
        self.assertEqual(data, self.client.retrieve_object(self.container,
1267
                                                           'large-object'))
1268
        
1269
        #wrong manifestation
1270
        self.client.create_manifestation(self.container, 'large-object',
1271
                                         '%s/invalid' % self.container)
1272
        self.assertEqual('', self.client.retrieve_object(self.container,
1273
                                                         'large-object'))
1274
    
1275
    def test_create_zero_length_object(self):
1276
        c = self.container
1277
        o = 'object'
1278
        zero = self.client.create_zero_length_object(c, o)
1279
        zero_meta = self.client.retrieve_object_metadata(c, o)
1280
        zero_hash = self.client.retrieve_object_hashmap(c, o)
1281
        zero_data = self.client.retrieve_object(c, o)
1282
        
1283
        self.assertEqual(int(zero_meta['content-length']), 0)
1284
        hasher = newhasher('sha256')
1285
        hasher.update("")
1286
        emptyhash = hasher.digest()
1287
        self.assertEqual(zero_hash, [hexlify(emptyhash)])
1288
        self.assertEqual(zero_data, '')
1289
    
1290
    def test_create_object_by_hashmap(self):
1291
        c = self.container
1292
        o = 'object'
1293
        self.upload_random_data(c, o)
1294
        hashmap = self.client.retrieve_object(c, o, format='json')
1295
        o2 = 'object-copy'
1296
        self.client.create_object_by_hashmap(c, o2, hashmap)
1297
        self.assertEqual(self.client.retrieve_object(c, o),
1298
                         self.client.retrieve_object(c, o))
1299

    
1300
class ObjectCopy(BaseTestCase):
1301
    def setUp(self):
1302
        BaseTestCase.setUp(self)
1303
        self.containers = ['c1', 'c2']
1304
        for c in self.containers:
1305
            self.client.create_container(c)
1306
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1307
        
1308
    def test_copy(self):
1309
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1310
                             self.containers[0], self.obj['name']):
1311
            #perform copy
1312
            meta = {'test':'testcopy'}
1313
            status = self.client.copy_object(self.containers[0],
1314
                                              self.obj['name'],
1315
                                              self.containers[0],
1316
                                              'testcopy',
1317
                                              meta)[0]
1318
            
1319
            #assert copy success
1320
            self.assertEqual(status, 201)
1321
            
1322
            #assert access the new object
1323
            headers = self.client.retrieve_object_metadata(self.containers[0],
1324
                                                           'testcopy')
1325
            self.assertTrue('x-object-meta-test' in headers.keys())
1326
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1327
            
1328
            #assert etag is the same
1329
            self.assertEqual(headers['etag'], self.obj['hash'])
1330
            
1331
            #assert src object still exists
1332
            self.assert_object_exists(self.containers[0], self.obj['name'])
1333
    
1334
    def test_copy_from_different_container(self):
1335
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1336
                             self.containers[0], self.obj['name']):
1337
            meta = {'test':'testcopy'}
1338
            status = self.client.copy_object(self.containers[0],
1339
                                             self.obj['name'],
1340
                                             self.containers[1],
1341
                                             'testcopy',
1342
                                             meta)[0]
1343
            self.assertEqual(status, 201)
1344
            
1345
            # assert updated metadata
1346
            meta = self.client.retrieve_object_metadata(self.containers[1],
1347
                                                           'testcopy',
1348
                                                           restricted=True)
1349
            self.assertTrue('test' in meta.keys())
1350
            self.assertTrue(meta['test'], 'testcopy')
1351
            
1352
            #assert src object still exists
1353
            self.assert_object_exists(self.containers[0], self.obj['name'])
1354
    
1355
    def test_copy_invalid(self):
1356
        #copy from invalid object
1357
        meta = {'test':'testcopy'}
1358
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1359
                                 'test.py', self.containers[1], 'testcopy', meta)
1360
        
1361
        #copy from invalid container
1362
        meta = {'test':'testcopy'}
1363
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1364
                                 self.obj['name'], self.containers[1],
1365
                                 'testcopy', meta)
1366

    
1367
class ObjectMove(BaseTestCase):
1368
    def setUp(self):
1369
        BaseTestCase.setUp(self)
1370
        self.containers = ['c1', 'c2']
1371
        for c in self.containers:
1372
            self.client.create_container(c)
1373
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1374
    
1375
    def test_move(self):
1376
        #perform move
1377
        meta = {'test':'testcopy'}
1378
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1379
        status = self.client.move_object(self.containers[0], self.obj['name'],
1380
                                         self.containers[0], 'testcopy',
1381
                                         meta)[0]
1382
        
1383
        #assert successful move
1384
        self.assertEqual(status, 201)
1385
        
1386
        #assert updated metadata
1387
        meta = self.client.retrieve_object_metadata(self.containers[0],
1388
                                                    'testcopy',
1389
                                                    restricted=True)
1390
        self.assertTrue('test' in meta.keys())
1391
        self.assertTrue(meta['test'], 'testcopy')
1392
        
1393
        #assert src object no more exists
1394
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1395

    
1396
class ObjectPost(BaseTestCase):
1397
    def setUp(self):
1398
        BaseTestCase.setUp(self)
1399
        self.containers = ['c1', 'c2']
1400
        for c in self.containers:
1401
            self.client.create_container(c)
1402
        self.obj = []
1403
        for i in range(2):
1404
            self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1405
    
1406
    def test_update_meta(self):
1407
        #perform update metadata
1408
        more = {'foo':'foo', 'bar':'bar'}
1409
        status = self.client.update_object_metadata(self.containers[0],
1410
                                                    self.obj[0]['name'],
1411
                                                    **more)[0]
1412
        #assert request accepted
1413
        self.assertEqual(status, 202)
1414
        
1415
        #assert old metadata are still there
1416
        headers = self.client.retrieve_object_metadata(self.containers[0],
1417
                                                       self.obj[0]['name'],
1418
                                                       restricted=True)
1419
        #assert new metadata have been updated
1420
        for k,v in more.items():
1421
            self.assertTrue(k in headers.keys())
1422
            self.assertTrue(headers[k], v)
1423
    
1424
    def test_update_object(self,
1425
                           first_byte_pos=0,
1426
                           last_byte_pos=499,
1427
                           instance_length = True,
1428
                           content_length = 500):
1429
        l = len(self.obj[0]['data'])
1430
        range = 'bytes %d-%d/%s' %(first_byte_pos,
1431
                                       last_byte_pos,
1432
                                        l if instance_length else '*')
1433
        partial = last_byte_pos - first_byte_pos + 1
1434
        length = first_byte_pos + partial
1435
        data = get_random_data(partial)
1436
        args = {'content_type':'application/octet-stream',
1437
                'content_range':'%s' %range}
1438
        if content_length:
1439
            args['content_length'] = content_length
1440
        
1441
        status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1442
                                  StringIO(data), **args)[0]
1443
        
1444
        if partial < 0 or (instance_length and l <= last_byte_pos):
1445
            self.assertEqual(status, 202)    
1446
        else:
1447
            self.assertEqual(status, 204)           
1448
            #check modified object
1449
            content = self.client.retrieve_object(self.containers[0],
1450
                                              self.obj[0]['name'])
1451
            self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1452
            self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1453
            self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1454
    
1455
    def test_update_object_lt_blocksize(self):
1456
        self.test_update_object(10, 20, content_length=None)
1457
    
1458
    def test_update_object_gt_blocksize(self):
1459
        o = self.upload_random_data(self.containers[0], o_names[1],
1460
                                length=4*1024*1024+5)
1461
        c = self.containers[0]
1462
        o_name = o['name']
1463
        o_data = o['data']
1464
        first_byte_pos = 4*1024*1024+1
1465
        last_byte_pos = 4*1024*1024+4
1466
        l = last_byte_pos - first_byte_pos + 1
1467
        data = get_random_data(l)
1468
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1469
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1470
        content = self.client.retrieve_object(c, o_name)
1471
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1472
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1473
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1474
    
1475
    def test_update_object_divided_by_blocksize(self):
1476
        o = self.upload_random_data(self.containers[0], o_names[1],
1477
                                length=4*1024*1024+5)
1478
        c = self.containers[0]
1479
        o_name = o['name']
1480
        o_data = o['data']
1481
        first_byte_pos = 4*1024*1024
1482
        last_byte_pos = 5*1024*1024
1483
        l = last_byte_pos - first_byte_pos + 1
1484
        data = get_random_data(l)
1485
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1486
        self.client.update_object(c, o_name, StringIO(data), content_range=range)
1487
        content = self.client.retrieve_object(c, o_name)
1488
        self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1489
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1490
        self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1491
    
1492
    def test_update_object_no_content_length(self):
1493
        self.test_update_object(content_length = None)
1494
    
1495
    def test_update_object_invalid_content_length(self):
1496
        with AssertContentInvariant(self.client.retrieve_object,
1497
                                    self.containers[0], self.obj[0]['name']):
1498
            self.assert_raises_fault(400, self.test_update_object,
1499
                                     content_length = 1000)
1500
    
1501
    def _test_update_object_invalid_range(self):
1502
        with AssertContentInvariant(self.client.retrieve_object,
1503
                                    self.containers[0], self.obj[0]['name']):
1504
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1505
    
1506
    def _test_update_object_invalid_range_and_length(self):
1507
        with AssertContentInvariant(self.client.retrieve_object,
1508
                                    self.containers[0], self.obj[0]['name']):
1509
            self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1510
                                     -1)
1511
    
1512
    def test_update_object_invalid_range_with_no_content_length(self):
1513
        with AssertContentInvariant(self.client.retrieve_object,
1514
                                    self.containers[0], self.obj[0]['name']):
1515
            self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1516
                                     content_length = None)
1517
    
1518
    def test_update_object_out_of_limits(self):    
1519
        with AssertContentInvariant(self.client.retrieve_object,
1520
                                    self.containers[0], self.obj[0]['name']):
1521
            l = len(self.obj[0]['data'])
1522
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1523
    
1524
    def test_append(self):
1525
        data = get_random_data(500)
1526
        headers = {}
1527
        self.client.update_object(self.containers[0], self.obj[0]['name'],
1528
                                  StringIO(data), content_length=500,
1529
                                  content_type='application/octet-stream')
1530
        
1531
        content = self.client.retrieve_object(self.containers[0],
1532
                                              self.obj[0]['name'])
1533
        self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1534
        self.assertEqual(content[:-500], self.obj[0]['data'])
1535
    
1536
    def test_update_with_chunked_transfer(self):
1537
        data = get_random_data(500)
1538
        dl = len(data)
1539
        fl = len(self.obj[0]['data'])
1540
        
1541
        self.client.update_object_using_chunks(self.containers[0],
1542
                                               self.obj[0]['name'],
1543
                                               StringIO(data),
1544
                                               offset=0,
1545
                                               content_type='application/octet-stream')
1546
        
1547
        #check modified object
1548
        content = self.client.retrieve_object(self.containers[0],
1549
                                              self.obj[0]['name'])
1550
        self.assertEqual(content[0:dl], data)
1551
        self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1552
    
1553
    def test_update_from_other_object(self):
1554
        c = self.containers[0]
1555
        src = o_names[0]
1556
        dest = 'object'
1557
        
1558
        source_data = self.client.retrieve_object(c, src)
1559
        source_meta = self.client.retrieve_object_metadata(c, src)
1560
        source_hash = self.client.retrieve_object_hashmap(c, src)
1561
        
1562
        #update zero length object
1563
        self.client.create_zero_length_object(c, dest)
1564
        source_object = '/%s/%s' % (c, src)
1565
        self.client.update_from_other_source(c, dest, source_object)
1566
        dest_data = self.client.retrieve_object(c, src)
1567
        dest_meta = self.client.retrieve_object_metadata(c, dest)
1568
        dest_hash = self.client.retrieve_object_hashmap(c, src)
1569
        self.assertEqual(source_data, dest_data)
1570
        self.assertEqual(source_hash, dest_hash)
1571
        
1572
        #test append
1573
        self.client.update_from_other_source(c, dest, source_object)
1574
        content = self.client.retrieve_object(c, dest)
1575
        self.assertEqual(source_data * 2, content)
1576
    
1577
    def test_update_range_from_other_object(self):
1578
        c = self.containers[0]
1579
        dest = 'object'
1580
        
1581
        #test update range
1582
        src = self.obj[1]['name']
1583
        src_data = self.client.retrieve_object(c, src)
1584
        
1585
        #update zero length object
1586
        prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1587
        source_object = '/%s/%s' % (c, src)
1588
        first_byte_pos = 4*1024*1024+1
1589
        last_byte_pos = 4*1024*1024+4
1590
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1591
        self.client.update_from_other_source(c, dest, source_object,
1592
                                             content_range=range)
1593
        content = self.client.retrieve_object(c, dest)
1594
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1595
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1596
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1597
    
1598
    def test_update_hashes_from_other_object(self):
1599
        c = self.containers[0]
1600
        dest = 'object'
1601
        
1602
        #test update range
1603
        src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1604
        
1605
        #update zero length object
1606
        prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1607
        source_object = '/%s/%s' % (c, o_names[0])
1608
        first_byte_pos = 4*1024*1024
1609
        last_byte_pos = 5*1024*1024
1610
        range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1611
        self.client.update_from_other_source(c, dest, source_object,
1612
                                             content_range=range)
1613
        content = self.client.retrieve_object(c, dest)
1614
        self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1615
        self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1616
        self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1617
    
1618
    
1619
    def test_update_zero_length_object(self):
1620
        c = self.containers[0]
1621
        o = 'object'
1622
        other = 'other'
1623
        zero = self.client.create_zero_length_object(c, o)
1624
        
1625
        data = get_random_data()
1626
        self.client.update_object(c, o, StringIO(data))
1627
        self.client.create_object(c, other, StringIO(data))
1628
        
1629
        self.assertEqual(self.client.retrieve_object(c, o),
1630
                         self.client.retrieve_object(c, other))
1631
        
1632
        self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1633
                         self.client.retrieve_object_hashmap(c, other))
1634
    
1635
class ObjectDelete(BaseTestCase):
1636
    def setUp(self):
1637
        BaseTestCase.setUp(self)
1638
        self.containers = ['c1', 'c2']
1639
        for c in self.containers:
1640
            self.client.create_container(c)
1641
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1642
    
1643
    def test_delete(self):
1644
        #perform delete object
1645
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1646
    
1647
    def test_delete_invalid(self):
1648
        #assert item not found
1649
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1650
                                 self.obj['name'])
1651

    
1652
class ListSharing(BaseTestCase):
1653
    def setUp(self):
1654
        BaseTestCase.setUp(self)
1655
        for i in range(2):
1656
            self.client.create_container('c%s' %i)
1657
        self.client.create_container('c')
1658
        for i in range(2):
1659
            self.upload_random_data('c1', 'o%s' %i)
1660
        accounts = OTHER_ACCOUNTS.copy()
1661
        self.o1_sharing_with = accounts.popitem()
1662
        self.o1_sharing = [self.o1_sharing_with[1]]
1663
        self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1664
        
1665
        l = []
1666
        for i in range(2):
1667
            l.append(accounts.popitem())
1668
    
1669
    def test_list_other_shared(self):
1670
        self.other = Pithos_Client(get_server(),
1671
                              self.o1_sharing_with[0],
1672
                              self.o1_sharing_with[1],
1673
                              get_api())
1674
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1675
    
1676
    def test_list_my_shared(self):
1677
        my_shared_containers = self.client.list_containers(shared=True)
1678
        self.assertTrue('c1' in my_shared_containers)
1679
        self.assertTrue('c2' not in my_shared_containers)
1680
        
1681
        my_shared_objects = self.client.list_objects('c1', shared=True)
1682
        self.assertTrue('o1' in my_shared_objects)
1683
        self.assertTrue('o2' not in my_shared_objects)
1684
    
1685
class TestGreek(BaseTestCase):
1686
    def test_create_container(self):
1687
        self.client.create_container('φάκελος')
1688
        self.assert_container_exists('φάκελος')
1689
        
1690
        self.assertTrue('φάκελος' in self.client.list_containers())
1691
    
1692
    def test_create_object(self):
1693
        self.client.create_container('φάκελος')
1694
        self.upload_random_data('φάκελος', 'αντικείμενο')
1695
        
1696
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1697
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1698
    
1699
    def test_copy_object(self):
1700
        src_container = 'φάκελος'
1701
        src_object = 'αντικείμενο'
1702
        dest_container = 'αντίγραφα'
1703
        dest_object = 'ασφαλές-αντίγραφο'
1704
        
1705
        self.client.create_container(src_container)
1706
        self.upload_random_data(src_container, src_object)
1707
        
1708
        self.client.create_container(dest_container)
1709
        self.client.copy_object(src_container, src_object, dest_container,
1710
                                dest_object)
1711
        
1712
        self.assert_object_exists(src_container, src_object)
1713
        self.assert_object_exists(dest_container, dest_object)
1714
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1715
    
1716
    def test_move_object(self):
1717
        src_container = 'φάκελος'
1718
        src_object = 'αντικείμενο'
1719
        dest_container = 'αντίγραφα'
1720
        dest_object = 'ασφαλές-αντίγραφο'
1721
        
1722
        self.client.create_container(src_container)
1723
        self.upload_random_data(src_container, src_object)
1724
        
1725
        self.client.create_container(dest_container)
1726
        self.client.move_object(src_container, src_object, dest_container,
1727
                                dest_object)
1728
        
1729
        self.assert_object_not_exists(src_container, src_object)
1730
        self.assert_object_exists(dest_container, dest_object)
1731
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1732
    
1733
    def test_delete_object(self):
1734
        self.client.create_container('φάκελος')
1735
        self.upload_random_data('φάκελος', 'αντικείμενο')
1736
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1737
    
1738
        self.client.delete_object('φάκελος', 'αντικείμενο')
1739
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1740
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1741
    
1742
    def test_delete_container(self):
1743
        self.client.create_container('φάκελος')
1744
        self.assert_container_exists('φάκελος')
1745
        
1746
        self.client.delete_container('φάκελος')
1747
        self.assert_container_not_exists('φάκελος')
1748
        self.assertTrue('φάκελος' not in self.client.list_containers())
1749

    
1750
    def test_account_meta(self):
1751
        meta = {'ποιότητα':'ΑΑΑ'}
1752
        self.client.update_account_metadata(**meta)
1753
        meta = self.client.retrieve_account_metadata(restricted=True)
1754
        self.assertTrue('ποιότητα' in meta.keys())
1755
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1756
    
1757
    def test_container_meta(self):
1758
        meta = {'ποιότητα':'ΑΑΑ'}
1759
        self.client.create_container('φάκελος', **meta)
1760
        
1761
        meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1762
        self.assertTrue('ποιότητα' in meta.keys())
1763
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1764
    
1765
    def test_object_meta(self):
1766
        self.client.create_container('φάκελος')
1767
        meta = {'ποιότητα':'ΑΑΑ'}
1768
        self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1769
        
1770
        meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1771
                                                    restricted=True)
1772
        self.assertTrue('ποιότητα' in meta.keys())
1773
        self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1774
    
1775
    def test_list_meta_filtering(self):
1776
        self.client.create_container('φάκελος')
1777
        meta = {'ποιότητα':'ΑΑΑ'}
1778
        self.upload_random_data('φάκελος', 'ο1', **meta)
1779
        self.upload_random_data('φάκελος', 'ο2')
1780
        self.upload_random_data('φάκελος', 'ο3')
1781
        
1782
        meta = {'ποσότητα':'μεγάλη'}
1783
        self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1784
        objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1785
        self.assertTrue('ο1' in objects)
1786
        self.assertTrue('ο2' in objects)
1787
        self.assertTrue('ο3' not in objects)
1788
    
1789
    def test_groups(self):
1790
        #create a group
1791
        groups = {'σεφς':'chazapis,διογένης'}
1792
        self.client.set_account_groups(**groups)
1793
        groups.update(self.initial_groups)
1794
        self.assertEqual(groups['σεφς'],
1795
                         self.client.retrieve_account_groups()['σεφς'])
1796
        
1797
        #check read access
1798
        self.client.create_container('φάκελος')
1799
        o = self.upload_random_data('φάκελος', 'ο1')
1800
        self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1801
        chef = Pithos_Client(get_server(),
1802
                            '0009',
1803
                            'διογένης',
1804
                            get_api())
1805
        self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1806
                                     'φάκελος', 'ο1', account=get_user())
1807
        
1808
        #check write access
1809
        self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1810
        new_data = get_random_data()
1811
        self.assert_not_raises_fault(403, chef.update_object,
1812
                                     'φάκελος', 'ο1', StringIO(new_data),
1813
                                     account=get_user())
1814
        
1815
        server_data = self.client.retrieve_object('φάκελος', 'ο1')
1816
        self.assertEqual(server_data[:len(o['data'])], o['data'])
1817
        self.assertEqual(server_data[len(o['data']):], new_data)
1818
    
1819
    def test_manifestation(self):
1820
        self.client.create_container('κουβάς')
1821
        prefix = 'μέρη/'
1822
        data = ''
1823
        for i in range(5):
1824
            part = '%s%d' %(prefix, i)
1825
            o = self.upload_random_data('κουβάς', part)
1826
            data += o['data']
1827
        
1828
        self.client.create_container('φάκελος')
1829
        manifest = '%s/%s' %('κουβάς', prefix)
1830
        self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1831
        
1832
        self.assert_object_exists('φάκελος', 'άπαντα')
1833
        self.assertEqual(data, self.client.retrieve_object('φάκελος',
1834
                                                           'άπαντα'))
1835
        
1836
        #wrong manifestation
1837
        self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1838
        self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1839
    
1840
    def test_update_from_another_object(self):
1841
        self.client.create_container('κουβάς')
1842
        src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1843
        initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1844
        source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1845
        self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1846
        
1847
        self.assertEqual(
1848
            self.client.retrieve_object('κουβάς', 'νέο'),
1849
            '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1850

    
1851
class TestPermissions(BaseTestCase):
1852
    def setUp(self):
1853
        BaseTestCase.setUp(self)
1854
        
1855
        #create a group
1856
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
1857
        groups = {'pithosdev':','.join(self.authorized)}
1858
        self.client.set_account_groups(**groups)
1859
    
1860
    def assert_read(self, authorized=[], any=False):
1861
        for token, account in OTHER_ACCOUNTS.items():
1862
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1863
            if account in authorized or any:
1864
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1865
                                             'c', 'o', account=get_user())
1866
            else:
1867
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1868
                                         'c', 'o', account=get_user())
1869
        
1870
        #check inheritance
1871
        o = self.upload_random_data('c', 'o/also-shared')
1872
        for token, account in OTHER_ACCOUNTS.items():
1873
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1874
            if account in authorized or any:
1875
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1876
                                             'c', 'o/also-shared', account=get_user())
1877
            else:
1878
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1879
                                         'c', 'o/also-shared', account=get_user())
1880
    
1881
    def assert_write(self, o_data, authorized=[], any=False):
1882
        for token, account in OTHER_ACCOUNTS.items():
1883
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1884
            new_data = get_random_data()
1885
            if account in authorized or any:
1886
                # test write access
1887
                self.assert_not_raises_fault(403, cl.update_object,
1888
                                             'c', 'o', StringIO(new_data),
1889
                                             account=get_user())
1890
                try:
1891
                    # test read access
1892
                    server_data = cl.retrieve_object('c', 'o', account=get_user())
1893
                    self.assertEqual(o_data, server_data[:len(o_data)])
1894
                    self.assertEqual(new_data, server_data[len(o_data):])
1895
                    o_data = server_data
1896
                except Fault, f:
1897
                    self.failIf(f.status == 403)
1898
            else:
1899
                self.assert_raises_fault(403, cl.update_object,
1900
                                             'c', 'o', StringIO(new_data),
1901
                                             account=get_user())
1902
        
1903
        #check inheritance
1904
        o = self.upload_random_data('c', 'o/also-shared')
1905
        o_data = o['data']
1906
        for token, account in OTHER_ACCOUNTS.items():
1907
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1908
            new_data = get_random_data()
1909
            if account in authorized or any:
1910
                # test write access
1911
                self.assert_not_raises_fault(403, cl.update_object,
1912
                                             'c', o['name'],
1913
                                             StringIO(new_data),
1914
                                             account=get_user())
1915
                try:
1916
                    server_data = cl.retrieve_object('c', o['name'], account=get_user())
1917
                    self.assertEqual(o_data, server_data[:len(o_data)])
1918
                    self.assertEqual(new_data, server_data[len(o_data):])
1919
                    o_data = server_data
1920
                except Fault, f:
1921
                    self.failIf(f.status == 403)
1922
            else:
1923
                self.assert_raises_fault(403, cl.update_object,
1924
                                             'c', o['name'],
1925
                                             StringIO(new_data),
1926
                                             account=get_user())
1927
    
1928
    def test_group_read(self):
1929
        self.client.create_container('c')
1930
        o = self.upload_random_data('c', 'o')
1931
        self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1932
        self.assert_read(authorized=self.authorized)
1933
    
1934
    def test_read_many(self):
1935
        #test read access
1936
        self.client.create_container('c')
1937
        o = self.upload_random_data('c', 'o')
1938
        self.client.share_object('c', 'o', self.authorized)
1939
        self.assert_read(authorized=self.authorized)
1940
    
1941
    def test_read_by_everyone(self):
1942
        self.client.create_container('c')
1943
        o = self.upload_random_data('c', 'o')
1944
        self.client.share_object('c', 'o', ['*'])
1945
        self.assert_read(any=True)
1946
    
1947
    def test_group_write(self):
1948
        self.client.create_container('c')
1949
        o = self.upload_random_data('c', 'o')
1950
        self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1951
        self.assert_write(o['data'], authorized=self.authorized)
1952
    
1953
    def test_write_many(self):
1954
        self.client.create_container('c')
1955
        o = self.upload_random_data('c', 'o')
1956
        self.client.share_object('c', 'o', self.authorized, read=False)
1957
        self.assert_write(o['data'], authorized=self.authorized)
1958
    
1959
    def test_write_by_everyone(self):
1960
        self.client.create_container('c')
1961
        o = self.upload_random_data('c', 'o')
1962
        self.client.share_object('c', 'o', ['*'], read=False)
1963
        o_data = o['data']
1964
        self.assert_write(o['data'], any=True)
1965

    
1966
class TestPublish(BaseTestCase):
1967
    def test_publish(self):
1968
        self.client.create_container('c')
1969
        o_data = self.upload_random_data('c', 'o')['data']
1970
        self.client.publish_object('c', 'o')
1971
        meta = self.client.retrieve_object_metadata('c', 'o')
1972
        self.assertTrue('x-object-public' in meta)
1973
        url = meta['x-object-public']
1974
        public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
1975
        data = public_client.get(url)[2]
1976
        self.assertEqual(o_data, data)
1977

    
1978
class AssertMappingInvariant(object):
1979
    def __init__(self, callable, *args, **kwargs):
1980
        self.callable = callable
1981
        self.args = args
1982
        self.kwargs = kwargs
1983
    
1984
    def __enter__(self):
1985
        self.map = self.callable(*self.args, **self.kwargs)
1986
        return self.map
1987
    
1988
    def __exit__(self, type, value, tb):
1989
        map = self.callable(*self.args, **self.kwargs)
1990
        for k in self.map.keys():
1991
            if is_date(self.map[k]):
1992
                continue
1993
            assert map[k] == self.map[k]
1994

    
1995
class AssertContentInvariant(object):
1996
    def __init__(self, callable, *args, **kwargs):
1997
        self.callable = callable
1998
        self.args = args
1999
        self.kwargs = kwargs
2000
    
2001
    def __enter__(self):
2002
        self.content = self.callable(*self.args, **self.kwargs)[2]
2003
        return self.content
2004
    
2005
    def __exit__(self, type, value, tb):
2006
        content = self.callable(*self.args, **self.kwargs)[2]
2007
        assert self.content == content
2008

    
2009
def get_content_splitted(response):
2010
    if response:
2011
        return response.content.split('\n')
2012

    
2013
def compute_md5_hash(data):
2014
    md5 = hashlib.md5()
2015
    offset = 0
2016
    md5.update(data)
2017
    return md5.hexdigest().lower()
2018

    
2019
def compute_block_hash(data, algorithm):
2020
    h = hashlib.new(algorithm)
2021
    h.update(data.rstrip('\x00'))
2022
    return h.hexdigest()
2023

    
2024
def get_random_data(length=500):
2025
    char_set = string.ascii_uppercase + string.digits
2026
    return ''.join(random.choice(char_set) for x in range(length))
2027

    
2028
def is_date(date):
2029
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2030
    __D = r'(?P<day>\d{2})'
2031
    __D2 = r'(?P<day>[ \d]\d)'
2032
    __M = r'(?P<mon>\w{3})'
2033
    __Y = r'(?P<year>\d{4})'
2034
    __Y2 = r'(?P<year>\d{2})'
2035
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2036
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2037
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2038
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2039
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2040
        m = regex.match(date)
2041
        if m is not None:
2042
            return True
2043
    return False
2044

    
2045
o_names = ['kate.jpg',
2046
           'kate_beckinsale.jpg',
2047
           'How To Win Friends And Influence People.pdf',
2048
           'moms_birthday.jpg',
2049
           'poodle_strut.mov',
2050
           'Disturbed - Down With The Sickness.mp3',
2051
           'army_of_darkness.avi',
2052
           'the_mad.avi',
2053
           'photos/animals/dogs/poodle.jpg',
2054
           'photos/animals/dogs/terrier.jpg',
2055
           'photos/animals/cats/persian.jpg',
2056
           'photos/animals/cats/siamese.jpg',
2057
           'photos/plants/fern.jpg',
2058
           'photos/plants/rose.jpg',
2059
           'photos/me.jpg']
2060

    
2061
if __name__ == "__main__":
2062
    if get_user() == 'test':
2063
        unittest.main()
2064
    else:
2065
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()