Statistics
| Branch: | Tag: | Revision:

root / snf-pithos-tools / pithos / tools / test.py @ 6e147ecc

History | View | Annotate | Download (92.8 kB)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1894
class TestPermissions(BaseTestCase):
1895
    def setUp(self):
1896
        BaseTestCase.setUp(self)
1897
        
1898
        #create a group
1899
        self.authorized = ['chazapis', 'verigak', 'gtsouk']
1900
        groups = {'pithosdev':','.join(self.authorized)}
1901
        self.client.set_account_groups(**groups)
1902
        
1903
        self.container = 'c'
1904
        self.object = 'o'
1905
        self.client.create_container(self.container)
1906
        self.upload_random_data(self.container, self.object)
1907
        self.upload_random_data(self.container, self.object+'/')
1908
        self.upload_random_data(self.container, self.object+'/a')
1909
        self.upload_random_data(self.container, self.object+'a')
1910
        self.upload_random_data(self.container, self.object+'a/')
1911
        self.dir_content_types = ('application/directory', 'application/folder')
1912
    
1913
    def assert_read(self, authorized=[], any=False, depth=0):
1914
        for token, account in OTHER_ACCOUNTS.items():
1915
            cl = Pithos_Client(get_url(), token, account)
1916
            if account in authorized or any:
1917
                self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1918
                                             self.container, self.object,
1919
                                             account=get_user())
1920
            else:
1921
                self.assert_raises_fault(403, cl.retrieve_object_metadata,
1922
                                         self.container, self.object,
1923
                                         account=get_user())
1924
        
1925
        #check inheritance
1926
        meta = self.client.retrieve_object_metadata(self.container, self.object)
1927
        type = meta['content-type']
1928
        derivatives = self.client.list_objects(self.container, prefix=self.object)
1929
        #exclude the self.object
1930
        del derivatives[derivatives.index(self.object)]
1931
        for o in derivatives:
1932
            for token, account in OTHER_ACCOUNTS.items():
1933
                cl = Pithos_Client(get_url(), token, account)
1934
                prefix = self.object if self.object.endswith('/') else self.object+'/'
1935
                if (account in authorized or any) and \
1936
                (type in self.dir_content_types) and \
1937
                o.startswith(prefix):
1938
                    self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1939
                                             self.container, o, account=get_user())
1940
                else:
1941
                    self.assert_raises_fault(403, cl.retrieve_object_metadata,
1942
                                         self.container, o, account=get_user())
1943
    
1944
    def assert_write(self, authorized=[], any=False):
1945
        o_data = self.client.retrieve_object(self.container, self.object)
1946
        for token, account in OTHER_ACCOUNTS.items():
1947
            cl = Pithos_Client(get_url(), token, account)
1948
            new_data = get_random_data()
1949
            if account in authorized or any:
1950
                # test write access
1951
                self.assert_not_raises_fault(403, cl.update_object,
1952
                                             self.container, self.object, StringIO(new_data),
1953
                                             account=get_user())
1954
                try:
1955
                    # test read access
1956
                    server_data = cl.retrieve_object(self.container, self.object, account=get_user())
1957
                    self.assertEqual(o_data, server_data[:len(o_data)])
1958
                    self.assertEqual(new_data, server_data[len(o_data):])
1959
                    o_data = server_data
1960
                except Fault, f:
1961
                    self.failIf(f.status == 403)
1962
            else:
1963
                self.assert_raises_fault(403, cl.update_object,
1964
                                             self.container, self.object, StringIO(new_data),
1965
                                             account=get_user())
1966
        #check inheritance
1967
        meta = self.client.retrieve_object_metadata(self.container, self.object)
1968
        type = meta['content-type']
1969
        derivatives = self.client.list_objects(self.container, prefix=self.object)
1970
        #exclude the object
1971
        del derivatives[derivatives.index(self.object)]
1972
        for o in derivatives:
1973
            for token, account in OTHER_ACCOUNTS.items():
1974
                prefix = self.object if self.object.endswith('/') else self.object+'/'
1975
                cl = Pithos_Client(get_url(), token, account)
1976
                new_data = get_random_data()
1977
                if (account in authorized or any) and \
1978
                (type in self.dir_content_types) and \
1979
                o.startswith(prefix):
1980
                    # test write access
1981
                    self.assert_not_raises_fault(403, cl.update_object,
1982
                                                 self.container, o,
1983
                                                 StringIO(new_data),
1984
                                                 account=get_user())
1985
                    try:
1986
                        server_data = cl.retrieve_object(self.container, o, account=get_user())
1987
                        self.assertEqual(new_data, server_data[-len(new_data):])
1988
                    except Fault, f:
1989
                        self.failIf(f.status == 403)
1990
                else:
1991
                    self.assert_raises_fault(403, cl.update_object,
1992
                                                 self.container, o,
1993
                                                 StringIO(new_data),
1994
                                                 account=get_user())
1995
    
1996
    def test_group_read(self):
1997
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
1998
        self.assert_read(authorized=self.authorized)
1999
    
2000
    def test_read_many(self):
2001
        self.client.share_object(self.container, self.object, self.authorized)
2002
        self.assert_read(authorized=self.authorized)
2003
    
2004
    def test_read_by_everyone(self):
2005
        self.client.share_object(self.container, self.object, ['*'])
2006
        self.assert_read(any=True)
2007
    
2008
    def test_read_directory(self):
2009
        for type in self.dir_content_types:
2010
            #change content type
2011
            self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2012
            self.client.share_object(self.container, self.object, ['*'])
2013
            self.assert_read(any=True)
2014
            self.client.share_object(self.container, self.object, self.authorized)
2015
            self.assert_read(authorized=self.authorized)
2016
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2017
            self.assert_read(authorized=self.authorized)
2018
    
2019
    def test_group_write(self):
2020
        self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2021
        self.assert_write(authorized=self.authorized)
2022
    
2023
    def test_write_many(self):
2024
        self.client.share_object(self.container, self.object, self.authorized, read=False)
2025
        self.assert_write(authorized=self.authorized)
2026
    
2027
    def test_write_by_everyone(self):
2028
        self.client.share_object(self.container, self.object, ['*'], read=False)
2029
        self.assert_write(any=True)
2030
    
2031
    def test_write_directory(self):
2032
        dir_content_types = ('application/directory', 'application/foler')
2033
        for type in dir_content_types:
2034
            #change content type
2035
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2036
            self.client.share_object(self.container, self.object, ['*'], read=False)
2037
            self.assert_write(any=True)
2038
            self.client.share_object(self.container, self.object, self.authorized, read=False)
2039
            self.assert_write(authorized=self.authorized)
2040
            self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2041
            self.assert_write(authorized=self.authorized)
2042
    
2043
    def test_shared_listing(self):
2044
        self.client.share_object(self.container, self.object, self.authorized)
2045
        
2046
        my_shared_containers = self.client.list_containers(shared=True)
2047
        self.assertEqual(['c'], my_shared_containers)
2048
        my_shared_objects = self.client.list_objects('c', shared=True)
2049
        self.assertEqual(['o'], my_shared_objects)
2050
        
2051
        dir_content_types = ('application/directory', 'application/foler')
2052
        for type in dir_content_types:
2053
            #change content type
2054
            self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2055
            my_shared_objects = self.client.list_objects('c', shared=True)
2056
            self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2057
        
2058
        for token, account in OTHER_ACCOUNTS.items():
2059
            if account in self.authorized:
2060
                self.other = Pithos_Client(get_url(), token, account)
2061
                self.assertTrue(get_user() in self.other.list_shared_by_others())
2062
    
2063
class TestPublish(BaseTestCase):
2064
    def test_publish(self):
2065
        self.client.create_container('c')
2066
        o_data = self.upload_random_data('c', 'o')['data']
2067
        self.client.publish_object('c', 'o')
2068
        meta = self.client.retrieve_object_metadata('c', 'o')
2069
        self.assertTrue('x-object-public' in meta)
2070
        url = meta['x-object-public']
2071
        
2072
        p = urlparse(get_url())
2073
        if p.scheme == 'http':
2074
            conn = HTTPConnection(p.netloc)
2075
        elif p.scheme == 'https':
2076
            conn = HTTPSConnection(p.netloc)
2077
        else:
2078
            raise Exception('Unknown URL scheme')
2079
        
2080
        conn.request('GET', url)
2081
        resp = conn.getresponse()
2082
        length = resp.getheader('content-length', None)
2083
        data = resp.read(length)
2084
        self.assertEqual(o_data, data)
2085

    
2086
class TestPolicies(BaseTestCase):
2087
    def test_none_versioning(self):
2088
        self.client.create_container('c', policies={'versioning':'none'})
2089
        o = self.upload_random_data('c', 'o')
2090
        meta = self.client.retrieve_object_metadata('c', 'o')
2091
        v = meta['x-object-version']
2092
        more_data = get_random_data()
2093
        self.client.update_object('c', 'o', StringIO(more_data))
2094
        vlist = self.client.retrieve_object_versionlist('c', 'o')
2095
        self.assert_raises_fault(404, self.client.retrieve_object_version,
2096
                                 'c', 'o', v)
2097
        data = self.client.retrieve_object('c', 'o')
2098
        end = len(o['data'])
2099
        self.assertEqual(data[:end], o['data'])
2100
        self.assertEqual(data[end:], more_data)
2101
    
2102
    def test_quota(self):
2103
        self.client.create_container('c', policies={'quota':'1'})
2104
        meta = self.client.retrieve_container_metadata('c')
2105
        self.assertEqual(meta['x-container-policy-quota'], '1')
2106
        self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2107
                                 length=1024*1024+1)
2108
    
2109
    def test_quota_none(self):
2110
        self.client.create_container('c', policies={'quota':'0'})
2111
        meta = self.client.retrieve_container_metadata('c')
2112
        self.assertEqual(meta['x-container-policy-quota'], '0')
2113
        self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2114
                                 length=1024*1024+1)
2115

    
2116
class AssertUUidInvariant(object):
2117
    def __init__(self, callable, *args, **kwargs):
2118
        self.callable = callable
2119
        self.args = args
2120
        self.kwargs = kwargs
2121
    
2122
    def __enter__(self):
2123
        self.map = self.callable(*self.args, **self.kwargs)
2124
        assert('x-object-uuid' in self.map)
2125
        self.uuid = self.map['x-object-uuid']
2126
        return self.map
2127
    
2128
    def __exit__(self, type, value, tb):
2129
        map = self.callable(*self.args, **self.kwargs)
2130
        assert('x-object-uuid' in self.map)
2131
        uuid = map['x-object-uuid']
2132
        assert(uuid == self.uuid)
2133

    
2134
class AssertMappingInvariant(object):
2135
    def __init__(self, callable, *args, **kwargs):
2136
        self.callable = callable
2137
        self.args = args
2138
        self.kwargs = kwargs
2139
    
2140
    def __enter__(self):
2141
        self.map = self.callable(*self.args, **self.kwargs)
2142
        return self.map
2143
    
2144
    def __exit__(self, type, value, tb):
2145
        map = self.callable(*self.args, **self.kwargs)
2146
        for k, v in self.map.items():
2147
            if is_date(v):
2148
                continue
2149
            #print '#', k, v, map[k]
2150
            assert(k in map)
2151
            assert v == map[k]
2152
    
2153
class AssertContentInvariant(object):
2154
    def __init__(self, callable, *args, **kwargs):
2155
        self.callable = callable
2156
        self.args = args
2157
        self.kwargs = kwargs
2158
    
2159
    def __enter__(self):
2160
        self.content = self.callable(*self.args, **self.kwargs)[2]
2161
        return self.content
2162
    
2163
    def __exit__(self, type, value, tb):
2164
        content = self.callable(*self.args, **self.kwargs)[2]
2165
        assert self.content == content
2166

    
2167
def get_content_splitted(response):
2168
    if response:
2169
        return response.content.split('\n')
2170

    
2171
def compute_md5_hash(data):
2172
    md5 = hashlib.md5()
2173
    offset = 0
2174
    md5.update(data)
2175
    return md5.hexdigest().lower()
2176

    
2177
def compute_block_hash(data, algorithm):
2178
    h = hashlib.new(algorithm)
2179
    h.update(data.rstrip('\x00'))
2180
    return h.hexdigest()
2181

    
2182
def get_random_data(length=500):
2183
    char_set = string.ascii_uppercase + string.digits
2184
    return ''.join(random.choice(char_set) for x in xrange(length))
2185

    
2186
def is_date(date):
2187
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2188
    __D = r'(?P<day>\d{2})'
2189
    __D2 = r'(?P<day>[ \d]\d)'
2190
    __M = r'(?P<mon>\w{3})'
2191
    __Y = r'(?P<year>\d{4})'
2192
    __Y2 = r'(?P<year>\d{2})'
2193
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2194
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2195
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2196
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2197
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2198
        m = regex.match(date)
2199
        if m is not None:
2200
            return True
2201
    return False
2202

    
2203
o_names = ['kate.jpg',
2204
           'kate_beckinsale.jpg',
2205
           'How To Win Friends And Influence People.pdf',
2206
           'moms_birthday.jpg',
2207
           'poodle_strut.mov',
2208
           'Disturbed - Down With The Sickness.mp3',
2209
           'army_of_darkness.avi',
2210
           'the_mad.avi',
2211
           'photos/animals/dogs/poodle.jpg',
2212
           'photos/animals/dogs/terrier.jpg',
2213
           'photos/animals/cats/persian.jpg',
2214
           'photos/animals/cats/siamese.jpg',
2215
           'photos/plants/fern.jpg',
2216
           'photos/plants/rose.jpg',
2217
           'photos/me.jpg']
2218

    
2219

    
2220
def main():
2221
    if get_user() == 'test':
2222
        unittest.main()
2223
    else:
2224
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2225

    
2226

    
2227
if __name__ == "__main__":
2228
    main()
2229