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