Statistics
| Branch: | Tag: | Revision:

root / pithos / api / tests.py @ 25c3841c

History | View | Annotate | Download (53.3 kB)

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

    
34
from pithos.lib.client import Pithos_Client, Fault
35
import unittest
36
from django.utils import simplejson as json
37
from xml.dom import minidom
38
from StringIO import StringIO
39
import types
40
import hashlib
41
import os
42
import mimetypes
43
import random
44
import datetime
45
import string
46
import re
47

    
48
#from pithos.backends import backend
49

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

    
54
DEFAULT_HOST = 'pithos.dev.grnet.gr'
55
#DEFAULT_HOST = '127.0.0.1:8000'
56
DEFAULT_API = 'v1'
57
DEFAULT_USER = 'papagian'
58
DEFAULT_AUTH = '0004'
59

    
60
class BaseTestCase(unittest.TestCase):
61
    #TODO unauthorized request
62
    def setUp(self):
63
        self.client = Pithos_Client(DEFAULT_HOST, DEFAULT_AUTH, DEFAULT_USER, DEFAULT_API)
64
        self.headers = {
65
            'account': ('x-account-container-count',
66
                        'x-account-bytes-used',
67
                        'last-modified',
68
                        'content-length',
69
                        'date',
70
                        'content_type',
71
                        'server',),
72
            'object': ('etag',
73
                       'content-length',
74
                       'content_type',
75
                       'content-encoding',
76
                       'last-modified',
77
                       'date',
78
                       'x-object-manifest',
79
                       'content-range',
80
                       'x-object-modified-by',
81
                       'x-object-version',
82
                       'x-object-version-timestamp',
83
                       'server',),
84
            'container': ('x-container-object-count',
85
                          'x-container-bytes-used',
86
                          'content_type',
87
                          'last-modified',
88
                          'content-length',
89
                          'date',
90
                          'x-container-block-size',
91
                          'x-container-block-hash',
92
                          'x-container-policy-quota',
93
                          'x-container-policy-versioning',
94
                          'server',
95
                          'x-container-object-meta',
96
                          'x-container-policy-versioning',
97
                          'server',)}
98
        
99
        self.contentTypes = {'xml':'application/xml',
100
                             'json':'application/json',
101
                             '':'text/plain'}
102
        self.extended = {
103
            'container':(
104
                'name',
105
                'count',
106
                'bytes',
107
                'last_modified'),
108
            'object':(
109
                'name',
110
                'hash',
111
                'bytes',
112
                'content_type',
113
                'content_encoding',
114
                'last_modified',)}
115
        self.return_codes = (400, 401, 404, 503,)
116
    
117
    def tearDown(self):
118
        for c in self.client.list_containers():
119
            for o in self.client.list_objects(c):
120
                self.client.delete_object(c, o)
121
            self.client.delete_container(c)
122
    
123
    def assert_status(self, status, codes):
124
        l = [elem for elem in self.return_codes]
125
        if type(codes) == types.ListType:
126
            l.extend(codes)
127
        else:
128
            l.append(codes)
129
        self.assertTrue(status in l)
130
    
131
    #def assert_list(self, path, entity, limit=10000, format='text', params=None, **headers):
132
    #    status, headers, data = self.client.get(path, format=format,
133
    #                                            headers=headers, params=params)
134
    #    
135
    #    self.assert_status(status, [200, 204, 304, 412])
136
    #    if format == 'text':
137
    #        data = data.strip().split('\n') if data else []
138
    #        self.assertTrue(len(data) <= limit)
139
    #    else:
140
    #        exp_content_type = self.contentTypes[format]
141
    #        self.assertEqual(headers['content_type'].find(exp_content_type), 0)
142
    #        #self.assert_extended(data, format, entity, limit)
143
    #        if format == 'json':
144
    #            data = json.loads(data) if data else []
145
    #        elif format == 'xml':
146
    #            data = minidom.parseString(data)
147
    #    return status, headers, data
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):
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
    #    print '#', data
167
    #    convert = lambda s: s.lower()
168
    #    info = [convert(elem) for elem in self.extended[type]]
169
    #    data = json.loads(data)
170
    #    self.assertTrue(len(data) <= size)
171
    #    for item in info:
172
    #        for i in data:
173
    #            if 'subdir' in i.keys():
174
    #                continue
175
    #            self.assertTrue(item in i.keys())
176
    
177
    #def _assert_xml(self, data, type, size):
178
    #    print '#', data
179
    #    convert = lambda s: s.lower()
180
    #    info = [convert(elem) for elem in self.extended[type]]
181
    #    try:
182
    #        info.remove('content_encoding')
183
    #    except ValueError:
184
    #        pass
185
    #    xml = minidom.parseString(data)
186
    #    entities = xml.getElementsByTagName(type)
187
    #    self.assertTrue(len(entities) <= size)
188
    #    for e in entities:
189
    #        for item in info:
190
    #            self.assertTrue(e.hasAttribute(item))
191
    
192
    def assert_raises_fault(self, status, callableObj, *args, **kwargs):
193
        """
194
        asserts that a Fault with a specific status is raised
195
        when callableObj is called with the specific arguments
196
        """
197
        try:
198
            callableObj(*args, **kwargs)
199
            self.fail('Should never reach here')
200
        except Fault, f:
201
            self.failUnless(f.status == status)
202
    
203
    def assert_container_exists(self, container):
204
        """
205
        asserts the existence of a container
206
        """
207
        try:
208
            self.client.retrieve_container_metadata(container)
209
        except Fault, f:
210
            self.failIf(f.status == 404)
211
    
212
    def assert_object_exists(self, container, object):
213
        """
214
        asserts the existence of an object
215
        """
216
        try:
217
            self.client.retrieve_object_metadata(container, object)
218
        except Fault, f:
219
            self.failIf(f.status == 404)
220
    
221
    def assert_object_not_exists(self, container, object):
222
        """
223
        asserts there is no such an object
224
        """
225
        self.assert_raises_fault(404, self.client.retrieve_object_metadata,
226
                                 container, object)
227
    
228
    def upload_random_data(self, container, name, length=1024, type=None,
229
                           enc=None, **meta):
230
        data = get_random_data(length)
231
        return self.upload_data(container, name, data, type, enc, **meta)
232
    
233
    def upload_data(self, container, name, data, type=None, enc=None, etag=None,
234
                    **meta):
235
        obj = {}
236
        obj['name'] = name
237
        try:
238
            obj['data'] = data
239
            obj['hash'] = compute_md5_hash(obj['data'])
240
            
241
            args = {}
242
            args['etag'] = etag if etag else obj['hash']
243
            
244
            guess = mimetypes.guess_type(name)
245
            type = type if type else guess[0]
246
            enc = enc if enc else guess[1]
247
            args['content_type'] = type if type else 'plain/text'
248
            args['content_encoding'] = enc if enc else None
249
            
250
            obj['meta'] = args
251
            
252
            path = '/%s/%s' % (container, name)
253
            self.client.create_object(container, name, StringIO(obj['data']),
254
                                      meta, **args)
255
            
256
            return obj
257
        except IOError:
258
            return
259

    
260
class AccountHead(BaseTestCase):
261
    def setUp(self):
262
        BaseTestCase.setUp(self)
263
        self.account = 'test'
264
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
265
        for item in self.containers:
266
            self.client.create_container(item)
267
    
268
    def test_get_account_meta(self):
269
        meta = self.client.retrieve_account_metadata()
270
        
271
        containers = self.client.list_containers()
272
        l = str(len(containers))
273
        self.assertEqual(meta['x-account-container-count'], l)
274
        size = 0
275
        for c in containers:
276
            m = self.client.retrieve_container_metadata(c)
277
            size = size + int(m['x-container-bytes-used'])
278
        self.assertEqual(meta['x-account-bytes-used'], str(size))
279
    
280
    #def test_get_account_401(self):
281
    #    response = self.get_account_meta('non-existing-account')
282
    #    print response
283
    #    self.assertEqual(response.status_code, 401)
284

    
285
class AccountGet(BaseTestCase):
286
    def setUp(self):
287
        BaseTestCase.setUp(self)
288
        self.account = 'test'
289
        #create some containers
290
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
291
        for item in self.containers:
292
            self.client.create_container(item)
293
    
294
    def test_list(self):
295
        #list containers
296
        containers = self.client.list_containers()
297
        self.assertEquals(self.containers, containers)
298
    
299
    #def test_list_204(self):
300
    #    response = self.list_containers('non-existing-account')
301
    #    self.assertEqual(response.status_code, 204)
302
    
303
    def test_list_with_limit(self):
304
        limit = 2
305
        containers = self.client.list_containers(limit=limit)
306
        self.assertEquals(len(containers), limit)
307
        self.assertEquals(self.containers[:2], containers)
308
    
309
    def test_list_with_marker(self):
310
        l = 2
311
        m = 'bananas'
312
        containers = self.client.list_containers(limit=l, marker=m)
313
        i = self.containers.index(m) + 1
314
        self.assertEquals(self.containers[i:(i+l)], containers)
315
        
316
        m = 'oranges'
317
        containers = self.client.list_containers(limit=l, marker=m)
318
        i = self.containers.index(m) + 1
319
        self.assertEquals(self.containers[i:(i+l)], containers)
320
    
321
    #def test_extended_list(self):
322
    #    self.list_containers(self.account, limit=3, format='xml')
323
    #    self.list_containers(self.account, limit=3, format='json')
324
    
325
    def test_list_json_with_marker(self):
326
        l = 2
327
        m = 'bananas'
328
        containers = self.client.list_containers(limit=l, marker=m, detail=True)
329
        self.assertEqual(containers[0]['name'], 'kiwis')
330
        self.assertEqual(containers[1]['name'], 'oranges')
331
    
332
    #def test_list_xml_with_marker(self):
333
    #    l = 2
334
    #    m = 'oranges'
335
    #    status, headers, xml = self.list_containers(limit=l, marker=m, format='xml')
336
    #    nodes = xml.getElementsByTagName('name')
337
    #    self.assertEqual(len(nodes), 1)
338
    #    self.assertEqual(nodes[0].childNodes[0].data, 'pears')
339
    
340
    def test_if_modified_since(self):
341
        t = datetime.datetime.utcnow()
342
        t2 = t - datetime.timedelta(minutes=10)
343
        
344
        #add a new container
345
        self.client.create_container('dummy')
346
        
347
        for f in DATE_FORMATS:
348
            past = t2.strftime(f)
349
            try:
350
                c = self.client.list_containers(if_modified_since=past)
351
                self.assertEqual(len(c), len(self.containers) + 1)
352
            except Fault, f:
353
                self.failIf(f.status == 304) #fail if not modified
354
    
355
    def test_if_modified_since_invalid_date(self):
356
        c = self.client.list_containers(if_modified_since='')
357
        self.assertEqual(len(c), len(self.containers))
358
    
359
    def test_if_not_modified_since(self):
360
        now = datetime.datetime.utcnow()
361
        since = now + datetime.timedelta(1)
362
        
363
        for f in DATE_FORMATS:
364
            args = {'if_modified_since':'%s' %since.strftime(f)}
365
            
366
            #assert not modified
367
            self.assert_raises_fault(304, self.client.list_containers, **args)
368
    
369
    def test_if_unmodified_since(self):
370
        now = datetime.datetime.utcnow()
371
        since = now + datetime.timedelta(1)
372
        
373
        for f in DATE_FORMATS:
374
            c = self.client.list_containers(if_unmodified_since=since.strftime(f))
375
            
376
            #assert success
377
            self.assertEqual(self.containers, c)
378
    
379
    def test_if_unmodified_since_precondition_failed(self):
380
        t = datetime.datetime.utcnow()
381
        t2 = t - datetime.timedelta(minutes=10)
382
        
383
        #add a new container
384
        self.client.create_container('dummy')
385
        
386
        for f in DATE_FORMATS:
387
            past = t2.strftime(f)
388
            
389
            args = {'if_unmodified_since':'%s' %past}
390
            
391
            #assert precondition failed
392
            self.assert_raises_fault(412, self.client.list_containers, **args)
393
    
394
class AccountPost(BaseTestCase):
395
    def setUp(self):
396
        BaseTestCase.setUp(self)
397
        self.account = 'test'
398
        self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
399
        for item in self.containers:
400
            self.client.create_container(item)
401
    
402
    def test_update_meta(self):
403
        meta = {'test':'test', 'tost':'tost'}
404
        self.client.update_account_metadata(**meta)
405
        self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
406
    
407
    #def test_invalid_account_update_meta(self):
408
    #    with AssertMappingInvariant(self.get_account_meta, self.account):
409
    #        meta = {'HTTP_X_ACCOUNT_META_TEST':'test',
410
    #               'HTTP_X_ACCOUNT_META_TOST':'tost'}
411
    #        response = self.update_account_meta('non-existing-account', **meta)
412

    
413
class ContainerHead(BaseTestCase):
414
    def setUp(self):
415
        BaseTestCase.setUp(self)
416
        self.account = 'test'
417
        self.container = 'apples'
418
        self.client.create_container(self.container)
419
    
420
    def test_get_meta(self):
421
        meta = {'trash':'true'}
422
        t1 = datetime.datetime.utcnow()
423
        o = self.upload_random_data(self.container, o_names[0], **meta)
424
        if o:
425
            headers = self.client.retrieve_container_metadata(self.container)
426
            self.assertEqual(headers['x-container-object-count'], '1')
427
            self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
428
            t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
429
            delta = (t2 - t1)
430
            threashold = datetime.timedelta(seconds=1) 
431
            self.assertTrue(delta < threashold)
432
            self.assertTrue(headers['x-container-object-meta'])
433
            self.assertTrue('Trash' in headers['x-container-object-meta'])
434

    
435
class ContainerGet(BaseTestCase):
436
    def setUp(self):
437
        BaseTestCase.setUp(self)
438
        self.account = 'test'
439
        self.container = ['pears', 'apples']
440
        for c in self.container:
441
            self.client.create_container(c)
442
        self.obj = []
443
        for o in o_names[:8]:
444
            self.obj.append(self.upload_random_data(self.container[0], o))
445
        for o in o_names[8:]:
446
            self.obj.append(self.upload_random_data(self.container[1], o))
447
    
448
    def test_list_objects(self):
449
        objects = self.client.list_objects(self.container[0])
450
        l = [elem['name'] for elem in self.obj[:8]]
451
        l.sort()
452
        self.assertEqual(objects, l)
453
    
454
    def test_list_objects_with_limit_marker(self):
455
        objects = self.client.list_objects(self.container[0], limit=2)
456
        l = [elem['name'] for elem in self.obj[:8]]
457
        l.sort()
458
        self.assertEqual(objects, l[:2])
459
        
460
        markers = ['How To Win Friends And Influence People.pdf',
461
                   'moms_birthday.jpg']
462
        limit = 4
463
        for m in markers:
464
            objects = self.client.list_objects(self.container[0], limit=limit,
465
                                               marker=m)
466
            l = [elem['name'] for elem in self.obj[:8]]
467
            l.sort()
468
            start = l.index(m) + 1
469
            end = start + limit
470
            end = len(l) >= end and end or len(l)
471
            self.assertEqual(objects, l[start:end])
472
    
473
    def test_list_pseudo_hierarchical_folders(self):
474
        objects = self.client.list_objects(self.container[1], prefix='photos',
475
                                           delimiter='/')
476
        self.assertEquals(['photos/animals/', 'photos/me.jpg',
477
                           'photos/plants/'], objects)
478
        
479
        objects = self.client.list_objects(self.container[1],
480
                                           prefix='photos/animals',
481
                                           delimiter='/')
482
        l = ['photos/animals/cats/', 'photos/animals/dogs/']
483
        self.assertEquals(l, objects)
484
        
485
        objects = self.client.list_objects(self.container[1], path='photos')
486
        self.assertEquals(['photos/me.jpg'], objects)
487
    
488
    def test_extended_list_json(self):
489
        objects = self.client.list_objects(self.container[1], detail=True,
490
                                           limit=2, prefix='photos/animals',
491
                                           delimiter='/')
492
        self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
493
        self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
494
    
495
    #def test_extended_list_xml(self):
496
    #    xml = self.client.list_objects(self.container[1], format='xml', limit=4,
497
    #                                   prefix='photos', delimiter='/')
498
    #    dirs = xml.getElementsByTagName('subdir')
499
    #    self.assertEqual(len(dirs), 2)
500
    #    self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
501
    #    self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
502
    #    
503
    #    objects = xml.getElementsByTagName('name')
504
    #    self.assertEqual(len(objects), 1)
505
    #    self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
506
    
507
    def test_list_meta_double_matching(self):
508
        meta = {'quality':'aaa', 'stock':'true'}
509
        self.client.update_object_metadata(self.container[0],
510
                                           self.obj[0]['name'], **meta)
511
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
512
        self.assertEqual(len(obj), 1)
513
        self.assertTrue(obj, self.obj[0]['name'])
514
    
515
    def test_list_using_meta(self):
516
        meta = {'quality':'aaa'}
517
        for o in self.obj[:2]:
518
            self.client.update_object_metadata(self.container[0], o['name'],
519
                                               **meta)
520
        meta = {'stock':'true'}
521
        for o in self.obj[3:5]:
522
            self.client.update_object_metadata(self.container[0], o['name'],
523
                                               **meta)
524
        
525
        obj = self.client.list_objects(self.container[0], meta='Quality')
526
        self.assertEqual(len(obj), 2)
527
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
528
        
529
        # test case insensitive
530
        obj = self.client.list_objects(self.container[0], meta='quality')
531
        self.assertEqual(len(obj), 2)
532
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
533
        
534
        # test multiple matches
535
        obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
536
        self.assertEqual(len(obj), 4)
537
        self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
538
        
539
        # test non 1-1 multiple match
540
        obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
541
        self.assertEqual(len(obj), 2)
542
        self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
543
    
544
    def test_if_modified_since(self):
545
        t = datetime.datetime.utcnow()
546
        t2 = t - datetime.timedelta(minutes=10)
547
        
548
        #add a new object
549
        self.upload_random_data(self.container[0], o_names[0])
550
        
551
        for f in DATE_FORMATS:
552
            past = t2.strftime(f)
553
            try:
554
                o = self.client.list_objects(self.container[0],
555
                                            if_modified_since=past)
556
                self.assertEqual(o,
557
                                 self.client.list_objects(self.container[0]))
558
            except Fault, f:
559
                self.failIf(f.status == 304) #fail if not modified
560
    
561
    def test_if_modified_since_invalid_date(self):
562
        headers = {'if-modified-since':''}
563
        o = self.client.list_objects(self.container[0], if_modified_since='')
564
        self.assertEqual(o, self.client.list_objects(self.container[0]))
565
    
566
    def test_if_not_modified_since(self):
567
        now = datetime.datetime.utcnow()
568
        since = now + datetime.timedelta(1)
569
        
570
        for f in DATE_FORMATS:
571
            args = {'if_modified_since':'%s' %since.strftime(f)}
572
            
573
            #assert not modified
574
            self.assert_raises_fault(304, self.client.list_objects,
575
                                     self.container[0], **args)
576
    
577
    def test_if_unmodified_since(self):
578
        now = datetime.datetime.utcnow()
579
        since = now + datetime.timedelta(1)
580
        
581
        for f in DATE_FORMATS:
582
            obj = self.client.list_objects(self.container[0],
583
                                           if_unmodified_since=since.strftime(f))
584
            
585
            #assert unmodified
586
            self.assertEqual(obj, self.client.list_objects(self.container[0]))
587
    
588
    def test_if_unmodified_since_precondition_failed(self):
589
        t = datetime.datetime.utcnow()
590
        t2 = t - datetime.timedelta(minutes=10)
591
        
592
        #add a new container
593
        self.client.create_container('dummy')
594
        
595
        for f in DATE_FORMATS:
596
            past = t2.strftime(f)
597
            
598
            args = {'if_unmodified_since':'%s' %past}
599
            
600
            #assert precondition failed
601
            self.assert_raises_fault(412, self.client.list_objects,
602
                                     self.container[0], **args)
603

    
604
class ContainerPut(BaseTestCase):
605
    def setUp(self):
606
        BaseTestCase.setUp(self)
607
        self.account = 'test'
608
        self.containers = ['c1', 'c2']
609
    
610
    def test_create(self):
611
        self.client.create_container(self.containers[0])
612
        containers = self.client.list_containers()
613
        self.assertTrue(self.containers[0] in containers)
614
        self.assert_container_exists(self.containers[0])
615
    
616
    def test_create_twice(self):
617
        self.client.create_container(self.containers[0])
618
        self.assertTrue(not self.client.create_container(self.containers[0]))
619
    
620
class ContainerPost(BaseTestCase):
621
    def setUp(self):
622
        BaseTestCase.setUp(self)
623
        self.account = 'test'
624
        self.container = 'apples'
625
        self.client.create_container(self.container)
626
    
627
    def test_update_meta(self):
628
        meta = {'test':'test33',
629
                'tost':'tost22'}
630
        self.client.update_container_metadata(self.container, **meta)
631
        headers = self.client.retrieve_container_metadata(self.container)
632
        for k,v in meta.items():
633
            k = 'x-container-meta-%s' % k
634
            self.assertTrue(headers[k])
635
            self.assertEqual(headers[k], v)
636

    
637
class ContainerDelete(BaseTestCase):
638
    def setUp(self):
639
        BaseTestCase.setUp(self)
640
        self.account = 'test'
641
        self.containers = ['c1', 'c2']
642
        for c in self.containers:
643
            self.client.create_container(c)
644
        self.upload_random_data(self.containers[1], o_names[0])
645
    
646
    def test_delete(self):
647
        status = self.client.delete_container(self.containers[0])[0]
648
        self.assertEqual(status, 204)
649
    
650
    def test_delete_non_empty(self):
651
        self.assert_raises_fault(409, self.client.delete_container,
652
                                 self.containers[1])
653
    
654
    def test_delete_invalid(self):
655
        self.assert_raises_fault(404, self.client.delete_container, 'c3')
656

    
657
class ObjectHead(BaseTestCase):
658
    pass
659

    
660
class ObjectGet(BaseTestCase):
661
    def setUp(self):
662
        BaseTestCase.setUp(self)
663
        self.account = 'test'
664
        self.containers = ['c1', 'c2']
665
        #create some containers
666
        for c in self.containers:
667
            self.client.create_container(c)
668
        
669
        #upload a file
670
        names = ('obj1', 'obj2')
671
        self.objects = []
672
        for n in names:
673
            self.objects.append(self.upload_random_data(self.containers[1], n))
674
    
675
    def test_get(self):
676
        #perform get
677
        o = self.client.retrieve_object(self.containers[1],
678
                                        self.objects[0]['name'],
679
                                        self.objects[0]['meta'])
680
        self.assertEqual(o, self.objects[0]['data'])
681
    
682
    def test_get_invalid(self):
683
        self.assert_raises_fault(404, self.client.retrieve_object,
684
                                 self.containers[0], self.objects[0]['name'])
685
    
686
    def test_get_partial(self):
687
        #perform get with range
688
        status, headers, data = self.client.request_object(self.containers[1],
689
                                                            self.objects[0]['name'],
690
                                                            range='bytes=0-499')
691
        
692
        #assert successful partial content
693
        self.assertEqual(status, 206)
694
        
695
        #assert content-type
696
        self.assertEqual(headers['content-type'],
697
                         self.objects[0]['meta']['content_type'])
698
        
699
        #assert content length
700
        self.assertEqual(int(headers['content-length']), 500)
701
        
702
        #assert content
703
        self.assertEqual(self.objects[0]['data'][:500], data)
704
    
705
    def test_get_final_500(self):
706
        #perform get with range
707
        headers = {'range':'bytes=-500'}
708
        status, headers, data = self.client.request_object(self.containers[1],
709
                                                            self.objects[0]['name'],
710
                                                            range='bytes=-500')
711
        
712
        #assert successful partial content
713
        self.assertEqual(status, 206)
714
        
715
        #assert content-type
716
        self.assertEqual(headers['content-type'],
717
                         self.objects[0]['meta']['content_type'])
718
        
719
        #assert content length
720
        self.assertEqual(int(headers['content-length']), 500)
721
        
722
        #assert content
723
        self.assertTrue(self.objects[0]['data'][-500:], data)
724
    
725
    def test_get_rest(self):
726
        #perform get with range
727
        offset = len(self.objects[0]['data']) - 500
728
        status, headers, data = self.client.request_object(self.containers[1],
729
                                                self.objects[0]['name'],
730
                                                range='bytes=%s-' %offset)
731
        
732
        #assert successful partial content
733
        self.assertEqual(status, 206)
734
        
735
        #assert content-type
736
        self.assertEqual(headers['content-type'],
737
                         self.objects[0]['meta']['content_type'])
738
        
739
        #assert content length
740
        self.assertEqual(int(headers['content-length']), 500)
741
        
742
        #assert content
743
        self.assertTrue(self.objects[0]['data'][-500:], data)
744
    
745
    def test_get_range_not_satisfiable(self):
746
        #perform get with range
747
        offset = len(self.objects[0]['data']) + 1
748
        
749
        #assert range not satisfiable
750
        self.assert_raises_fault(416, self.client.retrieve_object,
751
                                 self.containers[1], self.objects[0]['name'],
752
                                 range='bytes=0-%s' %offset)
753
    
754
    def test_multiple_range(self):
755
        #perform get with multiple range
756
        ranges = ['0-499', '-500', '1000-']
757
        bytes = 'bytes=%s' % ','.join(ranges)
758
        status, headers, data = self.client.request_object(self.containers[1],
759
                                                           self.objects[0]['name'],
760
                                                           range=bytes)
761
        
762
        # assert partial content
763
        self.assertEqual(status, 206)
764
        
765
        # assert Content-Type of the reply will be multipart/byteranges
766
        self.assertTrue(headers['content-type'])
767
        content_type_parts = headers['content-type'].split()
768
        self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
769
        
770
        boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
771
        cparts = data.split(boundary)[1:-1]
772
        
773
        # assert content parts are exactly 2
774
        self.assertEqual(len(cparts), len(ranges))
775
        
776
        # for each content part assert headers
777
        i = 0
778
        for cpart in cparts:
779
            content = cpart.split('\r\n')
780
            headers = content[1:3]
781
            content_range = headers[0].split(': ')
782
            self.assertEqual(content_range[0], 'Content-Range')
783
            
784
            r = ranges[i].split('-')
785
            if not r[0] and not r[1]:
786
                pass
787
            elif not r[0]:
788
                start = len(self.objects[0]['data']) - int(r[1])
789
                end = len(self.objects[0]['data'])
790
            elif not r[1]:
791
                start = int(r[0])
792
                end = len(self.objects[0]['data'])
793
            else:
794
                start = int(r[0])
795
                end = int(r[1]) + 1
796
            fdata = self.objects[0]['data'][start:end]
797
            sdata = '\r\n'.join(content[4:-1])
798
            self.assertEqual(len(fdata), len(sdata))
799
            self.assertEquals(fdata, sdata)
800
            i+=1
801
    
802
    def test_multiple_range_not_satisfiable(self):
803
        #perform get with multiple range
804
        out_of_range = len(self.objects[0]['data']) + 1
805
        ranges = ['0-499', '-500', '%d-' %out_of_range]
806
        bytes = 'bytes=%s' % ','.join(ranges)
807
        
808
        # assert partial content
809
        self.assert_raises_fault(416, self.client.retrieve_object,
810
                                 self.containers[1],
811
                                 self.objects[0]['name'], range=bytes)
812
    
813
    def test_get_with_if_match(self):
814
        #perform get with If-Match
815
        etag = self.objects[0]['hash']
816
        status, headers, data = self.client.request_object(self.containers[1],
817
                                                           self.objects[0]['name'],
818
                                                           if_match=etag)
819
        #assert get success
820
        self.assertEqual(status, 200)
821
        
822
        #assert content-type
823
        self.assertEqual(headers['content-type'],
824
                         self.objects[0]['meta']['content_type'])
825
        
826
        #assert response content
827
        self.assertEqual(self.objects[0]['data'], data)
828
    
829
    def test_get_with_if_match_star(self):
830
        #perform get with If-Match *
831
        headers = {'if-match':'*'}
832
        status, headers, data = self.client.request_object(self.containers[1],
833
                                                self.objects[0]['name'],
834
                                                **headers)
835
        #assert get success
836
        self.assertEqual(status, 200)
837
        
838
        #assert content-type
839
        self.assertEqual(headers['content-type'],
840
                         self.objects[0]['meta']['content_type'])
841
        
842
        #assert response content
843
        self.assertEqual(self.objects[0]['data'], data)
844
    
845
    def test_get_with_multiple_if_match(self):
846
        #perform get with If-Match
847
        etags = [i['hash'] for i in self.objects if i]
848
        etags = ','.join('"%s"' % etag for etag in etags)
849
        status, headers, data = self.client.request_object(self.containers[1],
850
                                                           self.objects[0]['name'],
851
                                                           if_match=etags)
852
        #assert get success
853
        self.assertEqual(status, 200)
854
        
855
        #assert content-type
856
        self.assertEqual(headers['content-type'],
857
                         self.objects[0]['meta']['content_type'])
858
        
859
        #assert content-type
860
        self.assertEqual(headers['content-type'],
861
                         self.objects[0]['meta']['content_type'])
862
        
863
        #assert response content
864
        self.assertEqual(self.objects[0]['data'], data)
865
    
866
    def test_if_match_precondition_failed(self):
867
        #assert precondition failed
868
        self.assert_raises_fault(412, self.client.retrieve_object,
869
                                 self.containers[1],
870
                                 self.objects[0]['name'], if_match='123')
871
    
872
    def test_if_none_match(self):
873
        #perform get with If-None-Match
874
        status, headers, data = self.client.request_object(self.containers[1],
875
                                                           self.objects[0]['name'],
876
                                                           if_none_match='123')
877
        
878
        #assert get success
879
        self.assertEqual(status, 200)
880
        
881
        #assert content-type
882
        self.assertEqual(headers['content_type'],
883
                         self.objects[0]['meta']['content_type'])
884
    
885
    def test_if_none_match(self):
886
        #perform get with If-None-Match * and assert not modified
887
        self.assert_raises_fault(304, self.client.retrieve_object,
888
                                 self.containers[1],
889
                                 self.objects[0]['name'],
890
                                 if_none_match='*')
891
    
892
    def test_if_none_match_not_modified(self):
893
        #perform get with If-None-Match and assert not modified
894
        self.assert_raises_fault(304, self.client.retrieve_object,
895
                                 self.containers[1],
896
                                 self.objects[0]['name'],
897
                                 if_none_match=self.objects[0]['hash'])
898
        
899
        meta = self.client.retrieve_object_metadata(self.containers[1],
900
                                                    self.objects[0]['name'])
901
        self.assertEqual(meta['etag'], self.objects[0]['hash'])
902
    
903
    def test_if_modified_since(self):
904
        t = datetime.datetime.utcnow()
905
        t2 = t - datetime.timedelta(minutes=10)
906
        
907
        #modify the object
908
        self.upload_data(self.containers[1],
909
                           self.objects[0]['name'],
910
                           self.objects[0]['data'][:200])
911
        
912
        for f in DATE_FORMATS:
913
            past = t2.strftime(f)
914
            
915
            headers = {'if-modified-since':'%s' %past}
916
            try:
917
                o = self.client.retrieve_object(self.containers[1],
918
                                                self.objects[0]['name'],
919
                                                if_modified_since=past)
920
                self.assertEqual(o,
921
                                 self.client.retrieve_object(self.containers[1],
922
                                                             self.objects[0]['name']))
923
            except Fault, f:
924
                self.failIf(f.status == 304)
925
    
926
    def test_if_modified_since_invalid_date(self):
927
        o = self.client.retrieve_object(self.containers[1],
928
                                        self.objects[0]['name'],
929
                                        if_modified_since='')
930
        self.assertEqual(o, self.client.retrieve_object(self.containers[1],
931
                                                        self.objects[0]['name']))
932
            
933
    def test_if_not_modified_since(self):
934
        now = datetime.datetime.utcnow()
935
        since = now + datetime.timedelta(1)
936
        
937
        for f in DATE_FORMATS:
938
            #assert not modified
939
            self.assert_raises_fault(304, self.client.retrieve_object,
940
                                     self.containers[1], self.objects[0]['name'],
941
                                     if_modified_since=since.strftime(f))
942
    
943
    def test_if_unmodified_since(self):
944
        now = datetime.datetime.utcnow()
945
        since = now + datetime.timedelta(1)
946
        
947
        for f in DATE_FORMATS:
948
            t = since.strftime(f)
949
            status, headers, data = self.client.request_object(self.containers[1],
950
                                                               self.objects[0]['name'],
951
                                                               if_unmodified_since=t)
952
            #assert success
953
            self.assertEqual(status, 200)
954
            self.assertEqual(self.objects[0]['data'], data)
955
            
956
            #assert content-type
957
            self.assertEqual(headers['content-type'],
958
                             self.objects[0]['meta']['content_type'])
959
    
960
    def test_if_unmodified_since_precondition_failed(self):
961
        t = datetime.datetime.utcnow()
962
        t2 = t - datetime.timedelta(minutes=10)
963
        
964
        #modify the object
965
        self.upload_data(self.containers[1],
966
                           self.objects[0]['name'],
967
                           self.objects[0]['data'][:200])
968
        
969
        for f in DATE_FORMATS:
970
            past = t2.strftime(f)
971
            #assert precondition failed
972
            self.assert_raises_fault(412, self.client.retrieve_object,
973
                                     self.containers[1], self.objects[0]['name'],
974
                                     if_unmodified_since=past)
975
    
976
    def test_hashes(self):
977
        l = 8388609
978
        fname = 'largefile'
979
        o = self.upload_random_data(self.containers[1], fname, l)
980
        if o:
981
            data = self.client.retrieve_object(self.containers[1], fname, detail=True)
982
            body = json.loads(data)
983
            hashes = body['hashes']
984
            block_size = body['block_size']
985
            block_hash = body['block_hash']
986
            block_num = l/block_size == 0 and l/block_size or l/block_size + 1
987
            self.assertTrue(len(hashes), block_num)
988
            i = 0
989
            for h in hashes:
990
                start = i * block_size
991
                end = (i + 1) * block_size
992
                hash = compute_block_hash(o['data'][start:end], block_hash)
993
                self.assertEqual(h, hash)
994
                i += 1
995

    
996
class ObjectPut(BaseTestCase):
997
    def setUp(self):
998
        BaseTestCase.setUp(self)
999
        self.account = 'test'
1000
        self.container = 'c1'
1001
        self.client.create_container(self.container)
1002
    
1003
    def test_upload(self):
1004
        name = o_names[0]
1005
        meta = {'test':'test1'}
1006
        o = self.upload_random_data(self.container, name, **meta)
1007
        
1008
        headers = self.client.retrieve_object_metadata(self.container,
1009
                                                       name,
1010
                                                       restricted=True)
1011
        self.assertTrue('test' in headers.keys())
1012
        self.assertEqual(headers['test'], meta['test'])
1013
        
1014
        #assert uploaded content
1015
        status, h, data = self.client.request_object(self.container, name)
1016
        self.assertEqual(len(o['data']), int(h['content-length']))
1017
        self.assertEqual(o['data'], data)
1018
        
1019
        #assert content-type
1020
        self.assertEqual(h['content-type'], o['meta']['content_type'])
1021
    
1022
    def test_upload_unprocessable_entity(self):
1023
        meta={'etag':'123', 'test':'test1'}
1024
        
1025
        #assert unprocessable entity
1026
        self.assert_raises_fault(422, self.upload_random_data, self.container,
1027
                                 o_names[0], **meta)
1028
    
1029
    def test_chucked_transfer(self):
1030
        data = get_random_data()
1031
        objname = 'object'
1032
        self.client.create_object_using_chunks(self.container, objname,
1033
                                               StringIO(data))
1034
        
1035
        uploaded_data = self.client.retrieve_object(self.container, objname)
1036
        self.assertEqual(data, uploaded_data)
1037

    
1038
class ObjectCopy(BaseTestCase):
1039
    def setUp(self):
1040
        BaseTestCase.setUp(self)
1041
        self.account = 'test'
1042
        self.containers = ['c1', 'c2']
1043
        for c in self.containers:
1044
            self.client.create_container(c)
1045
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1046
    
1047
    def test_copy(self):
1048
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1049
                             self.containers[0], self.obj['name']):
1050
            #perform copy
1051
            meta = {'test':'testcopy'}
1052
            status = self.client.copy_object(self.containers[0],
1053
                                              self.obj['name'],
1054
                                              self.containers[0],
1055
                                              'testcopy',
1056
                                              **meta)[0]
1057
            
1058
            #assert copy success
1059
            self.assertEqual(status, 201)
1060
            
1061
            #assert access the new object
1062
            headers = self.client.retrieve_object_metadata(self.containers[0],
1063
                                                           'testcopy')
1064
            self.assertTrue('x-object-meta-test' in headers.keys())
1065
            self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1066
            
1067
            #assert etag is the same
1068
            self.assertEqual(headers['etag'], self.obj['hash'])
1069
            
1070
            #assert src object still exists
1071
            self.assert_object_exists(self.containers[0], self.obj['name'])
1072
    
1073
    def test_copy_from_different_container(self):
1074
        with AssertMappingInvariant(self.client.retrieve_object_metadata,
1075
                             self.containers[0], self.obj['name']):
1076
            meta = {'test':'testcopy'}
1077
            status = self.client.copy_object(self.containers[0],
1078
                                             self.obj['name'],
1079
                                             self.containers[1],
1080
                                             'testcopy',
1081
                                             **meta)[0]
1082
            self.assertEqual(status, 201)
1083
            
1084
            # assert updated metadata
1085
            meta = self.client.retrieve_object_metadata(self.containers[1],
1086
                                                           'testcopy',
1087
                                                           restricted=True)
1088
            self.assertTrue('test' in meta.keys())
1089
            self.assertTrue(meta['test'], 'testcopy')
1090
            
1091
            #assert src object still exists
1092
            self.assert_object_exists(self.containers[0], self.obj['name'])
1093
    
1094
    def test_copy_invalid(self):
1095
        #copy from invalid object
1096
        meta = {'test':'testcopy'}
1097
        self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1098
                                 'test.py', self.containers[1], 'testcopy',
1099
                                 **meta)
1100
        
1101
        #copy from invalid container
1102
        meta = {'test':'testcopy'}
1103
        self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1104
                                 self.obj['name'], self.containers[1],
1105
                                 'testcopy', **meta)
1106
        
1107

    
1108
class ObjectMove(ObjectCopy):
1109
    def test_move(self):
1110
        #perform move
1111
        meta = {'test':'testcopy'}
1112
        src_path = os.path.join('/', self.containers[0], self.obj['name'])
1113
        status = self.client.move_object(self.containers[0], self.obj['name'],
1114
                                         self.containers[0], 'testcopy',
1115
                                         **meta)[0]
1116
        
1117
        #assert successful move
1118
        self.assertEqual(status, 201)
1119
        
1120
        #assert updated metadata
1121
        meta = self.client.retrieve_object_metadata(self.containers[0],
1122
                                                    'testcopy',
1123
                                                    restricted=True)
1124
        self.assertTrue('test' in meta.keys())
1125
        self.assertTrue(meta['test'], 'testcopy')
1126
        
1127
        #assert src object no more exists
1128
        self.assert_object_not_exists(self.containers[0], self.obj['name'])
1129

    
1130
class ObjectPost(BaseTestCase):
1131
    def setUp(self):
1132
        BaseTestCase.setUp(self)
1133
        self.account = 'test'
1134
        self.containers = ['c1', 'c2']
1135
        for c in self.containers:
1136
            self.client.create_container(c)
1137
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1138
    
1139
    def test_update_meta(self):
1140
        #perform update metadata
1141
        more = {'foo':'foo', 'bar':'bar'}
1142
        status = self.client.update_object_metadata(self.containers[0],
1143
                                                    self.obj['name'],
1144
                                                    **more)[0]
1145
        #assert request accepted
1146
        self.assertEqual(status, 202)
1147
        
1148
        #assert old metadata are still there
1149
        headers = self.client.retrieve_object_metadata(self.containers[0],
1150
                                                       self.obj['name'],
1151
                                                       restricted=True)
1152
        #assert new metadata have been updated
1153
        for k,v in more.items():
1154
            self.assertTrue(k in headers.keys())
1155
            self.assertTrue(headers[k], v)
1156
    
1157
    def test_update_object(self,
1158
                           first_byte_pos=0,
1159
                           last_byte_pos=499,
1160
                           instance_length = True,
1161
                           content_length = 500):
1162
        l = len(self.obj['data'])
1163
        length = l if instance_length else '*'
1164
        range = 'bytes %d-%d/%s' %(first_byte_pos,
1165
                                       last_byte_pos,
1166
                                       length)
1167
        partial = last_byte_pos - first_byte_pos + 1
1168
        data = get_random_data(partial)
1169
        args = {'content_type':'application/octet-stream',
1170
                'content_range':'%s' %range}
1171
        if content_length:
1172
            args['content_length'] = content_length
1173
        status = self.client.update_object(self.containers[0], self.obj['name'],
1174
                                  StringIO(data), **args)[0]
1175
        
1176
        if partial < 0 or (instance_length and l <= last_byte_pos):
1177
            self.assertEqual(status, 202)    
1178
        else:
1179
            self.assertEqual(status, 204)           
1180
            #check modified object
1181
            content = self.client.retrieve_object(self.containers[0],
1182
                                              self.obj['name'])
1183
            self.assertEqual(content[0:partial], data)
1184
            self.assertEqual(content[partial:l], self.obj['data'][partial:l])
1185
    
1186
    def test_update_object_no_content_length(self):
1187
        self.test_update_object(content_length = None)
1188
    
1189
    def test_update_object_invalid_content_length(self):
1190
        with AssertContentInvariant(self.client.retrieve_object,
1191
                                    self.containers[0], self.obj['name']):
1192
            self.assert_raises_fault(400, self.test_update_object,
1193
                                     content_length = 1000)
1194
    
1195
    def test_update_object_invalid_range(self):
1196
        with AssertContentInvariant(self.client.retrieve_object,
1197
                                    self.containers[0], self.obj['name']):
1198
            self.test_update_object(499, 0, True)
1199
    
1200
    #no use if the server resets the content-legth
1201
    def test_update_object_invalid_range_and_length(self):
1202
        with AssertContentInvariant(self.client.retrieve_object,
1203
                                    self.containers[0], self.obj['name']):
1204
            self.test_update_object(499, 0, True, -1)
1205
    
1206
    #no use if the server resets the content-legth
1207
    def test_update_object_invalid_range_with_no_content_length(self):
1208
        with AssertContentInvariant(self.client.retrieve_object,
1209
                                    self.containers[0], self.obj['name']):
1210
            self.test_update_object(499, 0, True, content_length = None)
1211
    
1212
    def test_update_object_out_of_limits(self):    
1213
        with AssertContentInvariant(self.client.retrieve_object,
1214
                                    self.containers[0], self.obj['name']):
1215
            l = len(self.obj['data'])
1216
            self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1217
    
1218
    def test_append(self):
1219
        data = get_random_data(500)
1220
        headers = {}
1221
        self.client.update_object(self.containers[0], self.obj['name'],
1222
                                  StringIO(data), content_length=500,
1223
                                  content_type='application/octet-stream')
1224
        
1225
        content = self.client.retrieve_object(self.containers[0],
1226
                                              self.obj['name'])
1227
        self.assertEqual(len(content), len(self.obj['data']) + 500)
1228
        self.assertEqual(content[:-500], self.obj['data'])
1229
    
1230
    def test_update_with_chunked_transfer(self):
1231
        data = get_random_data(500)
1232
        dl = len(data)
1233
        fl = len(self.obj['data'])
1234
        
1235
        self.client.update_object_using_chunks(self.containers[0],
1236
                                               self.obj['name'], StringIO(data),
1237
                                               offset=0,
1238
                                               content_type='application/octet-stream')
1239
        
1240
        #check modified object
1241
        content = self.client.retrieve_object(self.containers[0],
1242
                                              self.obj['name'])
1243
        self.assertEqual(content[0:dl], data)
1244
        self.assertEqual(content[dl:fl], self.obj['data'][dl:fl])
1245

    
1246
class ObjectDelete(BaseTestCase):
1247
    def setUp(self):
1248
        BaseTestCase.setUp(self)
1249
        self.account = 'test'
1250
        self.containers = ['c1', 'c2']
1251
        for c in self.containers:
1252
            self.client.create_container(c)
1253
        self.obj = self.upload_random_data(self.containers[0], o_names[0])
1254
    
1255
    def test_delete(self):
1256
        #perform delete object
1257
        self.client.delete_object(self.containers[0], self.obj['name'])[0]
1258
    
1259
    def test_delete_invalid(self):
1260
        #assert item not found
1261
        self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1262
                                 self.obj['name'])
1263

    
1264
class AssertMappingInvariant(object):
1265
    def __init__(self, callable, *args, **kwargs):
1266
        self.callable = callable
1267
        self.args = args
1268
        self.kwargs = kwargs
1269
    
1270
    def __enter__(self):
1271
        self.map = self.callable(*self.args, **self.kwargs)
1272
        return self.map
1273
    
1274
    def __exit__(self, type, value, tb):
1275
        map = self.callable(*self.args, **self.kwargs)
1276
        for k in self.map.keys():
1277
            if is_date(map[k]):
1278
                continue
1279
            assert map[k] == self.map[k]
1280

    
1281
class AssertContentInvariant(object):
1282
    def __init__(self, callable, *args, **kwargs):
1283
        self.callable = callable
1284
        self.args = args
1285
        self.kwargs = kwargs
1286
    
1287
    def __enter__(self):
1288
        self.content = self.callable(*self.args, **self.kwargs)[2]
1289
        return self.content
1290
    
1291
    def __exit__(self, type, value, tb):
1292
        content = self.callable(*self.args, **self.kwargs)[2]
1293
        assert self.content == content
1294

    
1295
def get_content_splitted(response):
1296
    if response:
1297
        return response.content.split('\n')
1298

    
1299
def compute_md5_hash(data):
1300
    md5 = hashlib.md5()
1301
    offset = 0
1302
    md5.update(data)
1303
    return md5.hexdigest().lower()
1304

    
1305
def compute_block_hash(data, algorithm):
1306
    h = hashlib.new(algorithm)
1307
    h.update(data.rstrip('\x00'))
1308
    return h.hexdigest()
1309

    
1310
def get_random_data(length=500):
1311
    char_set = string.ascii_uppercase + string.digits
1312
    return ''.join(random.choice(char_set) for x in range(length))
1313

    
1314
def is_date(date):
1315
    MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
1316
    __D = r'(?P<day>\d{2})'
1317
    __D2 = r'(?P<day>[ \d]\d)'
1318
    __M = r'(?P<mon>\w{3})'
1319
    __Y = r'(?P<year>\d{4})'
1320
    __Y2 = r'(?P<year>\d{2})'
1321
    __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
1322
    RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
1323
    RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
1324
    ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
1325
    for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
1326
        m = regex.match(date)
1327
        if m is not None:
1328
            return True
1329
    return False
1330

    
1331
o_names = ['kate.jpg',
1332
           'kate_beckinsale.jpg',
1333
           'How To Win Friends And Influence People.pdf',
1334
           'moms_birthday.jpg',
1335
           'poodle_strut.mov',
1336
           'Disturbed - Down With The Sickness.mp3',
1337
           'army_of_darkness.avi',
1338
           'the_mad.avi',
1339
           'photos/animals/dogs/poodle.jpg',
1340
           'photos/animals/dogs/terrier.jpg',
1341
           'photos/animals/cats/persian.jpg',
1342
           'photos/animals/cats/siamese.jpg',
1343
           'photos/plants/fern.jpg',
1344
           'photos/plants/rose.jpg',
1345
           'photos/me.jpg']
1346

    
1347
if __name__ == "__main__":
1348
    unittest.main()