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