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