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