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
41 from hashlib import new as newhasher
42 from binascii import hexlify
54 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
55 "%A, %d-%b-%y %H:%M:%S GMT",
56 "%a, %d %b %Y %H:%M:%S GMT"]
69 class BaseTestCase(unittest.TestCase):
70 #TODO unauthorized request
72 self.client = Pithos_Client(get_server(), get_auth(), get_user(),
75 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
78 #keep track of initial account groups
79 self.initial_groups = self.client.retrieve_account_groups()
81 #keep track of initial account meta
82 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
90 'x_container_policy_quota',
91 'x_container_policy_versioning',),
99 self.return_codes = (400, 401, 403, 404, 503,)
102 #delete additionally created meta
104 for m in self.client.retrieve_account_metadata(restricted=True):
105 if m not in self.initial_meta:
107 self.client.delete_account_metadata(l)
109 #delete additionally created groups
111 for g in self.client.retrieve_account_groups():
112 if g not in self.initial_groups:
114 self.client.unset_account_groups(l)
115 self._clean_account()
117 def _clean_account(self):
118 for c in self.client.list_containers():
120 #list objects returns at most 10000 objects
121 #so repeat until there are no more objects
122 objects = self.client.list_objects(c)
126 self.client.delete_object(c, o)
127 self.client.delete_container(c)
129 def assert_status(self, status, codes):
130 l = [elem for elem in self.return_codes]
131 if type(codes) == types.ListType:
135 self.assertTrue(status in l)
137 def assert_extended(self, data, format, type, size=10000):
139 self._assert_xml(data, type, size)
140 elif format == 'json':
141 self._assert_json(data, type, size)
143 def _assert_json(self, data, type, size):
144 convert = lambda s: s.lower()
145 info = [convert(elem) for elem in self.extended[type]]
146 self.assertTrue(len(data) <= size)
149 if 'subdir' in i.keys():
151 self.assertTrue(item in i.keys())
153 def _assert_xml(self, data, type, size):
154 convert = lambda s: s.lower()
155 info = [convert(elem) for elem in self.extended[type]]
157 info.remove('content_encoding')
161 entities = xml.getElementsByTagName(type)
162 self.assertTrue(len(entities) <= size)
165 self.assertTrue(e.getElementsByTagName(item))
167 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
169 asserts that a Fault with a specific status is raised
170 when callableObj is called with the specific arguments
173 r = callableObj(*args, **kwargs)
174 self.fail('Should never reach here')
176 if type(status) == types.ListType:
177 self.failUnless(f.status in status)
179 self.failUnless(f.status == status)
181 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
183 asserts that a Fault with a specific status is not raised
184 when callableObj is called with the specific arguments
187 r = callableObj(*args, **kwargs)
189 self.failIfEqual(f.status, status)
191 def assert_container_exists(self, container):
193 asserts the existence of a container
196 self.client.retrieve_container_metadata(container)
198 self.failIf(f.status == 404)
200 def assert_container_not_exists(self, container):
202 asserts there is no such a container
204 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
207 def assert_object_exists(self, container, object):
209 asserts the existence of an object
212 self.client.retrieve_object_metadata(container, object)
214 self.failIf(f.status == 404)
216 def assert_object_not_exists(self, container, object):
218 asserts there is no such an object
220 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
223 def assert_versionlist_structure(self, versionlist):
224 self.assertTrue(type(versionlist) == types.ListType)
225 for elem in versionlist:
226 self.assertTrue(type(elem) == types.ListType)
227 self.assertEqual(len(elem), 2)
229 def upload_random_data(self, container, name, length=1024, type=None,
231 data = get_random_data(length)
232 return self.upload_data(container, name, data, type, enc, **meta)
234 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
240 obj['hash'] = compute_md5_hash(obj['data'])
243 args['etag'] = etag if etag else obj['hash']
246 guess = mimetypes.guess_type(name)
247 type = type if type else guess[0]
248 enc = enc if enc else guess[1]
251 args['content_type'] = type if type else 'plain/text'
252 args['content_encoding'] = enc if enc else None
256 path = '/%s/%s' % (container, name)
257 self.client.create_object(container, name, f=StringIO(obj['data']),
264 class AccountHead(BaseTestCase):
266 BaseTestCase.setUp(self)
267 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
268 for item in self.containers:
269 self.client.create_container(item)
272 self.client.update_account_metadata(**meta)
273 #self.updated_meta = self.initial_meta.update(meta)
275 def test_get_account_meta(self):
276 meta = self.client.retrieve_account_metadata()
278 containers = self.client.list_containers()
279 l = str(len(containers))
280 self.assertEqual(meta['x-account-container-count'], l)
283 m = self.client.retrieve_container_metadata(c)
284 size = size + int(m['x-container-bytes-used'])
285 self.assertEqual(meta['x-account-bytes-used'], str(size))
287 def test_get_account_403(self):
288 self.assert_raises_fault(403,
289 self.invalid_client.retrieve_account_metadata)
291 def test_get_account_meta_until(self):
292 t = datetime.datetime.utcnow()
293 past = t - datetime.timedelta(minutes=-15)
294 past = int(_time.mktime(past.timetuple()))
296 meta = {'premium':True}
297 self.client.update_account_metadata(**meta)
298 meta = self.client.retrieve_account_metadata(restricted=True,
300 self.assertTrue('premium' not in meta)
302 meta = self.client.retrieve_account_metadata(restricted=True)
303 self.assertTrue('premium' in meta)
305 def test_get_account_meta_until_invalid_date(self):
306 meta = {'premium':True}
307 self.client.update_account_metadata(**meta)
308 meta = self.client.retrieve_account_metadata(restricted=True,
310 self.assertTrue('premium' in meta)
312 class AccountGet(BaseTestCase):
314 BaseTestCase.setUp(self)
315 #create some containers
316 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
317 for item in self.containers:
318 self.client.create_container(item)
322 containers = self.client.list_containers()
323 self.assertEquals(self.containers, containers)
325 def test_list_403(self):
326 self.assert_raises_fault(403, self.invalid_client.list_containers)
328 def test_list_with_limit(self):
330 containers = self.client.list_containers(limit=limit)
331 self.assertEquals(len(containers), limit)
332 self.assertEquals(self.containers[:2], containers)
334 def test_list_with_marker(self):
337 containers = self.client.list_containers(limit=l, marker=m)
338 i = self.containers.index(m) + 1
339 self.assertEquals(self.containers[i:(i+l)], containers)
342 containers = self.client.list_containers(limit=l, marker=m)
343 i = self.containers.index(m) + 1
344 self.assertEquals(self.containers[i:(i+l)], containers)
346 def test_list_json_with_marker(self):
349 containers = self.client.list_containers(limit=l, marker=m, format='json')
350 self.assert_extended(containers, 'json', 'container', l)
351 self.assertEqual(containers[0]['name'], 'kiwis')
352 self.assertEqual(containers[1]['name'], 'oranges')
354 def test_list_xml_with_marker(self):
357 xml = self.client.list_containers(limit=l, marker=m, format='xml')
358 self.assert_extended(xml, 'xml', 'container', l)
359 nodes = xml.getElementsByTagName('name')
360 self.assertEqual(len(nodes), 1)
361 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
363 def test_if_modified_since(self):
364 t = datetime.datetime.utcnow()
365 t2 = t - datetime.timedelta(minutes=10)
368 self.client.create_container('dummy')
370 for f in DATE_FORMATS:
371 past = t2.strftime(f)
373 c = self.client.list_containers(if_modified_since=past)
374 self.assertEqual(len(c), len(self.containers) + 1)
376 self.failIf(f.status == 304) #fail if not modified
378 def test_if_modified_since_invalid_date(self):
379 c = self.client.list_containers(if_modified_since='')
380 self.assertEqual(len(c), len(self.containers))
382 def test_if_not_modified_since(self):
383 now = datetime.datetime.utcnow()
384 since = now + datetime.timedelta(1)
386 for f in DATE_FORMATS:
387 args = {'if_modified_since':'%s' %since.strftime(f)}
390 self.assert_raises_fault(304, self.client.list_containers, **args)
392 def test_if_unmodified_since(self):
393 now = datetime.datetime.utcnow()
394 since = now + datetime.timedelta(1)
396 for f in DATE_FORMATS:
397 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
400 self.assertEqual(self.containers, c)
402 def test_if_unmodified_since_precondition_failed(self):
403 t = datetime.datetime.utcnow()
404 t2 = t - datetime.timedelta(minutes=10)
407 self.client.create_container('dummy')
409 for f in DATE_FORMATS:
410 past = t2.strftime(f)
412 args = {'if_unmodified_since':'%s' %past}
414 #assert precondition failed
415 self.assert_raises_fault(412, self.client.list_containers, **args)
417 class AccountPost(BaseTestCase):
419 BaseTestCase.setUp(self)
420 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
421 for item in self.containers:
422 self.client.create_container(item)
425 self.client.update_account_metadata(**meta)
426 self.updated_meta = self.initial_meta.update(meta)
428 def test_update_meta(self):
429 with AssertMappingInvariant(self.client.retrieve_account_groups):
430 meta = {'test':'test', 'tost':'tost'}
431 self.client.update_account_metadata(**meta)
433 meta.update(self.initial_meta)
434 self.assertEqual(meta,
435 self.client.retrieve_account_metadata(
438 def test_invalid_account_update_meta(self):
439 meta = {'test':'test', 'tost':'tost'}
440 self.assert_raises_fault(403,
441 self.invalid_client.update_account_metadata,
444 def test_reset_meta(self):
445 with AssertMappingInvariant(self.client.retrieve_account_groups):
446 meta = {'test':'test', 'tost':'tost'}
447 self.client.update_account_metadata(**meta)
449 meta = {'test':'test33'}
450 self.client.reset_account_metadata(**meta)
452 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
454 def test_delete_meta(self):
455 with AssertMappingInvariant(self.client.retrieve_account_groups):
456 meta = {'test':'test', 'tost':'tost'}
457 self.client.update_account_metadata(**meta)
459 self.client.delete_account_metadata(meta.keys())
461 account_meta = self.client.retrieve_account_metadata(restricted=True)
463 self.assertTrue(m not in account_meta.keys())
465 def test_set_account_groups(self):
466 with AssertMappingInvariant(self.client.retrieve_account_metadata):
467 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
468 self.client.set_account_groups(**groups)
470 self.assertEqual(set(groups['pithosdev']),
471 set(self.client.retrieve_account_groups()['pithosdev']))
473 more_groups = {'clientsdev':'pkanavos,mvasilak'}
474 self.client.set_account_groups(**more_groups)
476 groups.update(more_groups)
477 self.assertEqual(set(groups['clientsdev']),
478 set(self.client.retrieve_account_groups()['clientsdev']))
480 def test_reset_account_groups(self):
481 with AssertMappingInvariant(self.client.retrieve_account_metadata):
482 groups = {'pithosdev':'verigak,gtsouk,chazapis',
483 'clientsdev':'pkanavos,mvasilak'}
484 self.client.set_account_groups(**groups)
486 self.assertEqual(set(groups['pithosdev'].split(',')),
487 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
488 self.assertEqual(set(groups['clientsdev'].split(',')),
489 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
491 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
492 self.client.reset_account_groups(**groups)
494 self.assertEqual(set(groups['pithosdev'].split(',')),
495 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
497 def test_delete_account_groups(self):
498 with AssertMappingInvariant(self.client.retrieve_account_metadata):
499 groups = {'pithosdev':'verigak,gtsouk,chazapis',
500 'clientsdev':'pkanavos,mvasilak'}
501 self.client.set_account_groups(**groups)
503 self.client.unset_account_groups(groups.keys())
505 self.assertEqual({}, self.client.retrieve_account_groups())
507 class ContainerHead(BaseTestCase):
509 BaseTestCase.setUp(self)
510 self.container = 'apples'
511 self.client.create_container(self.container)
513 def test_get_meta(self):
514 meta = {'trash':'true'}
515 t1 = datetime.datetime.utcnow()
516 o = self.upload_random_data(self.container, o_names[0], **meta)
518 headers = self.client.retrieve_container_metadata(self.container)
519 self.assertEqual(headers['x-container-object-count'], '1')
520 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
521 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
523 threashold = datetime.timedelta(seconds=1)
524 self.assertTrue(delta < threashold)
525 self.assertTrue(headers['x-container-object-meta'])
526 self.assertTrue('Trash' in headers['x-container-object-meta'])
528 class ContainerGet(BaseTestCase):
530 BaseTestCase.setUp(self)
531 self.container = ['pears', 'apples']
532 for c in self.container:
533 self.client.create_container(c)
535 for o in o_names[:8]:
536 self.obj.append(self.upload_random_data(self.container[0], o))
537 for o in o_names[8:]:
538 self.obj.append(self.upload_random_data(self.container[1], o))
540 def test_list_objects(self):
541 objects = self.client.list_objects(self.container[0])
542 l = [elem['name'] for elem in self.obj[:8]]
544 self.assertEqual(objects, l)
546 def test_list_objects_containing_slash(self):
547 self.client.create_container('test')
548 self.upload_random_data('test', '/objectname')
550 objects = self.client.list_objects('test')
551 self.assertEqual(objects, ['/objectname'])
553 objects = self.client.list_objects('test', format='json')
554 self.assertEqual(objects[0]['name'], '/objectname')
556 objects = self.client.list_objects('test', format='xml')
557 self.assert_extended(objects, 'xml', 'object')
558 node_name = objects.getElementsByTagName('name')[0]
559 self.assertEqual(node_name.firstChild.data, '/objectname')
561 def test_list_objects_with_limit_marker(self):
562 objects = self.client.list_objects(self.container[0], limit=2)
563 l = [elem['name'] for elem in self.obj[:8]]
565 self.assertEqual(objects, l[:2])
567 markers = ['How To Win Friends And Influence People.pdf',
571 objects = self.client.list_objects(self.container[0], limit=limit,
573 l = [elem['name'] for elem in self.obj[:8]]
575 start = l.index(m) + 1
577 end = len(l) >= end and end or len(l)
578 self.assertEqual(objects, l[start:end])
581 def _test_list_limit_exceeds(self):
582 self.client.create_container('pithos')
584 for i in range(10001):
585 self.client.create_zero_length_object('pithos', i)
587 self.assertEqual(10000, len(self.client.list_objects('pithos')))
589 def test_list_empty_params(self):
590 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
592 objects = objects.strip().split('\n')
593 self.assertEqual(objects,
594 self.client.list_objects(self.container[0]))
596 def test_list_pseudo_hierarchical_folders(self):
597 objects = self.client.list_objects(self.container[1], prefix='photos',
599 self.assertEquals(['photos/animals/', 'photos/me.jpg',
600 'photos/plants/'], objects)
602 objects = self.client.list_objects(self.container[1],
603 prefix='photos/animals',
605 l = ['photos/animals/cats/', 'photos/animals/dogs/']
606 self.assertEquals(l, objects)
608 objects = self.client.list_objects(self.container[1], path='photos')
609 self.assertEquals(['photos/me.jpg'], objects)
611 def test_extended_list_json(self):
612 objects = self.client.list_objects(self.container[1], format='json',
613 limit=2, prefix='photos/animals',
615 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
616 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
618 def test_extended_list_xml(self):
619 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
620 prefix='photos', delimiter='/')
621 self.assert_extended(xml, 'xml', 'object', size=4)
622 dirs = xml.getElementsByTagName('subdir')
623 self.assertEqual(len(dirs), 2)
624 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
625 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
627 objects = xml.getElementsByTagName('name')
628 self.assertEqual(len(objects), 1)
629 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
631 def test_list_meta_double_matching(self):
632 meta = {'quality':'aaa', 'stock':'true'}
633 self.client.update_object_metadata(self.container[0],
634 self.obj[0]['name'], **meta)
635 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
636 self.assertEqual(len(obj), 1)
637 self.assertTrue(obj, self.obj[0]['name'])
639 def test_list_using_meta(self):
640 meta = {'quality':'aaa'}
641 for o in self.obj[:2]:
642 self.client.update_object_metadata(self.container[0], o['name'],
644 meta = {'stock':'true'}
645 for o in self.obj[3:5]:
646 self.client.update_object_metadata(self.container[0], o['name'],
649 obj = self.client.list_objects(self.container[0], meta='Quality')
650 self.assertEqual(len(obj), 2)
651 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
653 # test case insensitive
654 obj = self.client.list_objects(self.container[0], meta='quality')
655 self.assertEqual(len(obj), 2)
656 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
658 # test multiple matches
659 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
660 self.assertEqual(len(obj), 4)
661 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
663 # test non 1-1 multiple match
664 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
665 self.assertEqual(len(obj), 2)
666 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
668 def test_if_modified_since(self):
669 t = datetime.datetime.utcnow()
670 t2 = t - datetime.timedelta(minutes=10)
673 self.upload_random_data(self.container[0], o_names[0])
675 for f in DATE_FORMATS:
676 past = t2.strftime(f)
678 o = self.client.list_objects(self.container[0],
679 if_modified_since=past)
681 self.client.list_objects(self.container[0]))
683 self.failIf(f.status == 304) #fail if not modified
685 def test_if_modified_since_invalid_date(self):
686 headers = {'if-modified-since':''}
687 o = self.client.list_objects(self.container[0], if_modified_since='')
688 self.assertEqual(o, self.client.list_objects(self.container[0]))
690 def test_if_not_modified_since(self):
691 now = datetime.datetime.utcnow()
692 since = now + datetime.timedelta(1)
694 for f in DATE_FORMATS:
695 args = {'if_modified_since':'%s' %since.strftime(f)}
698 self.assert_raises_fault(304, self.client.list_objects,
699 self.container[0], **args)
701 def test_if_unmodified_since(self):
702 now = datetime.datetime.utcnow()
703 since = now + datetime.timedelta(1)
705 for f in DATE_FORMATS:
706 obj = self.client.list_objects(self.container[0],
707 if_unmodified_since=since.strftime(f))
710 self.assertEqual(obj, self.client.list_objects(self.container[0]))
712 def test_if_unmodified_since_precondition_failed(self):
713 t = datetime.datetime.utcnow()
714 t2 = t - datetime.timedelta(minutes=10)
717 self.client.create_container('dummy')
719 for f in DATE_FORMATS:
720 past = t2.strftime(f)
722 args = {'if_unmodified_since':'%s' %past}
724 #assert precondition failed
725 self.assert_raises_fault(412, self.client.list_objects,
726 self.container[0], **args)
728 class ContainerPut(BaseTestCase):
730 BaseTestCase.setUp(self)
731 self.containers = ['c1', 'c2']
733 def test_create(self):
734 self.client.create_container(self.containers[0])
735 containers = self.client.list_containers()
736 self.assertTrue(self.containers[0] in containers)
737 self.assert_container_exists(self.containers[0])
739 def test_create_twice(self):
740 self.client.create_container(self.containers[0])
741 self.assertTrue(not self.client.create_container(self.containers[0]))
743 def test_quota(self):
744 self.client.create_container(self.containers[0])
746 policy = {'quota':100}
747 self.client.set_container_policies('c1', **policy)
749 meta = self.client.retrieve_container_metadata('c1')
750 self.assertTrue('x-container-policy-quota' in meta)
751 self.assertEqual(meta['x-container-policy-quota'], '100')
754 kwargs = {'length':101}
755 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
759 self.client.set_container_policies('c1', **policy)
761 class ContainerPost(BaseTestCase):
763 BaseTestCase.setUp(self)
764 self.container = 'apples'
765 self.client.create_container(self.container)
767 def test_update_meta(self):
768 meta = {'test':'test33',
770 self.client.update_container_metadata(self.container, **meta)
771 headers = self.client.retrieve_container_metadata(self.container)
772 for k,v in meta.items():
773 k = 'x-container-meta-%s' % k
774 self.assertTrue(headers[k])
775 self.assertEqual(headers[k], v)
777 class ContainerDelete(BaseTestCase):
779 BaseTestCase.setUp(self)
780 self.containers = ['c1', 'c2']
781 for c in self.containers:
782 self.client.create_container(c)
784 def test_delete(self):
785 status = self.client.delete_container(self.containers[0])[0]
786 self.assertEqual(status, 204)
788 def test_delete_non_empty(self):
789 self.upload_random_data(self.containers[1], o_names[0])
790 self.assert_raises_fault(409, self.client.delete_container,
793 def test_delete_invalid(self):
794 self.assert_raises_fault(404, self.client.delete_container, 'c3')
796 class ObjectGet(BaseTestCase):
798 BaseTestCase.setUp(self)
799 self.containers = ['c1', 'c2']
800 #create some containers
801 for c in self.containers:
802 self.client.create_container(c)
805 names = ('obj1', 'obj2')
808 self.objects.append(self.upload_random_data(self.containers[1], n))
810 def test_versions(self):
811 c = self.containers[1]
813 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
814 self.assert_versionlist_structure(b)
817 meta = {'quality':'AAA', 'stock':True}
818 self.client.update_object_metadata(c, o['name'], **meta)
820 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
821 self.assert_versionlist_structure(a)
822 self.assertEqual(len(b)+1, len(a))
823 self.assertEqual(b, a[:-1])
825 #get exact previous version metadata
827 v_meta = self.client.retrieve_object_metadata(c, o['name'],
830 for k in meta.keys():
831 self.assertTrue(k not in v_meta)
834 data = get_random_data()
835 self.client.update_object(c, o['name'], StringIO(data))
837 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
838 self.assert_versionlist_structure(aa)
839 self.assertEqual(len(a)+1, len(aa))
840 self.assertEqual(a, aa[:-1])
842 #get exact previous version
844 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
845 self.assertEqual(o['data'], v_data)
846 self.assertEqual(self.client.retrieve_object(c, o['name']),
847 '%s%s' %(v_data, data))
851 o = self.client.retrieve_object(self.containers[1],
852 self.objects[0]['name'],
853 self.objects[0]['meta'])
854 self.assertEqual(o, self.objects[0]['data'])
856 def test_objects_with_trailing_spaces(self):
857 self.client.create_container('test')
859 self.upload_random_data('test', 'a')
860 #look for 'a ' object
861 self.assert_raises_fault(404, self.client.retrieve_object,
865 self.client.delete_object('test', 'a')
866 self.assert_raises_fault(404, self.client.retrieve_object,
870 self.upload_random_data('test', 'a ')
872 self.assert_raises_fault(404, self.client.retrieve_object,
875 def test_get_invalid(self):
876 self.assert_raises_fault(404, self.client.retrieve_object,
877 self.containers[0], self.objects[0]['name'])
879 def test_get_partial(self):
880 #perform get with range
881 status, headers, data = self.client.request_object(self.containers[1],
882 self.objects[0]['name'],
885 #assert successful partial content
886 self.assertEqual(status, 206)
889 self.assertEqual(headers['content-type'],
890 self.objects[0]['meta']['content_type'])
892 #assert content length
893 self.assertEqual(int(headers['content-length']), 500)
896 self.assertEqual(self.objects[0]['data'][:500], data)
898 def test_get_final_500(self):
899 #perform get with range
900 headers = {'range':'bytes=-500'}
901 status, headers, data = self.client.request_object(self.containers[1],
902 self.objects[0]['name'],
905 #assert successful partial content
906 self.assertEqual(status, 206)
909 self.assertEqual(headers['content-type'],
910 self.objects[0]['meta']['content_type'])
912 #assert content length
913 self.assertEqual(int(headers['content-length']), 500)
916 self.assertTrue(self.objects[0]['data'][-500:], data)
918 def test_get_rest(self):
919 #perform get with range
920 offset = len(self.objects[0]['data']) - 500
921 status, headers, data = self.client.request_object(self.containers[1],
922 self.objects[0]['name'],
923 range='bytes=%s-' %offset)
925 #assert successful partial content
926 self.assertEqual(status, 206)
929 self.assertEqual(headers['content-type'],
930 self.objects[0]['meta']['content_type'])
932 #assert content length
933 self.assertEqual(int(headers['content-length']), 500)
936 self.assertTrue(self.objects[0]['data'][-500:], data)
938 def test_get_range_not_satisfiable(self):
939 #perform get with range
940 offset = len(self.objects[0]['data']) + 1
942 #assert range not satisfiable
943 self.assert_raises_fault(416, self.client.retrieve_object,
944 self.containers[1], self.objects[0]['name'],
945 range='bytes=0-%s' %offset)
947 def test_multiple_range(self):
948 #perform get with multiple range
949 ranges = ['0-499', '-500', '1000-']
950 bytes = 'bytes=%s' % ','.join(ranges)
951 status, headers, data = self.client.request_object(self.containers[1],
952 self.objects[0]['name'],
955 # assert partial content
956 self.assertEqual(status, 206)
958 # assert Content-Type of the reply will be multipart/byteranges
959 self.assertTrue(headers['content-type'])
960 content_type_parts = headers['content-type'].split()
961 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
963 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
964 cparts = data.split(boundary)[1:-1]
966 # assert content parts are exactly 2
967 self.assertEqual(len(cparts), len(ranges))
969 # for each content part assert headers
972 content = cpart.split('\r\n')
973 headers = content[1:3]
974 content_range = headers[0].split(': ')
975 self.assertEqual(content_range[0], 'Content-Range')
977 r = ranges[i].split('-')
978 if not r[0] and not r[1]:
981 start = len(self.objects[0]['data']) - int(r[1])
982 end = len(self.objects[0]['data'])
985 end = len(self.objects[0]['data'])
989 fdata = self.objects[0]['data'][start:end]
990 sdata = '\r\n'.join(content[4:-1])
991 self.assertEqual(len(fdata), len(sdata))
992 self.assertEquals(fdata, sdata)
995 def test_multiple_range_not_satisfiable(self):
996 #perform get with multiple range
997 out_of_range = len(self.objects[0]['data']) + 1
998 ranges = ['0-499', '-500', '%d-' %out_of_range]
999 bytes = 'bytes=%s' % ','.join(ranges)
1001 # assert partial content
1002 self.assert_raises_fault(416, self.client.retrieve_object,
1004 self.objects[0]['name'], range=bytes)
1006 def test_get_with_if_match(self):
1007 #perform get with If-Match
1008 etag = self.objects[0]['hash']
1009 status, headers, data = self.client.request_object(self.containers[1],
1010 self.objects[0]['name'],
1013 self.assertEqual(status, 200)
1015 #assert content-type
1016 self.assertEqual(headers['content-type'],
1017 self.objects[0]['meta']['content_type'])
1019 #assert response content
1020 self.assertEqual(self.objects[0]['data'], data)
1022 def test_get_with_if_match_star(self):
1023 #perform get with If-Match *
1024 headers = {'if-match':'*'}
1025 status, headers, data = self.client.request_object(self.containers[1],
1026 self.objects[0]['name'],
1029 self.assertEqual(status, 200)
1031 #assert content-type
1032 self.assertEqual(headers['content-type'],
1033 self.objects[0]['meta']['content_type'])
1035 #assert response content
1036 self.assertEqual(self.objects[0]['data'], data)
1038 def test_get_with_multiple_if_match(self):
1039 #perform get with If-Match
1040 etags = [i['hash'] for i in self.objects if i]
1041 etags = ','.join('"%s"' % etag for etag in etags)
1042 status, headers, data = self.client.request_object(self.containers[1],
1043 self.objects[0]['name'],
1046 self.assertEqual(status, 200)
1048 #assert content-type
1049 self.assertEqual(headers['content-type'],
1050 self.objects[0]['meta']['content_type'])
1052 #assert content-type
1053 self.assertEqual(headers['content-type'],
1054 self.objects[0]['meta']['content_type'])
1056 #assert response content
1057 self.assertEqual(self.objects[0]['data'], data)
1059 def test_if_match_precondition_failed(self):
1060 #assert precondition failed
1061 self.assert_raises_fault(412, self.client.retrieve_object,
1063 self.objects[0]['name'], if_match='123')
1065 def test_if_none_match(self):
1066 #perform get with If-None-Match
1067 status, headers, data = self.client.request_object(self.containers[1],
1068 self.objects[0]['name'],
1069 if_none_match='123')
1072 self.assertEqual(status, 200)
1074 #assert content-type
1075 self.assertEqual(headers['content_type'],
1076 self.objects[0]['meta']['content_type'])
1078 def test_if_none_match(self):
1079 #perform get with If-None-Match * and assert not modified
1080 self.assert_raises_fault(304, self.client.retrieve_object,
1082 self.objects[0]['name'],
1085 def test_if_none_match_not_modified(self):
1086 #perform get with If-None-Match and assert not modified
1087 self.assert_raises_fault(304, self.client.retrieve_object,
1089 self.objects[0]['name'],
1090 if_none_match=self.objects[0]['hash'])
1092 meta = self.client.retrieve_object_metadata(self.containers[1],
1093 self.objects[0]['name'])
1094 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1096 def test_if_modified_since(self):
1097 t = datetime.datetime.utcnow()
1098 t2 = t - datetime.timedelta(minutes=10)
1101 self.upload_data(self.containers[1],
1102 self.objects[0]['name'],
1103 self.objects[0]['data'][:200])
1105 for f in DATE_FORMATS:
1106 past = t2.strftime(f)
1108 headers = {'if-modified-since':'%s' %past}
1110 o = self.client.retrieve_object(self.containers[1],
1111 self.objects[0]['name'],
1112 if_modified_since=past)
1114 self.client.retrieve_object(self.containers[1],
1115 self.objects[0]['name']))
1117 self.failIf(f.status == 304)
1119 def test_if_modified_since_invalid_date(self):
1120 o = self.client.retrieve_object(self.containers[1],
1121 self.objects[0]['name'],
1122 if_modified_since='')
1123 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1124 self.objects[0]['name']))
1126 def test_if_not_modified_since(self):
1127 now = datetime.datetime.utcnow()
1128 since = now + datetime.timedelta(1)
1130 for f in DATE_FORMATS:
1131 #assert not modified
1132 self.assert_raises_fault(304, self.client.retrieve_object,
1133 self.containers[1], self.objects[0]['name'],
1134 if_modified_since=since.strftime(f))
1136 def test_if_unmodified_since(self):
1137 now = datetime.datetime.utcnow()
1138 since = now + datetime.timedelta(1)
1140 for f in DATE_FORMATS:
1141 t = since.strftime(f)
1142 status, headers, data = self.client.request_object(self.containers[1],
1143 self.objects[0]['name'],
1144 if_unmodified_since=t)
1146 self.assertEqual(status, 200)
1147 self.assertEqual(self.objects[0]['data'], data)
1149 #assert content-type
1150 self.assertEqual(headers['content-type'],
1151 self.objects[0]['meta']['content_type'])
1153 def test_if_unmodified_since_precondition_failed(self):
1154 t = datetime.datetime.utcnow()
1155 t2 = t - datetime.timedelta(minutes=10)
1158 self.upload_data(self.containers[1],
1159 self.objects[0]['name'],
1160 self.objects[0]['data'][:200])
1162 for f in DATE_FORMATS:
1163 past = t2.strftime(f)
1164 #assert precondition failed
1165 self.assert_raises_fault(412, self.client.retrieve_object,
1166 self.containers[1], self.objects[0]['name'],
1167 if_unmodified_since=past)
1169 def test_hashes(self):
1172 o = self.upload_random_data(self.containers[1], fname, l)
1174 body = self.client.retrieve_object(self.containers[1], fname,
1176 hashes = body['hashes']
1177 block_size = body['block_size']
1178 block_hash = body['block_hash']
1179 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1180 self.assertTrue(len(hashes), block_num)
1183 start = i * block_size
1184 end = (i + 1) * block_size
1185 hash = compute_block_hash(o['data'][start:end], block_hash)
1186 self.assertEqual(h, hash)
1189 class ObjectPut(BaseTestCase):
1191 BaseTestCase.setUp(self)
1192 self.container = 'c1'
1193 self.client.create_container(self.container)
1195 def test_upload(self):
1197 meta = {'test':'test1'}
1198 o = self.upload_random_data(self.container, name, **meta)
1200 headers = self.client.retrieve_object_metadata(self.container,
1203 self.assertTrue('test' in headers.keys())
1204 self.assertEqual(headers['test'], meta['test'])
1206 #assert uploaded content
1207 status, h, data = self.client.request_object(self.container, name)
1208 self.assertEqual(len(o['data']), int(h['content-length']))
1209 self.assertEqual(o['data'], data)
1211 #assert content-type
1212 self.assertEqual(h['content-type'], o['meta']['content_type'])
1214 def _test_maximum_upload_size_exceeds(self):
1216 meta = {'test':'test1'}
1218 length=1024*1024*100
1219 self.assert_raises_fault(400, self.upload_random_data, self.container,
1220 name, length, **meta)
1222 def test_upload_with_name_containing_slash(self):
1223 name = '/%s' % o_names[0]
1224 meta = {'test':'test1'}
1225 o = self.upload_random_data(self.container, name, **meta)
1227 self.assertEqual(o['data'],
1228 self.client.retrieve_object(self.container, name))
1230 self.assertTrue(name in self.client.list_objects(self.container))
1232 def test_create_directory_marker(self):
1233 self.client.create_directory_marker(self.container, 'foo')
1234 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1235 self.assertEqual(meta['content-length'], '0')
1236 self.assertEqual(meta['content-type'], 'application/directory')
1238 def test_upload_unprocessable_entity(self):
1239 meta={'etag':'123', 'test':'test1'}
1241 #assert unprocessable entity
1242 self.assert_raises_fault(422, self.upload_random_data, self.container,
1245 def test_chunked_transfer(self):
1246 data = get_random_data()
1248 self.client.create_object_using_chunks(self.container, objname,
1251 uploaded_data = self.client.retrieve_object(self.container, objname)
1252 self.assertEqual(data, uploaded_data)
1254 def test_manifestation(self):
1255 prefix = 'myobject/'
1258 part = '%s%d' %(prefix, i)
1259 o = self.upload_random_data(self.container, part)
1262 manifest = '%s/%s' %(self.container, prefix)
1263 self.client.create_manifestation(self.container, 'large-object', manifest)
1265 self.assert_object_exists(self.container, 'large-object')
1266 self.assertEqual(data, self.client.retrieve_object(self.container,
1269 #wrong manifestation
1270 self.client.create_manifestation(self.container, 'large-object',
1271 '%s/invalid' % self.container)
1272 self.assertEqual('', self.client.retrieve_object(self.container,
1275 def test_create_zero_length_object(self):
1278 zero = self.client.create_zero_length_object(c, o)
1279 zero_meta = self.client.retrieve_object_metadata(c, o)
1280 zero_hash = self.client.retrieve_object_hashmap(c, o)
1281 zero_data = self.client.retrieve_object(c, o)
1283 self.assertEqual(int(zero_meta['content-length']), 0)
1284 hasher = newhasher('sha256')
1286 emptyhash = hasher.digest()
1287 self.assertEqual(zero_hash, [hexlify(emptyhash)])
1288 self.assertEqual(zero_data, '')
1290 def test_create_object_by_hashmap(self):
1293 self.upload_random_data(c, o)
1294 hashmap = self.client.retrieve_object(c, o, format='json')
1296 self.client.create_object_by_hashmap(c, o2, hashmap)
1297 self.assertEqual(self.client.retrieve_object(c, o),
1298 self.client.retrieve_object(c, o))
1300 class ObjectCopy(BaseTestCase):
1302 BaseTestCase.setUp(self)
1303 self.containers = ['c1', 'c2']
1304 for c in self.containers:
1305 self.client.create_container(c)
1306 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1308 def test_copy(self):
1309 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1310 self.containers[0], self.obj['name']):
1312 meta = {'test':'testcopy'}
1313 status = self.client.copy_object(self.containers[0],
1319 #assert copy success
1320 self.assertEqual(status, 201)
1322 #assert access the new object
1323 headers = self.client.retrieve_object_metadata(self.containers[0],
1325 self.assertTrue('x-object-meta-test' in headers.keys())
1326 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1328 #assert etag is the same
1329 self.assertEqual(headers['etag'], self.obj['hash'])
1331 #assert src object still exists
1332 self.assert_object_exists(self.containers[0], self.obj['name'])
1334 def test_copy_from_different_container(self):
1335 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1336 self.containers[0], self.obj['name']):
1337 meta = {'test':'testcopy'}
1338 status = self.client.copy_object(self.containers[0],
1343 self.assertEqual(status, 201)
1345 # assert updated metadata
1346 meta = self.client.retrieve_object_metadata(self.containers[1],
1349 self.assertTrue('test' in meta.keys())
1350 self.assertTrue(meta['test'], 'testcopy')
1352 #assert src object still exists
1353 self.assert_object_exists(self.containers[0], self.obj['name'])
1355 def test_copy_invalid(self):
1356 #copy from invalid object
1357 meta = {'test':'testcopy'}
1358 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1359 'test.py', self.containers[1], 'testcopy', meta)
1361 #copy from invalid container
1362 meta = {'test':'testcopy'}
1363 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1364 self.obj['name'], self.containers[1],
1367 class ObjectMove(BaseTestCase):
1369 BaseTestCase.setUp(self)
1370 self.containers = ['c1', 'c2']
1371 for c in self.containers:
1372 self.client.create_container(c)
1373 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1375 def test_move(self):
1377 meta = {'test':'testcopy'}
1378 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1379 status = self.client.move_object(self.containers[0], self.obj['name'],
1380 self.containers[0], 'testcopy',
1383 #assert successful move
1384 self.assertEqual(status, 201)
1386 #assert updated metadata
1387 meta = self.client.retrieve_object_metadata(self.containers[0],
1390 self.assertTrue('test' in meta.keys())
1391 self.assertTrue(meta['test'], 'testcopy')
1393 #assert src object no more exists
1394 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1396 class ObjectPost(BaseTestCase):
1398 BaseTestCase.setUp(self)
1399 self.containers = ['c1', 'c2']
1400 for c in self.containers:
1401 self.client.create_container(c)
1404 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1406 def test_update_meta(self):
1407 #perform update metadata
1408 more = {'foo':'foo', 'bar':'bar'}
1409 status = self.client.update_object_metadata(self.containers[0],
1410 self.obj[0]['name'],
1412 #assert request accepted
1413 self.assertEqual(status, 202)
1415 #assert old metadata are still there
1416 headers = self.client.retrieve_object_metadata(self.containers[0],
1417 self.obj[0]['name'],
1419 #assert new metadata have been updated
1420 for k,v in more.items():
1421 self.assertTrue(k in headers.keys())
1422 self.assertTrue(headers[k], v)
1424 def test_update_object(self,
1427 instance_length = True,
1428 content_length = 500):
1429 l = len(self.obj[0]['data'])
1430 range = 'bytes %d-%d/%s' %(first_byte_pos,
1432 l if instance_length else '*')
1433 partial = last_byte_pos - first_byte_pos + 1
1434 length = first_byte_pos + partial
1435 data = get_random_data(partial)
1436 args = {'content_type':'application/octet-stream',
1437 'content_range':'%s' %range}
1439 args['content_length'] = content_length
1441 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1442 StringIO(data), **args)[0]
1444 if partial < 0 or (instance_length and l <= last_byte_pos):
1445 self.assertEqual(status, 202)
1447 self.assertEqual(status, 204)
1448 #check modified object
1449 content = self.client.retrieve_object(self.containers[0],
1450 self.obj[0]['name'])
1451 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1452 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1453 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1455 def test_update_object_lt_blocksize(self):
1456 self.test_update_object(10, 20, content_length=None)
1458 def test_update_object_gt_blocksize(self):
1459 o = self.upload_random_data(self.containers[0], o_names[1],
1460 length=4*1024*1024+5)
1461 c = self.containers[0]
1464 first_byte_pos = 4*1024*1024+1
1465 last_byte_pos = 4*1024*1024+4
1466 l = last_byte_pos - first_byte_pos + 1
1467 data = get_random_data(l)
1468 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1469 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1470 content = self.client.retrieve_object(c, o_name)
1471 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1472 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1473 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1475 def test_update_object_divided_by_blocksize(self):
1476 o = self.upload_random_data(self.containers[0], o_names[1],
1477 length=4*1024*1024+5)
1478 c = self.containers[0]
1481 first_byte_pos = 4*1024*1024
1482 last_byte_pos = 5*1024*1024
1483 l = last_byte_pos - first_byte_pos + 1
1484 data = get_random_data(l)
1485 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1486 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1487 content = self.client.retrieve_object(c, o_name)
1488 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1489 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1490 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1492 def test_update_object_no_content_length(self):
1493 self.test_update_object(content_length = None)
1495 def test_update_object_invalid_content_length(self):
1496 with AssertContentInvariant(self.client.retrieve_object,
1497 self.containers[0], self.obj[0]['name']):
1498 self.assert_raises_fault(400, self.test_update_object,
1499 content_length = 1000)
1501 def _test_update_object_invalid_range(self):
1502 with AssertContentInvariant(self.client.retrieve_object,
1503 self.containers[0], self.obj[0]['name']):
1504 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1506 def _test_update_object_invalid_range_and_length(self):
1507 with AssertContentInvariant(self.client.retrieve_object,
1508 self.containers[0], self.obj[0]['name']):
1509 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1512 def test_update_object_invalid_range_with_no_content_length(self):
1513 with AssertContentInvariant(self.client.retrieve_object,
1514 self.containers[0], self.obj[0]['name']):
1515 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1516 content_length = None)
1518 def test_update_object_out_of_limits(self):
1519 with AssertContentInvariant(self.client.retrieve_object,
1520 self.containers[0], self.obj[0]['name']):
1521 l = len(self.obj[0]['data'])
1522 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1524 def test_append(self):
1525 data = get_random_data(500)
1527 self.client.update_object(self.containers[0], self.obj[0]['name'],
1528 StringIO(data), content_length=500,
1529 content_type='application/octet-stream')
1531 content = self.client.retrieve_object(self.containers[0],
1532 self.obj[0]['name'])
1533 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1534 self.assertEqual(content[:-500], self.obj[0]['data'])
1536 def test_update_with_chunked_transfer(self):
1537 data = get_random_data(500)
1539 fl = len(self.obj[0]['data'])
1541 self.client.update_object_using_chunks(self.containers[0],
1542 self.obj[0]['name'],
1545 content_type='application/octet-stream')
1547 #check modified object
1548 content = self.client.retrieve_object(self.containers[0],
1549 self.obj[0]['name'])
1550 self.assertEqual(content[0:dl], data)
1551 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1553 def test_update_from_other_object(self):
1554 c = self.containers[0]
1558 source_data = self.client.retrieve_object(c, src)
1559 source_meta = self.client.retrieve_object_metadata(c, src)
1560 source_hash = self.client.retrieve_object_hashmap(c, src)
1562 #update zero length object
1563 self.client.create_zero_length_object(c, dest)
1564 source_object = '/%s/%s' % (c, src)
1565 self.client.update_from_other_source(c, dest, source_object)
1566 dest_data = self.client.retrieve_object(c, src)
1567 dest_meta = self.client.retrieve_object_metadata(c, dest)
1568 dest_hash = self.client.retrieve_object_hashmap(c, src)
1569 self.assertEqual(source_data, dest_data)
1570 self.assertEqual(source_hash, dest_hash)
1573 self.client.update_from_other_source(c, dest, source_object)
1574 content = self.client.retrieve_object(c, dest)
1575 self.assertEqual(source_data * 2, content)
1577 def test_update_range_from_other_object(self):
1578 c = self.containers[0]
1582 src = self.obj[1]['name']
1583 src_data = self.client.retrieve_object(c, src)
1585 #update zero length object
1586 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1587 source_object = '/%s/%s' % (c, src)
1588 first_byte_pos = 4*1024*1024+1
1589 last_byte_pos = 4*1024*1024+4
1590 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1591 self.client.update_from_other_source(c, dest, source_object,
1592 content_range=range)
1593 content = self.client.retrieve_object(c, dest)
1594 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1595 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1596 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1598 def test_update_hashes_from_other_object(self):
1599 c = self.containers[0]
1603 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1605 #update zero length object
1606 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1607 source_object = '/%s/%s' % (c, o_names[0])
1608 first_byte_pos = 4*1024*1024
1609 last_byte_pos = 5*1024*1024
1610 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1611 self.client.update_from_other_source(c, dest, source_object,
1612 content_range=range)
1613 content = self.client.retrieve_object(c, dest)
1614 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1615 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1616 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1619 def test_update_zero_length_object(self):
1620 c = self.containers[0]
1623 zero = self.client.create_zero_length_object(c, o)
1625 data = get_random_data()
1626 self.client.update_object(c, o, StringIO(data))
1627 self.client.create_object(c, other, StringIO(data))
1629 self.assertEqual(self.client.retrieve_object(c, o),
1630 self.client.retrieve_object(c, other))
1632 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1633 self.client.retrieve_object_hashmap(c, other))
1635 class ObjectDelete(BaseTestCase):
1637 BaseTestCase.setUp(self)
1638 self.containers = ['c1', 'c2']
1639 for c in self.containers:
1640 self.client.create_container(c)
1641 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1643 def test_delete(self):
1644 #perform delete object
1645 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1647 def test_delete_invalid(self):
1648 #assert item not found
1649 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1652 class ListSharing(BaseTestCase):
1654 BaseTestCase.setUp(self)
1656 self.client.create_container('c%s' %i)
1657 self.client.create_container('c')
1659 self.upload_random_data('c1', 'o%s' %i)
1660 accounts = OTHER_ACCOUNTS.copy()
1661 self.o1_sharing_with = accounts.popitem()
1662 self.o1_sharing = [self.o1_sharing_with[1]]
1663 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1667 l.append(accounts.popitem())
1669 def test_list_other_shared(self):
1670 self.other = Pithos_Client(get_server(),
1671 self.o1_sharing_with[0],
1672 self.o1_sharing_with[1],
1674 self.assertTrue(get_user() in self.other.list_shared_by_others())
1676 def test_list_my_shared(self):
1677 my_shared_containers = self.client.list_containers(shared=True)
1678 self.assertTrue('c1' in my_shared_containers)
1679 self.assertTrue('c2' not in my_shared_containers)
1681 my_shared_objects = self.client.list_objects('c1', shared=True)
1682 self.assertTrue('o1' in my_shared_objects)
1683 self.assertTrue('o2' not in my_shared_objects)
1685 class TestGreek(BaseTestCase):
1686 def test_create_container(self):
1687 self.client.create_container('φάκελος')
1688 self.assert_container_exists('φάκελος')
1690 self.assertTrue('φάκελος' in self.client.list_containers())
1692 def test_create_object(self):
1693 self.client.create_container('φάκελος')
1694 self.upload_random_data('φάκελος', 'αντικείμενο')
1696 self.assert_object_exists('φάκελος', 'αντικείμενο')
1697 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1699 def test_copy_object(self):
1700 src_container = 'φάκελος'
1701 src_object = 'αντικείμενο'
1702 dest_container = 'αντίγραφα'
1703 dest_object = 'ασφαλές-αντίγραφο'
1705 self.client.create_container(src_container)
1706 self.upload_random_data(src_container, src_object)
1708 self.client.create_container(dest_container)
1709 self.client.copy_object(src_container, src_object, dest_container,
1712 self.assert_object_exists(src_container, src_object)
1713 self.assert_object_exists(dest_container, dest_object)
1714 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1716 def test_move_object(self):
1717 src_container = 'φάκελος'
1718 src_object = 'αντικείμενο'
1719 dest_container = 'αντίγραφα'
1720 dest_object = 'ασφαλές-αντίγραφο'
1722 self.client.create_container(src_container)
1723 self.upload_random_data(src_container, src_object)
1725 self.client.create_container(dest_container)
1726 self.client.move_object(src_container, src_object, dest_container,
1729 self.assert_object_not_exists(src_container, src_object)
1730 self.assert_object_exists(dest_container, dest_object)
1731 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1733 def test_delete_object(self):
1734 self.client.create_container('φάκελος')
1735 self.upload_random_data('φάκελος', 'αντικείμενο')
1736 self.assert_object_exists('φάκελος', 'αντικείμενο')
1738 self.client.delete_object('φάκελος', 'αντικείμενο')
1739 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1740 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1742 def test_delete_container(self):
1743 self.client.create_container('φάκελος')
1744 self.assert_container_exists('φάκελος')
1746 self.client.delete_container('φάκελος')
1747 self.assert_container_not_exists('φάκελος')
1748 self.assertTrue('φάκελος' not in self.client.list_containers())
1750 def test_account_meta(self):
1751 meta = {'ποιότητα':'ΑΑΑ'}
1752 self.client.update_account_metadata(**meta)
1753 meta = self.client.retrieve_account_metadata(restricted=True)
1754 self.assertTrue('ποιότητα' in meta.keys())
1755 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1757 def test_container_meta(self):
1758 meta = {'ποιότητα':'ΑΑΑ'}
1759 self.client.create_container('φάκελος', **meta)
1761 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1762 self.assertTrue('ποιότητα' in meta.keys())
1763 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1765 def test_object_meta(self):
1766 self.client.create_container('φάκελος')
1767 meta = {'ποιότητα':'ΑΑΑ'}
1768 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1770 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1772 self.assertTrue('ποιότητα' in meta.keys())
1773 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1775 def test_list_meta_filtering(self):
1776 self.client.create_container('φάκελος')
1777 meta = {'ποιότητα':'ΑΑΑ'}
1778 self.upload_random_data('φάκελος', 'ο1', **meta)
1779 self.upload_random_data('φάκελος', 'ο2')
1780 self.upload_random_data('φάκελος', 'ο3')
1782 meta = {'ποσότητα':'μεγάλη'}
1783 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1784 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1785 self.assertTrue('ο1' in objects)
1786 self.assertTrue('ο2' in objects)
1787 self.assertTrue('ο3' not in objects)
1789 def test_groups(self):
1791 groups = {'σεφς':'chazapis,διογένης'}
1792 self.client.set_account_groups(**groups)
1793 groups.update(self.initial_groups)
1794 self.assertEqual(groups['σεφς'],
1795 self.client.retrieve_account_groups()['σεφς'])
1798 self.client.create_container('φάκελος')
1799 o = self.upload_random_data('φάκελος', 'ο1')
1800 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1801 chef = Pithos_Client(get_server(),
1805 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1806 'φάκελος', 'ο1', account=get_user())
1809 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1810 new_data = get_random_data()
1811 self.assert_not_raises_fault(403, chef.update_object,
1812 'φάκελος', 'ο1', StringIO(new_data),
1815 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1816 self.assertEqual(server_data[:len(o['data'])], o['data'])
1817 self.assertEqual(server_data[len(o['data']):], new_data)
1819 def test_manifestation(self):
1820 self.client.create_container('κουβάς')
1824 part = '%s%d' %(prefix, i)
1825 o = self.upload_random_data('κουβάς', part)
1828 self.client.create_container('φάκελος')
1829 manifest = '%s/%s' %('κουβάς', prefix)
1830 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1832 self.assert_object_exists('φάκελος', 'άπαντα')
1833 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1836 #wrong manifestation
1837 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1838 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1840 def test_update_from_another_object(self):
1841 self.client.create_container('κουβάς')
1842 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1843 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1844 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1845 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1848 self.client.retrieve_object('κουβάς', 'νέο'),
1849 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1851 class TestPermissions(BaseTestCase):
1853 BaseTestCase.setUp(self)
1856 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1857 groups = {'pithosdev':','.join(self.authorized)}
1858 self.client.set_account_groups(**groups)
1860 def assert_read(self, authorized=[], any=False):
1861 for token, account in OTHER_ACCOUNTS.items():
1862 cl = Pithos_Client(get_server(), token, account, get_api())
1863 if account in authorized or any:
1864 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1865 'c', 'o', account=get_user())
1867 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1868 'c', 'o', account=get_user())
1871 o = self.upload_random_data('c', 'o/also-shared')
1872 for token, account in OTHER_ACCOUNTS.items():
1873 cl = Pithos_Client(get_server(), token, account, get_api())
1874 if account in authorized or any:
1875 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1876 'c', 'o/also-shared', account=get_user())
1878 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1879 'c', 'o/also-shared', account=get_user())
1881 def assert_write(self, o_data, authorized=[], any=False):
1882 for token, account in OTHER_ACCOUNTS.items():
1883 cl = Pithos_Client(get_server(), token, account, get_api())
1884 new_data = get_random_data()
1885 if account in authorized or any:
1887 self.assert_not_raises_fault(403, cl.update_object,
1888 'c', 'o', StringIO(new_data),
1892 server_data = cl.retrieve_object('c', 'o', account=get_user())
1893 self.assertEqual(o_data, server_data[:len(o_data)])
1894 self.assertEqual(new_data, server_data[len(o_data):])
1895 o_data = server_data
1897 self.failIf(f.status == 403)
1899 self.assert_raises_fault(403, cl.update_object,
1900 'c', 'o', StringIO(new_data),
1904 o = self.upload_random_data('c', 'o/also-shared')
1906 for token, account in OTHER_ACCOUNTS.items():
1907 cl = Pithos_Client(get_server(), token, account, get_api())
1908 new_data = get_random_data()
1909 if account in authorized or any:
1911 self.assert_not_raises_fault(403, cl.update_object,
1916 server_data = cl.retrieve_object('c', o['name'], account=get_user())
1917 self.assertEqual(o_data, server_data[:len(o_data)])
1918 self.assertEqual(new_data, server_data[len(o_data):])
1919 o_data = server_data
1921 self.failIf(f.status == 403)
1923 self.assert_raises_fault(403, cl.update_object,
1928 def test_group_read(self):
1929 self.client.create_container('c')
1930 o = self.upload_random_data('c', 'o')
1931 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1932 self.assert_read(authorized=self.authorized)
1934 def test_read_many(self):
1936 self.client.create_container('c')
1937 o = self.upload_random_data('c', 'o')
1938 self.client.share_object('c', 'o', self.authorized)
1939 self.assert_read(authorized=self.authorized)
1941 def test_read_by_everyone(self):
1942 self.client.create_container('c')
1943 o = self.upload_random_data('c', 'o')
1944 self.client.share_object('c', 'o', ['*'])
1945 self.assert_read(any=True)
1947 def test_group_write(self):
1948 self.client.create_container('c')
1949 o = self.upload_random_data('c', 'o')
1950 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1951 self.assert_write(o['data'], authorized=self.authorized)
1953 def test_write_many(self):
1954 self.client.create_container('c')
1955 o = self.upload_random_data('c', 'o')
1956 self.client.share_object('c', 'o', self.authorized, read=False)
1957 self.assert_write(o['data'], authorized=self.authorized)
1959 def test_write_by_everyone(self):
1960 self.client.create_container('c')
1961 o = self.upload_random_data('c', 'o')
1962 self.client.share_object('c', 'o', ['*'], read=False)
1964 self.assert_write(o['data'], any=True)
1966 class TestPublish(BaseTestCase):
1967 def test_publish(self):
1968 self.client.create_container('c')
1969 o_data = self.upload_random_data('c', 'o')['data']
1970 self.client.publish_object('c', 'o')
1971 meta = self.client.retrieve_object_metadata('c', 'o')
1972 self.assertTrue('x-object-public' in meta)
1973 url = '/public/%s/c/o' % get_user()
1974 self.assertEqual(meta['x-object-public'], url)
1975 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
1976 data = public_client.get(url)[2]
1977 self.assertEqual(o_data, data)
1979 class AssertMappingInvariant(object):
1980 def __init__(self, callable, *args, **kwargs):
1981 self.callable = callable
1983 self.kwargs = kwargs
1985 def __enter__(self):
1986 self.map = self.callable(*self.args, **self.kwargs)
1989 def __exit__(self, type, value, tb):
1990 map = self.callable(*self.args, **self.kwargs)
1991 for k in self.map.keys():
1992 if is_date(self.map[k]):
1994 assert map[k] == self.map[k]
1996 class AssertContentInvariant(object):
1997 def __init__(self, callable, *args, **kwargs):
1998 self.callable = callable
2000 self.kwargs = kwargs
2002 def __enter__(self):
2003 self.content = self.callable(*self.args, **self.kwargs)[2]
2006 def __exit__(self, type, value, tb):
2007 content = self.callable(*self.args, **self.kwargs)[2]
2008 assert self.content == content
2010 def get_content_splitted(response):
2012 return response.content.split('\n')
2014 def compute_md5_hash(data):
2018 return md5.hexdigest().lower()
2020 def compute_block_hash(data, algorithm):
2021 h = hashlib.new(algorithm)
2022 h.update(data.rstrip('\x00'))
2023 return h.hexdigest()
2025 def get_random_data(length=500):
2026 char_set = string.ascii_uppercase + string.digits
2027 return ''.join(random.choice(char_set) for x in range(length))
2030 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2031 __D = r'(?P<day>\d{2})'
2032 __D2 = r'(?P<day>[ \d]\d)'
2033 __M = r'(?P<mon>\w{3})'
2034 __Y = r'(?P<year>\d{4})'
2035 __Y2 = r'(?P<year>\d{2})'
2036 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2037 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2038 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2039 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2040 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2041 m = regex.match(date)
2046 o_names = ['kate.jpg',
2047 'kate_beckinsale.jpg',
2048 'How To Win Friends And Influence People.pdf',
2049 'moms_birthday.jpg',
2051 'Disturbed - Down With The Sickness.mp3',
2052 'army_of_darkness.avi',
2054 'photos/animals/dogs/poodle.jpg',
2055 'photos/animals/dogs/terrier.jpg',
2056 'photos/animals/cats/persian.jpg',
2057 'photos/animals/cats/siamese.jpg',
2058 'photos/plants/fern.jpg',
2059 'photos/plants/rose.jpg',
2062 if __name__ == "__main__":
2063 if get_user() == 'test':
2066 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()