sqlalchemy backend: fix metadata queries
[pithos] / pithos / tools / pithos-test
1 #!/usr/bin/env python
2 #coding=utf8
3
4 # Copyright 2011 GRNET S.A. All rights reserved.
5
6 # Redistribution and use in source and binary forms, with or
7 # without modification, are permitted provided that the following
8 # conditions are met:
9
10 #   1. Redistributions of source code must retain the above
11 #      copyright notice, this list of conditions and the following
12 #      disclaimer.
13
14 #   2. Redistributions in binary form must reproduce the above
15 #      copyright notice, this list of conditions and the following
16 #      disclaimer in the documentation and/or other materials
17 #      provided with the distribution.
18
19 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
31
32 # The views and conclusions contained in the software and
33 # documentation are those of the authors and should not be
34 # interpreted as representing official policies, either expressed
35 # or implied, of GRNET S.A.
36
37 from 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         #perform move
1378         meta = {'test':'testcopy'}
1379         src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1380         status = self.client.move_object(self.containers[0], self.obj['name'],
1381                                          self.containers[0], 'testcopy',
1382                                          meta)[0]
1383         
1384         #assert successful move
1385         self.assertEqual(status, 201)
1386         
1387         #assert updated metadata
1388         meta = self.client.retrieve_object_metadata(self.containers[0],
1389                                                     'testcopy',
1390                                                     restricted=True)
1391         self.assertTrue('test' in meta.keys())
1392         self.assertTrue(meta['test'], 'testcopy')
1393         
1394         #assert src object no more exists
1395         self.assert_object_not_exists(self.containers[0], self.obj['name'])
1396
1397 class ObjectPost(BaseTestCase):
1398     def setUp(self):
1399         BaseTestCase.setUp(self)
1400         self.containers = ['c1', 'c2']
1401         for c in self.containers:
1402             self.client.create_container(c)
1403         self.obj = []
1404         for i in range(2):
1405             self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1406     
1407     def test_update_meta(self):
1408         #perform update metadata
1409         more = {'foo':'foo', 'bar':'bar'}
1410         status = self.client.update_object_metadata(self.containers[0],
1411                                                     self.obj[0]['name'],
1412                                                     **more)[0]
1413         #assert request accepted
1414         self.assertEqual(status, 202)
1415         
1416         #assert old metadata are still there
1417         headers = self.client.retrieve_object_metadata(self.containers[0],
1418                                                        self.obj[0]['name'],
1419                                                        restricted=True)
1420         #assert new metadata have been updated
1421         for k,v in more.items():
1422             self.assertTrue(k in headers.keys())
1423             self.assertTrue(headers[k], v)
1424     
1425     def test_update_object(self,
1426                            first_byte_pos=0,
1427                            last_byte_pos=499,
1428                            instance_length = True,
1429                            content_length = 500):
1430         l = len(self.obj[0]['data'])
1431         range = 'bytes %d-%d/%s' %(first_byte_pos,
1432                                        last_byte_pos,
1433                                         l if instance_length else '*')
1434         partial = last_byte_pos - first_byte_pos + 1
1435         length = first_byte_pos + partial
1436         data = get_random_data(partial)
1437         args = {'content_type':'application/octet-stream',
1438                 'content_range':'%s' %range}
1439         if content_length:
1440             args['content_length'] = content_length
1441         
1442         status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1443                                   StringIO(data), **args)[0]
1444         
1445         if partial < 0 or (instance_length and l <= last_byte_pos):
1446             self.assertEqual(status, 202)    
1447         else:
1448             self.assertEqual(status, 204)           
1449             #check modified object
1450             content = self.client.retrieve_object(self.containers[0],
1451                                               self.obj[0]['name'])
1452             self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1453             self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1454             self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1455     
1456     def test_update_object_lt_blocksize(self):
1457         self.test_update_object(10, 20, content_length=None)
1458     
1459     def test_update_object_gt_blocksize(self):
1460         o = self.upload_random_data(self.containers[0], o_names[1],
1461                                 length=4*1024*1024+5)
1462         c = self.containers[0]
1463         o_name = o['name']
1464         o_data = o['data']
1465         first_byte_pos = 4*1024*1024+1
1466         last_byte_pos = 4*1024*1024+4
1467         l = last_byte_pos - first_byte_pos + 1
1468         data = get_random_data(l)
1469         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1470         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1471         content = self.client.retrieve_object(c, o_name)
1472         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1473         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1474         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1475     
1476     def test_update_object_divided_by_blocksize(self):
1477         o = self.upload_random_data(self.containers[0], o_names[1],
1478                                 length=4*1024*1024+5)
1479         c = self.containers[0]
1480         o_name = o['name']
1481         o_data = o['data']
1482         first_byte_pos = 4*1024*1024
1483         last_byte_pos = 5*1024*1024
1484         l = last_byte_pos - first_byte_pos + 1
1485         data = get_random_data(l)
1486         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1487         self.client.update_object(c, o_name, StringIO(data), content_range=range)
1488         content = self.client.retrieve_object(c, o_name)
1489         self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1490         self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1491         self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])    
1492     
1493     def test_update_object_no_content_length(self):
1494         self.test_update_object(content_length = None)
1495     
1496     def test_update_object_invalid_content_length(self):
1497         with AssertContentInvariant(self.client.retrieve_object,
1498                                     self.containers[0], self.obj[0]['name']):
1499             self.assert_raises_fault(400, self.test_update_object,
1500                                      content_length = 1000)
1501     
1502     def _test_update_object_invalid_range(self):
1503         with AssertContentInvariant(self.client.retrieve_object,
1504                                     self.containers[0], self.obj[0]['name']):
1505             self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1506     
1507     def _test_update_object_invalid_range_and_length(self):
1508         with AssertContentInvariant(self.client.retrieve_object,
1509                                     self.containers[0], self.obj[0]['name']):
1510             self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1511                                      -1)
1512     
1513     def test_update_object_invalid_range_with_no_content_length(self):
1514         with AssertContentInvariant(self.client.retrieve_object,
1515                                     self.containers[0], self.obj[0]['name']):
1516             self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1517                                      content_length = None)
1518     
1519     def test_update_object_out_of_limits(self):    
1520         with AssertContentInvariant(self.client.retrieve_object,
1521                                     self.containers[0], self.obj[0]['name']):
1522             l = len(self.obj[0]['data'])
1523             self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1524     
1525     def test_append(self):
1526         data = get_random_data(500)
1527         headers = {}
1528         self.client.update_object(self.containers[0], self.obj[0]['name'],
1529                                   StringIO(data), content_length=500,
1530                                   content_type='application/octet-stream')
1531         
1532         content = self.client.retrieve_object(self.containers[0],
1533                                               self.obj[0]['name'])
1534         self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1535         self.assertEqual(content[:-500], self.obj[0]['data'])
1536     
1537     def test_update_with_chunked_transfer(self):
1538         data = get_random_data(500)
1539         dl = len(data)
1540         fl = len(self.obj[0]['data'])
1541         
1542         self.client.update_object_using_chunks(self.containers[0],
1543                                                self.obj[0]['name'],
1544                                                StringIO(data),
1545                                                offset=0,
1546                                                content_type='application/octet-stream')
1547         
1548         #check modified object
1549         content = self.client.retrieve_object(self.containers[0],
1550                                               self.obj[0]['name'])
1551         self.assertEqual(content[0:dl], data)
1552         self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1553     
1554     def test_update_from_other_object(self):
1555         c = self.containers[0]
1556         src = o_names[0]
1557         dest = 'object'
1558         
1559         source_data = self.client.retrieve_object(c, src)
1560         source_meta = self.client.retrieve_object_metadata(c, src)
1561         source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1562         
1563         #update zero length object
1564         self.client.create_zero_length_object(c, dest)
1565         source_object = '/%s/%s' % (c, src)
1566         self.client.update_from_other_source(c, dest, source_object)
1567         dest_data = self.client.retrieve_object(c, src)
1568         dest_meta = self.client.retrieve_object_metadata(c, dest)
1569         dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1570         self.assertEqual(source_data, dest_data)
1571         self.assertEqual(source_hash, dest_hash)
1572         
1573         #test append
1574         self.client.update_from_other_source(c, dest, source_object)
1575         content = self.client.retrieve_object(c, dest)
1576         self.assertEqual(source_data * 2, content)
1577     
1578     def test_update_range_from_other_object(self):
1579         c = self.containers[0]
1580         dest = 'object'
1581         
1582         #test update range
1583         src = self.obj[1]['name']
1584         src_data = self.client.retrieve_object(c, src)
1585         
1586         #update zero length object
1587         prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1588         source_object = '/%s/%s' % (c, src)
1589         first_byte_pos = 4*1024*1024+1
1590         last_byte_pos = 4*1024*1024+4
1591         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1592         self.client.update_from_other_source(c, dest, source_object,
1593                                              content_range=range)
1594         content = self.client.retrieve_object(c, dest)
1595         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1596         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1597         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1598     
1599     def test_update_hashes_from_other_object(self):
1600         c = self.containers[0]
1601         dest = 'object'
1602         
1603         #test update range
1604         src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1605         
1606         #update zero length object
1607         prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1608         source_object = '/%s/%s' % (c, o_names[0])
1609         first_byte_pos = 4*1024*1024
1610         last_byte_pos = 5*1024*1024
1611         range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1612         self.client.update_from_other_source(c, dest, source_object,
1613                                              content_range=range)
1614         content = self.client.retrieve_object(c, dest)
1615         self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1616         self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1617         self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1618     
1619     
1620     def test_update_zero_length_object(self):
1621         c = self.containers[0]
1622         o = 'object'
1623         other = 'other'
1624         zero = self.client.create_zero_length_object(c, o)
1625         
1626         data = get_random_data()
1627         self.client.update_object(c, o, StringIO(data))
1628         self.client.create_object(c, other, StringIO(data))
1629         
1630         self.assertEqual(self.client.retrieve_object(c, o),
1631                          self.client.retrieve_object(c, other))
1632         
1633         self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1634                          self.client.retrieve_object_hashmap(c, other)["hashes"])
1635     
1636 class ObjectDelete(BaseTestCase):
1637     def setUp(self):
1638         BaseTestCase.setUp(self)
1639         self.containers = ['c1', 'c2']
1640         for c in self.containers:
1641             self.client.create_container(c)
1642         self.obj = self.upload_random_data(self.containers[0], o_names[0])
1643     
1644     def test_delete(self):
1645         #perform delete object
1646         self.client.delete_object(self.containers[0], self.obj['name'])[0]
1647     
1648     def test_delete_invalid(self):
1649         #assert item not found
1650         self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1651                                  self.obj['name'])
1652
1653 class ListSharing(BaseTestCase):
1654     def setUp(self):
1655         BaseTestCase.setUp(self)
1656         for i in range(2):
1657             self.client.create_container('c%s' %i)
1658         self.client.create_container('c')
1659         for i in range(2):
1660             self.upload_random_data('c1', 'o%s' %i)
1661         accounts = OTHER_ACCOUNTS.copy()
1662         self.o1_sharing_with = accounts.popitem()
1663         self.o1_sharing = [self.o1_sharing_with[1]]
1664         self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1665         
1666         l = []
1667         for i in range(2):
1668             l.append(accounts.popitem())
1669     
1670     def test_list_other_shared(self):
1671         self.other = Pithos_Client(get_server(),
1672                               self.o1_sharing_with[0],
1673                               self.o1_sharing_with[1],
1674                               get_api())
1675         self.assertTrue(get_user() in self.other.list_shared_by_others())
1676     
1677     def test_list_my_shared(self):
1678         my_shared_containers = self.client.list_containers(shared=True)
1679         self.assertTrue('c1' in my_shared_containers)
1680         self.assertTrue('c2' not in my_shared_containers)
1681         
1682         my_shared_objects = self.client.list_objects('c1', shared=True)
1683         self.assertTrue('o1' in my_shared_objects)
1684         self.assertTrue('o2' not in my_shared_objects)
1685     
1686 class TestGreek(BaseTestCase):
1687     def test_create_container(self):
1688         self.client.create_container('φάκελος')
1689         self.assert_container_exists('φάκελος')
1690         
1691         self.assertTrue('φάκελος' in self.client.list_containers())
1692     
1693     def test_create_object(self):
1694         self.client.create_container('φάκελος')
1695         self.upload_random_data('φάκελος', 'αντικείμενο')
1696         
1697         self.assert_object_exists('φάκελος', 'αντικείμενο')
1698         self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1699     
1700     def test_copy_object(self):
1701         src_container = 'φάκελος'
1702         src_object = 'αντικείμενο'
1703         dest_container = 'αντίγραφα'
1704         dest_object = 'ασφαλές-αντίγραφο'
1705         
1706         self.client.create_container(src_container)
1707         self.upload_random_data(src_container, src_object)
1708         
1709         self.client.create_container(dest_container)
1710         self.client.copy_object(src_container, src_object, dest_container,
1711                                 dest_object)
1712         
1713         self.assert_object_exists(src_container, src_object)
1714         self.assert_object_exists(dest_container, dest_object)
1715         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1716     
1717     def test_move_object(self):
1718         src_container = 'φάκελος'
1719         src_object = 'αντικείμενο'
1720         dest_container = 'αντίγραφα'
1721         dest_object = 'ασφαλές-αντίγραφο'
1722         
1723         self.client.create_container(src_container)
1724         self.upload_random_data(src_container, src_object)
1725         
1726         self.client.create_container(dest_container)
1727         self.client.move_object(src_container, src_object, dest_container,
1728                                 dest_object)
1729         
1730         self.assert_object_not_exists(src_container, src_object)
1731         self.assert_object_exists(dest_container, dest_object)
1732         self.assertTrue(dest_object in self.client.list_objects(dest_container))
1733     
1734     def test_delete_object(self):
1735         self.client.create_container('φάκελος')
1736         self.upload_random_data('φάκελος', 'αντικείμενο')
1737         self.assert_object_exists('φάκελος', 'αντικείμενο')
1738     
1739         self.client.delete_object('φάκελος', 'αντικείμενο')
1740         self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1741         self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1742     
1743     def test_delete_container(self):
1744         self.client.create_container('φάκελος')
1745         self.assert_container_exists('φάκελος')
1746         
1747         self.client.delete_container('φάκελος')
1748         self.assert_container_not_exists('φάκελος')
1749         self.assertTrue('φάκελος' not in self.client.list_containers())
1750
1751     def test_account_meta(self):
1752         meta = {'ποιότητα':'ΑΑΑ'}
1753         self.client.update_account_metadata(**meta)
1754         meta = self.client.retrieve_account_metadata(restricted=True)
1755         self.assertTrue('ποιότητα' in meta.keys())
1756         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1757     
1758     def test_container_meta(self):
1759         meta = {'ποιότητα':'ΑΑΑ'}
1760         self.client.create_container('φάκελος', **meta)
1761         
1762         meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1763         self.assertTrue('ποιότητα' in meta.keys())
1764         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1765     
1766     def test_object_meta(self):
1767         self.client.create_container('φάκελος')
1768         meta = {'ποιότητα':'ΑΑΑ'}
1769         self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1770         
1771         meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1772                                                     restricted=True)
1773         self.assertTrue('ποιότητα' in meta.keys())
1774         self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')    
1775     
1776     def test_list_meta_filtering(self):
1777         self.client.create_container('φάκελος')
1778         meta = {'ποιότητα':'ΑΑΑ'}
1779         self.upload_random_data('φάκελος', 'ο1', **meta)
1780         self.upload_random_data('φάκελος', 'ο2')
1781         self.upload_random_data('φάκελος', 'ο3')
1782         
1783         meta = {'ποσότητα':'μεγάλη'}
1784         self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1785         objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1786         self.assertEquals(objects, ['ο1', 'ο2'])
1787         
1788         objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
1789         self.assertEquals(objects, ['ο2', 'ο3'])
1790         
1791         objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
1792         self.assertEquals(objects, ['ο3'])
1793         
1794         meta = {'ποιότητα':'ΑΒ'}
1795         self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1796         objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
1797         self.assertEquals(objects, ['ο1'])
1798         objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
1799         self.assertEquals(objects, ['ο2'])
1800         
1801         meta = {'έτος':'2011'}
1802         self.client.update_object_metadata('φάκελος', 'ο3', **meta)
1803         meta = {'έτος':'2012'}
1804         self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1805         objects = self.client.list_objects('φάκελος', meta='έτος<2012')
1806         self.assertEquals(objects, ['ο3'])
1807         objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
1808         self.assertEquals(objects, ['ο2', 'ο3'])
1809         objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
1810         self.assertEquals(objects, '')
1811     
1812     def test_groups(self):
1813         #create a group
1814         groups = {'σεφς':'chazapis,διογένης'}
1815         self.client.set_account_groups(**groups)
1816         groups.update(self.initial_groups)
1817         self.assertEqual(groups['σεφς'],
1818                          self.client.retrieve_account_groups()['σεφς'])
1819         
1820         #check read access
1821         self.client.create_container('φάκελος')
1822         o = self.upload_random_data('φάκελος', 'ο1')
1823         self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1824         chef = Pithos_Client(get_server(),
1825                             '0009',
1826                             'διογένης',
1827                             get_api())
1828         self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1829                                      'φάκελος', 'ο1', account=get_user())
1830         
1831         #check write access
1832         self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1833         new_data = get_random_data()
1834         self.assert_not_raises_fault(403, chef.update_object,
1835                                      'φάκελος', 'ο1', StringIO(new_data),
1836                                      account=get_user())
1837         
1838         server_data = self.client.retrieve_object('φάκελος', 'ο1')
1839         self.assertEqual(server_data[:len(o['data'])], o['data'])
1840         self.assertEqual(server_data[len(o['data']):], new_data)
1841     
1842     def test_manifestation(self):
1843         self.client.create_container('κουβάς')
1844         prefix = 'μέρη/'
1845         data = ''
1846         for i in range(5):
1847             part = '%s%d' %(prefix, i)
1848             o = self.upload_random_data('κουβάς', part)
1849             data += o['data']
1850         
1851         self.client.create_container('φάκελος')
1852         manifest = '%s/%s' %('κουβάς', prefix)
1853         self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1854         
1855         self.assert_object_exists('φάκελος', 'άπαντα')
1856         self.assertEqual(data, self.client.retrieve_object('φάκελος',
1857                                                            'άπαντα'))
1858         
1859         #wrong manifestation
1860         self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1861         self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1862     
1863     def test_update_from_another_object(self):
1864         self.client.create_container('κουβάς')
1865         src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1866         initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1867         source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1868         self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1869         
1870         self.assertEqual(
1871             self.client.retrieve_object('κουβάς', 'νέο'),
1872             '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1873
1874 class TestPermissions(BaseTestCase):
1875     def setUp(self):
1876         BaseTestCase.setUp(self)
1877         
1878         #create a group
1879         self.authorized = ['chazapis', 'verigak', 'gtsouk']
1880         groups = {'pithosdev':','.join(self.authorized)}
1881         self.client.set_account_groups(**groups)
1882     
1883     def assert_read(self, authorized=[], any=False):
1884         for token, account in OTHER_ACCOUNTS.items():
1885             cl = Pithos_Client(get_server(), token, account, get_api()) 
1886             if account in authorized or any:
1887                 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1888                                              'c', 'o', account=get_user())
1889             else:
1890                 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1891                                          'c', 'o', account=get_user())
1892         
1893         #check inheritance
1894         o = self.upload_random_data('c', 'o/also-shared')
1895         for token, account in OTHER_ACCOUNTS.items():
1896             cl = Pithos_Client(get_server(), token, account, get_api()) 
1897             if account in authorized or any:
1898                 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1899                                              'c', 'o/also-shared', account=get_user())
1900             else:
1901                 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1902                                          'c', 'o/also-shared', account=get_user())
1903     
1904     def assert_write(self, o_data, authorized=[], any=False):
1905         for token, account in OTHER_ACCOUNTS.items():
1906             cl = Pithos_Client(get_server(), token, account, get_api()) 
1907             new_data = get_random_data()
1908             if account in authorized or any:
1909                 # test write access
1910                 self.assert_not_raises_fault(403, cl.update_object,
1911                                              'c', 'o', StringIO(new_data),
1912                                              account=get_user())
1913                 try:
1914                     # test read access
1915                     server_data = cl.retrieve_object('c', 'o', account=get_user())
1916                     self.assertEqual(o_data, server_data[:len(o_data)])
1917                     self.assertEqual(new_data, server_data[len(o_data):])
1918                     o_data = server_data
1919                 except Fault, f:
1920                     self.failIf(f.status == 403)
1921             else:
1922                 self.assert_raises_fault(403, cl.update_object,
1923                                              'c', 'o', StringIO(new_data),
1924                                              account=get_user())
1925         
1926         #check inheritance
1927         o = self.upload_random_data('c', 'o/also-shared')
1928         o_data = o['data']
1929         for token, account in OTHER_ACCOUNTS.items():
1930             cl = Pithos_Client(get_server(), token, account, get_api()) 
1931             new_data = get_random_data()
1932             if account in authorized or any:
1933                 # test write access
1934                 self.assert_not_raises_fault(403, cl.update_object,
1935                                              'c', o['name'],
1936                                              StringIO(new_data),
1937                                              account=get_user())
1938                 try:
1939                     server_data = cl.retrieve_object('c', o['name'], account=get_user())
1940                     self.assertEqual(o_data, server_data[:len(o_data)])
1941                     self.assertEqual(new_data, server_data[len(o_data):])
1942                     o_data = server_data
1943                 except Fault, f:
1944                     self.failIf(f.status == 403)
1945             else:
1946                 self.assert_raises_fault(403, cl.update_object,
1947                                              'c', o['name'],
1948                                              StringIO(new_data),
1949                                              account=get_user())
1950     
1951     def test_group_read(self):
1952         self.client.create_container('c')
1953         o = self.upload_random_data('c', 'o')
1954         self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1955         self.assert_read(authorized=self.authorized)
1956     
1957     def test_read_many(self):
1958         #test read access
1959         self.client.create_container('c')
1960         o = self.upload_random_data('c', 'o')
1961         self.client.share_object('c', 'o', self.authorized)
1962         self.assert_read(authorized=self.authorized)
1963     
1964     def test_read_by_everyone(self):
1965         self.client.create_container('c')
1966         o = self.upload_random_data('c', 'o')
1967         self.client.share_object('c', 'o', ['*'])
1968         self.assert_read(any=True)
1969     
1970     def test_group_write(self):
1971         self.client.create_container('c')
1972         o = self.upload_random_data('c', 'o')
1973         self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1974         self.assert_write(o['data'], authorized=self.authorized)
1975     
1976     def test_write_many(self):
1977         self.client.create_container('c')
1978         o = self.upload_random_data('c', 'o')
1979         self.client.share_object('c', 'o', self.authorized, read=False)
1980         self.assert_write(o['data'], authorized=self.authorized)
1981     
1982     def test_write_by_everyone(self):
1983         self.client.create_container('c')
1984         o = self.upload_random_data('c', 'o')
1985         self.client.share_object('c', 'o', ['*'], read=False)
1986         o_data = o['data']
1987         self.assert_write(o['data'], any=True)
1988
1989 class TestPublish(BaseTestCase):
1990     def test_publish(self):
1991         self.client.create_container('c')
1992         o_data = self.upload_random_data('c', 'o')['data']
1993         self.client.publish_object('c', 'o')
1994         meta = self.client.retrieve_object_metadata('c', 'o')
1995         self.assertTrue('x-object-public' in meta)
1996         url = meta['x-object-public']
1997         public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
1998         data = public_client.get(url)[2]
1999         self.assertEqual(o_data, data)
2000
2001 class AssertMappingInvariant(object):
2002     def __init__(self, callable, *args, **kwargs):
2003         self.callable = callable
2004         self.args = args
2005         self.kwargs = kwargs
2006     
2007     def __enter__(self):
2008         self.map = self.callable(*self.args, **self.kwargs)
2009         return self.map
2010     
2011     def __exit__(self, type, value, tb):
2012         map = self.callable(*self.args, **self.kwargs)
2013         for k in self.map.keys():
2014             if is_date(self.map[k]):
2015                 continue
2016             assert map[k] == self.map[k]
2017
2018 class AssertContentInvariant(object):
2019     def __init__(self, callable, *args, **kwargs):
2020         self.callable = callable
2021         self.args = args
2022         self.kwargs = kwargs
2023     
2024     def __enter__(self):
2025         self.content = self.callable(*self.args, **self.kwargs)[2]
2026         return self.content
2027     
2028     def __exit__(self, type, value, tb):
2029         content = self.callable(*self.args, **self.kwargs)[2]
2030         assert self.content == content
2031
2032 def get_content_splitted(response):
2033     if response:
2034         return response.content.split('\n')
2035
2036 def compute_md5_hash(data):
2037     md5 = hashlib.md5()
2038     offset = 0
2039     md5.update(data)
2040     return md5.hexdigest().lower()
2041
2042 def compute_block_hash(data, algorithm):
2043     h = hashlib.new(algorithm)
2044     h.update(data.rstrip('\x00'))
2045     return h.hexdigest()
2046
2047 def get_random_data(length=500):
2048     char_set = string.ascii_uppercase + string.digits
2049     return ''.join(random.choice(char_set) for x in range(length))
2050
2051 def is_date(date):
2052     MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2053     __D = r'(?P<day>\d{2})'
2054     __D2 = r'(?P<day>[ \d]\d)'
2055     __M = r'(?P<mon>\w{3})'
2056     __Y = r'(?P<year>\d{4})'
2057     __Y2 = r'(?P<year>\d{2})'
2058     __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2059     RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2060     RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2061     ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2062     for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2063         m = regex.match(date)
2064         if m is not None:
2065             return True
2066     return False
2067
2068 o_names = ['kate.jpg',
2069            'kate_beckinsale.jpg',
2070            'How To Win Friends And Influence People.pdf',
2071            'moms_birthday.jpg',
2072            'poodle_strut.mov',
2073            'Disturbed - Down With The Sickness.mp3',
2074            'army_of_darkness.avi',
2075            'the_mad.avi',
2076            'photos/animals/dogs/poodle.jpg',
2077            'photos/animals/dogs/terrier.jpg',
2078            'photos/animals/cats/persian.jpg',
2079            'photos/animals/cats/siamese.jpg',
2080            'photos/plants/fern.jpg',
2081            'photos/plants/rose.jpg',
2082            'photos/me.jpg']
2083
2084 if __name__ == "__main__":
2085     if get_user() == 'test':
2086         unittest.main()
2087     else:
2088         print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()