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