Fixed css
[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, 403, 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_403(self):
337         self.assert_raises_fault(403,
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_403(self):
375         self.assert_raises_fault(403, 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(403,
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     def test_list_objects_with_limit_marker(self):
634         objects = self.client.list_objects(self.container[0], limit=2)
635         l = [elem['name'] for elem in self.obj[:8]]
636         l.sort()
637         self.assertEqual(objects, l[:2])
638         
639         markers = ['How To Win Friends And Influence People.pdf',
640                    'moms_birthday.jpg']
641         limit = 4
642         for m in markers:
643             objects = self.client.list_objects(self.container[0], limit=limit,
644                                                marker=m)
645             l = [elem['name'] for elem in self.obj[:8]]
646             l.sort()
647             start = l.index(m) + 1
648             end = start + limit
649             end = len(l) >= end and end or len(l)
650             self.assertEqual(objects, l[start:end])
651     
652     #takes too long
653     def _test_list_limit_exceeds(self):
654         self.client.create_container('pithos')
655         
656         for i in range(10001):
657             self.client.create_zero_length_object('pithos', i)
658         
659         self.assertEqual(10000, len(self.client.list_objects('pithos')))
660     
661     def test_list_empty_params(self):
662         objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
663         if objects:
664             objects = objects.strip().split('\n')
665         self.assertEqual(objects,
666                          self.client.list_objects(self.container[0]))
667     
668     def test_list_pseudo_hierarchical_folders(self):
669         objects = self.client.list_objects(self.container[1], prefix='photos',
670                                            delimiter='/')
671         self.assertEquals(['photos/animals/', 'photos/me.jpg',
672                            'photos/plants/'], objects)
673         
674         objects = self.client.list_objects(self.container[1],
675                                            prefix='photos/animals',
676                                            delimiter='/')
677         l = ['photos/animals/cats/', 'photos/animals/dogs/']
678         self.assertEquals(l, objects)
679         
680         objects = self.client.list_objects(self.container[1], path='photos')
681         self.assertEquals(['photos/me.jpg'], objects)
682     
683     def test_extended_list_json(self):
684         objects = self.client.list_objects(self.container[1], format='json',
685                                            limit=2, prefix='photos/animals',
686                                            delimiter='/')
687         self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
688         self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
689     
690     def test_extended_list_xml(self):
691         xml = self.client.list_objects(self.container[1], format='xml', limit=4,
692                                        prefix='photos', delimiter='/')
693         self.assert_extended(xml, 'xml', 'object', size=4)
694         dirs = xml.getElementsByTagName('subdir')
695         self.assertEqual(len(dirs), 2)
696         self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
697         self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
698         
699         objects = xml.getElementsByTagName('name')
700         self.assertEqual(len(objects), 1)
701         self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
702     
703     def test_list_meta_double_matching(self):
704         meta = {'quality':'aaa', 'stock':'true'}
705         self.client.update_object_metadata(self.container[0],
706                                            self.obj[0]['name'], **meta)
707         obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
708         self.assertEqual(len(obj), 1)
709         self.assertTrue(obj, self.obj[0]['name'])
710     
711     def test_list_using_meta(self):
712         meta = {'quality':'aaa'}
713         for o in self.obj[:2]:
714             self.client.update_object_metadata(self.container[0], o['name'],
715                                                **meta)
716         meta = {'stock':'true'}
717         for o in self.obj[3:5]:
718             self.client.update_object_metadata(self.container[0], o['name'],
719                                                **meta)
720         
721         obj = self.client.list_objects(self.container[0], meta='Quality')
722         self.assertEqual(len(obj), 2)
723         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
724         
725         # test case insensitive
726         obj = self.client.list_objects(self.container[0], meta='quality')
727         self.assertEqual(len(obj), 2)
728         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
729         
730         # test multiple matches
731         obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
732         self.assertEqual(len(obj), 4)
733         self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
734         
735         # test non 1-1 multiple match
736         obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
737         self.assertEqual(len(obj), 2)
738         self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
739     
740     def test_if_modified_since(self):
741         t = datetime.datetime.utcnow()
742         t2 = t - datetime.timedelta(minutes=10)
743         
744         #add a new object
745         self.upload_random_data(self.container[0], o_names[0])
746         
747         for f in DATE_FORMATS:
748             past = t2.strftime(f)
749             try:
750                 o = self.client.list_objects(self.container[0],
751                                             if_modified_since=past)
752                 self.assertEqual(o,
753                                  self.client.list_objects(self.container[0]))
754             except Fault, f:
755                 self.failIf(f.status == 304) #fail if not modified
756     
757     def test_if_modified_since_invalid_date(self):
758         headers = {'if-modified-since':''}
759         o = self.client.list_objects(self.container[0], if_modified_since='')
760         self.assertEqual(o, self.client.list_objects(self.container[0]))
761     
762     def test_if_not_modified_since(self):
763         now = datetime.datetime.utcnow()
764         since = now + datetime.timedelta(1)
765         
766         for f in DATE_FORMATS:
767             args = {'if_modified_since':'%s' %since.strftime(f)}
768             
769             #assert not modified
770             self.assert_raises_fault(304, self.client.list_objects,
771                                      self.container[0], **args)
772     
773     def test_if_unmodified_since(self):
774         now = datetime.datetime.utcnow()
775         since = now + datetime.timedelta(1)
776         
777         for f in DATE_FORMATS:
778             obj = self.client.list_objects(self.container[0],
779                                            if_unmodified_since=since.strftime(f))
780             
781             #assert unmodified
782             self.assertEqual(obj, self.client.list_objects(self.container[0]))
783     
784     def test_if_unmodified_since_precondition_failed(self):
785         t = datetime.datetime.utcnow()
786         t2 = t - datetime.timedelta(minutes=10)
787         
788         #add a new container
789         self.client.create_container('dummy')
790         
791         for f in DATE_FORMATS:
792             past = t2.strftime(f)
793             
794             args = {'if_unmodified_since':'%s' %past}
795             
796             #assert precondition failed
797             self.assert_raises_fault(412, self.client.list_objects,
798                                      self.container[0], **args)
799
800 class ContainerPut(BaseTestCase):
801     def setUp(self):
802         BaseTestCase.setUp(self)
803         self.containers = ['c1', 'c2']
804     
805     def test_create(self):
806         self.client.create_container(self.containers[0])
807         containers = self.client.list_containers()
808         self.assertTrue(self.containers[0] in containers)
809         self.assert_container_exists(self.containers[0])
810     
811     def test_create_twice(self):
812         self.client.create_container(self.containers[0])
813         self.assertTrue(not self.client.create_container(self.containers[0]))
814     
815     def test_quota(self):
816         self.client.create_container(self.containers[0])
817         
818         policy = {'quota':100}
819         self.client.set_container_policies('c1', **policy)
820         
821         meta = self.client.retrieve_container_metadata('c1')
822         self.assertTrue('x-container-policy-quota' in meta)
823         self.assertEqual(meta['x-container-policy-quota'], '100')
824         
825         args = ['c1', 'o1']
826         kwargs = {'length':101}
827         self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
828         
829         #reset quota
830         policy = {'quota':0}
831         self.client.set_container_policies('c1', **policy)
832     
833 class ContainerPost(BaseTestCase):
834     def setUp(self):
835         BaseTestCase.setUp(self)
836         self.container = 'apples'
837         self.client.create_container(self.container)
838     
839     def test_update_meta(self):
840         meta = {'test':'test33',
841                 'tost':'tost22'}
842         self.client.update_container_metadata(self.container, **meta)
843         headers = self.client.retrieve_container_metadata(self.container)
844         for k,v in meta.items():
845             k = 'x-container-meta-%s' % k
846             self.assertTrue(headers[k])
847             self.assertEqual(headers[k], v)
848
849 class ContainerDelete(BaseTestCase):
850     def setUp(self):
851         BaseTestCase.setUp(self)
852         self.containers = ['c1', 'c2']
853         for c in self.containers:
854             self.client.create_container(c)
855     
856     def test_delete(self):
857         status = self.client.delete_container(self.containers[0])[0]
858         self.assertEqual(status, 204)
859     
860     def test_delete_non_empty(self):
861         self.upload_random_data(self.containers[1], o_names[0])
862         self.assert_raises_fault(409, self.client.delete_container,
863                                  self.containers[1])
864     
865     def test_delete_invalid(self):
866         self.assert_raises_fault(404, self.client.delete_container, 'c3')
867
868 class ObjectHead(BaseTestCase):
869     pass
870
871 class ObjectGet(BaseTestCase):
872     def setUp(self):
873         BaseTestCase.setUp(self)
874         self.containers = ['c1', 'c2']
875         #create some containers
876         for c in self.containers:
877             self.client.create_container(c)
878         
879         #upload a file
880         names = ('obj1', 'obj2')
881         self.objects = []
882         for n in names:
883             self.objects.append(self.upload_random_data(self.containers[1], n))
884     
885     def test_versions(self):
886         c = self.containers[1]
887         o = self.objects[0]
888         b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
889         self.assert_versionlist_structure(b)
890         
891         #update meta
892         meta = {'quality':'AAA', 'stock':True}
893         self.client.update_object_metadata(c, o['name'], **meta)
894         
895         a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
896         self.assert_versionlist_structure(a)
897         self.assertEqual(len(b)+1, len(a))
898         self.assertEqual(b, a[:-1])
899         
900         #get exact previous version metadata
901         v = a[-2][0]
902         v_meta = self.client.retrieve_object_metadata(c, o['name'],
903                                                       restricted=True,
904                                                       version=v)
905         for k in meta.keys():
906             self.assertTrue(k not in v_meta)
907         
908         #update obejct
909         data = get_random_data()
910         self.client.update_object(c, o['name'], StringIO(data))
911         
912         aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
913         self.assert_versionlist_structure(aa)
914         self.assertEqual(len(a)+1, len(aa))
915         self.assertEqual(a, aa[:-1])
916         
917         #get exact previous version
918         v = aa[-3][0]
919         v_data = self.client.retrieve_object_version(c, o['name'], version=v)
920         self.assertEqual(o['data'], v_data)
921         self.assertEqual(self.client.retrieve_object(c, o['name']),
922                          '%s%s' %(v_data, data))
923     
924     def test_get(self):
925         #perform get
926         o = self.client.retrieve_object(self.containers[1],
927                                         self.objects[0]['name'],
928                                         self.objects[0]['meta'])
929         self.assertEqual(o, self.objects[0]['data'])
930     
931     def test_objects_with_trailing_spaces(self):
932         self.client.create_container('test')
933         #create 'a' object
934         self.upload_random_data('test', 'a')
935         #look for 'a ' object
936         self.assert_raises_fault(404, self.client.retrieve_object,
937                                  'test', 'a ')
938         
939         #delete 'a' object
940         self.client.delete_object('test', 'a')
941         self.assert_raises_fault(404, self.client.retrieve_object,
942                                  'test', 'a')
943         
944         #create 'a ' object
945         self.upload_random_data('test', 'a ')
946         #look for 'a' object
947         self.assert_raises_fault(404, self.client.retrieve_object,
948                                  'test', 'a')
949     
950     def test_get_invalid(self):
951         self.assert_raises_fault(404, self.client.retrieve_object,
952                                  self.containers[0], self.objects[0]['name'])
953     
954     def test_get_partial(self):
955         #perform get with range
956         status, headers, data = self.client.request_object(self.containers[1],
957                                                             self.objects[0]['name'],
958                                                             range='bytes=0-499')
959         
960         #assert successful partial content
961         self.assertEqual(status, 206)
962         
963         #assert content-type
964         self.assertEqual(headers['content-type'],
965                          self.objects[0]['meta']['content_type'])
966         
967         #assert content length
968         self.assertEqual(int(headers['content-length']), 500)
969         
970         #assert content
971         self.assertEqual(self.objects[0]['data'][:500], data)
972     
973     def test_get_final_500(self):
974         #perform get with range
975         headers = {'range':'bytes=-500'}
976         status, headers, data = self.client.request_object(self.containers[1],
977                                                             self.objects[0]['name'],
978                                                             range='bytes=-500')
979         
980         #assert successful partial content
981         self.assertEqual(status, 206)
982         
983         #assert content-type
984         self.assertEqual(headers['content-type'],
985                          self.objects[0]['meta']['content_type'])
986         
987         #assert content length
988         self.assertEqual(int(headers['content-length']), 500)
989         
990         #assert content
991         self.assertTrue(self.objects[0]['data'][-500:], data)
992     
993     def test_get_rest(self):
994         #perform get with range
995         offset = len(self.objects[0]['data']) - 500
996         status, headers, data = self.client.request_object(self.containers[1],
997                                                 self.objects[0]['name'],
998                                                 range='bytes=%s-' %offset)
999         
1000         #assert successful partial content
1001         self.assertEqual(status, 206)
1002         
1003         #assert content-type
1004         self.assertEqual(headers['content-type'],
1005                          self.objects[0]['meta']['content_type'])
1006         
1007         #assert content length
1008         self.assertEqual(int(headers['content-length']), 500)
1009         
1010         #assert content
1011         self.assertTrue(self.objects[0]['data'][-500:], data)
1012     
1013     def test_get_range_not_satisfiable(self):
1014         #perform get with range
1015         offset = len(self.objects[0]['data']) + 1
1016         
1017         #assert range not satisfiable
1018         self.assert_raises_fault(416, self.client.retrieve_object,
1019                                  self.containers[1], self.objects[0]['name'],
1020                                  range='bytes=0-%s' %offset)
1021     
1022     def test_multiple_range(self):
1023         #perform get with multiple range
1024         ranges = ['0-499', '-500', '1000-']
1025         bytes = 'bytes=%s' % ','.join(ranges)
1026         status, headers, data = self.client.request_object(self.containers[1],
1027                                                            self.objects[0]['name'],
1028                                                            range=bytes)
1029         
1030         # assert partial content
1031         self.assertEqual(status, 206)
1032         
1033         # assert Content-Type of the reply will be multipart/byteranges
1034         self.assertTrue(headers['content-type'])
1035         content_type_parts = headers['content-type'].split()
1036         self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1037         
1038         boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1039         cparts = data.split(boundary)[1:-1]
1040         
1041         # assert content parts are exactly 2
1042         self.assertEqual(len(cparts), len(ranges))
1043         
1044         # for each content part assert headers
1045         i = 0
1046         for cpart in cparts:
1047             content = cpart.split('\r\n')
1048             headers = content[1:3]
1049             content_range = headers[0].split(': ')
1050             self.assertEqual(content_range[0], 'Content-Range')
1051             
1052             r = ranges[i].split('-')
1053             if not r[0] and not r[1]:
1054                 pass
1055             elif not r[0]:
1056                 start = len(self.objects[0]['data']) - int(r[1])
1057                 end = len(self.objects[0]['data'])
1058             elif not r[1]:
1059                 start = int(r[0])
1060                 end = len(self.objects[0]['data'])
1061             else:
1062                 start = int(r[0])
1063                 end = int(r[1]) + 1
1064             fdata = self.objects[0]['data'][start:end]
1065             sdata = '\r\n'.join(content[4:-1])
1066             self.assertEqual(len(fdata), len(sdata))
1067             self.assertEquals(fdata, sdata)
1068             i+=1
1069     
1070     def test_multiple_range_not_satisfiable(self):
1071         #perform get with multiple range
1072         out_of_range = len(self.objects[0]['data']) + 1
1073         ranges = ['0-499', '-500', '%d-' %out_of_range]
1074         bytes = 'bytes=%s' % ','.join(ranges)
1075         
1076         # assert partial content
1077         self.assert_raises_fault(416, self.client.retrieve_object,
1078                                  self.containers[1],
1079                                  self.objects[0]['name'], range=bytes)
1080     
1081     def test_get_with_if_match(self):
1082         #perform get with If-Match
1083         etag = self.objects[0]['hash']
1084         status, headers, data = self.client.request_object(self.containers[1],
1085                                                            self.objects[0]['name'],
1086                                                            if_match=etag)
1087         #assert get success
1088         self.assertEqual(status, 200)
1089         
1090         #assert content-type
1091         self.assertEqual(headers['content-type'],
1092                          self.objects[0]['meta']['content_type'])
1093         
1094         #assert response content
1095         self.assertEqual(self.objects[0]['data'], data)
1096     
1097     def test_get_with_if_match_star(self):
1098         #perform get with If-Match *
1099         headers = {'if-match':'*'}
1100         status, headers, data = self.client.request_object(self.containers[1],
1101                                                 self.objects[0]['name'],
1102                                                 **headers)
1103         #assert get success
1104         self.assertEqual(status, 200)
1105         
1106         #assert content-type
1107         self.assertEqual(headers['content-type'],
1108                          self.objects[0]['meta']['content_type'])
1109         
1110         #assert response content
1111         self.assertEqual(self.objects[0]['data'], data)
1112     
1113     def test_get_with_multiple_if_match(self):
1114         #perform get with If-Match
1115         etags = [i['hash'] for i in self.objects if i]
1116         etags = ','.join('"%s"' % etag for etag in etags)
1117         status, headers, data = self.client.request_object(self.containers[1],
1118                                                            self.objects[0]['name'],
1119                                                            if_match=etags)
1120         #assert get success
1121         self.assertEqual(status, 200)
1122         
1123         #assert content-type
1124         self.assertEqual(headers['content-type'],
1125                          self.objects[0]['meta']['content_type'])
1126         
1127         #assert content-type
1128         self.assertEqual(headers['content-type'],
1129                          self.objects[0]['meta']['content_type'])
1130         
1131         #assert response content
1132         self.assertEqual(self.objects[0]['data'], data)
1133     
1134     def test_if_match_precondition_failed(self):
1135         #assert precondition failed
1136         self.assert_raises_fault(412, self.client.retrieve_object,
1137                                  self.containers[1],
1138                                  self.objects[0]['name'], if_match='123')
1139     
1140     def test_if_none_match(self):
1141         #perform get with If-None-Match
1142         status, headers, data = self.client.request_object(self.containers[1],
1143                                                            self.objects[0]['name'],
1144                                                            if_none_match='123')
1145         
1146         #assert get success
1147         self.assertEqual(status, 200)
1148         
1149         #assert content-type
1150         self.assertEqual(headers['content_type'],
1151                          self.objects[0]['meta']['content_type'])
1152     
1153     def test_if_none_match(self):
1154         #perform get with If-None-Match * and assert not modified
1155         self.assert_raises_fault(304, self.client.retrieve_object,
1156                                  self.containers[1],
1157                                  self.objects[0]['name'],
1158                                  if_none_match='*')
1159     
1160     def test_if_none_match_not_modified(self):
1161         #perform get with If-None-Match and assert not modified
1162         self.assert_raises_fault(304, self.client.retrieve_object,
1163                                  self.containers[1],
1164                                  self.objects[0]['name'],
1165                                  if_none_match=self.objects[0]['hash'])
1166         
1167         meta = self.client.retrieve_object_metadata(self.containers[1],
1168                                                     self.objects[0]['name'])
1169         self.assertEqual(meta['etag'], self.objects[0]['hash'])
1170     
1171     def test_if_modified_since(self):
1172         t = datetime.datetime.utcnow()
1173         t2 = t - datetime.timedelta(minutes=10)
1174         
1175         #modify the object
1176         self.upload_data(self.containers[1],
1177                            self.objects[0]['name'],
1178                            self.objects[0]['data'][:200])
1179         
1180         for f in DATE_FORMATS:
1181             past = t2.strftime(f)
1182             
1183             headers = {'if-modified-since':'%s' %past}
1184             try:
1185                 o = self.client.retrieve_object(self.containers[1],
1186                                                 self.objects[0]['name'],
1187                                                 if_modified_since=past)
1188                 self.assertEqual(o,
1189                                  self.client.retrieve_object(self.containers[1],
1190                                                              self.objects[0]['name']))
1191             except Fault, f:
1192                 self.failIf(f.status == 304)
1193     
1194     def test_if_modified_since_invalid_date(self):
1195         o = self.client.retrieve_object(self.containers[1],
1196                                         self.objects[0]['name'],
1197                                         if_modified_since='')
1198         self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1199                                                         self.objects[0]['name']))
1200             
1201     def test_if_not_modified_since(self):
1202         now = datetime.datetime.utcnow()
1203         since = now + datetime.timedelta(1)
1204         
1205         for f in DATE_FORMATS:
1206             #assert not modified
1207             self.assert_raises_fault(304, self.client.retrieve_object,
1208                                      self.containers[1], self.objects[0]['name'],
1209                                      if_modified_since=since.strftime(f))
1210     
1211     def test_if_unmodified_since(self):
1212         now = datetime.datetime.utcnow()
1213         since = now + datetime.timedelta(1)
1214         
1215         for f in DATE_FORMATS:
1216             t = since.strftime(f)
1217             status, headers, data = self.client.request_object(self.containers[1],
1218                                                                self.objects[0]['name'],
1219                                                                if_unmodified_since=t)
1220             #assert success
1221             self.assertEqual(status, 200)
1222             self.assertEqual(self.objects[0]['data'], data)
1223             
1224             #assert content-type
1225             self.assertEqual(headers['content-type'],
1226                              self.objects[0]['meta']['content_type'])
1227     
1228     def test_if_unmodified_since_precondition_failed(self):
1229         t = datetime.datetime.utcnow()
1230         t2 = t - datetime.timedelta(minutes=10)
1231         
1232         #modify the object
1233         self.upload_data(self.containers[1],
1234                            self.objects[0]['name'],
1235                            self.objects[0]['data'][:200])
1236         
1237         for f in DATE_FORMATS:
1238             past = t2.strftime(f)
1239             #assert precondition failed
1240             self.assert_raises_fault(412, self.client.retrieve_object,
1241                                      self.containers[1], self.objects[0]['name'],
1242                                      if_unmodified_since=past)
1243     
1244     def test_hashes(self):
1245         l = 8388609
1246         fname = 'largefile'
1247         o = self.upload_random_data(self.containers[1], fname, l)
1248         if o:
1249             body = self.client.retrieve_object(self.containers[1], fname,
1250                                                format='json')
1251             hashes = body['hashes']
1252             block_size = body['block_size']
1253             block_hash = body['block_hash']
1254             block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1255             self.assertTrue(len(hashes), block_num)
1256             i = 0
1257             for h in hashes:
1258                 start = i * block_size
1259                 end = (i + 1) * block_size
1260                 hash = compute_block_hash(o['data'][start:end], block_hash)
1261                 self.assertEqual(h, hash)
1262                 i += 1
1263
1264 class ObjectPut(BaseTestCase):
1265     def setUp(self):
1266         BaseTestCase.setUp(self)
1267         self.container = 'c1'
1268         self.client.create_container(self.container)
1269     
1270     def test_upload(self):
1271         name = o_names[0]
1272         meta = {'test':'test1'}
1273         o = self.upload_random_data(self.container, name, **meta)
1274         
1275         headers = self.client.retrieve_object_metadata(self.container,
1276                                                        name,
1277                                                        restricted=True)
1278         self.assertTrue('test' in headers.keys())
1279         self.assertEqual(headers['test'], meta['test'])
1280         
1281         #assert uploaded content
1282         status, h, data = self.client.request_object(self.container, name)
1283         self.assertEqual(len(o['data']), int(h['content-length']))
1284         self.assertEqual(o['data'], data)
1285         
1286         #assert content-type
1287         self.assertEqual(h['content-type'], o['meta']['content_type'])
1288     
1289     def _test_maximum_upload_size_exceeds(self):
1290         name = o_names[0]
1291         meta = {'test':'test1'}
1292         #upload 100MB
1293         length=1024*1024*100
1294         self.assert_raises_fault(400, self.upload_random_data, self.container,
1295                                  name, length, **meta)
1296     
1297     def test_upload_with_name_containing_slash(self):
1298         name = '/%s' % o_names[0]
1299         meta = {'test':'test1'}
1300         o = self.upload_random_data(self.container, name, **meta)
1301         
1302         self.assertEqual(o['data'],
1303                          self.client.retrieve_object(self.container, name))
1304         
1305         self.assertTrue(name in self.client.list_objects(self.container))
1306     
1307     def test_create_directory_marker(self):
1308         self.client.create_directory_marker(self.container, 'foo')
1309         meta = self.client.retrieve_object_metadata(self.container, 'foo')
1310         self.assertEqual(meta['content-length'], '0')
1311         self.assertEqual(meta['content-type'], 'application/directory')
1312
1313     def test_upload_unprocessable_entity(self):
1314         meta={'etag':'123', 'test':'test1'}
1315         
1316         #assert unprocessable entity
1317         self.assert_raises_fault(422, self.upload_random_data, self.container,
1318                                  o_names[0], **meta)
1319     
1320     def test_chunked_transfer(self):
1321         data = get_random_data()
1322         objname = 'object'
1323         self.client.create_object_using_chunks(self.container, objname,
1324                                                StringIO(data))
1325         
1326         uploaded_data = self.client.retrieve_object(self.container, objname)
1327         self.assertEqual(data, uploaded_data)
1328     
1329     def test_manifestation(self):
1330         prefix = 'myobject/'
1331         data = ''
1332         for i in range(5):
1333             part = '%s%d' %(prefix, i)
1334             o = self.upload_random_data(self.container, part)
1335             data += o['data']
1336         
1337         manifest = '%s/%s' %(self.container, prefix)
1338         self.client.create_manifestation(self.container, 'large-object', manifest)
1339         
1340         self.assert_object_exists(self.container, 'large-object')
1341         self.assertEqual(data, self.client.retrieve_object(self.container,
1342                                                            'large-object'))
1343         
1344         #wrong manifestation
1345         self.client.create_manifestation(self.container, 'large-object',
1346                                          '%s/invalid' % self.container)
1347         self.assertEqual('', self.client.retrieve_object(self.container,
1348                                                          'large-object'))
1349     
1350     def test_create_zero_length_object(self):
1351         c = self.container
1352         o = 'object'
1353         zero = self.client.create_zero_length_object(c, o)
1354         zero_meta = self.client.retrieve_object_metadata(c, o)
1355         zero_hash = self.client.retrieve_object_hashmap(c, o)
1356         zero_data = self.client.retrieve_object(c, o)
1357         
1358         self.assertEqual(int(zero_meta['content-length']), 0)
1359         self.assertEqual(zero_hash, [])
1360         self.assertEqual(zero_data, '')
1361     
1362     def test_create_object_by_hashmap(self):
1363         c = self.container
1364         o = 'object'
1365         self.upload_random_data(c, o)
1366         hashmap = self.client.retrieve_object(c, o, format='json')
1367         o2 = 'object-copy'
1368         self.client.create_object_by_hashmap(c, o2, hashmap)
1369         self.assertEqual(self.client.retrieve_object(c, o),
1370                          self.client.retrieve_object(c, o))
1371
1372 class ObjectCopy(BaseTestCase):
1373     def setUp(self):
1374         BaseTestCase.setUp(self)
1375         self.containers = ['c1', 'c2']
1376         for c in self.containers:
1377             self.client.create_container(c)
1378         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1379         
1380     def test_copy(self):
1381         with AssertMappingInvariant(self.client.retrieve_object_metadata,
1382                              self.containers[0], self.obj['name']):
1383             #perform copy
1384             meta = {'test':'testcopy'}
1385             status = self.client.copy_object(self.containers[0],
1386                                               self.obj['name'],
1387                                               self.containers[0],
1388                                               'testcopy',
1389                                               meta)[0]
1390             
1391             #assert copy success
1392             self.assertEqual(status, 201)
1393             
1394             #assert access the new object
1395             headers = self.client.retrieve_object_metadata(self.containers[0],
1396                                                            'testcopy')
1397             self.assertTrue('x-object-meta-test' in headers.keys())
1398             self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1399             
1400             #assert etag is the same
1401             self.assertEqual(headers['etag'], self.obj['hash'])
1402             
1403             #assert src object still exists
1404             self.assert_object_exists(self.containers[0], self.obj['name'])
1405     
1406     def test_copy_from_different_container(self):
1407         with AssertMappingInvariant(self.client.retrieve_object_metadata,
1408                              self.containers[0], self.obj['name']):
1409             meta = {'test':'testcopy'}
1410             status = self.client.copy_object(self.containers[0],
1411                                              self.obj['name'],
1412                                              self.containers[1],
1413                                              'testcopy',
1414                                              meta)[0]
1415             self.assertEqual(status, 201)
1416             
1417             # assert updated metadata
1418             meta = self.client.retrieve_object_metadata(self.containers[1],
1419                                                            'testcopy',
1420                                                            restricted=True)
1421             self.assertTrue('test' in meta.keys())
1422             self.assertTrue(meta['test'], 'testcopy')
1423             
1424             #assert src object still exists
1425             self.assert_object_exists(self.containers[0], self.obj['name'])
1426     
1427     def test_copy_invalid(self):
1428         #copy from invalid object
1429         meta = {'test':'testcopy'}
1430         self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1431                                  'test.py', self.containers[1], 'testcopy', meta)
1432         
1433         #copy from invalid container
1434         meta = {'test':'testcopy'}
1435         self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1436                                  self.obj['name'], self.containers[1],
1437                                  'testcopy', meta)
1438
1439 class ObjectMove(BaseTestCase):
1440     def setUp(self):
1441         BaseTestCase.setUp(self)
1442         self.containers = ['c1', 'c2']
1443         for c in self.containers:
1444             self.client.create_container(c)
1445         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1446     
1447     def test_move(self):
1448         #perform move
1449         meta = {'test':'testcopy'}
1450         src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1451         status = self.client.move_object(self.containers[0], self.obj['name'],
1452                                          self.containers[0], 'testcopy',
1453                                          meta)[0]
1454         
1455         #assert successful move
1456         self.assertEqual(status, 201)
1457         
1458         #assert updated metadata
1459         meta = self.client.retrieve_object_metadata(self.containers[0],
1460                                                     'testcopy',
1461                                                     restricted=True)
1462         self.assertTrue('test' in meta.keys())
1463         self.assertTrue(meta['test'], 'testcopy')
1464         
1465         #assert src object no more exists
1466         self.assert_object_not_exists(self.containers[0], self.obj['name'])
1467
1468 class ObjectPost(BaseTestCase):
1469     def setUp(self):
1470         BaseTestCase.setUp(self)
1471         self.containers = ['c1', 'c2']
1472         for c in self.containers:
1473             self.client.create_container(c)
1474         self.obj = []
1475         for i in range(2):
1476             self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1477     
1478     def test_update_meta(self):
1479         #perform update metadata
1480         more = {'foo':'foo', 'bar':'bar'}
1481         status = self.client.update_object_metadata(self.containers[0],
1482                                                     self.obj[0]['name'],
1483                                                     **more)[0]
1484         #assert request accepted
1485         self.assertEqual(status, 202)
1486         
1487         #assert old metadata are still there
1488         headers = self.client.retrieve_object_metadata(self.containers[0],
1489                                                        self.obj[0]['name'],
1490                                                        restricted=True)
1491         #assert new metadata have been updated
1492         for k,v in more.items():
1493             self.assertTrue(k in headers.keys())
1494             self.assertTrue(headers[k], v)
1495     
1496     def test_update_object(self,
1497                            first_byte_pos=0,
1498                            last_byte_pos=499,
1499                            instance_length = True,
1500                            content_length = 500):
1501         l = len(self.obj[0]['data'])
1502         range = 'bytes %d-%d/%s' %(first_byte_pos,
1503                                        last_byte_pos,
1504                                         l if instance_length else '*')
1505         partial = last_byte_pos - first_byte_pos + 1
1506         length = first_byte_pos + partial
1507         data = get_random_data(partial)
1508         args = {'content_type':'application/octet-stream',
1509                 'content_range':'%s' %range}
1510         if content_length:
1511             args['content_length'] = content_length
1512         
1513         status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1514                                   StringIO(data), **args)[0]
1515         
1516         if partial < 0 or (instance_length and l <= last_byte_pos):
1517             self.assertEqual(status, 202)    
1518         else:
1519             self.assertEqual(status, 204)           
1520             #check modified object
1521             content = self.client.retrieve_object(self.containers[0],
1522                                               self.obj[0]['name'])
1523             self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1524             self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1525             self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1526     
1527     def test_update_object_lt_blocksize(self):
1528         self.test_update_object(10, 20, content_length=None)
1529     
1530     def test_update_object_gt_blocksize(self):
1531         o = self.upload_random_data(self.containers[0], o_names[1],
1532                                 length=4*1024*1024+5)
1533         c = self.containers[0]
1534         o_name = o['name']
1535         o_data = o['data']
1536         first_byte_pos = 4*1024*1024+1
1537         last_byte_pos = 4*1024*1024+4
1538         l = last_byte_pos - first_byte_pos + 1
1539         data = get_random_data(l)
1540         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1541         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1542         content = self.client.retrieve_object(c, o_name)
1543         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1544         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1545         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1546     
1547     def test_update_object_divided_by_blocksize(self):
1548         o = self.upload_random_data(self.containers[0], o_names[1],
1549                                 length=4*1024*1024+5)
1550         c = self.containers[0]
1551         o_name = o['name']
1552         o_data = o['data']
1553         first_byte_pos = 4*1024*1024
1554         last_byte_pos = 5*1024*1024
1555         l = last_byte_pos - first_byte_pos + 1
1556         data = get_random_data(l)
1557         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1558         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1559         content = self.client.retrieve_object(c, o_name)
1560         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1561         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1562         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1563     
1564     def test_update_object_no_content_length(self):
1565         self.test_update_object(content_length = None)
1566     
1567     def test_update_object_invalid_content_length(self):
1568         with AssertContentInvariant(self.client.retrieve_object,
1569                                     self.containers[0], self.obj[0]['name']):
1570             self.assert_raises_fault(400, self.test_update_object,
1571                                      content_length = 1000)
1572     
1573     def test_update_object_invalid_range(self):
1574         with AssertContentInvariant(self.client.retrieve_object,
1575                                     self.containers[0], self.obj[0]['name']):
1576             self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1577     
1578     def test_update_object_invalid_range_and_length(self):
1579         with AssertContentInvariant(self.client.retrieve_object,
1580                                     self.containers[0], self.obj[0]['name']):
1581             self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1582                                      -1)
1583     
1584     def test_update_object_invalid_range_with_no_content_length(self):
1585         with AssertContentInvariant(self.client.retrieve_object,
1586                                     self.containers[0], self.obj[0]['name']):
1587             self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1588                                      content_length = None)
1589     
1590     def test_update_object_out_of_limits(self):    
1591         with AssertContentInvariant(self.client.retrieve_object,
1592                                     self.containers[0], self.obj[0]['name']):
1593             l = len(self.obj[0]['data'])
1594             self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1595     
1596     def test_append(self):
1597         data = get_random_data(500)
1598         headers = {}
1599         self.client.update_object(self.containers[0], self.obj[0]['name'],
1600                                   StringIO(data), content_length=500,
1601                                   content_type='application/octet-stream')
1602         
1603         content = self.client.retrieve_object(self.containers[0],
1604                                               self.obj[0]['name'])
1605         self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1606         self.assertEqual(content[:-500], self.obj[0]['data'])
1607     
1608     def test_update_with_chunked_transfer(self):
1609         data = get_random_data(500)
1610         dl = len(data)
1611         fl = len(self.obj[0]['data'])
1612         
1613         self.client.update_object_using_chunks(self.containers[0],
1614                                                self.obj[0]['name'],
1615                                                StringIO(data),
1616                                                offset=0,
1617                                                content_type='application/octet-stream')
1618         
1619         #check modified object
1620         content = self.client.retrieve_object(self.containers[0],
1621                                               self.obj[0]['name'])
1622         self.assertEqual(content[0:dl], data)
1623         self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1624     
1625     def test_update_from_other_object(self):
1626         c = self.containers[0]
1627         src = o_names[0]
1628         dest = 'object'
1629         
1630         source_data = self.client.retrieve_object(c, src)
1631         source_meta = self.client.retrieve_object_metadata(c, src)
1632         source_hash = self.client.retrieve_object_hashmap(c, src)
1633         
1634         #update zero length object
1635         self.client.create_zero_length_object(c, dest)
1636         source_object = '/%s/%s' % (c, src)
1637         self.client.update_from_other_source(c, dest, source_object)
1638         dest_data = self.client.retrieve_object(c, src)
1639         dest_meta = self.client.retrieve_object_metadata(c, dest)
1640         dest_hash = self.client.retrieve_object_hashmap(c, src)
1641         self.assertEqual(source_data, dest_data)
1642         self.assertEqual(source_hash, dest_hash)
1643         
1644         #test append
1645         self.client.update_from_other_source(c, dest, source_object)
1646         content = self.client.retrieve_object(c, dest)
1647         self.assertEqual(source_data * 2, content)
1648     
1649     def test_update_range_from_other_object(self):
1650         c = self.containers[0]
1651         dest = 'object'
1652         
1653         #test update range
1654         src = self.obj[1]['name']
1655         src_data = self.client.retrieve_object(c, src)
1656         
1657         #update zero length object
1658         prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1659         source_object = '/%s/%s' % (c, src)
1660         first_byte_pos = 4*1024*1024+1
1661         last_byte_pos = 4*1024*1024+4
1662         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1663         self.client.update_from_other_source(c, dest, source_object,
1664                                              content_range=range)
1665         content = self.client.retrieve_object(c, dest)
1666         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1667         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1668         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1669     
1670     def test_update_hashes_from_other_object(self):
1671         c = self.containers[0]
1672         dest = 'object'
1673         
1674         #test update range
1675         src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1676         
1677         #update zero length object
1678         prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1679         source_object = '/%s/%s' % (c, o_names[0])
1680         first_byte_pos = 4*1024*1024
1681         last_byte_pos = 5*1024*1024
1682         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1683         self.client.update_from_other_source(c, dest, source_object,
1684                                              content_range=range)
1685         content = self.client.retrieve_object(c, dest)
1686         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1687         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1688         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1689     
1690     
1691     def test_update_zero_length_object(self):
1692         c = self.containers[0]
1693         o = 'object'
1694         other = 'other'
1695         zero = self.client.create_zero_length_object(c, o)
1696         
1697         data = get_random_data()
1698         self.client.update_object(c, o, StringIO(data))
1699         self.client.create_object(c, other, StringIO(data))
1700         
1701         self.assertEqual(self.client.retrieve_object(c, o),
1702                          self.client.retrieve_object(c, other))
1703         
1704         self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1705                          self.client.retrieve_object_hashmap(c, other))
1706     
1707 class ObjectDelete(BaseTestCase):
1708     def setUp(self):
1709         BaseTestCase.setUp(self)
1710         self.containers = ['c1', 'c2']
1711         for c in self.containers:
1712             self.client.create_container(c)
1713         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1714     
1715     def test_delete(self):
1716         #perform delete object
1717         self.client.delete_object(self.containers[0], self.obj['name'])[0]
1718     
1719     def test_delete_invalid(self):
1720         #assert item not found
1721         self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1722                                  self.obj['name'])
1723
1724 class ListSharing(BaseTestCase):
1725     def setUp(self):
1726         BaseTestCase.setUp(self)
1727         for i in range(2):
1728             self.client.create_container('c%s' %i)
1729         self.client.create_container('c')
1730         for i in range(2):
1731             self.upload_random_data('c1', 'o%s' %i)
1732         accounts = OTHER_ACCOUNTS.copy()
1733         self.o1_sharing_with = accounts.popitem()
1734         self.o1_sharing = [self.o1_sharing_with[1]]
1735         self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1736         
1737         l = []
1738         for i in range(2):
1739             l.append(accounts.popitem())
1740     
1741     def test_list_other_shared(self):
1742         self.other = Pithos_Client(get_server(),
1743                               self.o1_sharing_with[0],
1744                               self.o1_sharing_with[1],
1745                               get_api())
1746         self.assertTrue(get_user() in self.other.list_shared_by_others())
1747     
1748     def test_list_my_shared(self):
1749         my_shared_containers = self.client.list_containers(shared=True)
1750         self.assertTrue('c1' in my_shared_containers)
1751         self.assertTrue('c2' not in my_shared_containers)
1752         
1753         my_shared_objects = self.client.list_objects('c1', shared=True)
1754         self.assertTrue('o1' in my_shared_objects)
1755         self.assertTrue('o2' not in my_shared_objects)
1756     
1757 class TestGreek(BaseTestCase):
1758     def setUp(self):
1759         BaseTestCase.setUp(self)
1760         #keep track of initial account groups
1761         self.initial_groups = self.client.retrieve_account_groups()
1762         
1763         #keep track of initial account meta
1764         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1765     
1766     def tearDown(self):
1767         #delete additionally created meta
1768         l = []
1769         for m in self.client.retrieve_account_metadata(restricted=True):
1770             if m not in self.initial_meta:
1771                 l.append(m)
1772         self.client.delete_account_metadata(l)
1773         
1774         #delete additionally created groups
1775         l = []
1776         for g in self.client.retrieve_account_groups():
1777             if g not in self.initial_groups:
1778                 l.append(g)
1779         self.client.unset_account_groups(l)
1780         
1781         BaseTestCase.tearDown(self)
1782     
1783     def test_create_container(self):
1784         self.client.create_container('φάκελος')
1785         self.assert_container_exists('φάκελος')
1786         
1787         self.assertTrue('φάκελος' in self.client.list_containers())
1788     
1789     def test_create_object(self):
1790         self.client.create_container('φάκελος')
1791         self.upload_random_data('φάκελος', 'αντικείμενο')
1792         
1793         self.assert_object_exists('φάκελος', 'αντικείμενο')
1794         self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1795     
1796     def test_copy_object(self):
1797         src_container = 'φάκελος'
1798         src_object = 'αντικείμενο'
1799         dest_container = 'αντίγραφα'
1800         dest_object = 'ασφαλές-αντίγραφο'
1801         
1802         self.client.create_container(src_container)
1803         self.upload_random_data(src_container, src_object)
1804         
1805         self.client.create_container(dest_container)
1806         self.client.copy_object(src_container, src_object, dest_container,
1807                                 dest_object)
1808         
1809         self.assert_object_exists(src_container, src_object)
1810         self.assert_object_exists(dest_container, dest_object)
1811         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1812     
1813     def test_move_object(self):
1814         src_container = 'φάκελος'
1815         src_object = 'αντικείμενο'
1816         dest_container = 'αντίγραφα'
1817         dest_object = 'ασφαλές-αντίγραφο'
1818         
1819         self.client.create_container(src_container)
1820         self.upload_random_data(src_container, src_object)
1821         
1822         self.client.create_container(dest_container)
1823         self.client.move_object(src_container, src_object, dest_container,
1824                                 dest_object)
1825         
1826         self.assert_object_not_exists(src_container, src_object)
1827         self.assert_object_exists(dest_container, dest_object)
1828         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1829     
1830     def test_delete_object(self):
1831         self.client.create_container('φάκελος')
1832         self.upload_random_data('φάκελος', 'αντικείμενο')
1833         self.assert_object_exists('φάκελος', 'αντικείμενο')
1834     
1835         self.client.delete_object('φάκελος', 'αντικείμενο')
1836         self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1837         self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1838     
1839     def test_delete_container(self):
1840         self.client.create_container('φάκελος')
1841         self.assert_container_exists('φάκελος')
1842         
1843         self.client.delete_container('φάκελος')
1844         self.assert_container_not_exists('φάκελος')
1845         self.assertTrue('φάκελος' not in self.client.list_containers())
1846
1847     def test_account_meta(self):
1848         meta = {'ποιότητα':'ΑΑΑ'}
1849         self.client.update_account_metadata(**meta)
1850         meta = self.client.retrieve_account_metadata(restricted=True)
1851         self.assertTrue('ποιότητα' in meta.keys())
1852         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1853     
1854     def test_container_meta(self):
1855         meta = {'ποιότητα':'ΑΑΑ'}
1856         self.client.create_container('φάκελος', **meta)
1857         
1858         meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1859         self.assertTrue('ποιότητα' in meta.keys())
1860         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1861     
1862     def test_object_meta(self):
1863         self.client.create_container('φάκελος')
1864         meta = {'ποιότητα':'ΑΑΑ'}
1865         self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1866         
1867         meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1868                                                     restricted=True)
1869         self.assertTrue('ποιότητα' in meta.keys())
1870         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1871     
1872     def test_list_meta_filtering(self):
1873         self.client.create_container('φάκελος')
1874         meta = {'ποιότητα':'ΑΑΑ'}
1875         self.upload_random_data('φάκελος', 'ο1', **meta)
1876         self.upload_random_data('φάκελος', 'ο2')
1877         self.upload_random_data('φάκελος', 'ο3')
1878         
1879         meta = {'ποσότητα':'μεγάλη'}
1880         self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1881         objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1882         self.assertTrue('ο1' in objects)
1883         self.assertTrue('ο2' in objects)
1884         self.assertTrue('ο3' not in objects)
1885     
1886     def test_groups(self):
1887         #create a group
1888         groups = {'σεφς':'chazapis,διογένης'}
1889         self.client.set_account_groups(**groups)
1890         groups.update(self.initial_groups)
1891         self.assertEqual(groups['σεφς'],
1892                          self.client.retrieve_account_groups()['σεφς'])
1893         
1894         #check read access
1895         self.client.create_container('φάκελος')
1896         o = self.upload_random_data('φάκελος', 'ο1')
1897         self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1898         chef = Pithos_Client(get_server(),
1899                             '0009',
1900                             'διογένης',
1901                             get_api())
1902         self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1903                                      'φάκελος', 'ο1', account=get_user())
1904         
1905         #check write access
1906         self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1907         new_data = get_random_data()
1908         self.assert_not_raises_fault(403, chef.update_object,
1909                                      'φάκελος', 'ο1', StringIO(new_data),
1910                                      account=get_user())
1911         
1912         server_data = self.client.retrieve_object('φάκελος', 'ο1')
1913         self.assertEqual(server_data[:len(o['data'])], o['data'])
1914         self.assertEqual(server_data[len(o['data']):], new_data)
1915     
1916     def test_manifestation(self):
1917         self.client.create_container('κουβάς')
1918         prefix = 'μέρη/'
1919         data = ''
1920         for i in range(5):
1921             part = '%s%d' %(prefix, i)
1922             o = self.upload_random_data('κουβάς', part)
1923             data += o['data']
1924         
1925         self.client.create_container('φάκελος')
1926         manifest = '%s/%s' %('κουβάς', prefix)
1927         self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1928         
1929         self.assert_object_exists('φάκελος', 'άπαντα')
1930         self.assertEqual(data, self.client.retrieve_object('φάκελος',
1931                                                            'άπαντα'))
1932         
1933         #wrong manifestation
1934         self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1935         self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1936     
1937     def test_update_from_another_object(self):
1938         self.client.create_container('κουβάς')
1939         src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1940         initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1941         source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1942         self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1943         
1944         self.assertEqual(
1945             self.client.retrieve_object('κουβάς', 'νέο'),
1946             '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1947
1948 class TestPermissions(BaseTestCase):
1949     def setUp(self):
1950         BaseTestCase.setUp(self)
1951         #keep track of initial account groups
1952         self.initial_groups = self.client.retrieve_account_groups()
1953         #keep track of initial account meta
1954         self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1955         
1956         #create a group
1957         self.authorized = ['chazapis', 'verigak', 'gtsouk']
1958         groups = {'pithosdev':','.join(self.authorized)}
1959         self.client.set_account_groups(**groups)
1960     
1961     def tearDown(self):
1962         #delete additionally created meta
1963         l = []
1964         for m in self.client.retrieve_account_metadata(restricted=True):
1965             if m not in self.initial_meta:
1966                 l.append(m)
1967         self.client.delete_account_metadata(l)
1968         
1969         #delete additionally created groups
1970         l = []
1971         for g in self.client.retrieve_account_groups():
1972             if g not in self.initial_groups:
1973                 l.append(g)
1974         self.client.unset_account_groups(l)
1975         
1976         BaseTestCase.tearDown(self)
1977     
1978     def assert_read(self, authorized=[], any=False):
1979         for token, account in OTHER_ACCOUNTS.items():
1980             cl = Pithos_Client(get_server(), token, account, get_api()) 
1981             if account in authorized or any:
1982                 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1983                                              'c', 'o', account=get_user())
1984             else:
1985                 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1986                                          'c', 'o', account=get_user())
1987         
1988         #check inheritance
1989         o = self.upload_random_data('c', 'o/also-shared')
1990         for token, account in OTHER_ACCOUNTS.items():
1991             cl = Pithos_Client(get_server(), token, account, get_api()) 
1992             if account in authorized or any:
1993                 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1994                                              'c', 'o/also-shared', account=get_user())
1995             else:
1996                 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1997                                          'c', 'o/also-shared', account=get_user())
1998     
1999     def assert_write(self, o_data, authorized=[], any=False):
2000         for token, account in OTHER_ACCOUNTS.items():
2001             cl = Pithos_Client(get_server(), token, account, get_api()) 
2002             new_data = get_random_data()
2003             if account in authorized or any:
2004                 # test write access
2005                 self.assert_not_raises_fault(403, cl.update_object,
2006                                              'c', 'o', StringIO(new_data),
2007                                              account=get_user())
2008                 try:
2009                     # test read access
2010                     server_data = cl.retrieve_object('c', 'o', account=get_user())
2011                     self.assertEqual(o_data, server_data[:len(o_data)])
2012                     self.assertEqual(new_data, server_data[len(o_data):])
2013                     o_data = server_data
2014                 except Fault, f:
2015                     self.failIf(f.status == 403)
2016             else:
2017                 self.assert_raises_fault(403, cl.update_object,
2018                                              'c', 'o', StringIO(new_data),
2019                                              account=get_user())
2020         
2021         #check inheritance
2022         o = self.upload_random_data('c', 'o/also-shared')
2023         o_data = o['data']
2024         for token, account in OTHER_ACCOUNTS.items():
2025             cl = Pithos_Client(get_server(), token, account, get_api()) 
2026             new_data = get_random_data()
2027             if account in authorized or any:
2028                 # test write access
2029                 self.assert_not_raises_fault(403, cl.update_object,
2030                                              'c', o['name'],
2031                                              StringIO(new_data),
2032                                              account=get_user())
2033                 try:
2034                     server_data = cl.retrieve_object('c', o['name'], account=get_user())
2035                     self.assertEqual(o_data, server_data[:len(o_data)])
2036                     self.assertEqual(new_data, server_data[len(o_data):])
2037                     o_data = server_data
2038                 except Fault, f:
2039                     self.failIf(f.status == 403)
2040             else:
2041                 self.assert_raises_fault(403, cl.update_object,
2042                                              'c', o['name'],
2043                                              StringIO(new_data),
2044                                              account=get_user())
2045     
2046     def test_group_read(self):
2047         self.client.create_container('c')
2048         o = self.upload_random_data('c', 'o')
2049         self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
2050         self.assert_read(authorized=self.authorized)
2051     
2052     def test_read_many(self):
2053         #test read access
2054         self.client.create_container('c')
2055         o = self.upload_random_data('c', 'o')
2056         self.client.share_object('c', 'o', self.authorized)
2057         self.assert_read(authorized=self.authorized)
2058     
2059     def test_read_by_everyone(self):
2060         self.client.create_container('c')
2061         o = self.upload_random_data('c', 'o')
2062         self.client.share_object('c', 'o', ['*'])
2063         self.assert_read(any=True)
2064     
2065     def test_group_write(self):
2066         self.client.create_container('c')
2067         o = self.upload_random_data('c', 'o')
2068         self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
2069         self.assert_write(o['data'], authorized=self.authorized)
2070     
2071     def test_write_many(self):
2072         self.client.create_container('c')
2073         o = self.upload_random_data('c', 'o')
2074         self.client.share_object('c', 'o', self.authorized, read=False)
2075         self.assert_write(o['data'], authorized=self.authorized)
2076     
2077     def test_write_by_everyone(self):
2078         self.client.create_container('c')
2079         o = self.upload_random_data('c', 'o')
2080         self.client.share_object('c', 'o', ['*'], read=False)
2081         o_data = o['data']
2082         self.assert_write(o['data'], any=True)
2083
2084 class TestPublish(BaseTestCase):
2085     def test_publish(self):
2086         self.client.create_container('c')
2087         o_data = self.upload_random_data('c', 'o')['data']
2088         self.client.publish_object('c', 'o')
2089         meta = self.client.retrieve_object_metadata('c', 'o')
2090         self.assertTrue('x-object-public' in meta)
2091         url = '/public/%s/c/o' % get_user()
2092         self.assertEqual(meta['x-object-public'], url)
2093         public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2094         data = public_client.get(url)[2]
2095         self.assertEqual(o_data, data)
2096
2097 class AssertMappingInvariant(object):
2098     def __init__(self, callable, *args, **kwargs):
2099         self.callable = callable
2100         self.args = args
2101         self.kwargs = kwargs
2102     
2103     def __enter__(self):
2104         self.map = self.callable(*self.args, **self.kwargs)
2105         return self.map
2106     
2107     def __exit__(self, type, value, tb):
2108         map = self.callable(*self.args, **self.kwargs)
2109         for k in self.map.keys():
2110             if is_date(self.map[k]):
2111                 continue
2112             assert map[k] == self.map[k]
2113
2114 class AssertContentInvariant(object):
2115     def __init__(self, callable, *args, **kwargs):
2116         self.callable = callable
2117         self.args = args
2118         self.kwargs = kwargs
2119     
2120     def __enter__(self):
2121         self.content = self.callable(*self.args, **self.kwargs)[2]
2122         return self.content
2123     
2124     def __exit__(self, type, value, tb):
2125         content = self.callable(*self.args, **self.kwargs)[2]
2126         assert self.content == content
2127
2128 def get_content_splitted(response):
2129     if response:
2130         return response.content.split('\n')
2131
2132 def compute_md5_hash(data):
2133     md5 = hashlib.md5()
2134     offset = 0
2135     md5.update(data)
2136     return md5.hexdigest().lower()
2137
2138 def compute_block_hash(data, algorithm):
2139     h = hashlib.new(algorithm)
2140     h.update(data.rstrip('\x00'))
2141     return h.hexdigest()
2142
2143 def get_random_data(length=500):
2144     char_set = string.ascii_uppercase + string.digits
2145     return ''.join(random.choice(char_set) for x in range(length))
2146
2147 def is_date(date):
2148     MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2149     __D = r'(?P<day>\d{2})'
2150     __D2 = r'(?P<day>[ \d]\d)'
2151     __M = r'(?P<mon>\w{3})'
2152     __Y = r'(?P<year>\d{4})'
2153     __Y2 = r'(?P<year>\d{2})'
2154     __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2155     RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2156     RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2157     ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2158     for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2159         m = regex.match(date)
2160         if m is not None:
2161             return True
2162     return False
2163
2164 o_names = ['kate.jpg',
2165            'kate_beckinsale.jpg',
2166            'How To Win Friends And Influence People.pdf',
2167            'moms_birthday.jpg',
2168            'poodle_strut.mov',
2169            'Disturbed - Down With The Sickness.mp3',
2170            'army_of_darkness.avi',
2171            'the_mad.avi',
2172            'photos/animals/dogs/poodle.jpg',
2173            'photos/animals/dogs/terrier.jpg',
2174            'photos/animals/cats/persian.jpg',
2175            'photos/animals/cats/siamese.jpg',
2176            'photos/plants/fern.jpg',
2177            'photos/plants/rose.jpg',
2178            'photos/me.jpg']
2179
2180 if __name__ == "__main__":
2181     if get_user() == 'test':
2182         unittest.main()
2183     else:
2184         print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()