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(),
72 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
75 # 'account': ('x-account-container-count',
76 # 'x-account-bytes-used',
88 # 'x-object-manifest',
90 # 'x-object-modified-by',
92 # 'x-object-version-timestamp',
94 # 'container': ('x-container-object-count',
95 # 'x-container-bytes-used',
100 # 'x-container-block-size',
101 # 'x-container-block-hash',
102 # 'x-container-policy-quota',
103 # 'x-container-policy-versioning',
105 # 'x-container-object-meta',
106 # 'x-container-policy-versioning',
109 #self.contentTypes = {'xml':'application/xml',
110 # 'json':'application/json',
118 'x_container_policy_quota',
119 'x_container_policy_versioning',),
127 self.return_codes = (400, 401, 404, 503,)
130 for c in self.client.list_containers():
132 #list objects returns at most 10000 objects
133 #so repeat until there are no more objects
134 objects = self.client.list_objects(c)
138 self.client.delete_object(c, o)
139 self.client.delete_container(c)
141 def assert_status(self, status, codes):
142 l = [elem for elem in self.return_codes]
143 if type(codes) == types.ListType:
147 self.assertTrue(status in l)
149 #def assert_headers(self, headers, type, **exp_meta):
150 # prefix = 'x-%s-meta-' %type
151 # system_headers = [h for h in headers if not h.startswith(prefix)]
152 # for k,v in headers.items():
153 # if k in system_headers:
154 # self.assertTrue(k in headers[type])
156 # k = k.split(prefix)[-1]
157 # self.assertEqual(v, exp_meta[k])
159 def assert_extended(self, data, format, type, size=10000):
161 self._assert_xml(data, type, size)
162 elif format == 'json':
163 self._assert_json(data, type, size)
165 def _assert_json(self, data, type, size):
166 convert = lambda s: s.lower()
167 info = [convert(elem) for elem in self.extended[type]]
168 self.assertTrue(len(data) <= size)
171 if 'subdir' in i.keys():
173 self.assertTrue(item in i.keys())
175 def _assert_xml(self, data, type, size):
176 convert = lambda s: s.lower()
177 info = [convert(elem) for elem in self.extended[type]]
179 info.remove('content_encoding')
183 entities = xml.getElementsByTagName(type)
184 self.assertTrue(len(entities) <= size)
187 self.assertTrue(e.getElementsByTagName(item))
189 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
191 asserts that a Fault with a specific status is raised
192 when callableObj is called with the specific arguments
195 r = callableObj(*args, **kwargs)
196 self.fail('Should never reach here')
198 if type(status) == types.ListType:
199 self.failUnless(f.status in status)
201 self.failUnless(f.status == status)
203 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
205 asserts that a Fault with a specific status is not raised
206 when callableObj is called with the specific arguments
209 r = callableObj(*args, **kwargs)
211 self.failIfEqual(f.status, status)
213 def assert_container_exists(self, container):
215 asserts the existence of a container
218 self.client.retrieve_container_metadata(container)
220 self.failIf(f.status == 404)
222 def assert_container_not_exists(self, container):
224 asserts there is no such a container
226 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
229 def assert_object_exists(self, container, object):
231 asserts the existence of an object
234 self.client.retrieve_object_metadata(container, object)
236 self.failIf(f.status == 404)
238 def assert_object_not_exists(self, container, object):
240 asserts there is no such an object
242 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
245 def assert_versionlist_structure(self, versionlist):
246 self.assertTrue(type(versionlist) == types.ListType)
247 for elem in versionlist:
248 self.assertTrue(type(elem) == types.ListType)
249 self.assertEqual(len(elem), 2)
251 def upload_random_data(self, container, name, length=1024, type=None,
253 data = get_random_data(length)
254 return self.upload_data(container, name, data, type, enc, **meta)
256 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
262 obj['hash'] = compute_md5_hash(obj['data'])
265 args['etag'] = etag if etag else obj['hash']
268 guess = mimetypes.guess_type(name)
269 type = type if type else guess[0]
270 enc = enc if enc else guess[1]
273 args['content_type'] = type if type else 'plain/text'
274 args['content_encoding'] = enc if enc else None
278 path = '/%s/%s' % (container, name)
279 self.client.create_object(container, name, f=StringIO(obj['data']),
286 class AccountHead(BaseTestCase):
288 BaseTestCase.setUp(self)
289 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
290 for item in self.containers:
291 self.client.create_container(item)
293 #keep track of initial account groups
294 self.initial_groups = self.client.retrieve_account_groups()
296 #keep track of initial account meta
297 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
300 self.client.update_account_metadata(**meta)
301 self.updated_meta = self.initial_meta.update(meta)
304 #delete additionally created meta
306 for m in self.client.retrieve_account_metadata(restricted=True):
307 if m not in self.initial_meta:
309 self.client.delete_account_metadata(l)
311 #delete additionally created groups
313 for g in self.client.retrieve_account_groups():
314 if g not in self.initial_groups:
316 self.client.unset_account_groups(l)
318 BaseTestCase.tearDown(self)
320 def test_get_account_meta(self):
321 meta = self.client.retrieve_account_metadata()
323 containers = self.client.list_containers()
324 l = str(len(containers))
325 self.assertEqual(meta['x-account-container-count'], l)
328 m = self.client.retrieve_container_metadata(c)
329 size = size + int(m['x-container-bytes-used'])
330 self.assertEqual(meta['x-account-bytes-used'], str(size))
332 def test_get_account_401(self):
333 self.assert_raises_fault(401,
334 self.invalid_client.retrieve_account_metadata)
336 def test_get_account_meta_until(self):
337 t = datetime.datetime.utcnow()
338 past = t - datetime.timedelta(minutes=-15)
339 past = int(_time.mktime(past.timetuple()))
341 meta = {'premium':True}
342 self.client.update_account_metadata(**meta)
343 meta = self.client.retrieve_account_metadata(restricted=True,
345 self.assertTrue('premium' not in meta)
347 meta = self.client.retrieve_account_metadata(restricted=True)
348 self.assertTrue('premium' in meta)
350 def test_get_account_meta_until_invalid_date(self):
351 meta = {'premium':True}
352 self.client.update_account_metadata(**meta)
353 meta = self.client.retrieve_account_metadata(restricted=True,
355 self.assertTrue('premium' in meta)
357 class AccountGet(BaseTestCase):
359 BaseTestCase.setUp(self)
360 #create some containers
361 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
362 for item in self.containers:
363 self.client.create_container(item)
367 containers = self.client.list_containers()
368 self.assertEquals(self.containers, containers)
370 def test_list_401(self):
371 self.assert_raises_fault(401, self.invalid_client.list_containers)
373 def test_list_with_limit(self):
375 containers = self.client.list_containers(limit=limit)
376 self.assertEquals(len(containers), limit)
377 self.assertEquals(self.containers[:2], containers)
379 def test_list_with_marker(self):
382 containers = self.client.list_containers(limit=l, marker=m)
383 i = self.containers.index(m) + 1
384 self.assertEquals(self.containers[i:(i+l)], containers)
387 containers = self.client.list_containers(limit=l, marker=m)
388 i = self.containers.index(m) + 1
389 self.assertEquals(self.containers[i:(i+l)], containers)
391 def test_list_json_with_marker(self):
394 containers = self.client.list_containers(limit=l, marker=m, format='json')
395 self.assert_extended(containers, 'json', 'container', l)
396 self.assertEqual(containers[0]['name'], 'kiwis')
397 self.assertEqual(containers[1]['name'], 'oranges')
399 def test_list_xml_with_marker(self):
402 xml = self.client.list_containers(limit=l, marker=m, format='xml')
403 self.assert_extended(xml, 'xml', 'container', l)
404 nodes = xml.getElementsByTagName('name')
405 self.assertEqual(len(nodes), 1)
406 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
408 def test_if_modified_since(self):
409 t = datetime.datetime.utcnow()
410 t2 = t - datetime.timedelta(minutes=10)
413 self.client.create_container('dummy')
415 for f in DATE_FORMATS:
416 past = t2.strftime(f)
418 c = self.client.list_containers(if_modified_since=past)
419 self.assertEqual(len(c), len(self.containers) + 1)
421 self.failIf(f.status == 304) #fail if not modified
423 def test_if_modified_since_invalid_date(self):
424 c = self.client.list_containers(if_modified_since='')
425 self.assertEqual(len(c), len(self.containers))
427 def test_if_not_modified_since(self):
428 now = datetime.datetime.utcnow()
429 since = now + datetime.timedelta(1)
431 for f in DATE_FORMATS:
432 args = {'if_modified_since':'%s' %since.strftime(f)}
435 self.assert_raises_fault(304, self.client.list_containers, **args)
437 def test_if_unmodified_since(self):
438 now = datetime.datetime.utcnow()
439 since = now + datetime.timedelta(1)
441 for f in DATE_FORMATS:
442 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
445 self.assertEqual(self.containers, c)
447 def test_if_unmodified_since_precondition_failed(self):
448 t = datetime.datetime.utcnow()
449 t2 = t - datetime.timedelta(minutes=10)
452 self.client.create_container('dummy')
454 for f in DATE_FORMATS:
455 past = t2.strftime(f)
457 args = {'if_unmodified_since':'%s' %past}
459 #assert precondition failed
460 self.assert_raises_fault(412, self.client.list_containers, **args)
462 class AccountPost(BaseTestCase):
464 BaseTestCase.setUp(self)
465 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
466 for item in self.containers:
467 self.client.create_container(item)
469 #keep track of initial account groups
470 self.initial_groups = self.client.retrieve_account_groups()
472 #keep track of initial account meta
473 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
476 self.client.update_account_metadata(**meta)
477 self.updated_meta = self.initial_meta.update(meta)
480 #delete additionally created meta
482 for m in self.client.retrieve_account_metadata(restricted=True):
483 if m not in self.initial_meta:
485 self.client.delete_account_metadata(l)
487 #delete additionally created groups
489 for g in self.client.retrieve_account_groups():
490 if g not in self.initial_groups:
492 self.client.unset_account_groups(l)
494 BaseTestCase.tearDown(self)
496 def test_update_meta(self):
497 with AssertMappingInvariant(self.client.retrieve_account_groups):
498 meta = {'test':'test', 'tost':'tost'}
499 self.client.update_account_metadata(**meta)
501 meta.update(self.initial_meta)
502 self.assertEqual(meta,
503 self.client.retrieve_account_metadata(
506 def test_invalid_account_update_meta(self):
507 meta = {'test':'test', 'tost':'tost'}
508 self.assert_raises_fault(401,
509 self.invalid_client.update_account_metadata,
512 def test_reset_meta(self):
513 with AssertMappingInvariant(self.client.retrieve_account_groups):
514 meta = {'test':'test', 'tost':'tost'}
515 self.client.update_account_metadata(**meta)
517 meta = {'test':'test33'}
518 self.client.reset_account_metadata(**meta)
520 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
522 def test_delete_meta(self):
523 with AssertMappingInvariant(self.client.retrieve_account_groups):
524 meta = {'test':'test', 'tost':'tost'}
525 self.client.update_account_metadata(**meta)
527 self.client.delete_account_metadata(meta.keys())
529 account_meta = self.client.retrieve_account_metadata(restricted=True)
531 self.assertTrue(m not in account_meta.keys())
533 def test_set_account_groups(self):
534 with AssertMappingInvariant(self.client.retrieve_account_metadata):
535 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
536 self.client.set_account_groups(**groups)
538 self.assertEqual(set(groups['pithosdev']),
539 set(self.client.retrieve_account_groups()['pithosdev']))
541 more_groups = {'clientsdev':'pkanavos,mvasilak'}
542 self.client.set_account_groups(**more_groups)
544 groups.update(more_groups)
545 self.assertEqual(set(groups['clientsdev']),
546 set(self.client.retrieve_account_groups()['clientsdev']))
548 def test_reset_account_groups(self):
549 with AssertMappingInvariant(self.client.retrieve_account_metadata):
550 groups = {'pithosdev':'verigak,gtsouk,chazapis',
551 'clientsdev':'pkanavos,mvasilak'}
552 self.client.set_account_groups(**groups)
554 self.assertEqual(set(groups['pithosdev'].split(',')),
555 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
556 self.assertEqual(set(groups['clientsdev'].split(',')),
557 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
559 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
560 self.client.reset_account_groups(**groups)
562 self.assertEqual(set(groups['pithosdev'].split(',')),
563 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
565 def test_delete_account_groups(self):
566 with AssertMappingInvariant(self.client.retrieve_account_metadata):
567 groups = {'pithosdev':'verigak,gtsouk,chazapis',
568 'clientsdev':'pkanavos,mvasilak'}
569 self.client.set_account_groups(**groups)
571 self.client.unset_account_groups(groups.keys())
573 self.assertEqual({}, self.client.retrieve_account_groups())
575 class ContainerHead(BaseTestCase):
577 BaseTestCase.setUp(self)
578 self.container = 'apples'
579 self.client.create_container(self.container)
581 def test_get_meta(self):
582 meta = {'trash':'true'}
583 t1 = datetime.datetime.utcnow()
584 o = self.upload_random_data(self.container, o_names[0], **meta)
586 headers = self.client.retrieve_container_metadata(self.container)
587 self.assertEqual(headers['x-container-object-count'], '1')
588 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
589 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
591 threashold = datetime.timedelta(seconds=1)
592 self.assertTrue(delta < threashold)
593 self.assertTrue(headers['x-container-object-meta'])
594 self.assertTrue('Trash' in headers['x-container-object-meta'])
596 class ContainerGet(BaseTestCase):
598 BaseTestCase.setUp(self)
599 self.container = ['pears', 'apples']
600 for c in self.container:
601 self.client.create_container(c)
603 for o in o_names[:8]:
604 self.obj.append(self.upload_random_data(self.container[0], o))
605 for o in o_names[8:]:
606 self.obj.append(self.upload_random_data(self.container[1], o))
608 def test_list_objects(self):
609 objects = self.client.list_objects(self.container[0])
610 l = [elem['name'] for elem in self.obj[:8]]
612 self.assertEqual(objects, l)
614 def test_list_objects_containing_slash(self):
615 self.client.create_container('test')
616 self.upload_random_data('test', '/objectname')
618 objects = self.client.list_objects('test')
619 self.assertEqual(objects, ['/objectname'])
621 objects = self.client.list_objects('test', format='json')
622 self.assertEqual(objects[0]['name'], '/objectname')
624 objects = self.client.list_objects('test', format='xml')
625 self.assert_extended(objects, 'xml', 'object')
626 node_name = objects.getElementsByTagName('name')[0]
627 self.assertEqual(node_name.firstChild.data, '/objectname')
629 #objects = self.client.list_objects('test', prefix='/')
630 #self.assertEqual(objects, ['/objectname'])
632 #objects = self.client.list_objects('test', path='/')
633 #self.assertEqual(objects, ['/objectname'])
635 #objects = self.client.list_objects('test', prefix='/', delimiter='n')
636 #self.assertEqual(objects, ['/object'])
638 def test_list_objects_with_limit_marker(self):
639 objects = self.client.list_objects(self.container[0], limit=2)
640 l = [elem['name'] for elem in self.obj[:8]]
642 self.assertEqual(objects, l[:2])
644 markers = ['How To Win Friends And Influence People.pdf',
648 objects = self.client.list_objects(self.container[0], limit=limit,
650 l = [elem['name'] for elem in self.obj[:8]]
652 start = l.index(m) + 1
654 end = len(l) >= end and end or len(l)
655 self.assertEqual(objects, l[start:end])
658 def _test_list_limit_exceeds(self):
659 self.client.create_container('pithos')
661 for i in range(10001):
662 self.client.create_zero_length_object('pithos', i)
664 self.assertEqual(10000, len(self.client.list_objects('pithos')))
666 def test_list_empty_params(self):
667 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
669 objects = objects.strip().split('\n')
670 self.assertEqual(objects,
671 self.client.list_objects(self.container[0]))
673 def test_list_pseudo_hierarchical_folders(self):
674 objects = self.client.list_objects(self.container[1], prefix='photos',
676 self.assertEquals(['photos/animals/', 'photos/me.jpg',
677 'photos/plants/'], objects)
679 objects = self.client.list_objects(self.container[1],
680 prefix='photos/animals',
682 l = ['photos/animals/cats/', 'photos/animals/dogs/']
683 self.assertEquals(l, objects)
685 objects = self.client.list_objects(self.container[1], path='photos')
686 self.assertEquals(['photos/me.jpg'], objects)
688 def test_extended_list_json(self):
689 objects = self.client.list_objects(self.container[1], format='json',
690 limit=2, prefix='photos/animals',
692 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
693 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
695 def test_extended_list_xml(self):
696 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
697 prefix='photos', delimiter='/')
698 self.assert_extended(xml, 'xml', 'object', size=4)
699 dirs = xml.getElementsByTagName('subdir')
700 self.assertEqual(len(dirs), 2)
701 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
702 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
704 objects = xml.getElementsByTagName('name')
705 self.assertEqual(len(objects), 1)
706 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
708 def test_list_meta_double_matching(self):
709 meta = {'quality':'aaa', 'stock':'true'}
710 self.client.update_object_metadata(self.container[0],
711 self.obj[0]['name'], **meta)
712 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
713 self.assertEqual(len(obj), 1)
714 self.assertTrue(obj, self.obj[0]['name'])
716 def test_list_using_meta(self):
717 meta = {'quality':'aaa'}
718 for o in self.obj[:2]:
719 self.client.update_object_metadata(self.container[0], o['name'],
721 meta = {'stock':'true'}
722 for o in self.obj[3:5]:
723 self.client.update_object_metadata(self.container[0], o['name'],
726 obj = self.client.list_objects(self.container[0], meta='Quality')
727 self.assertEqual(len(obj), 2)
728 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
730 # test case insensitive
731 obj = self.client.list_objects(self.container[0], meta='quality')
732 self.assertEqual(len(obj), 2)
733 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
735 # test multiple matches
736 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
737 self.assertEqual(len(obj), 4)
738 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
740 # test non 1-1 multiple match
741 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
742 self.assertEqual(len(obj), 2)
743 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
745 def test_if_modified_since(self):
746 t = datetime.datetime.utcnow()
747 t2 = t - datetime.timedelta(minutes=10)
750 self.upload_random_data(self.container[0], o_names[0])
752 for f in DATE_FORMATS:
753 past = t2.strftime(f)
755 o = self.client.list_objects(self.container[0],
756 if_modified_since=past)
758 self.client.list_objects(self.container[0]))
760 self.failIf(f.status == 304) #fail if not modified
762 def test_if_modified_since_invalid_date(self):
763 headers = {'if-modified-since':''}
764 o = self.client.list_objects(self.container[0], if_modified_since='')
765 self.assertEqual(o, self.client.list_objects(self.container[0]))
767 def test_if_not_modified_since(self):
768 now = datetime.datetime.utcnow()
769 since = now + datetime.timedelta(1)
771 for f in DATE_FORMATS:
772 args = {'if_modified_since':'%s' %since.strftime(f)}
775 self.assert_raises_fault(304, self.client.list_objects,
776 self.container[0], **args)
778 def test_if_unmodified_since(self):
779 now = datetime.datetime.utcnow()
780 since = now + datetime.timedelta(1)
782 for f in DATE_FORMATS:
783 obj = self.client.list_objects(self.container[0],
784 if_unmodified_since=since.strftime(f))
787 self.assertEqual(obj, self.client.list_objects(self.container[0]))
789 def test_if_unmodified_since_precondition_failed(self):
790 t = datetime.datetime.utcnow()
791 t2 = t - datetime.timedelta(minutes=10)
794 self.client.create_container('dummy')
796 for f in DATE_FORMATS:
797 past = t2.strftime(f)
799 args = {'if_unmodified_since':'%s' %past}
801 #assert precondition failed
802 self.assert_raises_fault(412, self.client.list_objects,
803 self.container[0], **args)
805 class ContainerPut(BaseTestCase):
807 BaseTestCase.setUp(self)
808 self.containers = ['c1', 'c2']
810 def test_create(self):
811 self.client.create_container(self.containers[0])
812 containers = self.client.list_containers()
813 self.assertTrue(self.containers[0] in containers)
814 self.assert_container_exists(self.containers[0])
816 def test_create_twice(self):
817 self.client.create_container(self.containers[0])
818 self.assertTrue(not self.client.create_container(self.containers[0]))
820 class ContainerPost(BaseTestCase):
822 BaseTestCase.setUp(self)
823 self.container = 'apples'
824 self.client.create_container(self.container)
826 def test_update_meta(self):
827 meta = {'test':'test33',
829 self.client.update_container_metadata(self.container, **meta)
830 headers = self.client.retrieve_container_metadata(self.container)
831 for k,v in meta.items():
832 k = 'x-container-meta-%s' % k
833 self.assertTrue(headers[k])
834 self.assertEqual(headers[k], v)
836 class ContainerDelete(BaseTestCase):
838 BaseTestCase.setUp(self)
839 self.containers = ['c1', 'c2']
840 for c in self.containers:
841 self.client.create_container(c)
842 self.upload_random_data(self.containers[1], o_names[0])
844 def test_delete(self):
845 status = self.client.delete_container(self.containers[0])[0]
846 self.assertEqual(status, 204)
848 def test_delete_non_empty(self):
849 self.assert_raises_fault(409, self.client.delete_container,
852 def test_delete_invalid(self):
853 self.assert_raises_fault(404, self.client.delete_container, 'c3')
855 class ObjectHead(BaseTestCase):
858 class ObjectGet(BaseTestCase):
860 BaseTestCase.setUp(self)
861 self.containers = ['c1', 'c2']
862 #create some containers
863 for c in self.containers:
864 self.client.create_container(c)
867 names = ('obj1', 'obj2')
870 self.objects.append(self.upload_random_data(self.containers[1], n))
872 def test_versions(self):
873 c = self.containers[1]
875 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
876 self.assert_versionlist_structure(b)
879 meta = {'quality':'AAA', 'stock':True}
880 self.client.update_object_metadata(c, o['name'], **meta)
882 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
883 self.assert_versionlist_structure(a)
884 self.assertEqual(len(b)+1, len(a))
885 self.assertEqual(b, a[:-1])
887 #get exact previous version metadata
889 v_meta = self.client.retrieve_object_metadata(c, o['name'],
892 for k in meta.keys():
893 self.assertTrue(k not in v_meta)
896 data = get_random_data()
897 self.client.update_object(c, o['name'], StringIO(data))
899 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
900 self.assert_versionlist_structure(aa)
901 self.assertEqual(len(a)+1, len(aa))
902 self.assertEqual(a, aa[:-1])
904 #get exact previous version
906 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
907 self.assertEqual(o['data'], v_data)
908 self.assertEqual(self.client.retrieve_object(c, o['name']),
909 '%s%s' %(v_data, data))
913 o = self.client.retrieve_object(self.containers[1],
914 self.objects[0]['name'],
915 self.objects[0]['meta'])
916 self.assertEqual(o, self.objects[0]['data'])
918 def test_get_invalid(self):
919 self.assert_raises_fault(404, self.client.retrieve_object,
920 self.containers[0], self.objects[0]['name'])
922 def test_get_partial(self):
923 #perform get with range
924 status, headers, data = self.client.request_object(self.containers[1],
925 self.objects[0]['name'],
928 #assert successful partial content
929 self.assertEqual(status, 206)
932 self.assertEqual(headers['content-type'],
933 self.objects[0]['meta']['content_type'])
935 #assert content length
936 self.assertEqual(int(headers['content-length']), 500)
939 self.assertEqual(self.objects[0]['data'][:500], data)
941 def test_get_final_500(self):
942 #perform get with range
943 headers = {'range':'bytes=-500'}
944 status, headers, data = self.client.request_object(self.containers[1],
945 self.objects[0]['name'],
948 #assert successful partial content
949 self.assertEqual(status, 206)
952 self.assertEqual(headers['content-type'],
953 self.objects[0]['meta']['content_type'])
955 #assert content length
956 self.assertEqual(int(headers['content-length']), 500)
959 self.assertTrue(self.objects[0]['data'][-500:], data)
961 def test_get_rest(self):
962 #perform get with range
963 offset = len(self.objects[0]['data']) - 500
964 status, headers, data = self.client.request_object(self.containers[1],
965 self.objects[0]['name'],
966 range='bytes=%s-' %offset)
968 #assert successful partial content
969 self.assertEqual(status, 206)
972 self.assertEqual(headers['content-type'],
973 self.objects[0]['meta']['content_type'])
975 #assert content length
976 self.assertEqual(int(headers['content-length']), 500)
979 self.assertTrue(self.objects[0]['data'][-500:], data)
981 def test_get_range_not_satisfiable(self):
982 #perform get with range
983 offset = len(self.objects[0]['data']) + 1
985 #assert range not satisfiable
986 self.assert_raises_fault(416, self.client.retrieve_object,
987 self.containers[1], self.objects[0]['name'],
988 range='bytes=0-%s' %offset)
990 def test_multiple_range(self):
991 #perform get with multiple range
992 ranges = ['0-499', '-500', '1000-']
993 bytes = 'bytes=%s' % ','.join(ranges)
994 status, headers, data = self.client.request_object(self.containers[1],
995 self.objects[0]['name'],
998 # assert partial content
999 self.assertEqual(status, 206)
1001 # assert Content-Type of the reply will be multipart/byteranges
1002 self.assertTrue(headers['content-type'])
1003 content_type_parts = headers['content-type'].split()
1004 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1006 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1007 cparts = data.split(boundary)[1:-1]
1009 # assert content parts are exactly 2
1010 self.assertEqual(len(cparts), len(ranges))
1012 # for each content part assert headers
1014 for cpart in cparts:
1015 content = cpart.split('\r\n')
1016 headers = content[1:3]
1017 content_range = headers[0].split(': ')
1018 self.assertEqual(content_range[0], 'Content-Range')
1020 r = ranges[i].split('-')
1021 if not r[0] and not r[1]:
1024 start = len(self.objects[0]['data']) - int(r[1])
1025 end = len(self.objects[0]['data'])
1028 end = len(self.objects[0]['data'])
1032 fdata = self.objects[0]['data'][start:end]
1033 sdata = '\r\n'.join(content[4:-1])
1034 self.assertEqual(len(fdata), len(sdata))
1035 self.assertEquals(fdata, sdata)
1038 def test_multiple_range_not_satisfiable(self):
1039 #perform get with multiple range
1040 out_of_range = len(self.objects[0]['data']) + 1
1041 ranges = ['0-499', '-500', '%d-' %out_of_range]
1042 bytes = 'bytes=%s' % ','.join(ranges)
1044 # assert partial content
1045 self.assert_raises_fault(416, self.client.retrieve_object,
1047 self.objects[0]['name'], range=bytes)
1049 def test_get_with_if_match(self):
1050 #perform get with If-Match
1051 etag = self.objects[0]['hash']
1052 status, headers, data = self.client.request_object(self.containers[1],
1053 self.objects[0]['name'],
1056 self.assertEqual(status, 200)
1058 #assert content-type
1059 self.assertEqual(headers['content-type'],
1060 self.objects[0]['meta']['content_type'])
1062 #assert response content
1063 self.assertEqual(self.objects[0]['data'], data)
1065 def test_get_with_if_match_star(self):
1066 #perform get with If-Match *
1067 headers = {'if-match':'*'}
1068 status, headers, data = self.client.request_object(self.containers[1],
1069 self.objects[0]['name'],
1072 self.assertEqual(status, 200)
1074 #assert content-type
1075 self.assertEqual(headers['content-type'],
1076 self.objects[0]['meta']['content_type'])
1078 #assert response content
1079 self.assertEqual(self.objects[0]['data'], data)
1081 def test_get_with_multiple_if_match(self):
1082 #perform get with If-Match
1083 etags = [i['hash'] for i in self.objects if i]
1084 etags = ','.join('"%s"' % etag for etag in etags)
1085 status, headers, data = self.client.request_object(self.containers[1],
1086 self.objects[0]['name'],
1089 self.assertEqual(status, 200)
1091 #assert content-type
1092 self.assertEqual(headers['content-type'],
1093 self.objects[0]['meta']['content_type'])
1095 #assert content-type
1096 self.assertEqual(headers['content-type'],
1097 self.objects[0]['meta']['content_type'])
1099 #assert response content
1100 self.assertEqual(self.objects[0]['data'], data)
1102 def test_if_match_precondition_failed(self):
1103 #assert precondition failed
1104 self.assert_raises_fault(412, self.client.retrieve_object,
1106 self.objects[0]['name'], if_match='123')
1108 def test_if_none_match(self):
1109 #perform get with If-None-Match
1110 status, headers, data = self.client.request_object(self.containers[1],
1111 self.objects[0]['name'],
1112 if_none_match='123')
1115 self.assertEqual(status, 200)
1117 #assert content-type
1118 self.assertEqual(headers['content_type'],
1119 self.objects[0]['meta']['content_type'])
1121 def test_if_none_match(self):
1122 #perform get with If-None-Match * and assert not modified
1123 self.assert_raises_fault(304, self.client.retrieve_object,
1125 self.objects[0]['name'],
1128 def test_if_none_match_not_modified(self):
1129 #perform get with If-None-Match and assert not modified
1130 self.assert_raises_fault(304, self.client.retrieve_object,
1132 self.objects[0]['name'],
1133 if_none_match=self.objects[0]['hash'])
1135 meta = self.client.retrieve_object_metadata(self.containers[1],
1136 self.objects[0]['name'])
1137 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1139 def test_if_modified_since(self):
1140 t = datetime.datetime.utcnow()
1141 t2 = t - datetime.timedelta(minutes=10)
1144 self.upload_data(self.containers[1],
1145 self.objects[0]['name'],
1146 self.objects[0]['data'][:200])
1148 for f in DATE_FORMATS:
1149 past = t2.strftime(f)
1151 headers = {'if-modified-since':'%s' %past}
1153 o = self.client.retrieve_object(self.containers[1],
1154 self.objects[0]['name'],
1155 if_modified_since=past)
1157 self.client.retrieve_object(self.containers[1],
1158 self.objects[0]['name']))
1160 self.failIf(f.status == 304)
1162 def test_if_modified_since_invalid_date(self):
1163 o = self.client.retrieve_object(self.containers[1],
1164 self.objects[0]['name'],
1165 if_modified_since='')
1166 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1167 self.objects[0]['name']))
1169 def test_if_not_modified_since(self):
1170 now = datetime.datetime.utcnow()
1171 since = now + datetime.timedelta(1)
1173 for f in DATE_FORMATS:
1174 #assert not modified
1175 self.assert_raises_fault(304, self.client.retrieve_object,
1176 self.containers[1], self.objects[0]['name'],
1177 if_modified_since=since.strftime(f))
1179 def test_if_unmodified_since(self):
1180 now = datetime.datetime.utcnow()
1181 since = now + datetime.timedelta(1)
1183 for f in DATE_FORMATS:
1184 t = since.strftime(f)
1185 status, headers, data = self.client.request_object(self.containers[1],
1186 self.objects[0]['name'],
1187 if_unmodified_since=t)
1189 self.assertEqual(status, 200)
1190 self.assertEqual(self.objects[0]['data'], data)
1192 #assert content-type
1193 self.assertEqual(headers['content-type'],
1194 self.objects[0]['meta']['content_type'])
1196 def test_if_unmodified_since_precondition_failed(self):
1197 t = datetime.datetime.utcnow()
1198 t2 = t - datetime.timedelta(minutes=10)
1201 self.upload_data(self.containers[1],
1202 self.objects[0]['name'],
1203 self.objects[0]['data'][:200])
1205 for f in DATE_FORMATS:
1206 past = t2.strftime(f)
1207 #assert precondition failed
1208 self.assert_raises_fault(412, self.client.retrieve_object,
1209 self.containers[1], self.objects[0]['name'],
1210 if_unmodified_since=past)
1212 def test_hashes(self):
1215 o = self.upload_random_data(self.containers[1], fname, l)
1217 body = self.client.retrieve_object(self.containers[1], fname,
1219 hashes = body['hashes']
1220 block_size = body['block_size']
1221 block_hash = body['block_hash']
1222 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1223 self.assertTrue(len(hashes), block_num)
1226 start = i * block_size
1227 end = (i + 1) * block_size
1228 hash = compute_block_hash(o['data'][start:end], block_hash)
1229 self.assertEqual(h, hash)
1232 class ObjectPut(BaseTestCase):
1234 BaseTestCase.setUp(self)
1235 self.container = 'c1'
1236 self.client.create_container(self.container)
1238 def test_upload(self):
1240 meta = {'test':'test1'}
1241 o = self.upload_random_data(self.container, name, **meta)
1243 headers = self.client.retrieve_object_metadata(self.container,
1246 self.assertTrue('test' in headers.keys())
1247 self.assertEqual(headers['test'], meta['test'])
1249 #assert uploaded content
1250 status, h, data = self.client.request_object(self.container, name)
1251 self.assertEqual(len(o['data']), int(h['content-length']))
1252 self.assertEqual(o['data'], data)
1254 #assert content-type
1255 self.assertEqual(h['content-type'], o['meta']['content_type'])
1257 def _test_maximum_upload_size_exceeds(self):
1259 meta = {'test':'test1'}
1261 length=1024*1024*100
1262 self.assert_raises_fault(400, self.upload_random_data, self.container,
1263 name, length, **meta)
1265 def test_upload_with_name_containing_slash(self):
1266 name = '/%s' % o_names[0]
1267 meta = {'test':'test1'}
1268 o = self.upload_random_data(self.container, name, **meta)
1270 self.assertEqual(o['data'],
1271 self.client.retrieve_object(self.container, name))
1273 self.assertTrue(name in self.client.list_objects(self.container))
1275 def test_create_directory_marker(self):
1276 self.client.create_directory_marker(self.container, 'foo')
1277 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1278 self.assertEqual(meta['content-length'], '0')
1279 self.assertEqual(meta['content-type'], 'application/directory')
1281 def test_upload_unprocessable_entity(self):
1282 meta={'etag':'123', 'test':'test1'}
1284 #assert unprocessable entity
1285 self.assert_raises_fault(422, self.upload_random_data, self.container,
1288 def test_chunked_transfer(self):
1289 data = get_random_data()
1291 self.client.create_object_using_chunks(self.container, objname,
1294 uploaded_data = self.client.retrieve_object(self.container, objname)
1295 self.assertEqual(data, uploaded_data)
1297 def test_manifestation(self):
1298 prefix = 'myobject/'
1301 part = '%s%d' %(prefix, i)
1302 o = self.upload_random_data(self.container, part)
1305 manifest = '%s/%s' %(self.container, prefix)
1306 self.client.create_manifestation(self.container, 'large-object', manifest)
1308 self.assert_object_exists(self.container, 'large-object')
1309 self.assertEqual(data, self.client.retrieve_object(self.container,
1312 #wrong manifestation
1313 self.client.create_manifestation(self.container, 'large-object',
1314 '%s/invalid' % self.container)
1315 self.assertEqual('', self.client.retrieve_object(self.container,
1318 def test_create_zero_length_object(self):
1321 zero = self.client.create_zero_length_object(c, o)
1322 zero_meta = self.client.retrieve_object_metadata(c, o)
1323 zero_hash = self.client.retrieve_object_hashmap(c, o)
1324 zero_data = self.client.retrieve_object(c, o)
1326 self.assertEqual(int(zero_meta['content-length']), 0)
1327 self.assertEqual(zero_hash, [])
1328 self.assertEqual(zero_data, '')
1330 def test_create_object_by_hashmap(self):
1333 self.upload_random_data(c, o)
1334 hashmap = self.client.retrieve_object(c, o, format='json')
1336 self.client.create_object_by_hashmap(c, o2, hashmap)
1337 self.assertEqual(self.client.retrieve_object(c, o),
1338 self.client.retrieve_object(c, o))
1340 class ObjectCopy(BaseTestCase):
1342 BaseTestCase.setUp(self)
1343 self.containers = ['c1', 'c2']
1344 for c in self.containers:
1345 self.client.create_container(c)
1346 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1348 def test_copy(self):
1349 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1350 self.containers[0], self.obj['name']):
1352 meta = {'test':'testcopy'}
1353 status = self.client.copy_object(self.containers[0],
1359 #assert copy success
1360 self.assertEqual(status, 201)
1362 #assert access the new object
1363 headers = self.client.retrieve_object_metadata(self.containers[0],
1365 self.assertTrue('x-object-meta-test' in headers.keys())
1366 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1368 #assert etag is the same
1369 self.assertEqual(headers['etag'], self.obj['hash'])
1371 #assert src object still exists
1372 self.assert_object_exists(self.containers[0], self.obj['name'])
1374 def test_copy_from_different_container(self):
1375 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1376 self.containers[0], self.obj['name']):
1377 meta = {'test':'testcopy'}
1378 status = self.client.copy_object(self.containers[0],
1383 self.assertEqual(status, 201)
1385 # assert updated metadata
1386 meta = self.client.retrieve_object_metadata(self.containers[1],
1389 self.assertTrue('test' in meta.keys())
1390 self.assertTrue(meta['test'], 'testcopy')
1392 #assert src object still exists
1393 self.assert_object_exists(self.containers[0], self.obj['name'])
1395 def test_copy_invalid(self):
1396 #copy from invalid object
1397 meta = {'test':'testcopy'}
1398 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1399 'test.py', self.containers[1], 'testcopy', meta)
1401 #copy from invalid container
1402 meta = {'test':'testcopy'}
1403 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1404 self.obj['name'], self.containers[1],
1407 class ObjectMove(BaseTestCase):
1409 BaseTestCase.setUp(self)
1410 self.containers = ['c1', 'c2']
1411 for c in self.containers:
1412 self.client.create_container(c)
1413 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1415 def test_move(self):
1417 meta = {'test':'testcopy'}
1418 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1419 status = self.client.move_object(self.containers[0], self.obj['name'],
1420 self.containers[0], 'testcopy',
1423 #assert successful move
1424 self.assertEqual(status, 201)
1426 #assert updated metadata
1427 meta = self.client.retrieve_object_metadata(self.containers[0],
1430 self.assertTrue('test' in meta.keys())
1431 self.assertTrue(meta['test'], 'testcopy')
1433 #assert src object no more exists
1434 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1436 class ObjectPost(BaseTestCase):
1438 BaseTestCase.setUp(self)
1439 self.containers = ['c1', 'c2']
1440 for c in self.containers:
1441 self.client.create_container(c)
1444 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1446 def test_update_meta(self):
1447 #perform update metadata
1448 more = {'foo':'foo', 'bar':'bar'}
1449 status = self.client.update_object_metadata(self.containers[0],
1450 self.obj[0]['name'],
1452 #assert request accepted
1453 self.assertEqual(status, 202)
1455 #assert old metadata are still there
1456 headers = self.client.retrieve_object_metadata(self.containers[0],
1457 self.obj[0]['name'],
1459 #assert new metadata have been updated
1460 for k,v in more.items():
1461 self.assertTrue(k in headers.keys())
1462 self.assertTrue(headers[k], v)
1464 def test_update_object(self,
1467 instance_length = True,
1468 content_length = 500):
1469 l = len(self.obj[0]['data'])
1470 range = 'bytes %d-%d/%s' %(first_byte_pos,
1472 l if instance_length else '*')
1473 partial = last_byte_pos - first_byte_pos + 1
1474 length = first_byte_pos + partial
1475 data = get_random_data(partial)
1476 args = {'content_type':'application/octet-stream',
1477 'content_range':'%s' %range}
1479 args['content_length'] = content_length
1481 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1482 StringIO(data), **args)[0]
1484 if partial < 0 or (instance_length and l <= last_byte_pos):
1485 self.assertEqual(status, 202)
1487 self.assertEqual(status, 204)
1488 #check modified object
1489 content = self.client.retrieve_object(self.containers[0],
1490 self.obj[0]['name'])
1491 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1492 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1493 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1495 def test_update_object_lt_blocksize(self):
1496 self.test_update_object(10, 20, content_length=None)
1498 def test_update_object_gt_blocksize(self):
1499 o = self.upload_random_data(self.containers[0], o_names[1],
1500 length=4*1024*1024+5)
1501 c = self.containers[0]
1504 first_byte_pos = 4*1024*1024+1
1505 last_byte_pos = 4*1024*1024+4
1506 l = last_byte_pos - first_byte_pos + 1
1507 data = get_random_data(l)
1508 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1509 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1510 content = self.client.retrieve_object(c, o_name)
1511 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1512 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1513 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1515 def test_update_object_divided_by_blocksize(self):
1516 o = self.upload_random_data(self.containers[0], o_names[1],
1517 length=4*1024*1024+5)
1518 c = self.containers[0]
1521 first_byte_pos = 4*1024*1024
1522 last_byte_pos = 5*1024*1024
1523 l = last_byte_pos - first_byte_pos + 1
1524 data = get_random_data(l)
1525 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1526 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1527 content = self.client.retrieve_object(c, o_name)
1528 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1529 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1530 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1532 def test_update_object_no_content_length(self):
1533 self.test_update_object(content_length = None)
1535 def test_update_object_invalid_content_length(self):
1536 with AssertContentInvariant(self.client.retrieve_object,
1537 self.containers[0], self.obj[0]['name']):
1538 self.assert_raises_fault(400, self.test_update_object,
1539 content_length = 1000)
1541 def test_update_object_invalid_range(self):
1542 with AssertContentInvariant(self.client.retrieve_object,
1543 self.containers[0], self.obj[0]['name']):
1544 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1546 def test_update_object_invalid_range_and_length(self):
1547 with AssertContentInvariant(self.client.retrieve_object,
1548 self.containers[0], self.obj[0]['name']):
1549 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1552 def test_update_object_invalid_range_with_no_content_length(self):
1553 with AssertContentInvariant(self.client.retrieve_object,
1554 self.containers[0], self.obj[0]['name']):
1555 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1556 content_length = None)
1558 def test_update_object_out_of_limits(self):
1559 with AssertContentInvariant(self.client.retrieve_object,
1560 self.containers[0], self.obj[0]['name']):
1561 l = len(self.obj[0]['data'])
1562 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1564 def test_append(self):
1565 data = get_random_data(500)
1567 self.client.update_object(self.containers[0], self.obj[0]['name'],
1568 StringIO(data), content_length=500,
1569 content_type='application/octet-stream')
1571 content = self.client.retrieve_object(self.containers[0],
1572 self.obj[0]['name'])
1573 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1574 self.assertEqual(content[:-500], self.obj[0]['data'])
1576 def test_update_with_chunked_transfer(self):
1577 data = get_random_data(500)
1579 fl = len(self.obj[0]['data'])
1581 self.client.update_object_using_chunks(self.containers[0],
1582 self.obj[0]['name'],
1585 content_type='application/octet-stream')
1587 #check modified object
1588 content = self.client.retrieve_object(self.containers[0],
1589 self.obj[0]['name'])
1590 self.assertEqual(content[0:dl], data)
1591 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1593 def test_update_from_other_object(self):
1594 c = self.containers[0]
1598 source_data = self.client.retrieve_object(c, src)
1599 source_meta = self.client.retrieve_object_metadata(c, src)
1600 source_hash = self.client.retrieve_object_hashmap(c, src)
1602 #update zero length object
1603 self.client.create_zero_length_object(c, dest)
1604 source_object = '/%s/%s' % (c, src)
1605 self.client.update_from_other_source(c, dest, source_object)
1606 dest_data = self.client.retrieve_object(c, src)
1607 dest_meta = self.client.retrieve_object_metadata(c, dest)
1608 dest_hash = self.client.retrieve_object_hashmap(c, src)
1609 self.assertEqual(source_data, dest_data)
1610 self.assertEqual(source_hash, dest_hash)
1613 self.client.update_from_other_source(c, dest, source_object)
1614 content = self.client.retrieve_object(c, dest)
1615 self.assertEqual(source_data * 2, content)
1617 def test_update_range_from_other_object(self):
1618 c = self.containers[0]
1622 src = self.obj[1]['name']
1623 src_data = self.client.retrieve_object(c, src)
1625 #update zero length object
1626 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1627 source_object = '/%s/%s' % (c, src)
1628 first_byte_pos = 4*1024*1024+1
1629 last_byte_pos = 4*1024*1024+4
1630 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1631 self.client.update_from_other_source(c, dest, source_object,
1632 content_range=range)
1633 content = self.client.retrieve_object(c, dest)
1634 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1635 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1636 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1638 def test_update_hashes_from_other_object(self):
1639 c = self.containers[0]
1643 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1645 #update zero length object
1646 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1647 source_object = '/%s/%s' % (c, o_names[0])
1648 first_byte_pos = 4*1024*1024
1649 last_byte_pos = 5*1024*1024
1650 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1651 self.client.update_from_other_source(c, dest, source_object,
1652 content_range=range)
1653 content = self.client.retrieve_object(c, dest)
1654 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1655 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1656 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1659 def test_update_zero_length_object(self):
1660 c = self.containers[0]
1663 zero = self.client.create_zero_length_object(c, o)
1665 data = get_random_data()
1666 self.client.update_object(c, o, StringIO(data))
1667 self.client.create_object(c, other, StringIO(data))
1669 self.assertEqual(self.client.retrieve_object(c, o),
1670 self.client.retrieve_object(c, other))
1672 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1673 self.client.retrieve_object_hashmap(c, other))
1675 class ObjectDelete(BaseTestCase):
1677 BaseTestCase.setUp(self)
1678 self.containers = ['c1', 'c2']
1679 for c in self.containers:
1680 self.client.create_container(c)
1681 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1683 def test_delete(self):
1684 #perform delete object
1685 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1687 def test_delete_invalid(self):
1688 #assert item not found
1689 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1692 class ListSharing(BaseTestCase):
1694 BaseTestCase.setUp(self)
1696 self.client.create_container('c%s' %i)
1697 self.client.create_container('c')
1699 self.upload_random_data('c1', 'o%s' %i)
1700 accounts = OTHER_ACCOUNTS.copy()
1701 self.o1_sharing_with = accounts.popitem()
1702 self.o1_sharing = [self.o1_sharing_with[1]]
1703 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1707 l.append(accounts.popitem())
1709 def test_list_other_shared(self):
1710 self.other = Pithos_Client(get_server(),
1711 self.o1_sharing_with[0],
1712 self.o1_sharing_with[1],
1714 self.assertTrue(get_user() in self.other.list_shared_by_others())
1716 def test_list_my_shared(self):
1717 my_shared_containers = self.client.list_containers(shared=True)
1718 self.assertTrue('c1' in my_shared_containers)
1719 self.assertTrue('c2' not in my_shared_containers)
1721 my_shared_objects = self.client.list_objects('c1', shared=True)
1722 self.assertTrue('o1' in my_shared_objects)
1723 self.assertTrue('o2' not in my_shared_objects)
1725 class TestGreek(BaseTestCase):
1727 BaseTestCase.setUp(self)
1728 #keep track of initial account groups
1729 self.initial_groups = self.client.retrieve_account_groups()
1731 #keep track of initial account meta
1732 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1735 #delete additionally created meta
1737 for m in self.client.retrieve_account_metadata(restricted=True):
1738 if m not in self.initial_meta:
1740 self.client.delete_account_metadata(l)
1742 #delete additionally created groups
1744 for g in self.client.retrieve_account_groups():
1745 if g not in self.initial_groups:
1747 self.client.unset_account_groups(l)
1749 BaseTestCase.tearDown(self)
1751 def test_create_container(self):
1752 self.client.create_container('φάκελος')
1753 self.assert_container_exists('φάκελος')
1755 self.assertTrue('φάκελος' in self.client.list_containers())
1757 def test_create_object(self):
1758 self.client.create_container('φάκελος')
1759 self.upload_random_data('φάκελος', 'αντικείμενο')
1761 self.assert_object_exists('φάκελος', 'αντικείμενο')
1762 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1764 def test_copy_object(self):
1765 src_container = 'φάκελος'
1766 src_object = 'αντικείμενο'
1767 dest_container = 'αντίγραφα'
1768 dest_object = 'ασφαλές-αντίγραφο'
1770 self.client.create_container(src_container)
1771 self.upload_random_data(src_container, src_object)
1773 self.client.create_container(dest_container)
1774 self.client.copy_object(src_container, src_object, dest_container,
1777 self.assert_object_exists(src_container, src_object)
1778 self.assert_object_exists(dest_container, dest_object)
1779 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1781 def test_move_object(self):
1782 src_container = 'φάκελος'
1783 src_object = 'αντικείμενο'
1784 dest_container = 'αντίγραφα'
1785 dest_object = 'ασφαλές-αντίγραφο'
1787 self.client.create_container(src_container)
1788 self.upload_random_data(src_container, src_object)
1790 self.client.create_container(dest_container)
1791 self.client.move_object(src_container, src_object, dest_container,
1794 self.assert_object_not_exists(src_container, src_object)
1795 self.assert_object_exists(dest_container, dest_object)
1796 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1798 def test_delete_object(self):
1799 self.client.create_container('φάκελος')
1800 self.upload_random_data('φάκελος', 'αντικείμενο')
1801 self.assert_object_exists('φάκελος', 'αντικείμενο')
1803 self.client.delete_object('φάκελος', 'αντικείμενο')
1804 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1805 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1807 def test_delete_container(self):
1808 self.client.create_container('φάκελος')
1809 self.assert_container_exists('φάκελος')
1811 self.client.delete_container('φάκελος')
1812 self.assert_container_not_exists('φάκελος')
1813 self.assertTrue('φάκελος' not in self.client.list_containers())
1815 def test_account_meta(self):
1816 meta = {'ποιότητα':'ΑΑΑ'}
1817 self.client.update_account_metadata(**meta)
1818 meta = self.client.retrieve_account_metadata(restricted=True)
1819 self.assertTrue('ποιότητα' in meta.keys())
1820 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1822 def test_container_meta(self):
1823 meta = {'ποιότητα':'ΑΑΑ'}
1824 self.client.create_container('φάκελος', **meta)
1826 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1827 self.assertTrue('ποιότητα' in meta.keys())
1828 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1830 def test_object_meta(self):
1831 self.client.create_container('φάκελος')
1832 meta = {'ποιότητα':'ΑΑΑ'}
1833 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1835 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1837 self.assertTrue('ποιότητα' in meta.keys())
1838 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1840 def test_list_meta_filtering(self):
1841 self.client.create_container('φάκελος')
1842 meta = {'ποιότητα':'ΑΑΑ'}
1843 self.upload_random_data('φάκελος', 'ο1', **meta)
1844 self.upload_random_data('φάκελος', 'ο2')
1845 self.upload_random_data('φάκελος', 'ο3')
1847 meta = {'ποσότητα':'μεγάλη'}
1848 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1849 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1850 self.assertTrue('ο1' in objects)
1851 self.assertTrue('ο2' in objects)
1852 self.assertTrue('ο3' not in objects)
1854 def test_groups(self):
1856 groups = {'σεφς':'chazapis,διογένης'}
1857 self.client.set_account_groups(**groups)
1858 groups.update(self.initial_groups)
1859 self.assertEqual(groups['σεφς'],
1860 self.client.retrieve_account_groups()['σεφς'])
1863 self.client.create_container('φάκελος')
1864 o = self.upload_random_data('φάκελος', 'ο1')
1865 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1866 chef = Pithos_Client(get_server(),
1870 self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1871 'φάκελος', 'ο1', account=get_user())
1874 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1875 new_data = get_random_data()
1876 self.assert_not_raises_fault(401, chef.update_object,
1877 'φάκελος', 'ο1', StringIO(new_data),
1880 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1881 self.assertEqual(server_data[:len(o['data'])], o['data'])
1882 self.assertEqual(server_data[len(o['data']):], new_data)
1884 def test_manifestation(self):
1885 self.client.create_container('κουβάς')
1889 part = '%s%d' %(prefix, i)
1890 o = self.upload_random_data('κουβάς', part)
1893 self.client.create_container('φάκελος')
1894 manifest = '%s/%s' %('κουβάς', prefix)
1895 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1897 self.assert_object_exists('φάκελος', 'άπαντα')
1898 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1901 #wrong manifestation
1902 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1903 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1905 def test_update_from_another_object(self):
1906 self.client.create_container('κουβάς')
1907 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1908 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1909 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1910 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1913 self.client.retrieve_object('κουβάς', 'νέο'),
1914 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1916 class TestPermissions(BaseTestCase):
1918 BaseTestCase.setUp(self)
1919 #keep track of initial account groups
1920 self.initial_groups = self.client.retrieve_account_groups()
1921 #keep track of initial account meta
1922 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1925 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1926 groups = {'pithosdev':','.join(self.authorized)}
1927 self.client.set_account_groups(**groups)
1930 #delete additionally created meta
1932 for m in self.client.retrieve_account_metadata(restricted=True):
1933 if m not in self.initial_meta:
1935 self.client.delete_account_metadata(l)
1937 #delete additionally created groups
1939 for g in self.client.retrieve_account_groups():
1940 if g not in self.initial_groups:
1942 self.client.unset_account_groups(l)
1944 BaseTestCase.tearDown(self)
1946 def assert_read(self, authorized=[], any=False):
1947 for token, account in OTHER_ACCOUNTS.items():
1948 cl = Pithos_Client(get_server(), token, account, get_api())
1949 if account in authorized or any:
1950 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1951 'c', 'o', account=get_user())
1953 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1954 'c', 'o', account=get_user())
1957 o = self.upload_random_data('c', 'o/also-shared')
1958 for token, account in OTHER_ACCOUNTS.items():
1959 cl = Pithos_Client(get_server(), token, account, get_api())
1960 if account in authorized or any:
1961 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1962 'c', 'o/also-shared', account=get_user())
1964 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1965 'c', 'o/also-shared', account=get_user())
1967 def assert_write(self, o_data, authorized=[], any=False):
1968 for token, account in OTHER_ACCOUNTS.items():
1969 cl = Pithos_Client(get_server(), token, account, get_api())
1970 new_data = get_random_data()
1971 if account in authorized or any:
1973 self.assert_not_raises_fault(401, cl.update_object,
1974 'c', 'o', StringIO(new_data),
1978 server_data = cl.retrieve_object('c', 'o', account=get_user())
1979 self.assertEqual(o_data, server_data[:len(o_data)])
1980 self.assertEqual(new_data, server_data[len(o_data):])
1981 o_data = server_data
1983 self.failIf(f.status == 401)
1985 self.assert_raises_fault(401, cl.update_object,
1986 'c', 'o', StringIO(new_data),
1990 o = self.upload_random_data('c', 'o/also-shared')
1992 for token, account in OTHER_ACCOUNTS.items():
1993 cl = Pithos_Client(get_server(), token, account, get_api())
1994 new_data = get_random_data()
1995 if account in authorized or any:
1997 self.assert_not_raises_fault(401, cl.update_object,
2002 server_data = cl.retrieve_object('c', o['name'], account=get_user())
2003 self.assertEqual(o_data, server_data[:len(o_data)])
2004 self.assertEqual(new_data, server_data[len(o_data):])
2005 o_data = server_data
2007 self.failIf(f.status == 401)
2009 self.assert_raises_fault(401, cl.update_object,
2014 def test_group_read(self):
2015 self.client.create_container('c')
2016 o = self.upload_random_data('c', 'o')
2017 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
2018 self.assert_read(authorized=self.authorized)
2020 def test_read_many(self):
2022 self.client.create_container('c')
2023 o = self.upload_random_data('c', 'o')
2024 self.client.share_object('c', 'o', self.authorized)
2025 self.assert_read(authorized=self.authorized)
2027 def test_read_by_everyone(self):
2028 self.client.create_container('c')
2029 o = self.upload_random_data('c', 'o')
2030 self.client.share_object('c', 'o', ['*'])
2031 self.assert_read(any=True)
2033 def test_group_write(self):
2034 self.client.create_container('c')
2035 o = self.upload_random_data('c', 'o')
2036 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
2037 self.assert_write(o['data'], authorized=self.authorized)
2039 def test_write_many(self):
2040 self.client.create_container('c')
2041 o = self.upload_random_data('c', 'o')
2042 self.client.share_object('c', 'o', self.authorized, read=False)
2043 self.assert_write(o['data'], authorized=self.authorized)
2045 def test_write_by_everyone(self):
2046 self.client.create_container('c')
2047 o = self.upload_random_data('c', 'o')
2048 self.client.share_object('c', 'o', ['*'], read=False)
2050 self.assert_write(o['data'], any=True)
2052 class TestPublish(BaseTestCase):
2053 def test_publish(self):
2054 self.client.create_container('c')
2055 o_data = self.upload_random_data('c', 'o')['data']
2056 self.client.publish_object('c', 'o')
2057 meta = self.client.retrieve_object_metadata('c', 'o')
2058 self.assertTrue('x-object-public' in meta)
2059 url = '/public/%s/c/o' % get_user()
2060 self.assertEqual(meta['x-object-public'], url)
2061 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2062 data = public_client.get(url)[2]
2063 self.assertEqual(o_data, data)
2065 class AssertMappingInvariant(object):
2066 def __init__(self, callable, *args, **kwargs):
2067 self.callable = callable
2069 self.kwargs = kwargs
2071 def __enter__(self):
2072 self.map = self.callable(*self.args, **self.kwargs)
2075 def __exit__(self, type, value, tb):
2076 map = self.callable(*self.args, **self.kwargs)
2077 for k in self.map.keys():
2078 if is_date(self.map[k]):
2080 assert map[k] == self.map[k]
2082 class AssertContentInvariant(object):
2083 def __init__(self, callable, *args, **kwargs):
2084 self.callable = callable
2086 self.kwargs = kwargs
2088 def __enter__(self):
2089 self.content = self.callable(*self.args, **self.kwargs)[2]
2092 def __exit__(self, type, value, tb):
2093 content = self.callable(*self.args, **self.kwargs)[2]
2094 assert self.content == content
2096 def get_content_splitted(response):
2098 return response.content.split('\n')
2100 def compute_md5_hash(data):
2104 return md5.hexdigest().lower()
2106 def compute_block_hash(data, algorithm):
2107 h = hashlib.new(algorithm)
2108 h.update(data.rstrip('\x00'))
2109 return h.hexdigest()
2111 def get_random_data(length=500):
2112 char_set = string.ascii_uppercase + string.digits
2113 return ''.join(random.choice(char_set) for x in range(length))
2116 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2117 __D = r'(?P<day>\d{2})'
2118 __D2 = r'(?P<day>[ \d]\d)'
2119 __M = r'(?P<mon>\w{3})'
2120 __Y = r'(?P<year>\d{4})'
2121 __Y2 = r'(?P<year>\d{2})'
2122 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2123 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2124 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2125 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2126 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2127 m = regex.match(date)
2132 o_names = ['kate.jpg',
2133 'kate_beckinsale.jpg',
2134 'How To Win Friends And Influence People.pdf',
2135 'moms_birthday.jpg',
2137 'Disturbed - Down With The Sickness.mp3',
2138 'army_of_darkness.avi',
2140 'photos/animals/dogs/poodle.jpg',
2141 'photos/animals/dogs/terrier.jpg',
2142 'photos/animals/cats/persian.jpg',
2143 'photos/animals/cats/siamese.jpg',
2144 'photos/plants/fern.jpg',
2145 'photos/plants/rose.jpg',
2148 if __name__ == "__main__":
2149 if get_user() == 'test':
2152 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()