Statistics
| Branch: | Tag: | Revision:

root / tools / test @ 70516d86

History | View | Annotate | Download (85.4 kB)

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

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

    
37
from lib.client import Pithos_Client, Fault
38
from lib.util import get_user, get_auth, get_server, get_api
39
from xml.dom import minidom
40
from StringIO import StringIO
41
import json
42
import unittest
43
import time as _time
44
import types
45
import hashlib
46
import mimetypes
47
import random
48
import datetime
49
import string
50
import re
51

    
52
DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
53
                "%A, %d-%b-%y %H:%M:%S GMT",
54
                "%a, %d %b %Y %H:%M:%S GMT"]
55

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

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

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

    
593
class ContainerGet(BaseTestCase):
594
    def setUp(self):
595
        BaseTestCase.setUp(self)
596
        self.container = ['pears', 'apples']
597
        for c in self.container:
598
            self.client.create_container(c)
599
        self.obj = []
600
        for o in o_names[:8]:
601
            self.obj.append(self.upload_random_data(self.container[0], o))
602
        for o in o_names[8:]:
603
            self.obj.append(self.upload_random_data(self.container[1], o))
604
    
605
    def test_list_objects(self):
606
        objects = self.client.list_objects(self.container[0])
607
        l = [elem['name'] for elem in self.obj[:8]]
608
        l.sort()
609
        self.assertEqual(objects, l)
610
    
611
    def test_list_objects_containing_slash(self):
612
        self.client.create_container('test')
613
        self.upload_random_data('test', '/objectname')
614
        
615
        objects = self.client.list_objects('test')
616
        self.assertEqual(objects, ['/objectname'])
617
        
618
        objects = self.client.list_objects('test', format='json')
619
        self.assertEqual(objects[0]['name'], '/objectname')
620
        
621
        objects = self.client.list_objects('test', format='xml')
622
        self.assert_extended(objects, 'xml', 'object')
623
        node_name = objects.getElementsByTagName('name')[0]
624
        self.assertEqual(node_name.firstChild.data, '/objectname')
625
        
626
        #objects = self.client.list_objects('test', prefix='/')
627
        #self.assertEqual(objects, ['/objectname'])
628
        #
629
        #objects = self.client.list_objects('test', path='/')
630
        #self.assertEqual(objects, ['/objectname'])
631
        #
632
        #objects = self.client.list_objects('test', prefix='/', delimiter='n')
633
        #self.assertEqual(objects, ['/object'])
634

    
635
    def test_list_objects_with_limit_marker(self):
636
        objects = self.client.list_objects(self.container[0], limit=2)
637
        l = [elem['name'] for elem in self.obj[:8]]
638
        l.sort()
639
        self.assertEqual(objects, l[:2])
640
        
641
        markers = ['How To Win Friends And Influence People.pdf',
642
                   'moms_birthday.jpg']
643
        limit = 4
644
        for m in markers:
645
            objects = self.client.list_objects(self.container[0], limit=limit,
646
                                               marker=m)
647
            l = [elem['name'] for elem in self.obj[:8]]
648
            l.sort()
649
            start = l.index(m) + 1
650
            end = start + limit
651
            end = len(l) >= end and end or len(l)
652
            self.assertEqual(objects, l[start:end])
653
    
654
    #takes too long
655
    def _test_list_limit_exceeds(self):
656
        self.client.create_container('pithos')
657
        
658
        for i in range(10001):
659
            self.client.create_zero_length_object('pithos', i)
660
        
661
        self.assertEqual(10000, len(self.client.list_objects('pithos')))
662
    
663
    def test_list_empty_params(self):
664
        objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
665
        if objects:
666
            objects = objects.strip().split('\n')
667
        self.assertEqual(objects,
668
                         self.client.list_objects(self.container[0]))
669
    
670
    def test_list_pseudo_hierarchical_folders(self):
671
        objects = self.client.list_objects(self.container[1], prefix='photos',
672
                                           delimiter='/')
673
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
674
                           'photos/plants/'], objects)
675
        
676
        objects = self.client.list_objects(self.container[1],
677
                                           prefix='photos/animals',
678
                                           delimiter='/')
679
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
680
        self.assertEquals(l, objects)
681
        
682
        objects = self.client.list_objects(self.container[1], path='photos')
683
        self.assertEquals(['photos/me.jpg'], objects)
684
    
685
    def test_extended_list_json(self):
686
        objects = self.client.list_objects(self.container[1], format='json',
687
                                           limit=2, prefix='photos/animals',
688
                                           delimiter='/')
689
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
690
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
691
    
692
    def test_extended_list_xml(self):
693
        xml = self.client.list_objects(self.container[1], format='xml', limit=4,
694
                                       prefix='photos', delimiter='/')
695
        self.assert_extended(xml, 'xml', 'object', size=4)
696
        dirs = xml.getElementsByTagName('subdir')
697
        self.assertEqual(len(dirs), 2)
698
        self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
699
        self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
700
        
701
        objects = xml.getElementsByTagName('name')
702
        self.assertEqual(len(objects), 1)
703
        self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
704
    
705
    def test_list_meta_double_matching(self):
706
        meta = {'quality':'aaa', 'stock':'true'}
707
        self.client.update_object_metadata(self.container[0],
708
                                           self.obj[0]['name'], **meta)
709
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
710
        self.assertEqual(len(obj), 1)
711
        self.assertTrue(obj, self.obj[0]['name'])
712
    
713
    def test_list_using_meta(self):
714
        meta = {'quality':'aaa'}
715
        for o in self.obj[:2]:
716
            self.client.update_object_metadata(self.container[0], o['name'],
717
                                               **meta)
718
        meta = {'stock':'true'}
719
        for o in self.obj[3:5]:
720
            self.client.update_object_metadata(self.container[0], o['name'],
721
                                               **meta)
722
        
723
        obj = self.client.list_objects(self.container[0], meta='Quality')
724
        self.assertEqual(len(obj), 2)
725
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
726
        
727
        # test case insensitive
728
        obj = self.client.list_objects(self.container[0], meta='quality')
729
        self.assertEqual(len(obj), 2)
730
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
731
        
732
        # test multiple matches
733
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
734
        self.assertEqual(len(obj), 4)
735
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
736
        
737
        # test non 1-1 multiple match
738
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
739
        self.assertEqual(len(obj), 2)
740
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
741
    
742
    def test_if_modified_since(self):
743
        t = datetime.datetime.utcnow()
744
        t2 = t - datetime.timedelta(minutes=10)
745
        
746
        #add a new object
747
        self.upload_random_data(self.container[0], o_names[0])
748
        
749
        for f in DATE_FORMATS:
750
            past = t2.strftime(f)
751
            try:
752
                o = self.client.list_objects(self.container[0],
753
                                            if_modified_since=past)
754
                self.assertEqual(o,
755
                                 self.client.list_objects(self.container[0]))
756
            except Fault, f:
757
                self.failIf(f.status == 304) #fail if not modified
758
    
759
    def test_if_modified_since_invalid_date(self):
760
        headers = {'if-modified-since':''}
761
        o = self.client.list_objects(self.container[0], if_modified_since='')
762
        self.assertEqual(o, self.client.list_objects(self.container[0]))
763
    
764
    def test_if_not_modified_since(self):
765
        now = datetime.datetime.utcnow()
766
        since = now + datetime.timedelta(1)
767
        
768
        for f in DATE_FORMATS:
769
            args = {'if_modified_since':'%s' %since.strftime(f)}
770
            
771
            #assert not modified
772
            self.assert_raises_fault(304, self.client.list_objects,
773
                                     self.container[0], **args)
774
    
775
    def test_if_unmodified_since(self):
776
        now = datetime.datetime.utcnow()
777
        since = now + datetime.timedelta(1)
778
        
779
        for f in DATE_FORMATS:
780
            obj = self.client.list_objects(self.container[0],
781
                                           if_unmodified_since=since.strftime(f))
782
            
783
            #assert unmodified
784
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
785
    
786
    def test_if_unmodified_since_precondition_failed(self):
787
        t = datetime.datetime.utcnow()
788
        t2 = t - datetime.timedelta(minutes=10)
789
        
790
        #add a new container
791
        self.client.create_container('dummy')
792
        
793
        for f in DATE_FORMATS:
794
            past = t2.strftime(f)
795
            
796
            args = {'if_unmodified_since':'%s' %past}
797
            
798
            #assert precondition failed
799
            self.assert_raises_fault(412, self.client.list_objects,
800
                                     self.container[0], **args)
801

    
802
class ContainerPut(BaseTestCase):
803
    def setUp(self):
804
        BaseTestCase.setUp(self)
805
        self.containers = ['c1', 'c2']
806
    
807
    def test_create(self):
808
        self.client.create_container(self.containers[0])
809
        containers = self.client.list_containers()
810
        self.assertTrue(self.containers[0] in containers)
811
        self.assert_container_exists(self.containers[0])
812
    
813
    def test_create_twice(self):
814
        self.client.create_container(self.containers[0])
815
        self.assertTrue(not self.client.create_container(self.containers[0]))
816
    
817
class ContainerPost(BaseTestCase):
818
    def setUp(self):
819
        BaseTestCase.setUp(self)
820
        self.container = 'apples'
821
        self.client.create_container(self.container)
822
    
823
    def test_update_meta(self):
824
        meta = {'test':'test33',
825
                'tost':'tost22'}
826
        self.client.update_container_metadata(self.container, **meta)
827
        headers = self.client.retrieve_container_metadata(self.container)
828
        for k,v in meta.items():
829
            k = 'x-container-meta-%s' % k
830
            self.assertTrue(headers[k])
831
            self.assertEqual(headers[k], v)
832

    
833
class ContainerDelete(BaseTestCase):
834
    def setUp(self):
835
        BaseTestCase.setUp(self)
836
        self.containers = ['c1', 'c2']
837
        for c in self.containers:
838
            self.client.create_container(c)
839
        self.upload_random_data(self.containers[1], o_names[0])
840
    
841
    def test_delete(self):
842
        status = self.client.delete_container(self.containers[0])[0]
843
        self.assertEqual(status, 204)
844
    
845
    def test_delete_non_empty(self):
846
        self.assert_raises_fault(409, self.client.delete_container,
847
                                 self.containers[1])
848
    
849
    def test_delete_invalid(self):
850
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
851

    
852
class ObjectHead(BaseTestCase):
853
    pass
854

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

    
1229
class ObjectPut(BaseTestCase):
1230
    def setUp(self):
1231
        BaseTestCase.setUp(self)
1232
        self.container = 'c1'
1233
        self.client.create_container(self.container)
1234
    
1235
    def test_upload(self):
1236
        name = o_names[0]
1237
        meta = {'test':'test1'}
1238
        o = self.upload_random_data(self.container, name, **meta)
1239
        
1240
        headers = self.client.retrieve_object_metadata(self.container,
1241
                                                       name,
1242
                                                       restricted=True)
1243
        self.assertTrue('test' in headers.keys())
1244
        self.assertEqual(headers['test'], meta['test'])
1245
        
1246
        #assert uploaded content
1247
        status, h, data = self.client.request_object(self.container, name)
1248
        self.assertEqual(len(o['data']), int(h['content-length']))
1249
        self.assertEqual(o['data'], data)
1250
        
1251
        #assert content-type
1252
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1253
    
1254
    def _test_maximum_upload_size_exceeds(self):
1255
        name = o_names[0]
1256
        meta = {'test':'test1'}
1257
        #upload 100MB
1258
        length=1024*1024*100
1259
        self.assert_raises_fault(400, self.upload_random_data, self.container,
1260
                                 name, length, **meta)
1261
    
1262
    def test_upload_with_name_containing_slash(self):
1263
        name = '/%s' % o_names[0]
1264
        meta = {'test':'test1'}
1265
        o = self.upload_random_data(self.container, name, **meta)
1266
        
1267
        self.assertEqual(o['data'],
1268
                         self.client.retrieve_object(self.container, name))
1269
        
1270
        self.assertTrue(name in self.client.list_objects(self.container))
1271
    
1272
    def test_create_directory_marker(self):
1273
        self.client.create_directory_marker(self.container, 'foo')
1274
        meta = self.client.retrieve_object_metadata(self.container, 'foo')
1275
        self.assertEqual(meta['content-length'], '0')
1276
        self.assertEqual(meta['content-type'], 'application/directory')
1277

    
1278
    def test_upload_unprocessable_entity(self):
1279
        meta={'etag':'123', 'test':'test1'}
1280
        
1281
        #assert unprocessable entity
1282
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1283
                                 o_names[0], **meta)
1284
    
1285
    def test_chunked_transfer(self):
1286
        data = get_random_data()
1287
        objname = 'object'
1288
        self.client.create_object_using_chunks(self.container, objname,
1289
                                               StringIO(data))
1290
        
1291
        uploaded_data = self.client.retrieve_object(self.container, objname)
1292
        self.assertEqual(data, uploaded_data)
1293
    
1294
    def test_manifestation(self):
1295
        prefix = 'myobject/'
1296
        data = ''
1297
        for i in range(5):
1298
            part = '%s%d' %(prefix, i)
1299
            o = self.upload_random_data(self.container, part)
1300
            data += o['data']
1301
        
1302
        manifest = '%s/%s' %(self.container, prefix)
1303
        self.client.create_manifestation(self.container, 'large-object', manifest)
1304
        
1305
        self.assert_object_exists(self.container, 'large-object')
1306
        self.assertEqual(data, self.client.retrieve_object(self.container,
1307
                                                           'large-object'))
1308
        
1309
        #wrong manifestation
1310
        self.client.create_manifestation(self.container, 'large-object',
1311
                                         '%s/invalid' % self.container)
1312
        self.assertEqual('', self.client.retrieve_object(self.container,
1313
                                                         'large-object'))
1314
    
1315
    def test_create_zero_length_object(self):
1316
        c = self.container
1317
        o = 'object'
1318
        zero = self.client.create_zero_length_object(c, o)
1319
        zero_meta = self.client.retrieve_object_metadata(c, o)
1320
        zero_hash = self.client.retrieve_object_hashmap(c, o)
1321
        zero_data = self.client.retrieve_object(c, o)
1322
        
1323
        self.assertEqual(int(zero_meta['content-length']), 0)
1324
        self.assertEqual(zero_hash, [])
1325
        self.assertEqual(zero_data, '')
1326

    
1327
class ObjectCopy(BaseTestCase):
1328
    def setUp(self):
1329
        BaseTestCase.setUp(self)
1330
        self.containers = ['c1', 'c2']
1331
        for c in self.containers:
1332
            self.client.create_container(c)
1333
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1334
        
1335
    def test_copy(self):
1336
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1337
                             self.containers[0], self.obj['name']):
1338
            #perform copy
1339
            meta = {'test':'testcopy'}
1340
            status = self.client.copy_object(self.containers[0],
1341
                                              self.obj['name'],
1342
                                              self.containers[0],
1343
                                              'testcopy',
1344
                                              meta)[0]
1345
            
1346
            #assert copy success
1347
            self.assertEqual(status, 201)
1348
            
1349
            #assert access the new object
1350
            headers = self.client.retrieve_object_metadata(self.containers[0],
1351
                                                           'testcopy')
1352
            self.assertTrue('x-object-meta-test' in headers.keys())
1353
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1354
            
1355
            #assert etag is the same
1356
            self.assertEqual(headers['etag'], self.obj['hash'])
1357
            
1358
            #assert src object still exists
1359
            self.assert_object_exists(self.containers[0], self.obj['name'])
1360
    
1361
    def test_copy_from_different_container(self):
1362
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1363
                             self.containers[0], self.obj['name']):
1364
            meta = {'test':'testcopy'}
1365
            status = self.client.copy_object(self.containers[0],
1366
                                             self.obj['name'],
1367
                                             self.containers[1],
1368
                                             'testcopy',
1369
                                             meta)[0]
1370
            self.assertEqual(status, 201)
1371
            
1372
            # assert updated metadata
1373
            meta = self.client.retrieve_object_metadata(self.containers[1],
1374
                                                           'testcopy',
1375
                                                           restricted=True)
1376
            self.assertTrue('test' in meta.keys())
1377
            self.assertTrue(meta['test'], 'testcopy')
1378
            
1379
            #assert src object still exists
1380
            self.assert_object_exists(self.containers[0], self.obj['name'])
1381
    
1382
    def test_copy_invalid(self):
1383
        #copy from invalid object
1384
        meta = {'test':'testcopy'}
1385
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1386
                                 'test.py', self.containers[1], 'testcopy', meta)
1387
        
1388
        #copy from invalid container
1389
        meta = {'test':'testcopy'}
1390
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1391
                                 self.obj['name'], self.containers[1],
1392
                                 'testcopy', meta)
1393

    
1394
class ObjectMove(BaseTestCase):
1395
    def setUp(self):
1396
        BaseTestCase.setUp(self)
1397
        self.containers = ['c1', 'c2']
1398
        for c in self.containers:
1399
            self.client.create_container(c)
1400
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1401
    
1402
    def test_move(self):
1403
        #perform move
1404
        meta = {'test':'testcopy'}
1405
        src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1406
        status = self.client.move_object(self.containers[0], self.obj['name'],
1407
                                         self.containers[0], 'testcopy',
1408
                                         meta)[0]
1409
        
1410
        #assert successful move
1411
        self.assertEqual(status, 201)
1412
        
1413
        #assert updated metadata
1414
        meta = self.client.retrieve_object_metadata(self.containers[0],
1415
                                                    'testcopy',
1416
                                                    restricted=True)
1417
        self.assertTrue('test' in meta.keys())
1418
        self.assertTrue(meta['test'], 'testcopy')
1419
        
1420
        #assert src object no more exists
1421
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1422

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

    
1679
class ListSharing(BaseTestCase):
1680
    def setUp(self):
1681
        BaseTestCase.setUp(self)
1682
        for i in range(2):
1683
            self.client.create_container('c%s' %i)
1684
        self.client.create_container('c')
1685
        for i in range(2):
1686
            self.upload_random_data('c1', 'o%s' %i)
1687
        accounts = OTHER_ACCOUNTS.copy()
1688
        self.o1_sharing_with = accounts.popitem()
1689
        self.o1_sharing = [self.o1_sharing_with[1]]
1690
        self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1691
        
1692
        l = []
1693
        for i in range(2):
1694
            l.append(accounts.popitem())
1695
    
1696
    def test_list_other_shared(self):
1697
        self.other = Pithos_Client(get_server(),
1698
                              self.o1_sharing_with[0],
1699
                              self.o1_sharing_with[1],
1700
                              get_api())
1701
        self.assertTrue(get_user() in self.other.list_shared_by_others())
1702
    
1703
    def test_list_my_shared(self):
1704
        my_shared_containers = self.client.list_containers(shared=True)
1705
        self.assertTrue('c1' in my_shared_containers)
1706
        self.assertTrue('c2' not in my_shared_containers)
1707
        
1708
        my_shared_objects = self.client.list_objects('c1', shared=True)
1709
        self.assertTrue('o1' in my_shared_objects)
1710
        self.assertTrue('o2' not in my_shared_objects)
1711
    
1712
class TestGreek(BaseTestCase):
1713
    def setUp(self):
1714
        BaseTestCase.setUp(self)
1715
        #keep track of initial account groups
1716
        self.initial_groups = self.client.retrieve_account_groups()
1717
        
1718
        #keep track of initial account meta
1719
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1720
    
1721
    def tearDown(self):
1722
        #delete additionally created meta
1723
        l = []
1724
        for m in self.client.retrieve_account_metadata(restricted=True):
1725
            if m not in self.initial_meta:
1726
                l.append(m)
1727
        self.client.delete_account_metadata(l)
1728
        
1729
        #delete additionally created groups
1730
        l = []
1731
        for g in self.client.retrieve_account_groups():
1732
            if g not in self.initial_groups:
1733
                l.append(g)
1734
        self.client.unset_account_groups(l)
1735
        
1736
        BaseTestCase.tearDown(self)
1737
    
1738
    def test_create_container(self):
1739
        self.client.create_container('φάκελος')
1740
        self.assert_container_exists('φάκελος')
1741
        
1742
        self.assertTrue('φάκελος' in self.client.list_containers())
1743
    
1744
    def test_create_object(self):
1745
        self.client.create_container('φάκελος')
1746
        self.upload_random_data('φάκελος', 'αντικείμενο')
1747
        
1748
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1749
        self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1750
    
1751
    def test_copy_object(self):
1752
        src_container = 'φάκελος'
1753
        src_object = 'αντικείμενο'
1754
        dest_container = 'αντίγραφα'
1755
        dest_object = 'ασφαλές-αντίγραφο'
1756
        
1757
        self.client.create_container(src_container)
1758
        self.upload_random_data(src_container, src_object)
1759
        
1760
        self.client.create_container(dest_container)
1761
        self.client.copy_object(src_container, src_object, dest_container,
1762
                                dest_object)
1763
        
1764
        self.assert_object_exists(src_container, src_object)
1765
        self.assert_object_exists(dest_container, dest_object)
1766
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1767
    
1768
    def test_move_object(self):
1769
        src_container = 'φάκελος'
1770
        src_object = 'αντικείμενο'
1771
        dest_container = 'αντίγραφα'
1772
        dest_object = 'ασφαλές-αντίγραφο'
1773
        
1774
        self.client.create_container(src_container)
1775
        self.upload_random_data(src_container, src_object)
1776
        
1777
        self.client.create_container(dest_container)
1778
        self.client.move_object(src_container, src_object, dest_container,
1779
                                dest_object)
1780
        
1781
        self.assert_object_not_exists(src_container, src_object)
1782
        self.assert_object_exists(dest_container, dest_object)
1783
        self.assertTrue(dest_object in self.client.list_objects(dest_container))
1784
    
1785
    def test_delete_object(self):
1786
        self.client.create_container('φάκελος')
1787
        self.upload_random_data('φάκελος', 'αντικείμενο')
1788
        self.assert_object_exists('φάκελος', 'αντικείμενο')
1789
    
1790
        self.client.delete_object('φάκελος', 'αντικείμενο')
1791
        self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1792
        self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1793
    
1794
    def test_delete_container(self):
1795
        self.client.create_container('φάκελος')
1796
        self.assert_container_exists('φάκελος')
1797
        
1798
        self.client.delete_container('φάκελος')
1799
        self.assert_container_not_exists('φάκελος')
1800
        self.assertTrue('φάκελος' not in self.client.list_containers())
1801

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

    
1903
class TestPermissions(BaseTestCase):
1904
    def setUp(self):
1905
        BaseTestCase.setUp(self)
1906
        #keep track of initial account groups
1907
        self.initial_groups = self.client.retrieve_account_groups()
1908
        #keep track of initial account meta
1909
        self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1910
        
1911
        #create a group
1912
        self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1913
        groups = {'pithosdev':','.join(self.authorized)}
1914
        self.client.set_account_groups(**groups)
1915
    
1916
    def tearDown(self):
1917
        #delete additionally created meta
1918
        l = []
1919
        for m in self.client.retrieve_account_metadata(restricted=True):
1920
            if m not in self.initial_meta:
1921
                l.append(m)
1922
        self.client.delete_account_metadata(l)
1923
        
1924
        #delete additionally created groups
1925
        l = []
1926
        for g in self.client.retrieve_account_groups():
1927
            if g not in self.initial_groups:
1928
                l.append(g)
1929
        self.client.unset_account_groups(l)
1930
        
1931
        BaseTestCase.tearDown(self)
1932
    
1933
    def test_read_access(self):
1934
        self.client.create_container('c')
1935
        o = self.upload_random_data('c', 'o')
1936
        self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1937
        for token, account in OTHER_ACCOUNTS.items():
1938
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1939
            if account in self.authorized:
1940
                self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1941
                                             'c', 'o', account=get_user())
1942
            else:
1943
                self.assert_raises_fault(401, cl.retrieve_object_metadata,
1944
                                         'c', 'o', account=get_user())
1945
        
1946
        #check inheritance
1947
        o = self.upload_random_data('c', 'o/also-shared')
1948
        for token, account in OTHER_ACCOUNTS.items():
1949
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1950
            if account in self.authorized:
1951
                self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1952
                                             'c', 'o/also-shared', account=get_user())
1953
            else:
1954
                self.assert_raises_fault(401, cl.retrieve_object_metadata,
1955
                                         'c', 'o/also-shared', account=get_user())
1956
    
1957
    def test_write_access(self):
1958
        self.client.create_container('c')
1959
        o = self.upload_random_data('c', 'o')
1960
        self.client.share_object('c', 'o', ['chazapis'], read=False)
1961
        o_data = o['data']
1962
        for token, account in OTHER_ACCOUNTS.items():
1963
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1964
            new_data = get_random_data()
1965
            if account in [get_user(), 'chazapis']:
1966
                self.assert_not_raises_fault(401, cl.update_object,
1967
                                             'c', 'o', StringIO(new_data),
1968
                                             account=get_user())
1969
                server_data = self.client.retrieve_object('c', 'o')
1970
                self.assertEqual(o_data, server_data[:len(o_data)])
1971
                self.assertEqual(new_data, server_data[len(o_data):])
1972
                o_data = server_data
1973
            else:
1974
                self.assert_raises_fault(401, cl.update_object,
1975
                                             'c', 'o', StringIO(new_data),
1976
                                             account=get_user())
1977
        
1978
        #check inheritance
1979
        o = self.upload_random_data('c', 'o/also-shared')
1980
        o_data = o['data']
1981
        for token, account in OTHER_ACCOUNTS.items():
1982
            cl = Pithos_Client(get_server(), token, account, get_api()) 
1983
            new_data = get_random_data()
1984
            if account in [get_user(), 'chazapis']:
1985
                self.assert_not_raises_fault(401, cl.update_object,
1986
                                             'c', o['name'],
1987
                                             StringIO(new_data),
1988
                                             account=get_user())
1989
                server_data = self.client.retrieve_object('c', o['name'])
1990
                self.assertEqual(o_data, server_data[:len(o_data)])
1991
                self.assertEqual(new_data, server_data[len(o_data):])
1992
                o_data = server_data
1993
            else:
1994
                self.assert_raises_fault(401, cl.update_object,
1995
                                             'c', o['name'],
1996
                                             StringIO(new_data),
1997
                                             account=get_user())
1998

    
1999
class AssertMappingInvariant(object):
2000
    def __init__(self, callable, *args, **kwargs):
2001
        self.callable = callable
2002
        self.args = args
2003
        self.kwargs = kwargs
2004
    
2005
    def __enter__(self):
2006
        self.map = self.callable(*self.args, **self.kwargs)
2007
        return self.map
2008
    
2009
    def __exit__(self, type, value, tb):
2010
        map = self.callable(*self.args, **self.kwargs)
2011
        for k in self.map.keys():
2012
            if is_date(self.map[k]):
2013
                continue
2014
            assert map[k] == self.map[k]
2015

    
2016
class AssertContentInvariant(object):
2017
    def __init__(self, callable, *args, **kwargs):
2018
        self.callable = callable
2019
        self.args = args
2020
        self.kwargs = kwargs
2021
    
2022
    def __enter__(self):
2023
        self.content = self.callable(*self.args, **self.kwargs)[2]
2024
        return self.content
2025
    
2026
    def __exit__(self, type, value, tb):
2027
        content = self.callable(*self.args, **self.kwargs)[2]
2028
        assert self.content == content
2029

    
2030
def get_content_splitted(response):
2031
    if response:
2032
        return response.content.split('\n')
2033

    
2034
def compute_md5_hash(data):
2035
    md5 = hashlib.md5()
2036
    offset = 0
2037
    md5.update(data)
2038
    return md5.hexdigest().lower()
2039

    
2040
def compute_block_hash(data, algorithm):
2041
    h = hashlib.new(algorithm)
2042
    h.update(data.rstrip('\x00'))
2043
    return h.hexdigest()
2044

    
2045
def get_random_data(length=500):
2046
    char_set = string.ascii_uppercase + string.digits
2047
    return ''.join(random.choice(char_set) for x in range(length))
2048

    
2049
def is_date(date):
2050
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2051
    __D = r'(?P<day>\d{2})'
2052
    __D2 = r'(?P<day>[ \d]\d)'
2053
    __M = r'(?P<mon>\w{3})'
2054
    __Y = r'(?P<year>\d{4})'
2055
    __Y2 = r'(?P<year>\d{2})'
2056
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2057
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2058
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2059
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2060
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2061
        m = regex.match(date)
2062
        if m is not None:
2063
            return True
2064
    return False
2065

    
2066
o_names = ['kate.jpg',
2067
           'kate_beckinsale.jpg',
2068
           'How To Win Friends And Influence People.pdf',
2069
           'moms_birthday.jpg',
2070
           'poodle_strut.mov',
2071
           'Disturbed - Down With The Sickness.mp3',
2072
           'army_of_darkness.avi',
2073
           'the_mad.avi',
2074
           'photos/animals/dogs/poodle.jpg',
2075
           'photos/animals/dogs/terrier.jpg',
2076
           'photos/animals/cats/persian.jpg',
2077
           'photos/animals/cats/siamese.jpg',
2078
           'photos/plants/fern.jpg',
2079
           'photos/plants/rose.jpg',
2080
           'photos/me.jpg']
2081

    
2082
if __name__ == "__main__":
2083
    if get_user() == 'test':
2084
        unittest.main()
2085
    else:
2086
        print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()