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