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