4 # Copyright 2011 GRNET S.A. All rights reserved.
6 # Redistribution and use in source and binary forms, with or
7 # without modification, are permitted provided that the following
10 # 1. Redistributions of source code must retain the above
11 # copyright notice, this list of conditions and the following
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.
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.
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.
37 from lib.client import Pithos_Client, Fault
38 from lib.util import get_user, get_auth, get_server, get_api
39 from xml.dom import minidom
40 from StringIO import StringIO
52 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
53 "%A, %d-%b-%y %H:%M:%S GMT",
54 "%a, %d %b %Y %H:%M:%S GMT"]
67 class BaseTestCase(unittest.TestCase):
68 #TODO unauthorized request
70 self.client = Pithos_Client(get_server(), get_auth(), get_user(),
73 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
76 # 'account': ('x-account-container-count',
77 # 'x-account-bytes-used',
89 # 'x-object-manifest',
91 # 'x-object-modified-by',
93 # 'x-object-version-timestamp',
95 # 'container': ('x-container-object-count',
96 # 'x-container-bytes-used',
101 # 'x-container-block-size',
102 # 'x-container-block-hash',
103 # 'x-container-policy-quota',
104 # 'x-container-policy-versioning',
106 # 'x-container-object-meta',
107 # 'x-container-policy-versioning',
110 #self.contentTypes = {'xml':'application/xml',
111 # 'json':'application/json',
119 'x_container_policy_quota',
120 'x_container_policy_versioning',),
128 self.return_codes = (400, 401, 403, 404, 503,)
131 self._clean_account()
133 def _clean_account(self):
134 for c in self.client.list_containers():
136 #list objects returns at most 10000 objects
137 #so repeat until there are no more objects
138 objects = self.client.list_objects(c)
142 self.client.delete_object(c, o)
143 self.client.delete_container(c)
145 def assert_status(self, status, codes):
146 l = [elem for elem in self.return_codes]
147 if type(codes) == types.ListType:
151 self.assertTrue(status in l)
153 #def assert_headers(self, headers, type, **exp_meta):
154 # prefix = 'x-%s-meta-' %type
155 # system_headers = [h for h in headers if not h.startswith(prefix)]
156 # for k,v in headers.items():
157 # if k in system_headers:
158 # self.assertTrue(k in headers[type])
160 # k = k.split(prefix)[-1]
161 # self.assertEqual(v, exp_meta[k])
163 def assert_extended(self, data, format, type, size=10000):
165 self._assert_xml(data, type, size)
166 elif format == 'json':
167 self._assert_json(data, type, size)
169 def _assert_json(self, data, type, size):
170 convert = lambda s: s.lower()
171 info = [convert(elem) for elem in self.extended[type]]
172 self.assertTrue(len(data) <= size)
175 if 'subdir' in i.keys():
177 self.assertTrue(item in i.keys())
179 def _assert_xml(self, data, type, size):
180 convert = lambda s: s.lower()
181 info = [convert(elem) for elem in self.extended[type]]
183 info.remove('content_encoding')
187 entities = xml.getElementsByTagName(type)
188 self.assertTrue(len(entities) <= size)
191 self.assertTrue(e.getElementsByTagName(item))
193 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
195 asserts that a Fault with a specific status is raised
196 when callableObj is called with the specific arguments
199 r = callableObj(*args, **kwargs)
200 self.fail('Should never reach here')
202 if type(status) == types.ListType:
203 self.failUnless(f.status in status)
205 self.failUnless(f.status == status)
207 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
209 asserts that a Fault with a specific status is not raised
210 when callableObj is called with the specific arguments
213 r = callableObj(*args, **kwargs)
215 self.failIfEqual(f.status, status)
217 def assert_container_exists(self, container):
219 asserts the existence of a container
222 self.client.retrieve_container_metadata(container)
224 self.failIf(f.status == 404)
226 def assert_container_not_exists(self, container):
228 asserts there is no such a container
230 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
233 def assert_object_exists(self, container, object):
235 asserts the existence of an object
238 self.client.retrieve_object_metadata(container, object)
240 self.failIf(f.status == 404)
242 def assert_object_not_exists(self, container, object):
244 asserts there is no such an object
246 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
249 def assert_versionlist_structure(self, versionlist):
250 self.assertTrue(type(versionlist) == types.ListType)
251 for elem in versionlist:
252 self.assertTrue(type(elem) == types.ListType)
253 self.assertEqual(len(elem), 2)
255 def upload_random_data(self, container, name, length=1024, type=None,
257 data = get_random_data(length)
258 return self.upload_data(container, name, data, type, enc, **meta)
260 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
266 obj['hash'] = compute_md5_hash(obj['data'])
269 args['etag'] = etag if etag else obj['hash']
272 guess = mimetypes.guess_type(name)
273 type = type if type else guess[0]
274 enc = enc if enc else guess[1]
277 args['content_type'] = type if type else 'plain/text'
278 args['content_encoding'] = enc if enc else None
282 path = '/%s/%s' % (container, name)
283 self.client.create_object(container, name, f=StringIO(obj['data']),
290 class AccountHead(BaseTestCase):
292 BaseTestCase.setUp(self)
293 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
294 for item in self.containers:
295 self.client.create_container(item)
297 #keep track of initial account groups
298 self.initial_groups = self.client.retrieve_account_groups()
300 #keep track of initial account meta
301 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
304 self.client.update_account_metadata(**meta)
305 self.updated_meta = self.initial_meta.update(meta)
308 #delete additionally created meta
310 for m in self.client.retrieve_account_metadata(restricted=True):
311 if m not in self.initial_meta:
313 self.client.delete_account_metadata(l)
315 #delete additionally created groups
317 for g in self.client.retrieve_account_groups():
318 if g not in self.initial_groups:
320 self.client.unset_account_groups(l)
322 BaseTestCase.tearDown(self)
324 def test_get_account_meta(self):
325 meta = self.client.retrieve_account_metadata()
327 containers = self.client.list_containers()
328 l = str(len(containers))
329 self.assertEqual(meta['x-account-container-count'], l)
332 m = self.client.retrieve_container_metadata(c)
333 size = size + int(m['x-container-bytes-used'])
334 self.assertEqual(meta['x-account-bytes-used'], str(size))
336 def test_get_account_403(self):
337 self.assert_raises_fault(403,
338 self.invalid_client.retrieve_account_metadata)
340 def test_get_account_meta_until(self):
341 t = datetime.datetime.utcnow()
342 past = t - datetime.timedelta(minutes=-15)
343 past = int(_time.mktime(past.timetuple()))
345 meta = {'premium':True}
346 self.client.update_account_metadata(**meta)
347 meta = self.client.retrieve_account_metadata(restricted=True,
349 self.assertTrue('premium' not in meta)
351 meta = self.client.retrieve_account_metadata(restricted=True)
352 self.assertTrue('premium' in meta)
354 def test_get_account_meta_until_invalid_date(self):
355 meta = {'premium':True}
356 self.client.update_account_metadata(**meta)
357 meta = self.client.retrieve_account_metadata(restricted=True,
359 self.assertTrue('premium' in meta)
361 class AccountGet(BaseTestCase):
363 BaseTestCase.setUp(self)
364 #create some containers
365 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
366 for item in self.containers:
367 self.client.create_container(item)
371 containers = self.client.list_containers()
372 self.assertEquals(self.containers, containers)
374 def test_list_403(self):
375 self.assert_raises_fault(403, self.invalid_client.list_containers)
377 def test_list_with_limit(self):
379 containers = self.client.list_containers(limit=limit)
380 self.assertEquals(len(containers), limit)
381 self.assertEquals(self.containers[:2], containers)
383 def test_list_with_marker(self):
386 containers = self.client.list_containers(limit=l, marker=m)
387 i = self.containers.index(m) + 1
388 self.assertEquals(self.containers[i:(i+l)], containers)
391 containers = self.client.list_containers(limit=l, marker=m)
392 i = self.containers.index(m) + 1
393 self.assertEquals(self.containers[i:(i+l)], containers)
395 def test_list_json_with_marker(self):
398 containers = self.client.list_containers(limit=l, marker=m, format='json')
399 self.assert_extended(containers, 'json', 'container', l)
400 self.assertEqual(containers[0]['name'], 'kiwis')
401 self.assertEqual(containers[1]['name'], 'oranges')
403 def test_list_xml_with_marker(self):
406 xml = self.client.list_containers(limit=l, marker=m, format='xml')
407 self.assert_extended(xml, 'xml', 'container', l)
408 nodes = xml.getElementsByTagName('name')
409 self.assertEqual(len(nodes), 1)
410 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
412 def test_if_modified_since(self):
413 t = datetime.datetime.utcnow()
414 t2 = t - datetime.timedelta(minutes=10)
417 self.client.create_container('dummy')
419 for f in DATE_FORMATS:
420 past = t2.strftime(f)
422 c = self.client.list_containers(if_modified_since=past)
423 self.assertEqual(len(c), len(self.containers) + 1)
425 self.failIf(f.status == 304) #fail if not modified
427 def test_if_modified_since_invalid_date(self):
428 c = self.client.list_containers(if_modified_since='')
429 self.assertEqual(len(c), len(self.containers))
431 def test_if_not_modified_since(self):
432 now = datetime.datetime.utcnow()
433 since = now + datetime.timedelta(1)
435 for f in DATE_FORMATS:
436 args = {'if_modified_since':'%s' %since.strftime(f)}
439 self.assert_raises_fault(304, self.client.list_containers, **args)
441 def test_if_unmodified_since(self):
442 now = datetime.datetime.utcnow()
443 since = now + datetime.timedelta(1)
445 for f in DATE_FORMATS:
446 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
449 self.assertEqual(self.containers, c)
451 def test_if_unmodified_since_precondition_failed(self):
452 t = datetime.datetime.utcnow()
453 t2 = t - datetime.timedelta(minutes=10)
456 self.client.create_container('dummy')
458 for f in DATE_FORMATS:
459 past = t2.strftime(f)
461 args = {'if_unmodified_since':'%s' %past}
463 #assert precondition failed
464 self.assert_raises_fault(412, self.client.list_containers, **args)
466 class AccountPost(BaseTestCase):
468 BaseTestCase.setUp(self)
469 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
470 for item in self.containers:
471 self.client.create_container(item)
473 #keep track of initial account groups
474 self.initial_groups = self.client.retrieve_account_groups()
476 #keep track of initial account meta
477 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
480 self.client.update_account_metadata(**meta)
481 self.updated_meta = self.initial_meta.update(meta)
484 #delete additionally created meta
486 for m in self.client.retrieve_account_metadata(restricted=True):
487 if m not in self.initial_meta:
489 self.client.delete_account_metadata(l)
491 #delete additionally created groups
493 for g in self.client.retrieve_account_groups():
494 if g not in self.initial_groups:
496 self.client.unset_account_groups(l)
498 BaseTestCase.tearDown(self)
500 def test_update_meta(self):
501 with AssertMappingInvariant(self.client.retrieve_account_groups):
502 meta = {'test':'test', 'tost':'tost'}
503 self.client.update_account_metadata(**meta)
505 meta.update(self.initial_meta)
506 self.assertEqual(meta,
507 self.client.retrieve_account_metadata(
510 def test_invalid_account_update_meta(self):
511 meta = {'test':'test', 'tost':'tost'}
512 self.assert_raises_fault(403,
513 self.invalid_client.update_account_metadata,
516 def test_reset_meta(self):
517 with AssertMappingInvariant(self.client.retrieve_account_groups):
518 meta = {'test':'test', 'tost':'tost'}
519 self.client.update_account_metadata(**meta)
521 meta = {'test':'test33'}
522 self.client.reset_account_metadata(**meta)
524 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
526 def test_delete_meta(self):
527 with AssertMappingInvariant(self.client.retrieve_account_groups):
528 meta = {'test':'test', 'tost':'tost'}
529 self.client.update_account_metadata(**meta)
531 self.client.delete_account_metadata(meta.keys())
533 account_meta = self.client.retrieve_account_metadata(restricted=True)
535 self.assertTrue(m not in account_meta.keys())
537 def test_set_account_groups(self):
538 with AssertMappingInvariant(self.client.retrieve_account_metadata):
539 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
540 self.client.set_account_groups(**groups)
542 self.assertEqual(set(groups['pithosdev']),
543 set(self.client.retrieve_account_groups()['pithosdev']))
545 more_groups = {'clientsdev':'pkanavos,mvasilak'}
546 self.client.set_account_groups(**more_groups)
548 groups.update(more_groups)
549 self.assertEqual(set(groups['clientsdev']),
550 set(self.client.retrieve_account_groups()['clientsdev']))
552 def test_reset_account_groups(self):
553 with AssertMappingInvariant(self.client.retrieve_account_metadata):
554 groups = {'pithosdev':'verigak,gtsouk,chazapis',
555 'clientsdev':'pkanavos,mvasilak'}
556 self.client.set_account_groups(**groups)
558 self.assertEqual(set(groups['pithosdev'].split(',')),
559 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
560 self.assertEqual(set(groups['clientsdev'].split(',')),
561 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
563 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
564 self.client.reset_account_groups(**groups)
566 self.assertEqual(set(groups['pithosdev'].split(',')),
567 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
569 def test_delete_account_groups(self):
570 with AssertMappingInvariant(self.client.retrieve_account_metadata):
571 groups = {'pithosdev':'verigak,gtsouk,chazapis',
572 'clientsdev':'pkanavos,mvasilak'}
573 self.client.set_account_groups(**groups)
575 self.client.unset_account_groups(groups.keys())
577 self.assertEqual({}, self.client.retrieve_account_groups())
579 class ContainerHead(BaseTestCase):
581 BaseTestCase.setUp(self)
582 self.container = 'apples'
583 self.client.create_container(self.container)
585 def test_get_meta(self):
586 meta = {'trash':'true'}
587 t1 = datetime.datetime.utcnow()
588 o = self.upload_random_data(self.container, o_names[0], **meta)
590 headers = self.client.retrieve_container_metadata(self.container)
591 self.assertEqual(headers['x-container-object-count'], '1')
592 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
593 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
595 threashold = datetime.timedelta(seconds=1)
596 self.assertTrue(delta < threashold)
597 self.assertTrue(headers['x-container-object-meta'])
598 self.assertTrue('Trash' in headers['x-container-object-meta'])
600 class ContainerGet(BaseTestCase):
602 BaseTestCase.setUp(self)
603 self.container = ['pears', 'apples']
604 for c in self.container:
605 self.client.create_container(c)
607 for o in o_names[:8]:
608 self.obj.append(self.upload_random_data(self.container[0], o))
609 for o in o_names[8:]:
610 self.obj.append(self.upload_random_data(self.container[1], o))
612 def test_list_objects(self):
613 objects = self.client.list_objects(self.container[0])
614 l = [elem['name'] for elem in self.obj[:8]]
616 self.assertEqual(objects, l)
618 def test_list_objects_containing_slash(self):
619 self.client.create_container('test')
620 self.upload_random_data('test', '/objectname')
622 objects = self.client.list_objects('test')
623 self.assertEqual(objects, ['/objectname'])
625 objects = self.client.list_objects('test', format='json')
626 self.assertEqual(objects[0]['name'], '/objectname')
628 objects = self.client.list_objects('test', format='xml')
629 self.assert_extended(objects, 'xml', 'object')
630 node_name = objects.getElementsByTagName('name')[0]
631 self.assertEqual(node_name.firstChild.data, '/objectname')
633 #objects = self.client.list_objects('test', prefix='/')
634 #self.assertEqual(objects, ['/objectname'])
636 #objects = self.client.list_objects('test', path='/')
637 #self.assertEqual(objects, ['/objectname'])
639 #objects = self.client.list_objects('test', prefix='/', delimiter='n')
640 #self.assertEqual(objects, ['/object'])
642 def test_list_objects_with_limit_marker(self):
643 objects = self.client.list_objects(self.container[0], limit=2)
644 l = [elem['name'] for elem in self.obj[:8]]
646 self.assertEqual(objects, l[:2])
648 markers = ['How To Win Friends And Influence People.pdf',
652 objects = self.client.list_objects(self.container[0], limit=limit,
654 l = [elem['name'] for elem in self.obj[:8]]
656 start = l.index(m) + 1
658 end = len(l) >= end and end or len(l)
659 self.assertEqual(objects, l[start:end])
662 def _test_list_limit_exceeds(self):
663 self.client.create_container('pithos')
665 for i in range(10001):
666 self.client.create_zero_length_object('pithos', i)
668 self.assertEqual(10000, len(self.client.list_objects('pithos')))
670 def test_list_empty_params(self):
671 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
673 objects = objects.strip().split('\n')
674 self.assertEqual(objects,
675 self.client.list_objects(self.container[0]))
677 def test_list_pseudo_hierarchical_folders(self):
678 objects = self.client.list_objects(self.container[1], prefix='photos',
680 self.assertEquals(['photos/animals/', 'photos/me.jpg',
681 'photos/plants/'], objects)
683 objects = self.client.list_objects(self.container[1],
684 prefix='photos/animals',
686 l = ['photos/animals/cats/', 'photos/animals/dogs/']
687 self.assertEquals(l, objects)
689 objects = self.client.list_objects(self.container[1], path='photos')
690 self.assertEquals(['photos/me.jpg'], objects)
692 def test_extended_list_json(self):
693 objects = self.client.list_objects(self.container[1], format='json',
694 limit=2, prefix='photos/animals',
696 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
697 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
699 def test_extended_list_xml(self):
700 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
701 prefix='photos', delimiter='/')
702 self.assert_extended(xml, 'xml', 'object', size=4)
703 dirs = xml.getElementsByTagName('subdir')
704 self.assertEqual(len(dirs), 2)
705 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
706 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
708 objects = xml.getElementsByTagName('name')
709 self.assertEqual(len(objects), 1)
710 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
712 def test_list_meta_double_matching(self):
713 meta = {'quality':'aaa', 'stock':'true'}
714 self.client.update_object_metadata(self.container[0],
715 self.obj[0]['name'], **meta)
716 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
717 self.assertEqual(len(obj), 1)
718 self.assertTrue(obj, self.obj[0]['name'])
720 def test_list_using_meta(self):
721 meta = {'quality':'aaa'}
722 for o in self.obj[:2]:
723 self.client.update_object_metadata(self.container[0], o['name'],
725 meta = {'stock':'true'}
726 for o in self.obj[3:5]:
727 self.client.update_object_metadata(self.container[0], o['name'],
730 obj = self.client.list_objects(self.container[0], meta='Quality')
731 self.assertEqual(len(obj), 2)
732 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
734 # test case insensitive
735 obj = self.client.list_objects(self.container[0], meta='quality')
736 self.assertEqual(len(obj), 2)
737 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
739 # test multiple matches
740 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
741 self.assertEqual(len(obj), 4)
742 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
744 # test non 1-1 multiple match
745 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
746 self.assertEqual(len(obj), 2)
747 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
749 def test_if_modified_since(self):
750 t = datetime.datetime.utcnow()
751 t2 = t - datetime.timedelta(minutes=10)
754 self.upload_random_data(self.container[0], o_names[0])
756 for f in DATE_FORMATS:
757 past = t2.strftime(f)
759 o = self.client.list_objects(self.container[0],
760 if_modified_since=past)
762 self.client.list_objects(self.container[0]))
764 self.failIf(f.status == 304) #fail if not modified
766 def test_if_modified_since_invalid_date(self):
767 headers = {'if-modified-since':''}
768 o = self.client.list_objects(self.container[0], if_modified_since='')
769 self.assertEqual(o, self.client.list_objects(self.container[0]))
771 def test_if_not_modified_since(self):
772 now = datetime.datetime.utcnow()
773 since = now + datetime.timedelta(1)
775 for f in DATE_FORMATS:
776 args = {'if_modified_since':'%s' %since.strftime(f)}
779 self.assert_raises_fault(304, self.client.list_objects,
780 self.container[0], **args)
782 def test_if_unmodified_since(self):
783 now = datetime.datetime.utcnow()
784 since = now + datetime.timedelta(1)
786 for f in DATE_FORMATS:
787 obj = self.client.list_objects(self.container[0],
788 if_unmodified_since=since.strftime(f))
791 self.assertEqual(obj, self.client.list_objects(self.container[0]))
793 def test_if_unmodified_since_precondition_failed(self):
794 t = datetime.datetime.utcnow()
795 t2 = t - datetime.timedelta(minutes=10)
798 self.client.create_container('dummy')
800 for f in DATE_FORMATS:
801 past = t2.strftime(f)
803 args = {'if_unmodified_since':'%s' %past}
805 #assert precondition failed
806 self.assert_raises_fault(412, self.client.list_objects,
807 self.container[0], **args)
809 class ContainerPut(BaseTestCase):
811 BaseTestCase.setUp(self)
812 self.containers = ['c1', 'c2']
814 def test_create(self):
815 self.client.create_container(self.containers[0])
816 containers = self.client.list_containers()
817 self.assertTrue(self.containers[0] in containers)
818 self.assert_container_exists(self.containers[0])
820 def test_create_twice(self):
821 self.client.create_container(self.containers[0])
822 self.assertTrue(not self.client.create_container(self.containers[0]))
824 def test_quota(self):
825 self.client.create_container(self.containers[0])
827 policy = {'quota':100}
828 self.client.set_container_policies('c1', **policy)
830 meta = self.client.retrieve_container_metadata('c1')
831 self.assertTrue('x-container-policy-quota' in meta)
832 self.assertEqual(meta['x-container-policy-quota'], '100')
835 kwargs = {'length':101}
836 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
840 self.client.set_container_policies('c1', **policy)
842 class ContainerPost(BaseTestCase):
844 BaseTestCase.setUp(self)
845 self.container = 'apples'
846 self.client.create_container(self.container)
848 def test_update_meta(self):
849 meta = {'test':'test33',
851 self.client.update_container_metadata(self.container, **meta)
852 headers = self.client.retrieve_container_metadata(self.container)
853 for k,v in meta.items():
854 k = 'x-container-meta-%s' % k
855 self.assertTrue(headers[k])
856 self.assertEqual(headers[k], v)
858 class ContainerDelete(BaseTestCase):
860 BaseTestCase.setUp(self)
861 self.containers = ['c1', 'c2']
862 for c in self.containers:
863 self.client.create_container(c)
865 def test_delete(self):
866 status = self.client.delete_container(self.containers[0])[0]
867 self.assertEqual(status, 204)
869 def test_delete_non_empty(self):
870 self.upload_random_data(self.containers[1], o_names[0])
871 self.assert_raises_fault(409, self.client.delete_container,
874 def test_delete_invalid(self):
875 self.assert_raises_fault(404, self.client.delete_container, 'c3')
877 class ObjectHead(BaseTestCase):
880 class ObjectGet(BaseTestCase):
882 BaseTestCase.setUp(self)
883 self.containers = ['c1', 'c2']
884 #create some containers
885 for c in self.containers:
886 self.client.create_container(c)
889 names = ('obj1', 'obj2')
892 self.objects.append(self.upload_random_data(self.containers[1], n))
894 def test_versions(self):
895 c = self.containers[1]
897 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
898 self.assert_versionlist_structure(b)
901 meta = {'quality':'AAA', 'stock':True}
902 self.client.update_object_metadata(c, o['name'], **meta)
904 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
905 self.assert_versionlist_structure(a)
906 self.assertEqual(len(b)+1, len(a))
907 self.assertEqual(b, a[:-1])
909 #get exact previous version metadata
911 v_meta = self.client.retrieve_object_metadata(c, o['name'],
914 for k in meta.keys():
915 self.assertTrue(k not in v_meta)
918 data = get_random_data()
919 self.client.update_object(c, o['name'], StringIO(data))
921 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
922 self.assert_versionlist_structure(aa)
923 self.assertEqual(len(a)+1, len(aa))
924 self.assertEqual(a, aa[:-1])
926 #get exact previous version
928 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
929 self.assertEqual(o['data'], v_data)
930 self.assertEqual(self.client.retrieve_object(c, o['name']),
931 '%s%s' %(v_data, data))
935 o = self.client.retrieve_object(self.containers[1],
936 self.objects[0]['name'],
937 self.objects[0]['meta'])
938 self.assertEqual(o, self.objects[0]['data'])
940 def test_get_invalid(self):
941 self.assert_raises_fault(404, self.client.retrieve_object,
942 self.containers[0], self.objects[0]['name'])
944 def test_get_partial(self):
945 #perform get with range
946 status, headers, data = self.client.request_object(self.containers[1],
947 self.objects[0]['name'],
950 #assert successful partial content
951 self.assertEqual(status, 206)
954 self.assertEqual(headers['content-type'],
955 self.objects[0]['meta']['content_type'])
957 #assert content length
958 self.assertEqual(int(headers['content-length']), 500)
961 self.assertEqual(self.objects[0]['data'][:500], data)
963 def test_get_final_500(self):
964 #perform get with range
965 headers = {'range':'bytes=-500'}
966 status, headers, data = self.client.request_object(self.containers[1],
967 self.objects[0]['name'],
970 #assert successful partial content
971 self.assertEqual(status, 206)
974 self.assertEqual(headers['content-type'],
975 self.objects[0]['meta']['content_type'])
977 #assert content length
978 self.assertEqual(int(headers['content-length']), 500)
981 self.assertTrue(self.objects[0]['data'][-500:], data)
983 def test_get_rest(self):
984 #perform get with range
985 offset = len(self.objects[0]['data']) - 500
986 status, headers, data = self.client.request_object(self.containers[1],
987 self.objects[0]['name'],
988 range='bytes=%s-' %offset)
990 #assert successful partial content
991 self.assertEqual(status, 206)
994 self.assertEqual(headers['content-type'],
995 self.objects[0]['meta']['content_type'])
997 #assert content length
998 self.assertEqual(int(headers['content-length']), 500)
1001 self.assertTrue(self.objects[0]['data'][-500:], data)
1003 def test_get_range_not_satisfiable(self):
1004 #perform get with range
1005 offset = len(self.objects[0]['data']) + 1
1007 #assert range not satisfiable
1008 self.assert_raises_fault(416, self.client.retrieve_object,
1009 self.containers[1], self.objects[0]['name'],
1010 range='bytes=0-%s' %offset)
1012 def test_multiple_range(self):
1013 #perform get with multiple range
1014 ranges = ['0-499', '-500', '1000-']
1015 bytes = 'bytes=%s' % ','.join(ranges)
1016 status, headers, data = self.client.request_object(self.containers[1],
1017 self.objects[0]['name'],
1020 # assert partial content
1021 self.assertEqual(status, 206)
1023 # assert Content-Type of the reply will be multipart/byteranges
1024 self.assertTrue(headers['content-type'])
1025 content_type_parts = headers['content-type'].split()
1026 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1028 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1029 cparts = data.split(boundary)[1:-1]
1031 # assert content parts are exactly 2
1032 self.assertEqual(len(cparts), len(ranges))
1034 # for each content part assert headers
1036 for cpart in cparts:
1037 content = cpart.split('\r\n')
1038 headers = content[1:3]
1039 content_range = headers[0].split(': ')
1040 self.assertEqual(content_range[0], 'Content-Range')
1042 r = ranges[i].split('-')
1043 if not r[0] and not r[1]:
1046 start = len(self.objects[0]['data']) - int(r[1])
1047 end = len(self.objects[0]['data'])
1050 end = len(self.objects[0]['data'])
1054 fdata = self.objects[0]['data'][start:end]
1055 sdata = '\r\n'.join(content[4:-1])
1056 self.assertEqual(len(fdata), len(sdata))
1057 self.assertEquals(fdata, sdata)
1060 def test_multiple_range_not_satisfiable(self):
1061 #perform get with multiple range
1062 out_of_range = len(self.objects[0]['data']) + 1
1063 ranges = ['0-499', '-500', '%d-' %out_of_range]
1064 bytes = 'bytes=%s' % ','.join(ranges)
1066 # assert partial content
1067 self.assert_raises_fault(416, self.client.retrieve_object,
1069 self.objects[0]['name'], range=bytes)
1071 def test_get_with_if_match(self):
1072 #perform get with If-Match
1073 etag = self.objects[0]['hash']
1074 status, headers, data = self.client.request_object(self.containers[1],
1075 self.objects[0]['name'],
1078 self.assertEqual(status, 200)
1080 #assert content-type
1081 self.assertEqual(headers['content-type'],
1082 self.objects[0]['meta']['content_type'])
1084 #assert response content
1085 self.assertEqual(self.objects[0]['data'], data)
1087 def test_get_with_if_match_star(self):
1088 #perform get with If-Match *
1089 headers = {'if-match':'*'}
1090 status, headers, data = self.client.request_object(self.containers[1],
1091 self.objects[0]['name'],
1094 self.assertEqual(status, 200)
1096 #assert content-type
1097 self.assertEqual(headers['content-type'],
1098 self.objects[0]['meta']['content_type'])
1100 #assert response content
1101 self.assertEqual(self.objects[0]['data'], data)
1103 def test_get_with_multiple_if_match(self):
1104 #perform get with If-Match
1105 etags = [i['hash'] for i in self.objects if i]
1106 etags = ','.join('"%s"' % etag for etag in etags)
1107 status, headers, data = self.client.request_object(self.containers[1],
1108 self.objects[0]['name'],
1111 self.assertEqual(status, 200)
1113 #assert content-type
1114 self.assertEqual(headers['content-type'],
1115 self.objects[0]['meta']['content_type'])
1117 #assert content-type
1118 self.assertEqual(headers['content-type'],
1119 self.objects[0]['meta']['content_type'])
1121 #assert response content
1122 self.assertEqual(self.objects[0]['data'], data)
1124 def test_if_match_precondition_failed(self):
1125 #assert precondition failed
1126 self.assert_raises_fault(412, self.client.retrieve_object,
1128 self.objects[0]['name'], if_match='123')
1130 def test_if_none_match(self):
1131 #perform get with If-None-Match
1132 status, headers, data = self.client.request_object(self.containers[1],
1133 self.objects[0]['name'],
1134 if_none_match='123')
1137 self.assertEqual(status, 200)
1139 #assert content-type
1140 self.assertEqual(headers['content_type'],
1141 self.objects[0]['meta']['content_type'])
1143 def test_if_none_match(self):
1144 #perform get with If-None-Match * and assert not modified
1145 self.assert_raises_fault(304, self.client.retrieve_object,
1147 self.objects[0]['name'],
1150 def test_if_none_match_not_modified(self):
1151 #perform get with If-None-Match and assert not modified
1152 self.assert_raises_fault(304, self.client.retrieve_object,
1154 self.objects[0]['name'],
1155 if_none_match=self.objects[0]['hash'])
1157 meta = self.client.retrieve_object_metadata(self.containers[1],
1158 self.objects[0]['name'])
1159 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1161 def test_if_modified_since(self):
1162 t = datetime.datetime.utcnow()
1163 t2 = t - datetime.timedelta(minutes=10)
1166 self.upload_data(self.containers[1],
1167 self.objects[0]['name'],
1168 self.objects[0]['data'][:200])
1170 for f in DATE_FORMATS:
1171 past = t2.strftime(f)
1173 headers = {'if-modified-since':'%s' %past}
1175 o = self.client.retrieve_object(self.containers[1],
1176 self.objects[0]['name'],
1177 if_modified_since=past)
1179 self.client.retrieve_object(self.containers[1],
1180 self.objects[0]['name']))
1182 self.failIf(f.status == 304)
1184 def test_if_modified_since_invalid_date(self):
1185 o = self.client.retrieve_object(self.containers[1],
1186 self.objects[0]['name'],
1187 if_modified_since='')
1188 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1189 self.objects[0]['name']))
1191 def test_if_not_modified_since(self):
1192 now = datetime.datetime.utcnow()
1193 since = now + datetime.timedelta(1)
1195 for f in DATE_FORMATS:
1196 #assert not modified
1197 self.assert_raises_fault(304, self.client.retrieve_object,
1198 self.containers[1], self.objects[0]['name'],
1199 if_modified_since=since.strftime(f))
1201 def test_if_unmodified_since(self):
1202 now = datetime.datetime.utcnow()
1203 since = now + datetime.timedelta(1)
1205 for f in DATE_FORMATS:
1206 t = since.strftime(f)
1207 status, headers, data = self.client.request_object(self.containers[1],
1208 self.objects[0]['name'],
1209 if_unmodified_since=t)
1211 self.assertEqual(status, 200)
1212 self.assertEqual(self.objects[0]['data'], data)
1214 #assert content-type
1215 self.assertEqual(headers['content-type'],
1216 self.objects[0]['meta']['content_type'])
1218 def test_if_unmodified_since_precondition_failed(self):
1219 t = datetime.datetime.utcnow()
1220 t2 = t - datetime.timedelta(minutes=10)
1223 self.upload_data(self.containers[1],
1224 self.objects[0]['name'],
1225 self.objects[0]['data'][:200])
1227 for f in DATE_FORMATS:
1228 past = t2.strftime(f)
1229 #assert precondition failed
1230 self.assert_raises_fault(412, self.client.retrieve_object,
1231 self.containers[1], self.objects[0]['name'],
1232 if_unmodified_since=past)
1234 def test_hashes(self):
1237 o = self.upload_random_data(self.containers[1], fname, l)
1239 body = self.client.retrieve_object(self.containers[1], fname,
1241 hashes = body['hashes']
1242 block_size = body['block_size']
1243 block_hash = body['block_hash']
1244 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1245 self.assertTrue(len(hashes), block_num)
1248 start = i * block_size
1249 end = (i + 1) * block_size
1250 hash = compute_block_hash(o['data'][start:end], block_hash)
1251 self.assertEqual(h, hash)
1254 class ObjectPut(BaseTestCase):
1256 BaseTestCase.setUp(self)
1257 self.container = 'c1'
1258 self.client.create_container(self.container)
1260 def test_upload(self):
1262 meta = {'test':'test1'}
1263 o = self.upload_random_data(self.container, name, **meta)
1265 headers = self.client.retrieve_object_metadata(self.container,
1268 self.assertTrue('test' in headers.keys())
1269 self.assertEqual(headers['test'], meta['test'])
1271 #assert uploaded content
1272 status, h, data = self.client.request_object(self.container, name)
1273 self.assertEqual(len(o['data']), int(h['content-length']))
1274 self.assertEqual(o['data'], data)
1276 #assert content-type
1277 self.assertEqual(h['content-type'], o['meta']['content_type'])
1279 def _test_maximum_upload_size_exceeds(self):
1281 meta = {'test':'test1'}
1283 length=1024*1024*100
1284 self.assert_raises_fault(400, self.upload_random_data, self.container,
1285 name, length, **meta)
1287 def test_upload_with_name_containing_slash(self):
1288 name = '/%s' % o_names[0]
1289 meta = {'test':'test1'}
1290 o = self.upload_random_data(self.container, name, **meta)
1292 self.assertEqual(o['data'],
1293 self.client.retrieve_object(self.container, name))
1295 self.assertTrue(name in self.client.list_objects(self.container))
1297 def test_create_directory_marker(self):
1298 self.client.create_directory_marker(self.container, 'foo')
1299 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1300 self.assertEqual(meta['content-length'], '0')
1301 self.assertEqual(meta['content-type'], 'application/directory')
1303 def test_upload_unprocessable_entity(self):
1304 meta={'etag':'123', 'test':'test1'}
1306 #assert unprocessable entity
1307 self.assert_raises_fault(422, self.upload_random_data, self.container,
1310 def test_chunked_transfer(self):
1311 data = get_random_data()
1313 self.client.create_object_using_chunks(self.container, objname,
1316 uploaded_data = self.client.retrieve_object(self.container, objname)
1317 self.assertEqual(data, uploaded_data)
1319 def test_manifestation(self):
1320 prefix = 'myobject/'
1323 part = '%s%d' %(prefix, i)
1324 o = self.upload_random_data(self.container, part)
1327 manifest = '%s/%s' %(self.container, prefix)
1328 self.client.create_manifestation(self.container, 'large-object', manifest)
1330 self.assert_object_exists(self.container, 'large-object')
1331 self.assertEqual(data, self.client.retrieve_object(self.container,
1334 #wrong manifestation
1335 self.client.create_manifestation(self.container, 'large-object',
1336 '%s/invalid' % self.container)
1337 self.assertEqual('', self.client.retrieve_object(self.container,
1340 def test_create_zero_length_object(self):
1343 zero = self.client.create_zero_length_object(c, o)
1344 zero_meta = self.client.retrieve_object_metadata(c, o)
1345 zero_hash = self.client.retrieve_object_hashmap(c, o)
1346 zero_data = self.client.retrieve_object(c, o)
1348 self.assertEqual(int(zero_meta['content-length']), 0)
1349 self.assertEqual(zero_hash, [])
1350 self.assertEqual(zero_data, '')
1352 def test_create_object_by_hashmap(self):
1355 self.upload_random_data(c, o)
1356 hashmap = self.client.retrieve_object(c, o, format='json')
1358 self.client.create_object_by_hashmap(c, o2, hashmap)
1359 self.assertEqual(self.client.retrieve_object(c, o),
1360 self.client.retrieve_object(c, o))
1362 class ObjectCopy(BaseTestCase):
1364 BaseTestCase.setUp(self)
1365 self.containers = ['c1', 'c2']
1366 for c in self.containers:
1367 self.client.create_container(c)
1368 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1370 def test_copy(self):
1371 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1372 self.containers[0], self.obj['name']):
1374 meta = {'test':'testcopy'}
1375 status = self.client.copy_object(self.containers[0],
1381 #assert copy success
1382 self.assertEqual(status, 201)
1384 #assert access the new object
1385 headers = self.client.retrieve_object_metadata(self.containers[0],
1387 self.assertTrue('x-object-meta-test' in headers.keys())
1388 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1390 #assert etag is the same
1391 self.assertEqual(headers['etag'], self.obj['hash'])
1393 #assert src object still exists
1394 self.assert_object_exists(self.containers[0], self.obj['name'])
1396 def test_copy_from_different_container(self):
1397 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1398 self.containers[0], self.obj['name']):
1399 meta = {'test':'testcopy'}
1400 status = self.client.copy_object(self.containers[0],
1405 self.assertEqual(status, 201)
1407 # assert updated metadata
1408 meta = self.client.retrieve_object_metadata(self.containers[1],
1411 self.assertTrue('test' in meta.keys())
1412 self.assertTrue(meta['test'], 'testcopy')
1414 #assert src object still exists
1415 self.assert_object_exists(self.containers[0], self.obj['name'])
1417 def test_copy_invalid(self):
1418 #copy from invalid object
1419 meta = {'test':'testcopy'}
1420 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1421 'test.py', self.containers[1], 'testcopy', meta)
1423 #copy from invalid container
1424 meta = {'test':'testcopy'}
1425 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1426 self.obj['name'], self.containers[1],
1429 class ObjectMove(BaseTestCase):
1431 BaseTestCase.setUp(self)
1432 self.containers = ['c1', 'c2']
1433 for c in self.containers:
1434 self.client.create_container(c)
1435 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1437 def test_move(self):
1439 meta = {'test':'testcopy'}
1440 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1441 status = self.client.move_object(self.containers[0], self.obj['name'],
1442 self.containers[0], 'testcopy',
1445 #assert successful move
1446 self.assertEqual(status, 201)
1448 #assert updated metadata
1449 meta = self.client.retrieve_object_metadata(self.containers[0],
1452 self.assertTrue('test' in meta.keys())
1453 self.assertTrue(meta['test'], 'testcopy')
1455 #assert src object no more exists
1456 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1458 class ObjectPost(BaseTestCase):
1460 BaseTestCase.setUp(self)
1461 self.containers = ['c1', 'c2']
1462 for c in self.containers:
1463 self.client.create_container(c)
1466 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1468 def test_update_meta(self):
1469 #perform update metadata
1470 more = {'foo':'foo', 'bar':'bar'}
1471 status = self.client.update_object_metadata(self.containers[0],
1472 self.obj[0]['name'],
1474 #assert request accepted
1475 self.assertEqual(status, 202)
1477 #assert old metadata are still there
1478 headers = self.client.retrieve_object_metadata(self.containers[0],
1479 self.obj[0]['name'],
1481 #assert new metadata have been updated
1482 for k,v in more.items():
1483 self.assertTrue(k in headers.keys())
1484 self.assertTrue(headers[k], v)
1486 def test_update_object(self,
1489 instance_length = True,
1490 content_length = 500):
1491 l = len(self.obj[0]['data'])
1492 range = 'bytes %d-%d/%s' %(first_byte_pos,
1494 l if instance_length else '*')
1495 partial = last_byte_pos - first_byte_pos + 1
1496 length = first_byte_pos + partial
1497 data = get_random_data(partial)
1498 args = {'content_type':'application/octet-stream',
1499 'content_range':'%s' %range}
1501 args['content_length'] = content_length
1503 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1504 StringIO(data), **args)[0]
1506 if partial < 0 or (instance_length and l <= last_byte_pos):
1507 self.assertEqual(status, 202)
1509 self.assertEqual(status, 204)
1510 #check modified object
1511 content = self.client.retrieve_object(self.containers[0],
1512 self.obj[0]['name'])
1513 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1514 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1515 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1517 def test_update_object_lt_blocksize(self):
1518 self.test_update_object(10, 20, content_length=None)
1520 def test_update_object_gt_blocksize(self):
1521 o = self.upload_random_data(self.containers[0], o_names[1],
1522 length=4*1024*1024+5)
1523 c = self.containers[0]
1526 first_byte_pos = 4*1024*1024+1
1527 last_byte_pos = 4*1024*1024+4
1528 l = last_byte_pos - first_byte_pos + 1
1529 data = get_random_data(l)
1530 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1531 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1532 content = self.client.retrieve_object(c, o_name)
1533 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1534 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1535 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1537 def test_update_object_divided_by_blocksize(self):
1538 o = self.upload_random_data(self.containers[0], o_names[1],
1539 length=4*1024*1024+5)
1540 c = self.containers[0]
1543 first_byte_pos = 4*1024*1024
1544 last_byte_pos = 5*1024*1024
1545 l = last_byte_pos - first_byte_pos + 1
1546 data = get_random_data(l)
1547 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1548 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1549 content = self.client.retrieve_object(c, o_name)
1550 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1551 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1552 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1554 def test_update_object_no_content_length(self):
1555 self.test_update_object(content_length = None)
1557 def test_update_object_invalid_content_length(self):
1558 with AssertContentInvariant(self.client.retrieve_object,
1559 self.containers[0], self.obj[0]['name']):
1560 self.assert_raises_fault(400, self.test_update_object,
1561 content_length = 1000)
1563 def test_update_object_invalid_range(self):
1564 with AssertContentInvariant(self.client.retrieve_object,
1565 self.containers[0], self.obj[0]['name']):
1566 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1568 def test_update_object_invalid_range_and_length(self):
1569 with AssertContentInvariant(self.client.retrieve_object,
1570 self.containers[0], self.obj[0]['name']):
1571 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1574 def test_update_object_invalid_range_with_no_content_length(self):
1575 with AssertContentInvariant(self.client.retrieve_object,
1576 self.containers[0], self.obj[0]['name']):
1577 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1578 content_length = None)
1580 def test_update_object_out_of_limits(self):
1581 with AssertContentInvariant(self.client.retrieve_object,
1582 self.containers[0], self.obj[0]['name']):
1583 l = len(self.obj[0]['data'])
1584 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1586 def test_append(self):
1587 data = get_random_data(500)
1589 self.client.update_object(self.containers[0], self.obj[0]['name'],
1590 StringIO(data), content_length=500,
1591 content_type='application/octet-stream')
1593 content = self.client.retrieve_object(self.containers[0],
1594 self.obj[0]['name'])
1595 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1596 self.assertEqual(content[:-500], self.obj[0]['data'])
1598 def test_update_with_chunked_transfer(self):
1599 data = get_random_data(500)
1601 fl = len(self.obj[0]['data'])
1603 self.client.update_object_using_chunks(self.containers[0],
1604 self.obj[0]['name'],
1607 content_type='application/octet-stream')
1609 #check modified object
1610 content = self.client.retrieve_object(self.containers[0],
1611 self.obj[0]['name'])
1612 self.assertEqual(content[0:dl], data)
1613 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1615 def test_update_from_other_object(self):
1616 c = self.containers[0]
1620 source_data = self.client.retrieve_object(c, src)
1621 source_meta = self.client.retrieve_object_metadata(c, src)
1622 source_hash = self.client.retrieve_object_hashmap(c, src)
1624 #update zero length object
1625 self.client.create_zero_length_object(c, dest)
1626 source_object = '/%s/%s' % (c, src)
1627 self.client.update_from_other_source(c, dest, source_object)
1628 dest_data = self.client.retrieve_object(c, src)
1629 dest_meta = self.client.retrieve_object_metadata(c, dest)
1630 dest_hash = self.client.retrieve_object_hashmap(c, src)
1631 self.assertEqual(source_data, dest_data)
1632 self.assertEqual(source_hash, dest_hash)
1635 self.client.update_from_other_source(c, dest, source_object)
1636 content = self.client.retrieve_object(c, dest)
1637 self.assertEqual(source_data * 2, content)
1639 def test_update_range_from_other_object(self):
1640 c = self.containers[0]
1644 src = self.obj[1]['name']
1645 src_data = self.client.retrieve_object(c, src)
1647 #update zero length object
1648 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1649 source_object = '/%s/%s' % (c, src)
1650 first_byte_pos = 4*1024*1024+1
1651 last_byte_pos = 4*1024*1024+4
1652 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1653 self.client.update_from_other_source(c, dest, source_object,
1654 content_range=range)
1655 content = self.client.retrieve_object(c, dest)
1656 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1657 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1658 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1660 def test_update_hashes_from_other_object(self):
1661 c = self.containers[0]
1665 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1667 #update zero length object
1668 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1669 source_object = '/%s/%s' % (c, o_names[0])
1670 first_byte_pos = 4*1024*1024
1671 last_byte_pos = 5*1024*1024
1672 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1673 self.client.update_from_other_source(c, dest, source_object,
1674 content_range=range)
1675 content = self.client.retrieve_object(c, dest)
1676 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1677 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1678 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1681 def test_update_zero_length_object(self):
1682 c = self.containers[0]
1685 zero = self.client.create_zero_length_object(c, o)
1687 data = get_random_data()
1688 self.client.update_object(c, o, StringIO(data))
1689 self.client.create_object(c, other, StringIO(data))
1691 self.assertEqual(self.client.retrieve_object(c, o),
1692 self.client.retrieve_object(c, other))
1694 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1695 self.client.retrieve_object_hashmap(c, other))
1697 class ObjectDelete(BaseTestCase):
1699 BaseTestCase.setUp(self)
1700 self.containers = ['c1', 'c2']
1701 for c in self.containers:
1702 self.client.create_container(c)
1703 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1705 def test_delete(self):
1706 #perform delete object
1707 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1709 def test_delete_invalid(self):
1710 #assert item not found
1711 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1714 class ListSharing(BaseTestCase):
1716 BaseTestCase.setUp(self)
1718 self.client.create_container('c%s' %i)
1719 self.client.create_container('c')
1721 self.upload_random_data('c1', 'o%s' %i)
1722 accounts = OTHER_ACCOUNTS.copy()
1723 self.o1_sharing_with = accounts.popitem()
1724 self.o1_sharing = [self.o1_sharing_with[1]]
1725 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1729 l.append(accounts.popitem())
1731 def test_list_other_shared(self):
1732 self.other = Pithos_Client(get_server(),
1733 self.o1_sharing_with[0],
1734 self.o1_sharing_with[1],
1736 self.assertTrue(get_user() in self.other.list_shared_by_others())
1738 def test_list_my_shared(self):
1739 my_shared_containers = self.client.list_containers(shared=True)
1740 self.assertTrue('c1' in my_shared_containers)
1741 self.assertTrue('c2' not in my_shared_containers)
1743 my_shared_objects = self.client.list_objects('c1', shared=True)
1744 self.assertTrue('o1' in my_shared_objects)
1745 self.assertTrue('o2' not in my_shared_objects)
1747 class TestGreek(BaseTestCase):
1749 BaseTestCase.setUp(self)
1750 #keep track of initial account groups
1751 self.initial_groups = self.client.retrieve_account_groups()
1753 #keep track of initial account meta
1754 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1757 #delete additionally created meta
1759 for m in self.client.retrieve_account_metadata(restricted=True):
1760 if m not in self.initial_meta:
1762 self.client.delete_account_metadata(l)
1764 #delete additionally created groups
1766 for g in self.client.retrieve_account_groups():
1767 if g not in self.initial_groups:
1769 self.client.unset_account_groups(l)
1771 BaseTestCase.tearDown(self)
1773 def test_create_container(self):
1774 self.client.create_container('φάκελος')
1775 self.assert_container_exists('φάκελος')
1777 self.assertTrue('φάκελος' in self.client.list_containers())
1779 def test_create_object(self):
1780 self.client.create_container('φάκελος')
1781 self.upload_random_data('φάκελος', 'αντικείμενο')
1783 self.assert_object_exists('φάκελος', 'αντικείμενο')
1784 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1786 def test_copy_object(self):
1787 src_container = 'φάκελος'
1788 src_object = 'αντικείμενο'
1789 dest_container = 'αντίγραφα'
1790 dest_object = 'ασφαλές-αντίγραφο'
1792 self.client.create_container(src_container)
1793 self.upload_random_data(src_container, src_object)
1795 self.client.create_container(dest_container)
1796 self.client.copy_object(src_container, src_object, dest_container,
1799 self.assert_object_exists(src_container, src_object)
1800 self.assert_object_exists(dest_container, dest_object)
1801 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1803 def test_move_object(self):
1804 src_container = 'φάκελος'
1805 src_object = 'αντικείμενο'
1806 dest_container = 'αντίγραφα'
1807 dest_object = 'ασφαλές-αντίγραφο'
1809 self.client.create_container(src_container)
1810 self.upload_random_data(src_container, src_object)
1812 self.client.create_container(dest_container)
1813 self.client.move_object(src_container, src_object, dest_container,
1816 self.assert_object_not_exists(src_container, src_object)
1817 self.assert_object_exists(dest_container, dest_object)
1818 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1820 def test_delete_object(self):
1821 self.client.create_container('φάκελος')
1822 self.upload_random_data('φάκελος', 'αντικείμενο')
1823 self.assert_object_exists('φάκελος', 'αντικείμενο')
1825 self.client.delete_object('φάκελος', 'αντικείμενο')
1826 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1827 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1829 def test_delete_container(self):
1830 self.client.create_container('φάκελος')
1831 self.assert_container_exists('φάκελος')
1833 self.client.delete_container('φάκελος')
1834 self.assert_container_not_exists('φάκελος')
1835 self.assertTrue('φάκελος' not in self.client.list_containers())
1837 def test_account_meta(self):
1838 meta = {'ποιότητα':'ΑΑΑ'}
1839 self.client.update_account_metadata(**meta)
1840 meta = self.client.retrieve_account_metadata(restricted=True)
1841 self.assertTrue('ποιότητα' in meta.keys())
1842 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1844 def test_container_meta(self):
1845 meta = {'ποιότητα':'ΑΑΑ'}
1846 self.client.create_container('φάκελος', **meta)
1848 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1849 self.assertTrue('ποιότητα' in meta.keys())
1850 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1852 def test_object_meta(self):
1853 self.client.create_container('φάκελος')
1854 meta = {'ποιότητα':'ΑΑΑ'}
1855 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1857 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1859 self.assertTrue('ποιότητα' in meta.keys())
1860 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1862 def test_list_meta_filtering(self):
1863 self.client.create_container('φάκελος')
1864 meta = {'ποιότητα':'ΑΑΑ'}
1865 self.upload_random_data('φάκελος', 'ο1', **meta)
1866 self.upload_random_data('φάκελος', 'ο2')
1867 self.upload_random_data('φάκελος', 'ο3')
1869 meta = {'ποσότητα':'μεγάλη'}
1870 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1871 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1872 self.assertTrue('ο1' in objects)
1873 self.assertTrue('ο2' in objects)
1874 self.assertTrue('ο3' not in objects)
1876 def test_groups(self):
1878 groups = {'σεφς':'chazapis,διογένης'}
1879 self.client.set_account_groups(**groups)
1880 groups.update(self.initial_groups)
1881 self.assertEqual(groups['σεφς'],
1882 self.client.retrieve_account_groups()['σεφς'])
1885 self.client.create_container('φάκελος')
1886 o = self.upload_random_data('φάκελος', 'ο1')
1887 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1888 chef = Pithos_Client(get_server(),
1892 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1893 'φάκελος', 'ο1', account=get_user())
1896 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1897 new_data = get_random_data()
1898 self.assert_not_raises_fault(403, chef.update_object,
1899 'φάκελος', 'ο1', StringIO(new_data),
1902 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1903 self.assertEqual(server_data[:len(o['data'])], o['data'])
1904 self.assertEqual(server_data[len(o['data']):], new_data)
1906 def test_manifestation(self):
1907 self.client.create_container('κουβάς')
1911 part = '%s%d' %(prefix, i)
1912 o = self.upload_random_data('κουβάς', part)
1915 self.client.create_container('φάκελος')
1916 manifest = '%s/%s' %('κουβάς', prefix)
1917 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1919 self.assert_object_exists('φάκελος', 'άπαντα')
1920 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1923 #wrong manifestation
1924 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1925 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1927 def test_update_from_another_object(self):
1928 self.client.create_container('κουβάς')
1929 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1930 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1931 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1932 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1935 self.client.retrieve_object('κουβάς', 'νέο'),
1936 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1938 class TestPermissions(BaseTestCase):
1940 BaseTestCase.setUp(self)
1941 #keep track of initial account groups
1942 self.initial_groups = self.client.retrieve_account_groups()
1943 #keep track of initial account meta
1944 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1947 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1948 groups = {'pithosdev':','.join(self.authorized)}
1949 self.client.set_account_groups(**groups)
1952 #delete additionally created meta
1954 for m in self.client.retrieve_account_metadata(restricted=True):
1955 if m not in self.initial_meta:
1957 self.client.delete_account_metadata(l)
1959 #delete additionally created groups
1961 for g in self.client.retrieve_account_groups():
1962 if g not in self.initial_groups:
1964 self.client.unset_account_groups(l)
1966 BaseTestCase.tearDown(self)
1968 def assert_read(self, authorized=[], any=False):
1969 for token, account in OTHER_ACCOUNTS.items():
1970 cl = Pithos_Client(get_server(), token, account, get_api())
1971 if account in authorized or any:
1972 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1973 'c', 'o', account=get_user())
1975 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1976 'c', 'o', account=get_user())
1979 o = self.upload_random_data('c', 'o/also-shared')
1980 for token, account in OTHER_ACCOUNTS.items():
1981 cl = Pithos_Client(get_server(), token, account, get_api())
1982 if account in authorized or any:
1983 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1984 'c', 'o/also-shared', account=get_user())
1986 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1987 'c', 'o/also-shared', account=get_user())
1989 def assert_write(self, o_data, authorized=[], any=False):
1990 for token, account in OTHER_ACCOUNTS.items():
1991 cl = Pithos_Client(get_server(), token, account, get_api())
1992 new_data = get_random_data()
1993 if account in authorized or any:
1995 self.assert_not_raises_fault(403, cl.update_object,
1996 'c', 'o', StringIO(new_data),
2000 server_data = cl.retrieve_object('c', 'o', account=get_user())
2001 self.assertEqual(o_data, server_data[:len(o_data)])
2002 self.assertEqual(new_data, server_data[len(o_data):])
2003 o_data = server_data
2005 self.failIf(f.status == 403)
2007 self.assert_raises_fault(403, cl.update_object,
2008 'c', 'o', StringIO(new_data),
2012 o = self.upload_random_data('c', 'o/also-shared')
2014 for token, account in OTHER_ACCOUNTS.items():
2015 cl = Pithos_Client(get_server(), token, account, get_api())
2016 new_data = get_random_data()
2017 if account in authorized or any:
2019 self.assert_not_raises_fault(403, cl.update_object,
2024 server_data = cl.retrieve_object('c', o['name'], account=get_user())
2025 self.assertEqual(o_data, server_data[:len(o_data)])
2026 self.assertEqual(new_data, server_data[len(o_data):])
2027 o_data = server_data
2029 self.failIf(f.status == 403)
2031 self.assert_raises_fault(403, cl.update_object,
2036 def test_group_read(self):
2037 self.client.create_container('c')
2038 o = self.upload_random_data('c', 'o')
2039 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
2040 self.assert_read(authorized=self.authorized)
2042 def test_read_many(self):
2044 self.client.create_container('c')
2045 o = self.upload_random_data('c', 'o')
2046 self.client.share_object('c', 'o', self.authorized)
2047 self.assert_read(authorized=self.authorized)
2049 def test_read_by_everyone(self):
2050 self.client.create_container('c')
2051 o = self.upload_random_data('c', 'o')
2052 self.client.share_object('c', 'o', ['*'])
2053 self.assert_read(any=True)
2055 def test_group_write(self):
2056 self.client.create_container('c')
2057 o = self.upload_random_data('c', 'o')
2058 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
2059 self.assert_write(o['data'], authorized=self.authorized)
2061 def test_write_many(self):
2062 self.client.create_container('c')
2063 o = self.upload_random_data('c', 'o')
2064 self.client.share_object('c', 'o', self.authorized, read=False)
2065 self.assert_write(o['data'], authorized=self.authorized)
2067 def test_write_by_everyone(self):
2068 self.client.create_container('c')
2069 o = self.upload_random_data('c', 'o')
2070 self.client.share_object('c', 'o', ['*'], read=False)
2072 self.assert_write(o['data'], any=True)
2074 class TestPublish(BaseTestCase):
2075 def test_publish(self):
2076 self.client.create_container('c')
2077 o_data = self.upload_random_data('c', 'o')['data']
2078 self.client.publish_object('c', 'o')
2079 meta = self.client.retrieve_object_metadata('c', 'o')
2080 self.assertTrue('x-object-public' in meta)
2081 url = '/public/%s/c/o' % get_user()
2082 self.assertEqual(meta['x-object-public'], url)
2083 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2084 data = public_client.get(url)[2]
2085 self.assertEqual(o_data, data)
2087 class AssertMappingInvariant(object):
2088 def __init__(self, callable, *args, **kwargs):
2089 self.callable = callable
2091 self.kwargs = kwargs
2093 def __enter__(self):
2094 self.map = self.callable(*self.args, **self.kwargs)
2097 def __exit__(self, type, value, tb):
2098 map = self.callable(*self.args, **self.kwargs)
2099 for k in self.map.keys():
2100 if is_date(self.map[k]):
2102 assert map[k] == self.map[k]
2104 class AssertContentInvariant(object):
2105 def __init__(self, callable, *args, **kwargs):
2106 self.callable = callable
2108 self.kwargs = kwargs
2110 def __enter__(self):
2111 self.content = self.callable(*self.args, **self.kwargs)[2]
2114 def __exit__(self, type, value, tb):
2115 content = self.callable(*self.args, **self.kwargs)[2]
2116 assert self.content == content
2118 def get_content_splitted(response):
2120 return response.content.split('\n')
2122 def compute_md5_hash(data):
2126 return md5.hexdigest().lower()
2128 def compute_block_hash(data, algorithm):
2129 h = hashlib.new(algorithm)
2130 h.update(data.rstrip('\x00'))
2131 return h.hexdigest()
2133 def get_random_data(length=500):
2134 char_set = string.ascii_uppercase + string.digits
2135 return ''.join(random.choice(char_set) for x in range(length))
2138 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2139 __D = r'(?P<day>\d{2})'
2140 __D2 = r'(?P<day>[ \d]\d)'
2141 __M = r'(?P<mon>\w{3})'
2142 __Y = r'(?P<year>\d{4})'
2143 __Y2 = r'(?P<year>\d{2})'
2144 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2145 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2146 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2147 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2148 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2149 m = regex.match(date)
2154 o_names = ['kate.jpg',
2155 'kate_beckinsale.jpg',
2156 'How To Win Friends And Influence People.pdf',
2157 'moms_birthday.jpg',
2159 'Disturbed - Down With The Sickness.mp3',
2160 'army_of_darkness.avi',
2162 'photos/animals/dogs/poodle.jpg',
2163 'photos/animals/dogs/terrier.jpg',
2164 'photos/animals/cats/persian.jpg',
2165 'photos/animals/cats/siamese.jpg',
2166 'photos/plants/fern.jpg',
2167 'photos/plants/rose.jpg',
2170 if __name__ == "__main__":
2171 if get_user() == 'test':
2174 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()