Update documentation.
[pithos] / tools / test
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 upload_random_data(self, container, name, length=1024, type=None,
243                            enc=None, **meta):
244         data = get_random_data(length)
245         return self.upload_data(container, name, data, type, enc, **meta)
246     
247     def upload_data(self, container, name, data, type=None, enc=None, etag=None,
248                     **meta):
249         obj = {}
250         obj['name'] = name
251         try:
252             obj['data'] = data
253             obj['hash'] = compute_md5_hash(obj['data'])
254             
255             args = {}
256             args['etag'] = etag if etag else obj['hash']
257             
258             try:
259                 guess = mimetypes.guess_type(name)
260                 type = type if type else guess[0]
261                 enc = enc if enc else guess[1]
262             except:
263                 pass
264             args['content_type'] = type if type else 'plain/text'
265             args['content_encoding'] = enc if enc else None
266             
267             obj['meta'] = args
268             
269             path = '/%s/%s' % (container, name)
270             self.client.create_object(container, name, StringIO(obj['data']),
271                                       meta, **args)
272             
273             return obj
274         except IOError:
275             return
276
277 class AccountHead(BaseTestCase):
278     def setUp(self):
279         BaseTestCase.setUp(self)
280         self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
281         for item in self.containers:
282             self.client.create_container(item)
283         
284                 #keep track of initial account groups
285         self.initial_groups = self.client.retrieve_account_groups()
286         
287         #keep track of initial account meta
288         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
289         
290         meta = {'foo':'bar'}
291         self.client.update_account_metadata(**meta)
292         self.updated_meta = self.initial_meta.update(meta)
293     
294     def tearDown(self):
295         #delete additionally created meta
296         l = []
297         for m in self.client.retrieve_account_metadata(restricted=True):
298             if m not in self.initial_meta:
299                 l.append(m)
300         self.client.delete_account_metadata(l)
301         
302         #delete additionally created groups
303         l = []
304         for g in self.client.retrieve_account_groups():
305             if g not in self.initial_groups:
306                 l.append(g)
307         self.client.unset_account_groups(l)
308         
309         BaseTestCase.tearDown(self)
310     
311     def test_get_account_meta(self):
312         meta = self.client.retrieve_account_metadata()
313         
314         containers = self.client.list_containers()
315         l = str(len(containers))
316         self.assertEqual(meta['x-account-container-count'], l)
317         size = 0
318         for c in containers:
319             m = self.client.retrieve_container_metadata(c)
320             size = size + int(m['x-container-bytes-used'])
321         self.assertEqual(meta['x-account-bytes-used'], str(size))
322     
323     def test_get_account_401(self):
324         self.assert_raises_fault(401,
325                                  self.invalid_client.retrieve_account_metadata)
326     
327     def test_get_account_meta_until(self):
328         t = datetime.datetime.utcnow()
329         past = t - datetime.timedelta(minutes=-15)
330         past = int(_time.mktime(past.timetuple()))
331         
332         meta = {'premium':True}
333         self.client.update_account_metadata(**meta)
334         meta = self.client.retrieve_account_metadata(restricted=True,
335                                                      until=past)
336         self.assertTrue('premium' not in meta)
337         
338         meta = self.client.retrieve_account_metadata(restricted=True)
339         self.assertTrue('premium' in meta)
340     
341     def test_get_account_meta_until_invalid_date(self):
342         meta = {'premium':True}
343         self.client.update_account_metadata(**meta)
344         meta = self.client.retrieve_account_metadata(restricted=True,
345                                                      until='kshfksfh')
346         self.assertTrue('premium' in meta)
347     
348 class AccountGet(BaseTestCase):
349     def setUp(self):
350         BaseTestCase.setUp(self)
351         #create some containers
352         self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
353         for item in self.containers:
354             self.client.create_container(item)
355     
356     def test_list(self):
357         #list containers
358         containers = self.client.list_containers()
359         self.assertEquals(self.containers, containers)
360     
361     def test_list_401(self):
362         self.assert_raises_fault(401, self.invalid_client.list_containers)
363     
364     def test_list_with_limit(self):
365         limit = 2
366         containers = self.client.list_containers(limit=limit)
367         self.assertEquals(len(containers), limit)
368         self.assertEquals(self.containers[:2], containers)
369     
370     def test_list_with_marker(self):
371         l = 2
372         m = 'bananas'
373         containers = self.client.list_containers(limit=l, marker=m)
374         i = self.containers.index(m) + 1
375         self.assertEquals(self.containers[i:(i+l)], containers)
376         
377         m = 'oranges'
378         containers = self.client.list_containers(limit=l, marker=m)
379         i = self.containers.index(m) + 1
380         self.assertEquals(self.containers[i:(i+l)], containers)
381     
382     def test_list_json_with_marker(self):
383         l = 2
384         m = 'bananas'
385         containers = self.client.list_containers(limit=l, marker=m, format='json')
386         self.assert_extended(containers, 'json', 'container', l)
387         self.assertEqual(containers[0]['name'], 'kiwis')
388         self.assertEqual(containers[1]['name'], 'oranges')
389     
390     def test_list_xml_with_marker(self):
391         l = 2
392         m = 'oranges'
393         xml = self.client.list_containers(limit=l, marker=m, format='xml')
394         self.assert_extended(xml, 'xml', 'container', l)
395         nodes = xml.getElementsByTagName('name')
396         self.assertEqual(len(nodes), 1)
397         self.assertEqual(nodes[0].childNodes[0].data, 'pears')
398     
399     def test_if_modified_since(self):
400         t = datetime.datetime.utcnow()
401         t2 = t - datetime.timedelta(minutes=10)
402         
403         #add a new container
404         self.client.create_container('dummy')
405         
406         for f in DATE_FORMATS:
407             past = t2.strftime(f)
408             try:
409                 c = self.client.list_containers(if_modified_since=past)
410                 self.assertEqual(len(c), len(self.containers) + 1)
411             except Fault, f:
412                 self.failIf(f.status == 304) #fail if not modified
413     
414     def test_if_modified_since_invalid_date(self):
415         c = self.client.list_containers(if_modified_since='')
416         self.assertEqual(len(c), len(self.containers))
417     
418     def test_if_not_modified_since(self):
419         now = datetime.datetime.utcnow()
420         since = now + datetime.timedelta(1)
421         
422         for f in DATE_FORMATS:
423             args = {'if_modified_since':'%s' %since.strftime(f)}
424             
425             #assert not modified
426             self.assert_raises_fault(304, self.client.list_containers, **args)
427     
428     def test_if_unmodified_since(self):
429         now = datetime.datetime.utcnow()
430         since = now + datetime.timedelta(1)
431         
432         for f in DATE_FORMATS:
433             c = self.client.list_containers(if_unmodified_since=since.strftime(f))
434             
435             #assert success
436             self.assertEqual(self.containers, c)
437     
438     def test_if_unmodified_since_precondition_failed(self):
439         t = datetime.datetime.utcnow()
440         t2 = t - datetime.timedelta(minutes=10)
441         
442         #add a new container
443         self.client.create_container('dummy')
444         
445         for f in DATE_FORMATS:
446             past = t2.strftime(f)
447             
448             args = {'if_unmodified_since':'%s' %past}
449             
450             #assert precondition failed
451             self.assert_raises_fault(412, self.client.list_containers, **args)
452     
453 class AccountPost(BaseTestCase):
454     def setUp(self):
455         BaseTestCase.setUp(self)
456         self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
457         for item in self.containers:
458             self.client.create_container(item)
459         
460         #keep track of initial account groups
461         self.initial_groups = self.client.retrieve_account_groups()
462         
463         #keep track of initial account meta
464         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
465         
466         meta = {'foo':'bar'}
467         self.client.update_account_metadata(**meta)
468         self.updated_meta = self.initial_meta.update(meta)
469     
470     def tearDown(self):
471         #delete additionally created meta
472         l = []
473         for m in self.client.retrieve_account_metadata(restricted=True):
474             if m not in self.initial_meta:
475                 l.append(m)
476         self.client.delete_account_metadata(l)
477         
478         #delete additionally created groups
479         l = []
480         for g in self.client.retrieve_account_groups():
481             if g not in self.initial_groups:
482                 l.append(g)
483         self.client.unset_account_groups(l)
484         
485         BaseTestCase.tearDown(self)
486     
487     def test_update_meta(self):
488         with AssertMappingInvariant(self.client.retrieve_account_groups):
489             meta = {'test':'test', 'tost':'tost'}
490             self.client.update_account_metadata(**meta)
491             
492             meta.update(self.initial_meta)
493             self.assertEqual(meta,
494                              self.client.retrieve_account_metadata(
495                                 restricted=True))
496         
497     def test_invalid_account_update_meta(self):
498         meta = {'test':'test', 'tost':'tost'}
499         self.assert_raises_fault(401,
500                                  self.invalid_client.update_account_metadata,
501                                  **meta)
502     
503     def test_reset_meta(self):
504         with AssertMappingInvariant(self.client.retrieve_account_groups):
505             meta = {'test':'test', 'tost':'tost'}
506             self.client.update_account_metadata(**meta)
507             
508             meta = {'test':'test33'}
509             self.client.reset_account_metadata(**meta)
510             
511             self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
512     
513     def test_delete_meta(self):
514         with AssertMappingInvariant(self.client.retrieve_account_groups):
515             meta = {'test':'test', 'tost':'tost'}
516             self.client.update_account_metadata(**meta)
517             
518             self.client.delete_account_metadata(meta.keys())
519             
520             account_meta = self.client.retrieve_account_metadata(restricted=True)
521             for m in meta:
522                 self.assertTrue(m not in account_meta.keys())
523     
524     def test_set_account_groups(self):
525         with AssertMappingInvariant(self.client.retrieve_account_metadata):
526             groups = {'pithosdev':'verigak,gtsouk,chazapis'}
527             self.client.set_account_groups(**groups)
528             
529             self.assertEqual(set(groups['pithosdev']),
530                              set(self.client.retrieve_account_groups()['pithosdev']))
531             
532             more_groups = {'clientsdev':'pkanavos,mvasilak'}
533             self.client.set_account_groups(**more_groups)
534             
535             groups.update(more_groups)
536             self.assertEqual(set(groups['clientsdev']),
537                              set(self.client.retrieve_account_groups()['clientsdev']))
538     
539     def test_reset_account_groups(self):
540         with AssertMappingInvariant(self.client.retrieve_account_metadata):
541             groups = {'pithosdev':'verigak,gtsouk,chazapis',
542                       'clientsdev':'pkanavos,mvasilak'}
543             self.client.set_account_groups(**groups)
544             
545             self.assertEqual(set(groups['pithosdev'].split(',')),
546                              set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
547             self.assertEqual(set(groups['clientsdev'].split(',')),
548                              set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
549             
550             groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
551             self.client.reset_account_groups(**groups)
552             
553             self.assertEqual(set(groups['pithosdev'].split(',')),
554                              set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
555     
556     def test_delete_account_groups(self):
557         with AssertMappingInvariant(self.client.retrieve_account_metadata):
558             groups = {'pithosdev':'verigak,gtsouk,chazapis',
559                       'clientsdev':'pkanavos,mvasilak'}
560             self.client.set_account_groups(**groups)
561             
562             self.client.unset_account_groups(groups.keys())
563             
564             self.assertEqual({}, self.client.retrieve_account_groups())
565     
566 class ContainerHead(BaseTestCase):
567     def setUp(self):
568         BaseTestCase.setUp(self)
569         self.container = 'apples'
570         self.client.create_container(self.container)
571     
572     def test_get_meta(self):
573         meta = {'trash':'true'}
574         t1 = datetime.datetime.utcnow()
575         o = self.upload_random_data(self.container, o_names[0], **meta)
576         if o:
577             headers = self.client.retrieve_container_metadata(self.container)
578             self.assertEqual(headers['x-container-object-count'], '1')
579             self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
580             t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
581             delta = (t2 - t1)
582             threashold = datetime.timedelta(seconds=1) 
583             self.assertTrue(delta < threashold)
584             self.assertTrue(headers['x-container-object-meta'])
585             self.assertTrue('Trash' in headers['x-container-object-meta'])
586
587 class ContainerGet(BaseTestCase):
588     def setUp(self):
589         BaseTestCase.setUp(self)
590         self.container = ['pears', 'apples']
591         for c in self.container:
592             self.client.create_container(c)
593         self.obj = []
594         for o in o_names[:8]:
595             self.obj.append(self.upload_random_data(self.container[0], o))
596         for o in o_names[8:]:
597             self.obj.append(self.upload_random_data(self.container[1], o))
598     
599     def test_list_objects(self):
600         objects = self.client.list_objects(self.container[0])
601         l = [elem['name'] for elem in self.obj[:8]]
602         l.sort()
603         self.assertEqual(objects, l)
604     
605     def test_list_objects_containing_slash(self):
606         self.client.create_container('test')
607         self.upload_random_data('test', '/objectname')
608         
609         objects = self.client.list_objects('test')
610         self.assertEqual(objects, ['/objectname'])
611         
612         objects = self.client.list_objects('test', format='json')
613         self.assertEqual(objects[0]['name'], '/objectname')
614         
615         objects = self.client.list_objects('test', format='xml')
616         self.assert_extended(objects, 'xml', 'object')
617         node_name = objects.getElementsByTagName('name')[0]
618         self.assertEqual(node_name.firstChild.data, '/objectname')
619         
620         #objects = self.client.list_objects('test', prefix='/')
621         #self.assertEqual(objects, ['/objectname'])
622         #
623         #objects = self.client.list_objects('test', path='/')
624         #self.assertEqual(objects, ['/objectname'])
625         #
626         #objects = self.client.list_objects('test', prefix='/', delimiter='n')
627         #self.assertEqual(objects, ['/object'])
628
629     def test_list_objects_with_limit_marker(self):
630         objects = self.client.list_objects(self.container[0], limit=2)
631         l = [elem['name'] for elem in self.obj[:8]]
632         l.sort()
633         self.assertEqual(objects, l[:2])
634         
635         markers = ['How To Win Friends And Influence People.pdf',
636                    'moms_birthday.jpg']
637         limit = 4
638         for m in markers:
639             objects = self.client.list_objects(self.container[0], limit=limit,
640                                                marker=m)
641             l = [elem['name'] for elem in self.obj[:8]]
642             l.sort()
643             start = l.index(m) + 1
644             end = start + limit
645             end = len(l) >= end and end or len(l)
646             self.assertEqual(objects, l[start:end])
647     
648     #takes too long
649     def _test_list_limit_exceeds(self):
650         self.client.create_container('pithos')
651         
652         for i in range(10001):
653             self.client.create_zero_length_object('pithos', i)
654         
655         self.assertEqual(10000, len(self.client.list_objects('pithos')))
656     
657     def test_list_empty_params(self):
658         objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
659         if objects:
660             objects = objects.strip().split('\n')
661         self.assertEqual(objects,
662                          self.client.list_objects(self.container[0]))
663     
664     def test_list_pseudo_hierarchical_folders(self):
665         objects = self.client.list_objects(self.container[1], prefix='photos',
666                                            delimiter='/')
667         self.assertEquals(['photos/animals/', 'photos/me.jpg',
668                            'photos/plants/'], objects)
669         
670         objects = self.client.list_objects(self.container[1],
671                                            prefix='photos/animals',
672                                            delimiter='/')
673         l = ['photos/animals/cats/', 'photos/animals/dogs/']
674         self.assertEquals(l, objects)
675         
676         objects = self.client.list_objects(self.container[1], path='photos')
677         self.assertEquals(['photos/me.jpg'], objects)
678     
679     def test_extended_list_json(self):
680         objects = self.client.list_objects(self.container[1], format='json',
681                                            limit=2, prefix='photos/animals',
682                                            delimiter='/')
683         self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
684         self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
685     
686     def test_extended_list_xml(self):
687         xml = self.client.list_objects(self.container[1], format='xml', limit=4,
688                                        prefix='photos', delimiter='/')
689         self.assert_extended(xml, 'xml', 'object', size=4)
690         dirs = xml.getElementsByTagName('subdir')
691         self.assertEqual(len(dirs), 2)
692         self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
693         self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
694         
695         objects = xml.getElementsByTagName('name')
696         self.assertEqual(len(objects), 1)
697         self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
698     
699     def test_list_meta_double_matching(self):
700         meta = {'quality':'aaa', 'stock':'true'}
701         self.client.update_object_metadata(self.container[0],
702                                            self.obj[0]['name'], **meta)
703         obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
704         self.assertEqual(len(obj), 1)
705         self.assertTrue(obj, self.obj[0]['name'])
706     
707     def test_list_using_meta(self):
708         meta = {'quality':'aaa'}
709         for o in self.obj[:2]:
710             self.client.update_object_metadata(self.container[0], o['name'],
711                                                **meta)
712         meta = {'stock':'true'}
713         for o in self.obj[3:5]:
714             self.client.update_object_metadata(self.container[0], o['name'],
715                                                **meta)
716         
717         obj = self.client.list_objects(self.container[0], meta='Quality')
718         self.assertEqual(len(obj), 2)
719         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
720         
721         # test case insensitive
722         obj = self.client.list_objects(self.container[0], meta='quality')
723         self.assertEqual(len(obj), 2)
724         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
725         
726         # test multiple matches
727         obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
728         self.assertEqual(len(obj), 4)
729         self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
730         
731         # test non 1-1 multiple match
732         obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
733         self.assertEqual(len(obj), 2)
734         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
735     
736     def test_if_modified_since(self):
737         t = datetime.datetime.utcnow()
738         t2 = t - datetime.timedelta(minutes=10)
739         
740         #add a new object
741         self.upload_random_data(self.container[0], o_names[0])
742         
743         for f in DATE_FORMATS:
744             past = t2.strftime(f)
745             try:
746                 o = self.client.list_objects(self.container[0],
747                                             if_modified_since=past)
748                 self.assertEqual(o,
749                                  self.client.list_objects(self.container[0]))
750             except Fault, f:
751                 self.failIf(f.status == 304) #fail if not modified
752     
753     def test_if_modified_since_invalid_date(self):
754         headers = {'if-modified-since':''}
755         o = self.client.list_objects(self.container[0], if_modified_since='')
756         self.assertEqual(o, self.client.list_objects(self.container[0]))
757     
758     def test_if_not_modified_since(self):
759         now = datetime.datetime.utcnow()
760         since = now + datetime.timedelta(1)
761         
762         for f in DATE_FORMATS:
763             args = {'if_modified_since':'%s' %since.strftime(f)}
764             
765             #assert not modified
766             self.assert_raises_fault(304, self.client.list_objects,
767                                      self.container[0], **args)
768     
769     def test_if_unmodified_since(self):
770         now = datetime.datetime.utcnow()
771         since = now + datetime.timedelta(1)
772         
773         for f in DATE_FORMATS:
774             obj = self.client.list_objects(self.container[0],
775                                            if_unmodified_since=since.strftime(f))
776             
777             #assert unmodified
778             self.assertEqual(obj, self.client.list_objects(self.container[0]))
779     
780     def test_if_unmodified_since_precondition_failed(self):
781         t = datetime.datetime.utcnow()
782         t2 = t - datetime.timedelta(minutes=10)
783         
784         #add a new container
785         self.client.create_container('dummy')
786         
787         for f in DATE_FORMATS:
788             past = t2.strftime(f)
789             
790             args = {'if_unmodified_since':'%s' %past}
791             
792             #assert precondition failed
793             self.assert_raises_fault(412, self.client.list_objects,
794                                      self.container[0], **args)
795
796 class ContainerPut(BaseTestCase):
797     def setUp(self):
798         BaseTestCase.setUp(self)
799         self.containers = ['c1', 'c2']
800     
801     def test_create(self):
802         self.client.create_container(self.containers[0])
803         containers = self.client.list_containers()
804         self.assertTrue(self.containers[0] in containers)
805         self.assert_container_exists(self.containers[0])
806     
807     def test_create_twice(self):
808         self.client.create_container(self.containers[0])
809         self.assertTrue(not self.client.create_container(self.containers[0]))
810     
811 class ContainerPost(BaseTestCase):
812     def setUp(self):
813         BaseTestCase.setUp(self)
814         self.container = 'apples'
815         self.client.create_container(self.container)
816     
817     def test_update_meta(self):
818         meta = {'test':'test33',
819                 'tost':'tost22'}
820         self.client.update_container_metadata(self.container, **meta)
821         headers = self.client.retrieve_container_metadata(self.container)
822         for k,v in meta.items():
823             k = 'x-container-meta-%s' % k
824             self.assertTrue(headers[k])
825             self.assertEqual(headers[k], v)
826
827 class ContainerDelete(BaseTestCase):
828     def setUp(self):
829         BaseTestCase.setUp(self)
830         self.containers = ['c1', 'c2']
831         for c in self.containers:
832             self.client.create_container(c)
833         self.upload_random_data(self.containers[1], o_names[0])
834     
835     def test_delete(self):
836         status = self.client.delete_container(self.containers[0])[0]
837         self.assertEqual(status, 204)
838     
839     def test_delete_non_empty(self):
840         self.assert_raises_fault(409, self.client.delete_container,
841                                  self.containers[1])
842     
843     def test_delete_invalid(self):
844         self.assert_raises_fault(404, self.client.delete_container, 'c3')
845
846 class ObjectHead(BaseTestCase):
847     pass
848
849 class ObjectGet(BaseTestCase):
850     def setUp(self):
851         BaseTestCase.setUp(self)
852         self.containers = ['c1', 'c2']
853         #create some containers
854         for c in self.containers:
855             self.client.create_container(c)
856         
857         #upload a file
858         names = ('obj1', 'obj2')
859         self.objects = []
860         for n in names:
861             self.objects.append(self.upload_random_data(self.containers[1], n))
862     
863     def test_get(self):
864         #perform get
865         o = self.client.retrieve_object(self.containers[1],
866                                         self.objects[0]['name'],
867                                         self.objects[0]['meta'])
868         self.assertEqual(o, self.objects[0]['data'])
869     
870     def test_get_invalid(self):
871         self.assert_raises_fault(404, self.client.retrieve_object,
872                                  self.containers[0], self.objects[0]['name'])
873     
874     def test_get_partial(self):
875         #perform get with range
876         status, headers, data = self.client.request_object(self.containers[1],
877                                                             self.objects[0]['name'],
878                                                             range='bytes=0-499')
879         
880         #assert successful partial content
881         self.assertEqual(status, 206)
882         
883         #assert content-type
884         self.assertEqual(headers['content-type'],
885                          self.objects[0]['meta']['content_type'])
886         
887         #assert content length
888         self.assertEqual(int(headers['content-length']), 500)
889         
890         #assert content
891         self.assertEqual(self.objects[0]['data'][:500], data)
892     
893     def test_get_final_500(self):
894         #perform get with range
895         headers = {'range':'bytes=-500'}
896         status, headers, data = self.client.request_object(self.containers[1],
897                                                             self.objects[0]['name'],
898                                                             range='bytes=-500')
899         
900         #assert successful partial content
901         self.assertEqual(status, 206)
902         
903         #assert content-type
904         self.assertEqual(headers['content-type'],
905                          self.objects[0]['meta']['content_type'])
906         
907         #assert content length
908         self.assertEqual(int(headers['content-length']), 500)
909         
910         #assert content
911         self.assertTrue(self.objects[0]['data'][-500:], data)
912     
913     def test_get_rest(self):
914         #perform get with range
915         offset = len(self.objects[0]['data']) - 500
916         status, headers, data = self.client.request_object(self.containers[1],
917                                                 self.objects[0]['name'],
918                                                 range='bytes=%s-' %offset)
919         
920         #assert successful partial content
921         self.assertEqual(status, 206)
922         
923         #assert content-type
924         self.assertEqual(headers['content-type'],
925                          self.objects[0]['meta']['content_type'])
926         
927         #assert content length
928         self.assertEqual(int(headers['content-length']), 500)
929         
930         #assert content
931         self.assertTrue(self.objects[0]['data'][-500:], data)
932     
933     def test_get_range_not_satisfiable(self):
934         #perform get with range
935         offset = len(self.objects[0]['data']) + 1
936         
937         #assert range not satisfiable
938         self.assert_raises_fault(416, self.client.retrieve_object,
939                                  self.containers[1], self.objects[0]['name'],
940                                  range='bytes=0-%s' %offset)
941     
942     def test_multiple_range(self):
943         #perform get with multiple range
944         ranges = ['0-499', '-500', '1000-']
945         bytes = 'bytes=%s' % ','.join(ranges)
946         status, headers, data = self.client.request_object(self.containers[1],
947                                                            self.objects[0]['name'],
948                                                            range=bytes)
949         
950         # assert partial content
951         self.assertEqual(status, 206)
952         
953         # assert Content-Type of the reply will be multipart/byteranges
954         self.assertTrue(headers['content-type'])
955         content_type_parts = headers['content-type'].split()
956         self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
957         
958         boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
959         cparts = data.split(boundary)[1:-1]
960         
961         # assert content parts are exactly 2
962         self.assertEqual(len(cparts), len(ranges))
963         
964         # for each content part assert headers
965         i = 0
966         for cpart in cparts:
967             content = cpart.split('\r\n')
968             headers = content[1:3]
969             content_range = headers[0].split(': ')
970             self.assertEqual(content_range[0], 'Content-Range')
971             
972             r = ranges[i].split('-')
973             if not r[0] and not r[1]:
974                 pass
975             elif not r[0]:
976                 start = len(self.objects[0]['data']) - int(r[1])
977                 end = len(self.objects[0]['data'])
978             elif not r[1]:
979                 start = int(r[0])
980                 end = len(self.objects[0]['data'])
981             else:
982                 start = int(r[0])
983                 end = int(r[1]) + 1
984             fdata = self.objects[0]['data'][start:end]
985             sdata = '\r\n'.join(content[4:-1])
986             self.assertEqual(len(fdata), len(sdata))
987             self.assertEquals(fdata, sdata)
988             i+=1
989     
990     def test_multiple_range_not_satisfiable(self):
991         #perform get with multiple range
992         out_of_range = len(self.objects[0]['data']) + 1
993         ranges = ['0-499', '-500', '%d-' %out_of_range]
994         bytes = 'bytes=%s' % ','.join(ranges)
995         
996         # assert partial content
997         self.assert_raises_fault(416, self.client.retrieve_object,
998                                  self.containers[1],
999                                  self.objects[0]['name'], range=bytes)
1000     
1001     def test_get_with_if_match(self):
1002         #perform get with If-Match
1003         etag = self.objects[0]['hash']
1004         status, headers, data = self.client.request_object(self.containers[1],
1005                                                            self.objects[0]['name'],
1006                                                            if_match=etag)
1007         #assert get success
1008         self.assertEqual(status, 200)
1009         
1010         #assert content-type
1011         self.assertEqual(headers['content-type'],
1012                          self.objects[0]['meta']['content_type'])
1013         
1014         #assert response content
1015         self.assertEqual(self.objects[0]['data'], data)
1016     
1017     def test_get_with_if_match_star(self):
1018         #perform get with If-Match *
1019         headers = {'if-match':'*'}
1020         status, headers, data = self.client.request_object(self.containers[1],
1021                                                 self.objects[0]['name'],
1022                                                 **headers)
1023         #assert get success
1024         self.assertEqual(status, 200)
1025         
1026         #assert content-type
1027         self.assertEqual(headers['content-type'],
1028                          self.objects[0]['meta']['content_type'])
1029         
1030         #assert response content
1031         self.assertEqual(self.objects[0]['data'], data)
1032     
1033     def test_get_with_multiple_if_match(self):
1034         #perform get with If-Match
1035         etags = [i['hash'] for i in self.objects if i]
1036         etags = ','.join('"%s"' % etag for etag in etags)
1037         status, headers, data = self.client.request_object(self.containers[1],
1038                                                            self.objects[0]['name'],
1039                                                            if_match=etags)
1040         #assert get success
1041         self.assertEqual(status, 200)
1042         
1043         #assert content-type
1044         self.assertEqual(headers['content-type'],
1045                          self.objects[0]['meta']['content_type'])
1046         
1047         #assert content-type
1048         self.assertEqual(headers['content-type'],
1049                          self.objects[0]['meta']['content_type'])
1050         
1051         #assert response content
1052         self.assertEqual(self.objects[0]['data'], data)
1053     
1054     def test_if_match_precondition_failed(self):
1055         #assert precondition failed
1056         self.assert_raises_fault(412, self.client.retrieve_object,
1057                                  self.containers[1],
1058                                  self.objects[0]['name'], if_match='123')
1059     
1060     def test_if_none_match(self):
1061         #perform get with If-None-Match
1062         status, headers, data = self.client.request_object(self.containers[1],
1063                                                            self.objects[0]['name'],
1064                                                            if_none_match='123')
1065         
1066         #assert get success
1067         self.assertEqual(status, 200)
1068         
1069         #assert content-type
1070         self.assertEqual(headers['content_type'],
1071                          self.objects[0]['meta']['content_type'])
1072     
1073     def test_if_none_match(self):
1074         #perform get with If-None-Match * and assert not modified
1075         self.assert_raises_fault(304, self.client.retrieve_object,
1076                                  self.containers[1],
1077                                  self.objects[0]['name'],
1078                                  if_none_match='*')
1079     
1080     def test_if_none_match_not_modified(self):
1081         #perform get with If-None-Match and assert not modified
1082         self.assert_raises_fault(304, self.client.retrieve_object,
1083                                  self.containers[1],
1084                                  self.objects[0]['name'],
1085                                  if_none_match=self.objects[0]['hash'])
1086         
1087         meta = self.client.retrieve_object_metadata(self.containers[1],
1088                                                     self.objects[0]['name'])
1089         self.assertEqual(meta['etag'], self.objects[0]['hash'])
1090     
1091     def test_if_modified_since(self):
1092         t = datetime.datetime.utcnow()
1093         t2 = t - datetime.timedelta(minutes=10)
1094         
1095         #modify the object
1096         self.upload_data(self.containers[1],
1097                            self.objects[0]['name'],
1098                            self.objects[0]['data'][:200])
1099         
1100         for f in DATE_FORMATS:
1101             past = t2.strftime(f)
1102             
1103             headers = {'if-modified-since':'%s' %past}
1104             try:
1105                 o = self.client.retrieve_object(self.containers[1],
1106                                                 self.objects[0]['name'],
1107                                                 if_modified_since=past)
1108                 self.assertEqual(o,
1109                                  self.client.retrieve_object(self.containers[1],
1110                                                              self.objects[0]['name']))
1111             except Fault, f:
1112                 self.failIf(f.status == 304)
1113     
1114     def test_if_modified_since_invalid_date(self):
1115         o = self.client.retrieve_object(self.containers[1],
1116                                         self.objects[0]['name'],
1117                                         if_modified_since='')
1118         self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1119                                                         self.objects[0]['name']))
1120             
1121     def test_if_not_modified_since(self):
1122         now = datetime.datetime.utcnow()
1123         since = now + datetime.timedelta(1)
1124         
1125         for f in DATE_FORMATS:
1126             #assert not modified
1127             self.assert_raises_fault(304, self.client.retrieve_object,
1128                                      self.containers[1], self.objects[0]['name'],
1129                                      if_modified_since=since.strftime(f))
1130     
1131     def test_if_unmodified_since(self):
1132         now = datetime.datetime.utcnow()
1133         since = now + datetime.timedelta(1)
1134         
1135         for f in DATE_FORMATS:
1136             t = since.strftime(f)
1137             status, headers, data = self.client.request_object(self.containers[1],
1138                                                                self.objects[0]['name'],
1139                                                                if_unmodified_since=t)
1140             #assert success
1141             self.assertEqual(status, 200)
1142             self.assertEqual(self.objects[0]['data'], data)
1143             
1144             #assert content-type
1145             self.assertEqual(headers['content-type'],
1146                              self.objects[0]['meta']['content_type'])
1147     
1148     def test_if_unmodified_since_precondition_failed(self):
1149         t = datetime.datetime.utcnow()
1150         t2 = t - datetime.timedelta(minutes=10)
1151         
1152         #modify the object
1153         self.upload_data(self.containers[1],
1154                            self.objects[0]['name'],
1155                            self.objects[0]['data'][:200])
1156         
1157         for f in DATE_FORMATS:
1158             past = t2.strftime(f)
1159             #assert precondition failed
1160             self.assert_raises_fault(412, self.client.retrieve_object,
1161                                      self.containers[1], self.objects[0]['name'],
1162                                      if_unmodified_since=past)
1163     
1164     def test_hashes(self):
1165         l = 8388609
1166         fname = 'largefile'
1167         o = self.upload_random_data(self.containers[1], fname, l)
1168         if o:
1169             body = self.client.retrieve_object(self.containers[1], fname,
1170                                                format='json')
1171             hashes = body['hashes']
1172             block_size = body['block_size']
1173             block_hash = body['block_hash']
1174             block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1175             self.assertTrue(len(hashes), block_num)
1176             i = 0
1177             for h in hashes:
1178                 start = i * block_size
1179                 end = (i + 1) * block_size
1180                 hash = compute_block_hash(o['data'][start:end], block_hash)
1181                 self.assertEqual(h, hash)
1182                 i += 1
1183
1184 class ObjectPut(BaseTestCase):
1185     def setUp(self):
1186         BaseTestCase.setUp(self)
1187         self.container = 'c1'
1188         self.client.create_container(self.container)
1189     
1190     def test_upload(self):
1191         name = o_names[0]
1192         meta = {'test':'test1'}
1193         o = self.upload_random_data(self.container, name, **meta)
1194         
1195         headers = self.client.retrieve_object_metadata(self.container,
1196                                                        name,
1197                                                        restricted=True)
1198         self.assertTrue('test' in headers.keys())
1199         self.assertEqual(headers['test'], meta['test'])
1200         
1201         #assert uploaded content
1202         status, h, data = self.client.request_object(self.container, name)
1203         self.assertEqual(len(o['data']), int(h['content-length']))
1204         self.assertEqual(o['data'], data)
1205         
1206         #assert content-type
1207         self.assertEqual(h['content-type'], o['meta']['content_type'])
1208     
1209     def _test_maximum_upload_size_exceeds(self):
1210         name = o_names[0]
1211         meta = {'test':'test1'}
1212         #upload 100MB
1213         length=1024*1024*100
1214         self.assert_raises_fault(400, self.upload_random_data, self.container,
1215                                  name, length, **meta)
1216     
1217     def test_upload_with_name_containing_slash(self):
1218         name = '/%s' % o_names[0]
1219         meta = {'test':'test1'}
1220         o = self.upload_random_data(self.container, name, **meta)
1221         
1222         self.assertEqual(o['data'],
1223                          self.client.retrieve_object(self.container, name))
1224         
1225         self.assertTrue(name in self.client.list_objects(self.container))
1226     
1227     def test_create_directory_marker(self):
1228         self.client.create_directory_marker(self.container, 'foo')
1229         meta = self.client.retrieve_object_metadata(self.container, 'foo')
1230         self.assertEqual(meta['content-length'], '0')
1231         self.assertEqual(meta['content-type'], 'application/directory')
1232
1233     def test_upload_unprocessable_entity(self):
1234         meta={'etag':'123', 'test':'test1'}
1235         
1236         #assert unprocessable entity
1237         self.assert_raises_fault(422, self.upload_random_data, self.container,
1238                                  o_names[0], **meta)
1239     
1240     def test_chunked_transfer(self):
1241         data = get_random_data()
1242         objname = 'object'
1243         self.client.create_object_using_chunks(self.container, objname,
1244                                                StringIO(data))
1245         
1246         uploaded_data = self.client.retrieve_object(self.container, objname)
1247         self.assertEqual(data, uploaded_data)
1248     
1249     def test_manifestation(self):
1250         prefix = 'myobject/'
1251         data = ''
1252         for i in range(5):
1253             part = '%s%d' %(prefix, i)
1254             o = self.upload_random_data(self.container, part)
1255             data += o['data']
1256         
1257         manifest = '%s/%s' %(self.container, prefix)
1258         self.client.create_manifestation(self.container, 'large-object', manifest)
1259         
1260         self.assert_object_exists(self.container, 'large-object')
1261         self.assertEqual(data, self.client.retrieve_object(self.container,
1262                                                            'large-object'))
1263         
1264         #wrong manifestation
1265         self.client.create_manifestation(self.container, 'large-object',
1266                                          '%s/invalid' % self.container)
1267         self.assertEqual('', self.client.retrieve_object(self.container,
1268                                                          'large-object'))
1269     
1270     def test_create_zero_length_object(self):
1271         c = self.container
1272         o = 'object'
1273         zero = self.client.create_zero_length_object(c, o)
1274         zero_meta = self.client.retrieve_object_metadata(c, o)
1275         zero_hash = self.client.retrieve_object_hashmap(c, o)
1276         zero_data = self.client.retrieve_object(c, o)
1277         
1278         self.assertEqual(int(zero_meta['content-length']), 0)
1279         self.assertEqual(zero_hash, [])
1280         self.assertEqual(zero_data, '')
1281
1282 class ObjectCopy(BaseTestCase):
1283     def setUp(self):
1284         BaseTestCase.setUp(self)
1285         self.containers = ['c1', 'c2']
1286         for c in self.containers:
1287             self.client.create_container(c)
1288         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1289     
1290     def tearDown(self):
1291         pass
1292     
1293     def test_copy(self):
1294         with AssertMappingInvariant(self.client.retrieve_object_metadata,
1295                              self.containers[0], self.obj['name']):
1296             #perform copy
1297             meta = {'test':'testcopy'}
1298             status = self.client.copy_object(self.containers[0],
1299                                               self.obj['name'],
1300                                               self.containers[0],
1301                                               'testcopy',
1302                                               meta)[0]
1303             
1304             #assert copy success
1305             self.assertEqual(status, 201)
1306             
1307             #assert access the new object
1308             headers = self.client.retrieve_object_metadata(self.containers[0],
1309                                                            'testcopy')
1310             self.assertTrue('x-object-meta-test' in headers.keys())
1311             self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1312             
1313             #assert etag is the same
1314             self.assertEqual(headers['etag'], self.obj['hash'])
1315             
1316             #assert src object still exists
1317             self.assert_object_exists(self.containers[0], self.obj['name'])
1318     
1319     def test_copy_from_different_container(self):
1320         with AssertMappingInvariant(self.client.retrieve_object_metadata,
1321                              self.containers[0], self.obj['name']):
1322             meta = {'test':'testcopy'}
1323             status = self.client.copy_object(self.containers[0],
1324                                              self.obj['name'],
1325                                              self.containers[1],
1326                                              'testcopy',
1327                                              meta)[0]
1328             self.assertEqual(status, 201)
1329             
1330             # assert updated metadata
1331             meta = self.client.retrieve_object_metadata(self.containers[1],
1332                                                            'testcopy',
1333                                                            restricted=True)
1334             self.assertTrue('test' in meta.keys())
1335             self.assertTrue(meta['test'], 'testcopy')
1336             
1337             #assert src object still exists
1338             self.assert_object_exists(self.containers[0], self.obj['name'])
1339     
1340     def test_copy_invalid(self):
1341         #copy from invalid object
1342         meta = {'test':'testcopy'}
1343         self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1344                                  'test.py', self.containers[1], 'testcopy', meta)
1345         
1346         #copy from invalid container
1347         meta = {'test':'testcopy'}
1348         self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1349                                  self.obj['name'], self.containers[1],
1350                                  'testcopy', meta)
1351
1352 class ObjectMove(BaseTestCase):
1353     def setUp(self):
1354         BaseTestCase.setUp(self)
1355         self.containers = ['c1', 'c2']
1356         for c in self.containers:
1357             self.client.create_container(c)
1358         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1359     
1360     def test_move(self):
1361         #perform move
1362         meta = {'test':'testcopy'}
1363         src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1364         status = self.client.move_object(self.containers[0], self.obj['name'],
1365                                          self.containers[0], 'testcopy',
1366                                          meta)[0]
1367         
1368         #assert successful move
1369         self.assertEqual(status, 201)
1370         
1371         #assert updated metadata
1372         meta = self.client.retrieve_object_metadata(self.containers[0],
1373                                                     'testcopy',
1374                                                     restricted=True)
1375         self.assertTrue('test' in meta.keys())
1376         self.assertTrue(meta['test'], 'testcopy')
1377         
1378         #assert src object no more exists
1379         self.assert_object_not_exists(self.containers[0], self.obj['name'])
1380
1381 class ObjectPost(BaseTestCase):
1382     def setUp(self):
1383         BaseTestCase.setUp(self)
1384         self.containers = ['c1', 'c2']
1385         for c in self.containers:
1386             self.client.create_container(c)
1387         self.obj = []
1388         for i in range(2):
1389             self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1390     
1391     def test_update_meta(self):
1392         #perform update metadata
1393         more = {'foo':'foo', 'bar':'bar'}
1394         status = self.client.update_object_metadata(self.containers[0],
1395                                                     self.obj[0]['name'],
1396                                                     **more)[0]
1397         #assert request accepted
1398         self.assertEqual(status, 202)
1399         
1400         #assert old metadata are still there
1401         headers = self.client.retrieve_object_metadata(self.containers[0],
1402                                                        self.obj[0]['name'],
1403                                                        restricted=True)
1404         #assert new metadata have been updated
1405         for k,v in more.items():
1406             self.assertTrue(k in headers.keys())
1407             self.assertTrue(headers[k], v)
1408     
1409     def test_update_object(self,
1410                            first_byte_pos=0,
1411                            last_byte_pos=499,
1412                            instance_length = True,
1413                            content_length = 500):
1414         l = len(self.obj[0]['data'])
1415         range = 'bytes %d-%d/%s' %(first_byte_pos,
1416                                        last_byte_pos,
1417                                         l if instance_length else '*')
1418         partial = last_byte_pos - first_byte_pos + 1
1419         length = first_byte_pos + partial
1420         data = get_random_data(partial)
1421         args = {'content_type':'application/octet-stream',
1422                 'content_range':'%s' %range}
1423         if content_length:
1424             args['content_length'] = content_length
1425         
1426         status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1427                                   StringIO(data), **args)[0]
1428         
1429         if partial < 0 or (instance_length and l <= last_byte_pos):
1430             self.assertEqual(status, 202)    
1431         else:
1432             self.assertEqual(status, 204)           
1433             #check modified object
1434             content = self.client.retrieve_object(self.containers[0],
1435                                               self.obj[0]['name'])
1436             self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1437             self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1438             self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1439     
1440     def test_update_object_lt_blocksize(self):
1441         self.test_update_object(10, 20, content_length=None)
1442     
1443     def test_update_object_gt_blocksize(self):
1444         o = self.upload_random_data(self.containers[0], o_names[1],
1445                                 length=4*1024*1024+5)
1446         c = self.containers[0]
1447         o_name = o['name']
1448         o_data = o['data']
1449         first_byte_pos = 4*1024*1024+1
1450         last_byte_pos = 4*1024*1024+4
1451         l = last_byte_pos - first_byte_pos + 1
1452         data = get_random_data(l)
1453         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1454         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1455         content = self.client.retrieve_object(c, o_name)
1456         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1457         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1458         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1459     
1460     def test_update_object_divided_by_blocksize(self):
1461         o = self.upload_random_data(self.containers[0], o_names[1],
1462                                 length=4*1024*1024+5)
1463         c = self.containers[0]
1464         o_name = o['name']
1465         o_data = o['data']
1466         first_byte_pos = 4*1024*1024
1467         last_byte_pos = 5*1024*1024
1468         l = last_byte_pos - first_byte_pos + 1
1469         data = get_random_data(l)
1470         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1471         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1472         content = self.client.retrieve_object(c, o_name)
1473         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1474         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1475         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1476     
1477     def test_update_object_no_content_length(self):
1478         self.test_update_object(content_length = None)
1479     
1480     def test_update_object_invalid_content_length(self):
1481         with AssertContentInvariant(self.client.retrieve_object,
1482                                     self.containers[0], self.obj[0]['name']):
1483             self.assert_raises_fault(400, self.test_update_object,
1484                                      content_length = 1000)
1485     
1486     def test_update_object_invalid_range(self):
1487         with AssertContentInvariant(self.client.retrieve_object,
1488                                     self.containers[0], self.obj[0]['name']):
1489             self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1490     
1491     def test_update_object_invalid_range_and_length(self):
1492         with AssertContentInvariant(self.client.retrieve_object,
1493                                     self.containers[0], self.obj[0]['name']):
1494             self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1495                                      -1)
1496     
1497     def test_update_object_invalid_range_with_no_content_length(self):
1498         with AssertContentInvariant(self.client.retrieve_object,
1499                                     self.containers[0], self.obj[0]['name']):
1500             self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1501                                      content_length = None)
1502     
1503     def test_update_object_out_of_limits(self):    
1504         with AssertContentInvariant(self.client.retrieve_object,
1505                                     self.containers[0], self.obj[0]['name']):
1506             l = len(self.obj[0]['data'])
1507             self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1508     
1509     def test_append(self):
1510         data = get_random_data(500)
1511         headers = {}
1512         self.client.update_object(self.containers[0], self.obj[0]['name'],
1513                                   StringIO(data), content_length=500,
1514                                   content_type='application/octet-stream')
1515         
1516         content = self.client.retrieve_object(self.containers[0],
1517                                               self.obj[0]['name'])
1518         self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1519         self.assertEqual(content[:-500], self.obj[0]['data'])
1520     
1521     def test_update_with_chunked_transfer(self):
1522         data = get_random_data(500)
1523         dl = len(data)
1524         fl = len(self.obj[0]['data'])
1525         
1526         self.client.update_object_using_chunks(self.containers[0],
1527                                                self.obj[0]['name'],
1528                                                StringIO(data),
1529                                                offset=0,
1530                                                content_type='application/octet-stream')
1531         
1532         #check modified object
1533         content = self.client.retrieve_object(self.containers[0],
1534                                               self.obj[0]['name'])
1535         self.assertEqual(content[0:dl], data)
1536         self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1537     
1538     def test_update_from_other_object(self):
1539         c = self.containers[0]
1540         src = o_names[0]
1541         dest = 'object'
1542         
1543         source_data = self.client.retrieve_object(c, src)
1544         source_meta = self.client.retrieve_object_metadata(c, src)
1545         source_hash = self.client.retrieve_object_hashmap(c, src)
1546         
1547         #update zero length object
1548         self.client.create_zero_length_object(c, dest)
1549         source_object = '/%s/%s' % (c, src)
1550         self.client.update_from_other_source(c, dest, source_object)
1551         dest_data = self.client.retrieve_object(c, src)
1552         dest_meta = self.client.retrieve_object_metadata(c, dest)
1553         dest_hash = self.client.retrieve_object_hashmap(c, src)
1554         self.assertEqual(source_data, dest_data)
1555         self.assertEqual(source_hash, dest_hash)
1556         
1557         #test append
1558         self.client.update_from_other_source(c, dest, source_object)
1559         content = self.client.retrieve_object(c, dest)
1560         self.assertEqual(source_data * 2, content)
1561     
1562     def test_update_range_from_other_object(self):
1563         c = self.containers[0]
1564         dest = 'object'
1565         
1566         #test update range
1567         src = self.obj[1]['name']
1568         src_data = self.client.retrieve_object(c, src)
1569         
1570         #update zero length object
1571         prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1572         source_object = '/%s/%s' % (c, src)
1573         first_byte_pos = 4*1024*1024+1
1574         last_byte_pos = 4*1024*1024+4
1575         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1576         self.client.update_from_other_source(c, dest, source_object,
1577                                              content_range=range)
1578         content = self.client.retrieve_object(c, dest)
1579         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1580         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1581         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1582     
1583     def test_update_hashes_from_other_object(self):
1584         c = self.containers[0]
1585         dest = 'object'
1586         
1587         #test update range
1588         src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1589         
1590         #update zero length object
1591         prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1592         source_object = '/%s/%s' % (c, o_names[0])
1593         first_byte_pos = 4*1024*1024
1594         last_byte_pos = 5*1024*1024
1595         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1596         self.client.update_from_other_source(c, dest, source_object,
1597                                              content_range=range)
1598         content = self.client.retrieve_object(c, dest)
1599         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1600         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1601         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1602     
1603     
1604     def test_update_zero_length_object(self):
1605         c = self.containers[0]
1606         o = 'object'
1607         other = 'other'
1608         zero = self.client.create_zero_length_object(c, o)
1609         
1610         data = get_random_data()
1611         self.client.update_object(c, o, StringIO(data))
1612         self.client.create_object(c, other, StringIO(data))
1613         
1614         self.assertEqual(self.client.retrieve_object(c, o),
1615                          self.client.retrieve_object(c, other))
1616         
1617         self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1618                          self.client.retrieve_object_hashmap(c, other))
1619     
1620 class ObjectDelete(BaseTestCase):
1621     def setUp(self):
1622         BaseTestCase.setUp(self)
1623         self.containers = ['c1', 'c2']
1624         for c in self.containers:
1625             self.client.create_container(c)
1626         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1627     
1628     def test_delete(self):
1629         #perform delete object
1630         self.client.delete_object(self.containers[0], self.obj['name'])[0]
1631     
1632     def test_delete_invalid(self):
1633         #assert item not found
1634         self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1635                                  self.obj['name'])
1636
1637 class ListSharing(BaseTestCase):
1638     def setUp(self):
1639         BaseTestCase.setUp(self)
1640         self.client.create_container('c')
1641         for i in range(2):
1642             self.upload_random_data('c', 'o%s' %i)
1643         accounts = OTHER_ACCOUNTS.copy()
1644         self.o1_sharing_with = accounts.popitem()
1645         self.o1_sharing = [self.o1_sharing_with[1]]
1646         self.client.share_object('c', 'o1', self.o1_sharing, read=True)
1647         
1648         l = []
1649         for i in range(2):
1650             l.append(accounts.popitem())
1651         #self.client.set_account_groups({'pithos-dev':'chazapis,verigak,papagian'})
1652         #self.o2_sharing = 'write=%s' % 
1653         #self.client.share_object('c', 'o2', self.o2_sharing)
1654     
1655     def test_listing(self):
1656         self.other = Pithos_Client(get_server(),
1657                               self.o1_sharing_with[0],
1658                               self.o1_sharing_with[1],
1659                               get_api())
1660         self.assertTrue(get_user() in self.other.list_shared_by_others())
1661
1662 class TestGreek(BaseTestCase):
1663     def setUp(self):
1664         BaseTestCase.setUp(self)
1665         #keep track of initial account groups
1666         self.initial_groups = self.client.retrieve_account_groups()
1667         
1668         #keep track of initial account meta
1669         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1670     
1671     def tearDown(self):
1672         #delete additionally created meta
1673         l = []
1674         for m in self.client.retrieve_account_metadata(restricted=True):
1675             if m not in self.initial_meta:
1676                 l.append(m)
1677         self.client.delete_account_metadata(l)
1678         
1679         #delete additionally created groups
1680         l = []
1681         for g in self.client.retrieve_account_groups():
1682             if g not in self.initial_groups:
1683                 l.append(g)
1684         self.client.unset_account_groups(l)
1685         
1686         BaseTestCase.tearDown(self)
1687     
1688     def test_create_container(self):
1689         self.client.create_container('φάκελος')
1690         self.assert_container_exists('φάκελος')
1691         
1692         self.assertTrue('φάκελος' in self.client.list_containers())
1693     
1694     def test_create_object(self):
1695         self.client.create_container('φάκελος')
1696         self.upload_random_data('φάκελος', 'αντικείμενο')
1697         
1698         self.assert_object_exists('φάκελος', 'αντικείμενο')
1699         self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1700     
1701     def test_copy_object(self):
1702         src_container = 'φάκελος'
1703         src_object = 'αντικείμενο'
1704         dest_container = 'αντίγραφα'
1705         dest_object = 'ασφαλές-αντίγραφο'
1706         
1707         self.client.create_container(src_container)
1708         self.upload_random_data(src_container, src_object)
1709         
1710         self.client.create_container(dest_container)
1711         self.client.copy_object(src_container, src_object, dest_container,
1712                                 dest_object)
1713         
1714         self.assert_object_exists(src_container, src_object)
1715         self.assert_object_exists(dest_container, dest_object)
1716         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1717     
1718     def test_move_object(self):
1719         src_container = 'φάκελος'
1720         src_object = 'αντικείμενο'
1721         dest_container = 'αντίγραφα'
1722         dest_object = 'ασφαλές-αντίγραφο'
1723         
1724         self.client.create_container(src_container)
1725         self.upload_random_data(src_container, src_object)
1726         
1727         self.client.create_container(dest_container)
1728         self.client.move_object(src_container, src_object, dest_container,
1729                                 dest_object)
1730         
1731         self.assert_object_not_exists(src_container, src_object)
1732         self.assert_object_exists(dest_container, dest_object)
1733         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1734     
1735     def test_delete_object(self):
1736         self.client.create_container('φάκελος')
1737         self.upload_random_data('φάκελος', 'αντικείμενο')
1738         self.assert_object_exists('φάκελος', 'αντικείμενο')
1739     
1740         self.client.delete_object('φάκελος', 'αντικείμενο')
1741         self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1742         self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1743     
1744     def test_delete_container(self):
1745         self.client.create_container('φάκελος')
1746         self.assert_container_exists('φάκελος')
1747         
1748         self.client.delete_container('φάκελος')
1749         self.assert_container_not_exists('φάκελος')
1750         self.assertTrue('φάκελος' not in self.client.list_containers())
1751
1752     def test_account_meta(self):
1753         meta = {'ποιότητα':'ΑΑΑ'}
1754         self.client.update_account_metadata(**meta)
1755         meta = self.client.retrieve_account_metadata(restricted=True)
1756         self.assertTrue('ποιότητα' in meta.keys())
1757         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1758     
1759     def test_container_meta(self):
1760         meta = {'ποιότητα':'ΑΑΑ'}
1761         self.client.create_container('φάκελος', **meta)
1762         
1763         meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1764         self.assertTrue('ποιότητα' in meta.keys())
1765         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1766     
1767     def test_object_meta(self):
1768         self.client.create_container('φάκελος')
1769         meta = {'ποιότητα':'ΑΑΑ'}
1770         self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1771         
1772         meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1773                                                     restricted=True)
1774         self.assertTrue('ποιότητα' in meta.keys())
1775         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1776     
1777     def test_list_meta_filtering(self):
1778         self.client.create_container('φάκελος')
1779         meta = {'ποιότητα':'ΑΑΑ'}
1780         self.upload_random_data('φάκελος', 'ο1', **meta)
1781         self.upload_random_data('φάκελος', 'ο2')
1782         self.upload_random_data('φάκελος', 'ο3')
1783         
1784         meta = {'ποσότητα':'μεγάλη'}
1785         self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1786         objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1787         self.assertTrue('ο1' in objects)
1788         self.assertTrue('ο2' in objects)
1789         self.assertTrue('ο3' not in objects)
1790     
1791     def test_groups(self):
1792         #create a group
1793         groups = {'σεφς':'chazapis,διογένης'}
1794         self.client.set_account_groups(**groups)
1795         groups.update(self.initial_groups)
1796         self.assertEqual(groups['σεφς'],
1797                          self.client.retrieve_account_groups()['σεφς'])
1798         
1799         #check read access
1800         self.client.create_container('φάκελος')
1801         o = self.upload_random_data('φάκελος', 'ο1')
1802         self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1803         chef = Pithos_Client(get_server(),
1804                             '0009',
1805                             'διογένης',
1806                             get_api())
1807         self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1808                                      'φάκελος', 'ο1', account=get_user())
1809         
1810         #check write access
1811         self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1812         new_data = get_random_data()
1813         self.assert_not_raises_fault(401, chef.update_object,
1814                                      'φάκελος', 'ο1', StringIO(new_data),
1815                                      account=get_user())
1816         
1817         server_data = self.client.retrieve_object('φάκελος', 'ο1')
1818         self.assertEqual(server_data[:len(o['data'])], o['data'])
1819         self.assertEqual(server_data[len(o['data']):], new_data)
1820     
1821     def test_manifestation(self):
1822         self.client.create_container('κουβάς')
1823         prefix = 'μέρη/'
1824         data = ''
1825         for i in range(5):
1826             part = '%s%d' %(prefix, i)
1827             o = self.upload_random_data('κουβάς', part)
1828             data += o['data']
1829         
1830         self.client.create_container('φάκελος')
1831         manifest = '%s/%s' %('κουβάς', prefix)
1832         self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1833         
1834         self.assert_object_exists('φάκελος', 'άπαντα')
1835         self.assertEqual(data, self.client.retrieve_object('φάκελος',
1836                                                            'άπαντα'))
1837         
1838         #wrong manifestation
1839         self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1840         self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1841     
1842     def test_update_from_another_object(self):
1843         self.client.create_container('κουβάς')
1844         src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1845         initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1846         source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1847         self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1848         
1849         self.assertEqual(
1850             self.client.retrieve_object('κουβάς', 'νέο'),
1851             '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1852
1853 class TestPermissions(BaseTestCase):
1854     def setUp(self):
1855         BaseTestCase.setUp(self)
1856         #keep track of initial account groups
1857         self.initial_groups = self.client.retrieve_account_groups()
1858         #keep track of initial account meta
1859         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1860         
1861         #create a group
1862         self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1863         groups = {'pithosdev':','.join(self.authorized)}
1864         self.client.set_account_groups(**groups)
1865     
1866     def tearDown(self):
1867         #delete additionally created meta
1868         l = []
1869         for m in self.client.retrieve_account_metadata(restricted=True):
1870             if m not in self.initial_meta:
1871                 l.append(m)
1872         self.client.delete_account_metadata(l)
1873         
1874         #delete additionally created groups
1875         l = []
1876         for g in self.client.retrieve_account_groups():
1877             if g not in self.initial_groups:
1878                 l.append(g)
1879         self.client.unset_account_groups(l)
1880         
1881         BaseTestCase.tearDown(self)
1882     
1883     def test_read_access(self):
1884         self.client.create_container('c')
1885         o = self.upload_random_data('c', 'o')
1886         self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1887         for token, account in OTHER_ACCOUNTS.items():
1888             cl = Pithos_Client(get_server(), token, account, get_api()) 
1889             if account in self.authorized:
1890                 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1891                                              'c', 'o', account=get_user())
1892             else:
1893                 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1894                                          'c', 'o', account=get_user())
1895         
1896     
1897     def test_write_access(self):
1898         self.client.create_container('c')
1899         o = self.upload_random_data('c', 'o')
1900         self.client.share_object('c', 'o', ['chazapis'], read=False)
1901         for token, account in OTHER_ACCOUNTS.items():
1902             cl = Pithos_Client(get_server(), token, account, get_api()) 
1903             new_data = get_random_data()
1904             if account == 'chazapis':
1905                 self.assert_not_raises_fault(401, cl.update_object,
1906                                              'c', 'o', StringIO(new_data),
1907                                              account=get_user())
1908                 server_data = self.client.retrieve_object('c', 'o')
1909                 self.assertEqual(o['data'], server_data[:len(o['data'])])
1910                 self.assertEqual(new_data, server_data[len(o['data']):])
1911             else:
1912                 self.assert_raises_fault(401, cl.update_object,
1913                                              'c', 'o', StringIO(new_data),
1914                                              account=get_user())
1915
1916 class AssertMappingInvariant(object):
1917     def __init__(self, callable, *args, **kwargs):
1918         self.callable = callable
1919         self.args = args
1920         self.kwargs = kwargs
1921     
1922     def __enter__(self):
1923         self.map = self.callable(*self.args, **self.kwargs)
1924         return self.map
1925     
1926     def __exit__(self, type, value, tb):
1927         map = self.callable(*self.args, **self.kwargs)
1928         for k in self.map.keys():
1929             if is_date(self.map[k]):
1930                 continue
1931             assert map[k] == self.map[k]
1932
1933 class AssertContentInvariant(object):
1934     def __init__(self, callable, *args, **kwargs):
1935         self.callable = callable
1936         self.args = args
1937         self.kwargs = kwargs
1938     
1939     def __enter__(self):
1940         self.content = self.callable(*self.args, **self.kwargs)[2]
1941         return self.content
1942     
1943     def __exit__(self, type, value, tb):
1944         content = self.callable(*self.args, **self.kwargs)[2]
1945         assert self.content == content
1946
1947 def get_content_splitted(response):
1948     if response:
1949         return response.content.split('\n')
1950
1951 def compute_md5_hash(data):
1952     md5 = hashlib.md5()
1953     offset = 0
1954     md5.update(data)
1955     return md5.hexdigest().lower()
1956
1957 def compute_block_hash(data, algorithm):
1958     h = hashlib.new(algorithm)
1959     h.update(data.rstrip('\x00'))
1960     return h.hexdigest()
1961
1962 def get_random_data(length=500):
1963     char_set = string.ascii_uppercase + string.digits
1964     return ''.join(random.choice(char_set) for x in range(length))
1965
1966 def is_date(date):
1967     MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
1968     __D = r'(?P<day>\d{2})'
1969     __D2 = r'(?P<day>[ \d]\d)'
1970     __M = r'(?P<mon>\w{3})'
1971     __Y = r'(?P<year>\d{4})'
1972     __Y2 = r'(?P<year>\d{2})'
1973     __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
1974     RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
1975     RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
1976     ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
1977     for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
1978         m = regex.match(date)
1979         if m is not None:
1980             return True
1981     return False
1982
1983 o_names = ['kate.jpg',
1984            'kate_beckinsale.jpg',
1985            'How To Win Friends And Influence People.pdf',
1986            'moms_birthday.jpg',
1987            'poodle_strut.mov',
1988            'Disturbed - Down With The Sickness.mp3',
1989            'army_of_darkness.avi',
1990            'the_mad.avi',
1991            'photos/animals/dogs/poodle.jpg',
1992            'photos/animals/dogs/terrier.jpg',
1993            'photos/animals/cats/persian.jpg',
1994            'photos/animals/cats/siamese.jpg',
1995            'photos/plants/fern.jpg',
1996            'photos/plants/rose.jpg',
1997            'photos/me.jpg']
1998
1999 if __name__ == "__main__":
2000     unittest.main()