Statistics
| Branch: | Tag: | Revision:

root / pithos / tools / pithos-test @ 43cc6f76

History | View | Annotate | Download (87.1 kB)

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

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

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

    
40
from xml.dom import minidom
41
from StringIO import StringIO
42
from hashlib import new as newhasher
43
from binascii import hexlify
44

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1884
class TestPermissions(BaseTestCase):
1885
    def setUp(self):
1886
        BaseTestCase.setUp(self)
1887
        
1888
        #create a group
1889
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
1890
        groups = {'pithosdev':','.join(self.authorized)}
1891
        self.client.set_account_groups(**groups)
1892
    
1893
    def assert_read(self, authorized=[], any=False):
1894
        for token, account in OTHER_ACCOUNTS.items():
1895
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1896
            if account in authorized or any:
1897
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1898
                                             'c', 'o', account=get_user())
1899
            else:
1900
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1901
                                         'c', 'o', account=get_user())
1902
        
1903
        #check inheritance
1904
        o = self.upload_random_data('c', 'o/also-shared')
1905
        for token, account in OTHER_ACCOUNTS.items():
1906
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1907
            if account in authorized or any:
1908
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1909
                                             'c', 'o/also-shared', account=get_user())
1910
            else:
1911
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1912
                                         'c', 'o/also-shared', account=get_user())
1913
    
1914
    def assert_write(self, o_data, authorized=[], any=False):
1915
        for token, account in OTHER_ACCOUNTS.items():
1916
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1917
            new_data = get_random_data()
1918
            if account in authorized or any:
1919
                # test write access
1920
                self.assert_not_raises_fault(403, cl.update_object,
1921
                                             'c', 'o', StringIO(new_data),
1922
                                             account=get_user())
1923
                try:
1924
                    # test read access
1925
                    server_data = cl.retrieve_object('c', 'o', account=get_user())
1926
                    self.assertEqual(o_data, server_data[:len(o_data)])
1927
                    self.assertEqual(new_data, server_data[len(o_data):])
1928
                    o_data = server_data
1929
                except Fault, f:
1930
                    self.failIf(f.status == 403)
1931
            else:
1932
                self.assert_raises_fault(403, cl.update_object,
1933
                                             'c', 'o', StringIO(new_data),
1934
                                             account=get_user())
1935
        
1936
        #check inheritance
1937
        o = self.upload_random_data('c', 'o/also-shared')
1938
        o_data = o['data']
1939
        for token, account in OTHER_ACCOUNTS.items():
1940
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1941
            new_data = get_random_data()
1942
            if account in authorized or any:
1943
                # test write access
1944
                self.assert_not_raises_fault(403, cl.update_object,
1945
                                             'c', o['name'],
1946
                                             StringIO(new_data),
1947
                                             account=get_user())
1948
                try:
1949
                    server_data = cl.retrieve_object('c', o['name'], account=get_user())
1950
                    self.assertEqual(o_data, server_data[:len(o_data)])
1951
                    self.assertEqual(new_data, server_data[len(o_data):])
1952
                    o_data = server_data
1953
                except Fault, f:
1954
                    self.failIf(f.status == 403)
1955
            else:
1956
                self.assert_raises_fault(403, cl.update_object,
1957
                                             'c', o['name'],
1958
                                             StringIO(new_data),
1959
                                             account=get_user())
1960
    
1961
    def test_group_read(self):
1962
        self.client.create_container('c')
1963
        o = self.upload_random_data('c', 'o')
1964
        self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1965
        self.assert_read(authorized=self.authorized)
1966
    
1967
    def test_read_many(self):
1968
        #test read access
1969
        self.client.create_container('c')
1970
        o = self.upload_random_data('c', 'o')
1971
        self.client.share_object('c', 'o', self.authorized)
1972
        self.assert_read(authorized=self.authorized)
1973
    
1974
    def test_read_by_everyone(self):
1975
        self.client.create_container('c')
1976
        o = self.upload_random_data('c', 'o')
1977
        self.client.share_object('c', 'o', ['*'])
1978
        self.assert_read(any=True)
1979
    
1980
    def test_group_write(self):
1981
        self.client.create_container('c')
1982
        o = self.upload_random_data('c', 'o')
1983
        self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1984
        self.assert_write(o['data'], authorized=self.authorized)
1985
    
1986
    def test_write_many(self):
1987
        self.client.create_container('c')
1988
        o = self.upload_random_data('c', 'o')
1989
        self.client.share_object('c', 'o', self.authorized, read=False)
1990
        self.assert_write(o['data'], authorized=self.authorized)
1991
    
1992
    def test_write_by_everyone(self):
1993
        self.client.create_container('c')
1994
        o = self.upload_random_data('c', 'o')
1995
        self.client.share_object('c', 'o', ['*'], read=False)
1996
        o_data = o['data']
1997
        self.assert_write(o['data'], any=True)
1998

    
1999
class TestPublish(BaseTestCase):
2000
    def test_publish(self):
2001
        self.client.create_container('c')
2002
        o_data = self.upload_random_data('c', 'o')['data']
2003
        self.client.publish_object('c', 'o')
2004
        meta = self.client.retrieve_object_metadata('c', 'o')
2005
        self.assertTrue('x-object-public' in meta)
2006
        url = meta['x-object-public']
2007
        public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2008
        data = public_client.get(url)[2]
2009
        self.assertEqual(o_data, data)
2010

    
2011
class AssertUUidInvariant(object):
2012
    def __init__(self, callable, *args, **kwargs):
2013
        self.callable = callable
2014
        self.args = args
2015
        self.kwargs = kwargs
2016
    
2017
    def __enter__(self):
2018
        self.map = self.callable(*self.args, **self.kwargs)
2019
        assert('x-object-uuid' in self.map)
2020
        self.uuid = self.map['x-object-uuid']
2021
        return self.map
2022
    
2023
    def __exit__(self, type, value, tb):
2024
        map = self.callable(*self.args, **self.kwargs)
2025
        assert('x-object-uuid' in self.map)
2026
        uuid = map['x-object-uuid']
2027
        assert(uuid == self.uuid)
2028

    
2029
class AssertMappingInvariant(object):
2030
    def __init__(self, callable, *args, **kwargs):
2031
        self.callable = callable
2032
        self.args = args
2033
        self.kwargs = kwargs
2034
    
2035
    def __enter__(self):
2036
        self.map = self.callable(*self.args, **self.kwargs)
2037
        return self.map
2038
    
2039
    def __exit__(self, type, value, tb):
2040
        map = self.callable(*self.args, **self.kwargs)
2041
        for k, v in self.map.items():
2042
            if is_date(v):
2043
                continue
2044
            #print '#', k, v, map[k]
2045
            assert(k in map)
2046
            assert v == map[k]
2047
    
2048
class AssertContentInvariant(object):
2049
    def __init__(self, callable, *args, **kwargs):
2050
        self.callable = callable
2051
        self.args = args
2052
        self.kwargs = kwargs
2053
    
2054
    def __enter__(self):
2055
        self.content = self.callable(*self.args, **self.kwargs)[2]
2056
        return self.content
2057
    
2058
    def __exit__(self, type, value, tb):
2059
        content = self.callable(*self.args, **self.kwargs)[2]
2060
        assert self.content == content
2061

    
2062
def get_content_splitted(response):
2063
    if response:
2064
        return response.content.split('\n')
2065

    
2066
def compute_md5_hash(data):
2067
    md5 = hashlib.md5()
2068
    offset = 0
2069
    md5.update(data)
2070
    return md5.hexdigest().lower()
2071

    
2072
def compute_block_hash(data, algorithm):
2073
    h = hashlib.new(algorithm)
2074
    h.update(data.rstrip('\x00'))
2075
    return h.hexdigest()
2076

    
2077
def get_random_data(length=500):
2078
    char_set = string.ascii_uppercase + string.digits
2079
    return ''.join(random.choice(char_set) for x in range(length))
2080

    
2081
def is_date(date):
2082
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2083
    __D = r'(?P<day>\d{2})'
2084
    __D2 = r'(?P<day>[ \d]\d)'
2085
    __M = r'(?P<mon>\w{3})'
2086
    __Y = r'(?P<year>\d{4})'
2087
    __Y2 = r'(?P<year>\d{2})'
2088
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2089
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2090
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2091
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2092
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2093
        m = regex.match(date)
2094
        if m is not None:
2095
            return True
2096
    return False
2097

    
2098
o_names = ['kate.jpg',
2099
           'kate_beckinsale.jpg',
2100
           'How To Win Friends And Influence People.pdf',
2101
           'moms_birthday.jpg',
2102
           'poodle_strut.mov',
2103
           'Disturbed - Down With The Sickness.mp3',
2104
           'army_of_darkness.avi',
2105
           'the_mad.avi',
2106
           'photos/animals/dogs/poodle.jpg',
2107
           'photos/animals/dogs/terrier.jpg',
2108
           'photos/animals/cats/persian.jpg',
2109
           'photos/animals/cats/siamese.jpg',
2110
           'photos/plants/fern.jpg',
2111
           'photos/plants/rose.jpg',
2112
           'photos/me.jpg']
2113

    
2114
if __name__ == "__main__":
2115
    if get_user() == 'test':
2116
        unittest.main()
2117
    else:
2118
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()