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