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 self.failUnless(f.status == status)
200 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
202 asserts that a Fault with a specific status is not raised
203 when callableObj is called with the specific arguments
206 r = callableObj(*args, **kwargs)
208 self.failIfEqual(f.status, status)
210 def assert_container_exists(self, container):
212 asserts the existence of a container
215 self.client.retrieve_container_metadata(container)
217 self.failIf(f.status == 404)
219 def assert_container_not_exists(self, container):
221 asserts there is no such a container
223 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
226 def assert_object_exists(self, container, object):
228 asserts the existence of an object
231 self.client.retrieve_object_metadata(container, object)
233 self.failIf(f.status == 404)
235 def assert_object_not_exists(self, container, object):
237 asserts there is no such an object
239 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
242 def upload_random_data(self, container, name, length=1024, type=None,
244 data = get_random_data(length)
245 return self.upload_data(container, name, data, type, enc, **meta)
247 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
253 obj['hash'] = compute_md5_hash(obj['data'])
256 args['etag'] = etag if etag else obj['hash']
259 guess = mimetypes.guess_type(name)
260 type = type if type else guess[0]
261 enc = enc if enc else guess[1]
264 args['content_type'] = type if type else 'plain/text'
265 args['content_encoding'] = enc if enc else None
269 path = '/%s/%s' % (container, name)
270 self.client.create_object(container, name, StringIO(obj['data']),
277 class AccountHead(BaseTestCase):
279 BaseTestCase.setUp(self)
280 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
281 for item in self.containers:
282 self.client.create_container(item)
284 #keep track of initial account groups
285 self.initial_groups = self.client.retrieve_account_groups()
287 #keep track of initial account meta
288 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
291 self.client.update_account_metadata(**meta)
292 self.updated_meta = self.initial_meta.update(meta)
295 #delete additionally created meta
297 for m in self.client.retrieve_account_metadata(restricted=True):
298 if m not in self.initial_meta:
300 self.client.delete_account_metadata(l)
302 #delete additionally created groups
304 for g in self.client.retrieve_account_groups():
305 if g not in self.initial_groups:
307 self.client.unset_account_groups(l)
309 BaseTestCase.tearDown(self)
311 def test_get_account_meta(self):
312 meta = self.client.retrieve_account_metadata()
314 containers = self.client.list_containers()
315 l = str(len(containers))
316 self.assertEqual(meta['x-account-container-count'], l)
319 m = self.client.retrieve_container_metadata(c)
320 size = size + int(m['x-container-bytes-used'])
321 self.assertEqual(meta['x-account-bytes-used'], str(size))
323 def test_get_account_401(self):
324 self.assert_raises_fault(401,
325 self.invalid_client.retrieve_account_metadata)
327 def test_get_account_meta_until(self):
328 t = datetime.datetime.utcnow()
329 past = t - datetime.timedelta(minutes=-15)
330 past = int(_time.mktime(past.timetuple()))
332 meta = {'premium':True}
333 self.client.update_account_metadata(**meta)
334 meta = self.client.retrieve_account_metadata(restricted=True,
336 self.assertTrue('premium' not in meta)
338 meta = self.client.retrieve_account_metadata(restricted=True)
339 self.assertTrue('premium' in meta)
341 def test_get_account_meta_until_invalid_date(self):
342 meta = {'premium':True}
343 self.client.update_account_metadata(**meta)
344 meta = self.client.retrieve_account_metadata(restricted=True,
346 self.assertTrue('premium' in meta)
348 class AccountGet(BaseTestCase):
350 BaseTestCase.setUp(self)
351 #create some containers
352 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
353 for item in self.containers:
354 self.client.create_container(item)
358 containers = self.client.list_containers()
359 self.assertEquals(self.containers, containers)
361 def test_list_401(self):
362 self.assert_raises_fault(401, self.invalid_client.list_containers)
364 def test_list_with_limit(self):
366 containers = self.client.list_containers(limit=limit)
367 self.assertEquals(len(containers), limit)
368 self.assertEquals(self.containers[:2], containers)
370 def test_list_with_marker(self):
373 containers = self.client.list_containers(limit=l, marker=m)
374 i = self.containers.index(m) + 1
375 self.assertEquals(self.containers[i:(i+l)], containers)
378 containers = self.client.list_containers(limit=l, marker=m)
379 i = self.containers.index(m) + 1
380 self.assertEquals(self.containers[i:(i+l)], containers)
382 def test_list_json_with_marker(self):
385 containers = self.client.list_containers(limit=l, marker=m, format='json')
386 self.assert_extended(containers, 'json', 'container', l)
387 self.assertEqual(containers[0]['name'], 'kiwis')
388 self.assertEqual(containers[1]['name'], 'oranges')
390 def test_list_xml_with_marker(self):
393 xml = self.client.list_containers(limit=l, marker=m, format='xml')
394 self.assert_extended(xml, 'xml', 'container', l)
395 nodes = xml.getElementsByTagName('name')
396 self.assertEqual(len(nodes), 1)
397 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
399 def test_if_modified_since(self):
400 t = datetime.datetime.utcnow()
401 t2 = t - datetime.timedelta(minutes=10)
404 self.client.create_container('dummy')
406 for f in DATE_FORMATS:
407 past = t2.strftime(f)
409 c = self.client.list_containers(if_modified_since=past)
410 self.assertEqual(len(c), len(self.containers) + 1)
412 self.failIf(f.status == 304) #fail if not modified
414 def test_if_modified_since_invalid_date(self):
415 c = self.client.list_containers(if_modified_since='')
416 self.assertEqual(len(c), len(self.containers))
418 def test_if_not_modified_since(self):
419 now = datetime.datetime.utcnow()
420 since = now + datetime.timedelta(1)
422 for f in DATE_FORMATS:
423 args = {'if_modified_since':'%s' %since.strftime(f)}
426 self.assert_raises_fault(304, self.client.list_containers, **args)
428 def test_if_unmodified_since(self):
429 now = datetime.datetime.utcnow()
430 since = now + datetime.timedelta(1)
432 for f in DATE_FORMATS:
433 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
436 self.assertEqual(self.containers, c)
438 def test_if_unmodified_since_precondition_failed(self):
439 t = datetime.datetime.utcnow()
440 t2 = t - datetime.timedelta(minutes=10)
443 self.client.create_container('dummy')
445 for f in DATE_FORMATS:
446 past = t2.strftime(f)
448 args = {'if_unmodified_since':'%s' %past}
450 #assert precondition failed
451 self.assert_raises_fault(412, self.client.list_containers, **args)
453 class AccountPost(BaseTestCase):
455 BaseTestCase.setUp(self)
456 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
457 for item in self.containers:
458 self.client.create_container(item)
460 #keep track of initial account groups
461 self.initial_groups = self.client.retrieve_account_groups()
463 #keep track of initial account meta
464 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
467 self.client.update_account_metadata(**meta)
468 self.updated_meta = self.initial_meta.update(meta)
471 #delete additionally created meta
473 for m in self.client.retrieve_account_metadata(restricted=True):
474 if m not in self.initial_meta:
476 self.client.delete_account_metadata(l)
478 #delete additionally created groups
480 for g in self.client.retrieve_account_groups():
481 if g not in self.initial_groups:
483 self.client.unset_account_groups(l)
485 BaseTestCase.tearDown(self)
487 def test_update_meta(self):
488 with AssertMappingInvariant(self.client.retrieve_account_groups):
489 meta = {'test':'test', 'tost':'tost'}
490 self.client.update_account_metadata(**meta)
492 meta.update(self.initial_meta)
493 self.assertEqual(meta,
494 self.client.retrieve_account_metadata(
497 def test_invalid_account_update_meta(self):
498 meta = {'test':'test', 'tost':'tost'}
499 self.assert_raises_fault(401,
500 self.invalid_client.update_account_metadata,
503 def test_reset_meta(self):
504 with AssertMappingInvariant(self.client.retrieve_account_groups):
505 meta = {'test':'test', 'tost':'tost'}
506 self.client.update_account_metadata(**meta)
508 meta = {'test':'test33'}
509 self.client.reset_account_metadata(**meta)
511 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
513 def test_delete_meta(self):
514 with AssertMappingInvariant(self.client.retrieve_account_groups):
515 meta = {'test':'test', 'tost':'tost'}
516 self.client.update_account_metadata(**meta)
518 self.client.delete_account_metadata(meta.keys())
520 account_meta = self.client.retrieve_account_metadata(restricted=True)
522 self.assertTrue(m not in account_meta.keys())
524 def test_set_account_groups(self):
525 with AssertMappingInvariant(self.client.retrieve_account_metadata):
526 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
527 self.client.set_account_groups(**groups)
529 self.assertEqual(set(groups['pithosdev']),
530 set(self.client.retrieve_account_groups()['pithosdev']))
532 more_groups = {'clientsdev':'pkanavos,mvasilak'}
533 self.client.set_account_groups(**more_groups)
535 groups.update(more_groups)
536 self.assertEqual(set(groups['clientsdev']),
537 set(self.client.retrieve_account_groups()['clientsdev']))
539 def test_reset_account_groups(self):
540 with AssertMappingInvariant(self.client.retrieve_account_metadata):
541 groups = {'pithosdev':'verigak,gtsouk,chazapis',
542 'clientsdev':'pkanavos,mvasilak'}
543 self.client.set_account_groups(**groups)
545 self.assertEqual(set(groups['pithosdev'].split(',')),
546 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
547 self.assertEqual(set(groups['clientsdev'].split(',')),
548 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
550 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
551 self.client.reset_account_groups(**groups)
553 self.assertEqual(set(groups['pithosdev'].split(',')),
554 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
556 def test_delete_account_groups(self):
557 with AssertMappingInvariant(self.client.retrieve_account_metadata):
558 groups = {'pithosdev':'verigak,gtsouk,chazapis',
559 'clientsdev':'pkanavos,mvasilak'}
560 self.client.set_account_groups(**groups)
562 self.client.unset_account_groups(groups.keys())
564 self.assertEqual({}, self.client.retrieve_account_groups())
566 class ContainerHead(BaseTestCase):
568 BaseTestCase.setUp(self)
569 self.container = 'apples'
570 self.client.create_container(self.container)
572 def test_get_meta(self):
573 meta = {'trash':'true'}
574 t1 = datetime.datetime.utcnow()
575 o = self.upload_random_data(self.container, o_names[0], **meta)
577 headers = self.client.retrieve_container_metadata(self.container)
578 self.assertEqual(headers['x-container-object-count'], '1')
579 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
580 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
582 threashold = datetime.timedelta(seconds=1)
583 self.assertTrue(delta < threashold)
584 self.assertTrue(headers['x-container-object-meta'])
585 self.assertTrue('Trash' in headers['x-container-object-meta'])
587 class ContainerGet(BaseTestCase):
589 BaseTestCase.setUp(self)
590 self.container = ['pears', 'apples']
591 for c in self.container:
592 self.client.create_container(c)
594 for o in o_names[:8]:
595 self.obj.append(self.upload_random_data(self.container[0], o))
596 for o in o_names[8:]:
597 self.obj.append(self.upload_random_data(self.container[1], o))
599 def test_list_objects(self):
600 objects = self.client.list_objects(self.container[0])
601 l = [elem['name'] for elem in self.obj[:8]]
603 self.assertEqual(objects, l)
605 def test_list_objects_containing_slash(self):
606 self.client.create_container('test')
607 self.upload_random_data('test', '/objectname')
609 objects = self.client.list_objects('test')
610 self.assertEqual(objects, ['/objectname'])
612 objects = self.client.list_objects('test', format='json')
613 self.assertEqual(objects[0]['name'], '/objectname')
615 objects = self.client.list_objects('test', format='xml')
616 self.assert_extended(objects, 'xml', 'object')
617 node_name = objects.getElementsByTagName('name')[0]
618 self.assertEqual(node_name.firstChild.data, '/objectname')
620 #objects = self.client.list_objects('test', prefix='/')
621 #self.assertEqual(objects, ['/objectname'])
623 #objects = self.client.list_objects('test', path='/')
624 #self.assertEqual(objects, ['/objectname'])
626 #objects = self.client.list_objects('test', prefix='/', delimiter='n')
627 #self.assertEqual(objects, ['/object'])
629 def test_list_objects_with_limit_marker(self):
630 objects = self.client.list_objects(self.container[0], limit=2)
631 l = [elem['name'] for elem in self.obj[:8]]
633 self.assertEqual(objects, l[:2])
635 markers = ['How To Win Friends And Influence People.pdf',
639 objects = self.client.list_objects(self.container[0], limit=limit,
641 l = [elem['name'] for elem in self.obj[:8]]
643 start = l.index(m) + 1
645 end = len(l) >= end and end or len(l)
646 self.assertEqual(objects, l[start:end])
649 def _test_list_limit_exceeds(self):
650 self.client.create_container('pithos')
652 for i in range(10001):
653 self.client.create_zero_length_object('pithos', i)
655 self.assertEqual(10000, len(self.client.list_objects('pithos')))
657 def test_list_empty_params(self):
658 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
660 objects = objects.strip().split('\n')
661 self.assertEqual(objects,
662 self.client.list_objects(self.container[0]))
664 def test_list_pseudo_hierarchical_folders(self):
665 objects = self.client.list_objects(self.container[1], prefix='photos',
667 self.assertEquals(['photos/animals/', 'photos/me.jpg',
668 'photos/plants/'], objects)
670 objects = self.client.list_objects(self.container[1],
671 prefix='photos/animals',
673 l = ['photos/animals/cats/', 'photos/animals/dogs/']
674 self.assertEquals(l, objects)
676 objects = self.client.list_objects(self.container[1], path='photos')
677 self.assertEquals(['photos/me.jpg'], objects)
679 def test_extended_list_json(self):
680 objects = self.client.list_objects(self.container[1], format='json',
681 limit=2, prefix='photos/animals',
683 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
684 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
686 def test_extended_list_xml(self):
687 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
688 prefix='photos', delimiter='/')
689 self.assert_extended(xml, 'xml', 'object', size=4)
690 dirs = xml.getElementsByTagName('subdir')
691 self.assertEqual(len(dirs), 2)
692 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
693 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
695 objects = xml.getElementsByTagName('name')
696 self.assertEqual(len(objects), 1)
697 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
699 def test_list_meta_double_matching(self):
700 meta = {'quality':'aaa', 'stock':'true'}
701 self.client.update_object_metadata(self.container[0],
702 self.obj[0]['name'], **meta)
703 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
704 self.assertEqual(len(obj), 1)
705 self.assertTrue(obj, self.obj[0]['name'])
707 def test_list_using_meta(self):
708 meta = {'quality':'aaa'}
709 for o in self.obj[:2]:
710 self.client.update_object_metadata(self.container[0], o['name'],
712 meta = {'stock':'true'}
713 for o in self.obj[3:5]:
714 self.client.update_object_metadata(self.container[0], o['name'],
717 obj = self.client.list_objects(self.container[0], meta='Quality')
718 self.assertEqual(len(obj), 2)
719 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
721 # test case insensitive
722 obj = self.client.list_objects(self.container[0], meta='quality')
723 self.assertEqual(len(obj), 2)
724 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
726 # test multiple matches
727 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
728 self.assertEqual(len(obj), 4)
729 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
731 # test non 1-1 multiple match
732 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
733 self.assertEqual(len(obj), 2)
734 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
736 def test_if_modified_since(self):
737 t = datetime.datetime.utcnow()
738 t2 = t - datetime.timedelta(minutes=10)
741 self.upload_random_data(self.container[0], o_names[0])
743 for f in DATE_FORMATS:
744 past = t2.strftime(f)
746 o = self.client.list_objects(self.container[0],
747 if_modified_since=past)
749 self.client.list_objects(self.container[0]))
751 self.failIf(f.status == 304) #fail if not modified
753 def test_if_modified_since_invalid_date(self):
754 headers = {'if-modified-since':''}
755 o = self.client.list_objects(self.container[0], if_modified_since='')
756 self.assertEqual(o, self.client.list_objects(self.container[0]))
758 def test_if_not_modified_since(self):
759 now = datetime.datetime.utcnow()
760 since = now + datetime.timedelta(1)
762 for f in DATE_FORMATS:
763 args = {'if_modified_since':'%s' %since.strftime(f)}
766 self.assert_raises_fault(304, self.client.list_objects,
767 self.container[0], **args)
769 def test_if_unmodified_since(self):
770 now = datetime.datetime.utcnow()
771 since = now + datetime.timedelta(1)
773 for f in DATE_FORMATS:
774 obj = self.client.list_objects(self.container[0],
775 if_unmodified_since=since.strftime(f))
778 self.assertEqual(obj, self.client.list_objects(self.container[0]))
780 def test_if_unmodified_since_precondition_failed(self):
781 t = datetime.datetime.utcnow()
782 t2 = t - datetime.timedelta(minutes=10)
785 self.client.create_container('dummy')
787 for f in DATE_FORMATS:
788 past = t2.strftime(f)
790 args = {'if_unmodified_since':'%s' %past}
792 #assert precondition failed
793 self.assert_raises_fault(412, self.client.list_objects,
794 self.container[0], **args)
796 class ContainerPut(BaseTestCase):
798 BaseTestCase.setUp(self)
799 self.containers = ['c1', 'c2']
801 def test_create(self):
802 self.client.create_container(self.containers[0])
803 containers = self.client.list_containers()
804 self.assertTrue(self.containers[0] in containers)
805 self.assert_container_exists(self.containers[0])
807 def test_create_twice(self):
808 self.client.create_container(self.containers[0])
809 self.assertTrue(not self.client.create_container(self.containers[0]))
811 class ContainerPost(BaseTestCase):
813 BaseTestCase.setUp(self)
814 self.container = 'apples'
815 self.client.create_container(self.container)
817 def test_update_meta(self):
818 meta = {'test':'test33',
820 self.client.update_container_metadata(self.container, **meta)
821 headers = self.client.retrieve_container_metadata(self.container)
822 for k,v in meta.items():
823 k = 'x-container-meta-%s' % k
824 self.assertTrue(headers[k])
825 self.assertEqual(headers[k], v)
827 class ContainerDelete(BaseTestCase):
829 BaseTestCase.setUp(self)
830 self.containers = ['c1', 'c2']
831 for c in self.containers:
832 self.client.create_container(c)
833 self.upload_random_data(self.containers[1], o_names[0])
835 def test_delete(self):
836 status = self.client.delete_container(self.containers[0])[0]
837 self.assertEqual(status, 204)
839 def test_delete_non_empty(self):
840 self.assert_raises_fault(409, self.client.delete_container,
843 def test_delete_invalid(self):
844 self.assert_raises_fault(404, self.client.delete_container, 'c3')
846 class ObjectHead(BaseTestCase):
849 class ObjectGet(BaseTestCase):
851 BaseTestCase.setUp(self)
852 self.containers = ['c1', 'c2']
853 #create some containers
854 for c in self.containers:
855 self.client.create_container(c)
858 names = ('obj1', 'obj2')
861 self.objects.append(self.upload_random_data(self.containers[1], n))
865 o = self.client.retrieve_object(self.containers[1],
866 self.objects[0]['name'],
867 self.objects[0]['meta'])
868 self.assertEqual(o, self.objects[0]['data'])
870 def test_get_invalid(self):
871 self.assert_raises_fault(404, self.client.retrieve_object,
872 self.containers[0], self.objects[0]['name'])
874 def test_get_partial(self):
875 #perform get with range
876 status, headers, data = self.client.request_object(self.containers[1],
877 self.objects[0]['name'],
880 #assert successful partial content
881 self.assertEqual(status, 206)
884 self.assertEqual(headers['content-type'],
885 self.objects[0]['meta']['content_type'])
887 #assert content length
888 self.assertEqual(int(headers['content-length']), 500)
891 self.assertEqual(self.objects[0]['data'][:500], data)
893 def test_get_final_500(self):
894 #perform get with range
895 headers = {'range':'bytes=-500'}
896 status, headers, data = self.client.request_object(self.containers[1],
897 self.objects[0]['name'],
900 #assert successful partial content
901 self.assertEqual(status, 206)
904 self.assertEqual(headers['content-type'],
905 self.objects[0]['meta']['content_type'])
907 #assert content length
908 self.assertEqual(int(headers['content-length']), 500)
911 self.assertTrue(self.objects[0]['data'][-500:], data)
913 def test_get_rest(self):
914 #perform get with range
915 offset = len(self.objects[0]['data']) - 500
916 status, headers, data = self.client.request_object(self.containers[1],
917 self.objects[0]['name'],
918 range='bytes=%s-' %offset)
920 #assert successful partial content
921 self.assertEqual(status, 206)
924 self.assertEqual(headers['content-type'],
925 self.objects[0]['meta']['content_type'])
927 #assert content length
928 self.assertEqual(int(headers['content-length']), 500)
931 self.assertTrue(self.objects[0]['data'][-500:], data)
933 def test_get_range_not_satisfiable(self):
934 #perform get with range
935 offset = len(self.objects[0]['data']) + 1
937 #assert range not satisfiable
938 self.assert_raises_fault(416, self.client.retrieve_object,
939 self.containers[1], self.objects[0]['name'],
940 range='bytes=0-%s' %offset)
942 def test_multiple_range(self):
943 #perform get with multiple range
944 ranges = ['0-499', '-500', '1000-']
945 bytes = 'bytes=%s' % ','.join(ranges)
946 status, headers, data = self.client.request_object(self.containers[1],
947 self.objects[0]['name'],
950 # assert partial content
951 self.assertEqual(status, 206)
953 # assert Content-Type of the reply will be multipart/byteranges
954 self.assertTrue(headers['content-type'])
955 content_type_parts = headers['content-type'].split()
956 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
958 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
959 cparts = data.split(boundary)[1:-1]
961 # assert content parts are exactly 2
962 self.assertEqual(len(cparts), len(ranges))
964 # for each content part assert headers
967 content = cpart.split('\r\n')
968 headers = content[1:3]
969 content_range = headers[0].split(': ')
970 self.assertEqual(content_range[0], 'Content-Range')
972 r = ranges[i].split('-')
973 if not r[0] and not r[1]:
976 start = len(self.objects[0]['data']) - int(r[1])
977 end = len(self.objects[0]['data'])
980 end = len(self.objects[0]['data'])
984 fdata = self.objects[0]['data'][start:end]
985 sdata = '\r\n'.join(content[4:-1])
986 self.assertEqual(len(fdata), len(sdata))
987 self.assertEquals(fdata, sdata)
990 def test_multiple_range_not_satisfiable(self):
991 #perform get with multiple range
992 out_of_range = len(self.objects[0]['data']) + 1
993 ranges = ['0-499', '-500', '%d-' %out_of_range]
994 bytes = 'bytes=%s' % ','.join(ranges)
996 # assert partial content
997 self.assert_raises_fault(416, self.client.retrieve_object,
999 self.objects[0]['name'], range=bytes)
1001 def test_get_with_if_match(self):
1002 #perform get with If-Match
1003 etag = self.objects[0]['hash']
1004 status, headers, data = self.client.request_object(self.containers[1],
1005 self.objects[0]['name'],
1008 self.assertEqual(status, 200)
1010 #assert content-type
1011 self.assertEqual(headers['content-type'],
1012 self.objects[0]['meta']['content_type'])
1014 #assert response content
1015 self.assertEqual(self.objects[0]['data'], data)
1017 def test_get_with_if_match_star(self):
1018 #perform get with If-Match *
1019 headers = {'if-match':'*'}
1020 status, headers, data = self.client.request_object(self.containers[1],
1021 self.objects[0]['name'],
1024 self.assertEqual(status, 200)
1026 #assert content-type
1027 self.assertEqual(headers['content-type'],
1028 self.objects[0]['meta']['content_type'])
1030 #assert response content
1031 self.assertEqual(self.objects[0]['data'], data)
1033 def test_get_with_multiple_if_match(self):
1034 #perform get with If-Match
1035 etags = [i['hash'] for i in self.objects if i]
1036 etags = ','.join('"%s"' % etag for etag in etags)
1037 status, headers, data = self.client.request_object(self.containers[1],
1038 self.objects[0]['name'],
1041 self.assertEqual(status, 200)
1043 #assert content-type
1044 self.assertEqual(headers['content-type'],
1045 self.objects[0]['meta']['content_type'])
1047 #assert content-type
1048 self.assertEqual(headers['content-type'],
1049 self.objects[0]['meta']['content_type'])
1051 #assert response content
1052 self.assertEqual(self.objects[0]['data'], data)
1054 def test_if_match_precondition_failed(self):
1055 #assert precondition failed
1056 self.assert_raises_fault(412, self.client.retrieve_object,
1058 self.objects[0]['name'], if_match='123')
1060 def test_if_none_match(self):
1061 #perform get with If-None-Match
1062 status, headers, data = self.client.request_object(self.containers[1],
1063 self.objects[0]['name'],
1064 if_none_match='123')
1067 self.assertEqual(status, 200)
1069 #assert content-type
1070 self.assertEqual(headers['content_type'],
1071 self.objects[0]['meta']['content_type'])
1073 def test_if_none_match(self):
1074 #perform get with If-None-Match * and assert not modified
1075 self.assert_raises_fault(304, self.client.retrieve_object,
1077 self.objects[0]['name'],
1080 def test_if_none_match_not_modified(self):
1081 #perform get with If-None-Match and assert not modified
1082 self.assert_raises_fault(304, self.client.retrieve_object,
1084 self.objects[0]['name'],
1085 if_none_match=self.objects[0]['hash'])
1087 meta = self.client.retrieve_object_metadata(self.containers[1],
1088 self.objects[0]['name'])
1089 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1091 def test_if_modified_since(self):
1092 t = datetime.datetime.utcnow()
1093 t2 = t - datetime.timedelta(minutes=10)
1096 self.upload_data(self.containers[1],
1097 self.objects[0]['name'],
1098 self.objects[0]['data'][:200])
1100 for f in DATE_FORMATS:
1101 past = t2.strftime(f)
1103 headers = {'if-modified-since':'%s' %past}
1105 o = self.client.retrieve_object(self.containers[1],
1106 self.objects[0]['name'],
1107 if_modified_since=past)
1109 self.client.retrieve_object(self.containers[1],
1110 self.objects[0]['name']))
1112 self.failIf(f.status == 304)
1114 def test_if_modified_since_invalid_date(self):
1115 o = self.client.retrieve_object(self.containers[1],
1116 self.objects[0]['name'],
1117 if_modified_since='')
1118 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1119 self.objects[0]['name']))
1121 def test_if_not_modified_since(self):
1122 now = datetime.datetime.utcnow()
1123 since = now + datetime.timedelta(1)
1125 for f in DATE_FORMATS:
1126 #assert not modified
1127 self.assert_raises_fault(304, self.client.retrieve_object,
1128 self.containers[1], self.objects[0]['name'],
1129 if_modified_since=since.strftime(f))
1131 def test_if_unmodified_since(self):
1132 now = datetime.datetime.utcnow()
1133 since = now + datetime.timedelta(1)
1135 for f in DATE_FORMATS:
1136 t = since.strftime(f)
1137 status, headers, data = self.client.request_object(self.containers[1],
1138 self.objects[0]['name'],
1139 if_unmodified_since=t)
1141 self.assertEqual(status, 200)
1142 self.assertEqual(self.objects[0]['data'], data)
1144 #assert content-type
1145 self.assertEqual(headers['content-type'],
1146 self.objects[0]['meta']['content_type'])
1148 def test_if_unmodified_since_precondition_failed(self):
1149 t = datetime.datetime.utcnow()
1150 t2 = t - datetime.timedelta(minutes=10)
1153 self.upload_data(self.containers[1],
1154 self.objects[0]['name'],
1155 self.objects[0]['data'][:200])
1157 for f in DATE_FORMATS:
1158 past = t2.strftime(f)
1159 #assert precondition failed
1160 self.assert_raises_fault(412, self.client.retrieve_object,
1161 self.containers[1], self.objects[0]['name'],
1162 if_unmodified_since=past)
1164 def test_hashes(self):
1167 o = self.upload_random_data(self.containers[1], fname, l)
1169 body = self.client.retrieve_object(self.containers[1], fname,
1171 hashes = body['hashes']
1172 block_size = body['block_size']
1173 block_hash = body['block_hash']
1174 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1175 self.assertTrue(len(hashes), block_num)
1178 start = i * block_size
1179 end = (i + 1) * block_size
1180 hash = compute_block_hash(o['data'][start:end], block_hash)
1181 self.assertEqual(h, hash)
1184 class ObjectPut(BaseTestCase):
1186 BaseTestCase.setUp(self)
1187 self.container = 'c1'
1188 self.client.create_container(self.container)
1190 def test_upload(self):
1192 meta = {'test':'test1'}
1193 o = self.upload_random_data(self.container, name, **meta)
1195 headers = self.client.retrieve_object_metadata(self.container,
1198 self.assertTrue('test' in headers.keys())
1199 self.assertEqual(headers['test'], meta['test'])
1201 #assert uploaded content
1202 status, h, data = self.client.request_object(self.container, name)
1203 self.assertEqual(len(o['data']), int(h['content-length']))
1204 self.assertEqual(o['data'], data)
1206 #assert content-type
1207 self.assertEqual(h['content-type'], o['meta']['content_type'])
1209 def _test_maximum_upload_size_exceeds(self):
1211 meta = {'test':'test1'}
1213 length=1024*1024*100
1214 self.assert_raises_fault(400, self.upload_random_data, self.container,
1215 name, length, **meta)
1217 def test_upload_with_name_containing_slash(self):
1218 name = '/%s' % o_names[0]
1219 meta = {'test':'test1'}
1220 o = self.upload_random_data(self.container, name, **meta)
1222 self.assertEqual(o['data'],
1223 self.client.retrieve_object(self.container, name))
1225 self.assertTrue(name in self.client.list_objects(self.container))
1227 def test_create_directory_marker(self):
1228 self.client.create_directory_marker(self.container, 'foo')
1229 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1230 self.assertEqual(meta['content-length'], '0')
1231 self.assertEqual(meta['content-type'], 'application/directory')
1233 def test_upload_unprocessable_entity(self):
1234 meta={'etag':'123', 'test':'test1'}
1236 #assert unprocessable entity
1237 self.assert_raises_fault(422, self.upload_random_data, self.container,
1240 def test_chunked_transfer(self):
1241 data = get_random_data()
1243 self.client.create_object_using_chunks(self.container, objname,
1246 uploaded_data = self.client.retrieve_object(self.container, objname)
1247 self.assertEqual(data, uploaded_data)
1249 def test_manifestation(self):
1250 prefix = 'myobject/'
1253 part = '%s%d' %(prefix, i)
1254 o = self.upload_random_data(self.container, part)
1257 manifest = '%s/%s' %(self.container, prefix)
1258 self.client.create_manifestation(self.container, 'large-object', manifest)
1260 self.assert_object_exists(self.container, 'large-object')
1261 self.assertEqual(data, self.client.retrieve_object(self.container,
1264 #wrong manifestation
1265 self.client.create_manifestation(self.container, 'large-object',
1266 '%s/invalid' % self.container)
1267 self.assertEqual('', self.client.retrieve_object(self.container,
1270 def test_create_zero_length_object(self):
1273 zero = self.client.create_zero_length_object(c, o)
1274 zero_meta = self.client.retrieve_object_metadata(c, o)
1275 zero_hash = self.client.retrieve_object_hashmap(c, o)
1276 zero_data = self.client.retrieve_object(c, o)
1278 self.assertEqual(int(zero_meta['content-length']), 0)
1279 self.assertEqual(zero_hash, [])
1280 self.assertEqual(zero_data, '')
1282 class ObjectCopy(BaseTestCase):
1284 BaseTestCase.setUp(self)
1285 self.containers = ['c1', 'c2']
1286 for c in self.containers:
1287 self.client.create_container(c)
1288 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1293 def test_copy(self):
1294 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1295 self.containers[0], self.obj['name']):
1297 meta = {'test':'testcopy'}
1298 status = self.client.copy_object(self.containers[0],
1304 #assert copy success
1305 self.assertEqual(status, 201)
1307 #assert access the new object
1308 headers = self.client.retrieve_object_metadata(self.containers[0],
1310 self.assertTrue('x-object-meta-test' in headers.keys())
1311 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1313 #assert etag is the same
1314 self.assertEqual(headers['etag'], self.obj['hash'])
1316 #assert src object still exists
1317 self.assert_object_exists(self.containers[0], self.obj['name'])
1319 def test_copy_from_different_container(self):
1320 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1321 self.containers[0], self.obj['name']):
1322 meta = {'test':'testcopy'}
1323 status = self.client.copy_object(self.containers[0],
1328 self.assertEqual(status, 201)
1330 # assert updated metadata
1331 meta = self.client.retrieve_object_metadata(self.containers[1],
1334 self.assertTrue('test' in meta.keys())
1335 self.assertTrue(meta['test'], 'testcopy')
1337 #assert src object still exists
1338 self.assert_object_exists(self.containers[0], self.obj['name'])
1340 def test_copy_invalid(self):
1341 #copy from invalid object
1342 meta = {'test':'testcopy'}
1343 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1344 'test.py', self.containers[1], 'testcopy', meta)
1346 #copy from invalid container
1347 meta = {'test':'testcopy'}
1348 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1349 self.obj['name'], self.containers[1],
1352 class ObjectMove(BaseTestCase):
1354 BaseTestCase.setUp(self)
1355 self.containers = ['c1', 'c2']
1356 for c in self.containers:
1357 self.client.create_container(c)
1358 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1360 def test_move(self):
1362 meta = {'test':'testcopy'}
1363 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1364 status = self.client.move_object(self.containers[0], self.obj['name'],
1365 self.containers[0], 'testcopy',
1368 #assert successful move
1369 self.assertEqual(status, 201)
1371 #assert updated metadata
1372 meta = self.client.retrieve_object_metadata(self.containers[0],
1375 self.assertTrue('test' in meta.keys())
1376 self.assertTrue(meta['test'], 'testcopy')
1378 #assert src object no more exists
1379 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1381 class ObjectPost(BaseTestCase):
1383 BaseTestCase.setUp(self)
1384 self.containers = ['c1', 'c2']
1385 for c in self.containers:
1386 self.client.create_container(c)
1389 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1391 def test_update_meta(self):
1392 #perform update metadata
1393 more = {'foo':'foo', 'bar':'bar'}
1394 status = self.client.update_object_metadata(self.containers[0],
1395 self.obj[0]['name'],
1397 #assert request accepted
1398 self.assertEqual(status, 202)
1400 #assert old metadata are still there
1401 headers = self.client.retrieve_object_metadata(self.containers[0],
1402 self.obj[0]['name'],
1404 #assert new metadata have been updated
1405 for k,v in more.items():
1406 self.assertTrue(k in headers.keys())
1407 self.assertTrue(headers[k], v)
1409 def test_update_object(self,
1412 instance_length = True,
1413 content_length = 500):
1414 l = len(self.obj[0]['data'])
1415 range = 'bytes %d-%d/%s' %(first_byte_pos,
1417 l if instance_length else '*')
1418 partial = last_byte_pos - first_byte_pos + 1
1419 length = first_byte_pos + partial
1420 data = get_random_data(partial)
1421 args = {'content_type':'application/octet-stream',
1422 'content_range':'%s' %range}
1424 args['content_length'] = content_length
1426 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1427 StringIO(data), **args)[0]
1429 if partial < 0 or (instance_length and l <= last_byte_pos):
1430 self.assertEqual(status, 202)
1432 self.assertEqual(status, 204)
1433 #check modified object
1434 content = self.client.retrieve_object(self.containers[0],
1435 self.obj[0]['name'])
1436 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1437 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1438 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1440 def test_update_object_lt_blocksize(self):
1441 self.test_update_object(10, 20, content_length=None)
1443 def test_update_object_gt_blocksize(self):
1444 o = self.upload_random_data(self.containers[0], o_names[1],
1445 length=4*1024*1024+5)
1446 c = self.containers[0]
1449 first_byte_pos = 4*1024*1024+1
1450 last_byte_pos = 4*1024*1024+4
1451 l = last_byte_pos - first_byte_pos + 1
1452 data = get_random_data(l)
1453 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1454 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1455 content = self.client.retrieve_object(c, o_name)
1456 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1457 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1458 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1460 def test_update_object_divided_by_blocksize(self):
1461 o = self.upload_random_data(self.containers[0], o_names[1],
1462 length=4*1024*1024+5)
1463 c = self.containers[0]
1466 first_byte_pos = 4*1024*1024
1467 last_byte_pos = 5*1024*1024
1468 l = last_byte_pos - first_byte_pos + 1
1469 data = get_random_data(l)
1470 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1471 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1472 content = self.client.retrieve_object(c, o_name)
1473 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1474 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1475 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1477 def test_update_object_no_content_length(self):
1478 self.test_update_object(content_length = None)
1480 def test_update_object_invalid_content_length(self):
1481 with AssertContentInvariant(self.client.retrieve_object,
1482 self.containers[0], self.obj[0]['name']):
1483 self.assert_raises_fault(400, self.test_update_object,
1484 content_length = 1000)
1486 def test_update_object_invalid_range(self):
1487 with AssertContentInvariant(self.client.retrieve_object,
1488 self.containers[0], self.obj[0]['name']):
1489 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1491 def test_update_object_invalid_range_and_length(self):
1492 with AssertContentInvariant(self.client.retrieve_object,
1493 self.containers[0], self.obj[0]['name']):
1494 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1497 def test_update_object_invalid_range_with_no_content_length(self):
1498 with AssertContentInvariant(self.client.retrieve_object,
1499 self.containers[0], self.obj[0]['name']):
1500 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1501 content_length = None)
1503 def test_update_object_out_of_limits(self):
1504 with AssertContentInvariant(self.client.retrieve_object,
1505 self.containers[0], self.obj[0]['name']):
1506 l = len(self.obj[0]['data'])
1507 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1509 def test_append(self):
1510 data = get_random_data(500)
1512 self.client.update_object(self.containers[0], self.obj[0]['name'],
1513 StringIO(data), content_length=500,
1514 content_type='application/octet-stream')
1516 content = self.client.retrieve_object(self.containers[0],
1517 self.obj[0]['name'])
1518 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1519 self.assertEqual(content[:-500], self.obj[0]['data'])
1521 def test_update_with_chunked_transfer(self):
1522 data = get_random_data(500)
1524 fl = len(self.obj[0]['data'])
1526 self.client.update_object_using_chunks(self.containers[0],
1527 self.obj[0]['name'],
1530 content_type='application/octet-stream')
1532 #check modified object
1533 content = self.client.retrieve_object(self.containers[0],
1534 self.obj[0]['name'])
1535 self.assertEqual(content[0:dl], data)
1536 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1538 def test_update_from_other_object(self):
1539 c = self.containers[0]
1543 source_data = self.client.retrieve_object(c, src)
1544 source_meta = self.client.retrieve_object_metadata(c, src)
1545 source_hash = self.client.retrieve_object_hashmap(c, src)
1547 #update zero length object
1548 self.client.create_zero_length_object(c, dest)
1549 source_object = '/%s/%s' % (c, src)
1550 self.client.update_from_other_source(c, dest, source_object)
1551 dest_data = self.client.retrieve_object(c, src)
1552 dest_meta = self.client.retrieve_object_metadata(c, dest)
1553 dest_hash = self.client.retrieve_object_hashmap(c, src)
1554 self.assertEqual(source_data, dest_data)
1555 self.assertEqual(source_hash, dest_hash)
1558 self.client.update_from_other_source(c, dest, source_object)
1559 content = self.client.retrieve_object(c, dest)
1560 self.assertEqual(source_data * 2, content)
1562 def test_update_range_from_other_object(self):
1563 c = self.containers[0]
1567 src = self.obj[1]['name']
1568 src_data = self.client.retrieve_object(c, src)
1570 #update zero length object
1571 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1572 source_object = '/%s/%s' % (c, src)
1573 first_byte_pos = 4*1024*1024+1
1574 last_byte_pos = 4*1024*1024+4
1575 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1576 self.client.update_from_other_source(c, dest, source_object,
1577 content_range=range)
1578 content = self.client.retrieve_object(c, dest)
1579 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1580 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1581 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1583 def test_update_hashes_from_other_object(self):
1584 c = self.containers[0]
1588 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1590 #update zero length object
1591 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1592 source_object = '/%s/%s' % (c, o_names[0])
1593 first_byte_pos = 4*1024*1024
1594 last_byte_pos = 5*1024*1024
1595 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1596 self.client.update_from_other_source(c, dest, source_object,
1597 content_range=range)
1598 content = self.client.retrieve_object(c, dest)
1599 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1600 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1601 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1604 def test_update_zero_length_object(self):
1605 c = self.containers[0]
1608 zero = self.client.create_zero_length_object(c, o)
1610 data = get_random_data()
1611 self.client.update_object(c, o, StringIO(data))
1612 self.client.create_object(c, other, StringIO(data))
1614 self.assertEqual(self.client.retrieve_object(c, o),
1615 self.client.retrieve_object(c, other))
1617 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1618 self.client.retrieve_object_hashmap(c, other))
1620 class ObjectDelete(BaseTestCase):
1622 BaseTestCase.setUp(self)
1623 self.containers = ['c1', 'c2']
1624 for c in self.containers:
1625 self.client.create_container(c)
1626 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1628 def test_delete(self):
1629 #perform delete object
1630 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1632 def test_delete_invalid(self):
1633 #assert item not found
1634 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1637 class ListSharing(BaseTestCase):
1639 BaseTestCase.setUp(self)
1640 self.client.create_container('c')
1642 self.upload_random_data('c', 'o%s' %i)
1643 accounts = OTHER_ACCOUNTS.copy()
1644 self.o1_sharing_with = accounts.popitem()
1645 self.o1_sharing = [self.o1_sharing_with[1]]
1646 self.client.share_object('c', 'o1', self.o1_sharing, read=True)
1650 l.append(accounts.popitem())
1651 #self.client.set_account_groups({'pithos-dev':'chazapis,verigak,papagian'})
1652 #self.o2_sharing = 'write=%s' %
1653 #self.client.share_object('c', 'o2', self.o2_sharing)
1655 def test_listing(self):
1656 self.other = Pithos_Client(get_server(),
1657 self.o1_sharing_with[0],
1658 self.o1_sharing_with[1],
1660 self.assertTrue(get_user() in self.other.list_shared_by_others())
1662 class TestGreek(BaseTestCase):
1664 BaseTestCase.setUp(self)
1665 #keep track of initial account groups
1666 self.initial_groups = self.client.retrieve_account_groups()
1668 #keep track of initial account meta
1669 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1672 #delete additionally created meta
1674 for m in self.client.retrieve_account_metadata(restricted=True):
1675 if m not in self.initial_meta:
1677 self.client.delete_account_metadata(l)
1679 #delete additionally created groups
1681 for g in self.client.retrieve_account_groups():
1682 if g not in self.initial_groups:
1684 self.client.unset_account_groups(l)
1686 BaseTestCase.tearDown(self)
1688 def test_create_container(self):
1689 self.client.create_container('φάκελος')
1690 self.assert_container_exists('φάκελος')
1692 self.assertTrue('φάκελος' in self.client.list_containers())
1694 def test_create_object(self):
1695 self.client.create_container('φάκελος')
1696 self.upload_random_data('φάκελος', 'αντικείμενο')
1698 self.assert_object_exists('φάκελος', 'αντικείμενο')
1699 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1701 def test_copy_object(self):
1702 src_container = 'φάκελος'
1703 src_object = 'αντικείμενο'
1704 dest_container = 'αντίγραφα'
1705 dest_object = 'ασφαλές-αντίγραφο'
1707 self.client.create_container(src_container)
1708 self.upload_random_data(src_container, src_object)
1710 self.client.create_container(dest_container)
1711 self.client.copy_object(src_container, src_object, dest_container,
1714 self.assert_object_exists(src_container, src_object)
1715 self.assert_object_exists(dest_container, dest_object)
1716 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1718 def test_move_object(self):
1719 src_container = 'φάκελος'
1720 src_object = 'αντικείμενο'
1721 dest_container = 'αντίγραφα'
1722 dest_object = 'ασφαλές-αντίγραφο'
1724 self.client.create_container(src_container)
1725 self.upload_random_data(src_container, src_object)
1727 self.client.create_container(dest_container)
1728 self.client.move_object(src_container, src_object, dest_container,
1731 self.assert_object_not_exists(src_container, src_object)
1732 self.assert_object_exists(dest_container, dest_object)
1733 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1735 def test_delete_object(self):
1736 self.client.create_container('φάκελος')
1737 self.upload_random_data('φάκελος', 'αντικείμενο')
1738 self.assert_object_exists('φάκελος', 'αντικείμενο')
1740 self.client.delete_object('φάκελος', 'αντικείμενο')
1741 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1742 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1744 def test_delete_container(self):
1745 self.client.create_container('φάκελος')
1746 self.assert_container_exists('φάκελος')
1748 self.client.delete_container('φάκελος')
1749 self.assert_container_not_exists('φάκελος')
1750 self.assertTrue('φάκελος' not in self.client.list_containers())
1752 def test_account_meta(self):
1753 meta = {'ποιότητα':'ΑΑΑ'}
1754 self.client.update_account_metadata(**meta)
1755 meta = self.client.retrieve_account_metadata(restricted=True)
1756 self.assertTrue('ποιότητα' in meta.keys())
1757 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1759 def test_container_meta(self):
1760 meta = {'ποιότητα':'ΑΑΑ'}
1761 self.client.create_container('φάκελος', **meta)
1763 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1764 self.assertTrue('ποιότητα' in meta.keys())
1765 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1767 def test_object_meta(self):
1768 self.client.create_container('φάκελος')
1769 meta = {'ποιότητα':'ΑΑΑ'}
1770 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1772 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1774 self.assertTrue('ποιότητα' in meta.keys())
1775 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1777 def test_list_meta_filtering(self):
1778 self.client.create_container('φάκελος')
1779 meta = {'ποιότητα':'ΑΑΑ'}
1780 self.upload_random_data('φάκελος', 'ο1', **meta)
1781 self.upload_random_data('φάκελος', 'ο2')
1782 self.upload_random_data('φάκελος', 'ο3')
1784 meta = {'ποσότητα':'μεγάλη'}
1785 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1786 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1787 self.assertTrue('ο1' in objects)
1788 self.assertTrue('ο2' in objects)
1789 self.assertTrue('ο3' not in objects)
1791 def test_groups(self):
1793 groups = {'σεφς':'chazapis,διογένης'}
1794 self.client.set_account_groups(**groups)
1795 groups.update(self.initial_groups)
1796 self.assertEqual(groups['σεφς'],
1797 self.client.retrieve_account_groups()['σεφς'])
1800 self.client.create_container('φάκελος')
1801 o = self.upload_random_data('φάκελος', 'ο1')
1802 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1803 chef = Pithos_Client(get_server(),
1807 self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1808 'φάκελος', 'ο1', account=get_user())
1811 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1812 new_data = get_random_data()
1813 self.assert_not_raises_fault(401, chef.update_object,
1814 'φάκελος', 'ο1', StringIO(new_data),
1817 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1818 self.assertEqual(server_data[:len(o['data'])], o['data'])
1819 self.assertEqual(server_data[len(o['data']):], new_data)
1821 def test_manifestation(self):
1822 self.client.create_container('κουβάς')
1826 part = '%s%d' %(prefix, i)
1827 o = self.upload_random_data('κουβάς', part)
1830 self.client.create_container('φάκελος')
1831 manifest = '%s/%s' %('κουβάς', prefix)
1832 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1834 self.assert_object_exists('φάκελος', 'άπαντα')
1835 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1838 #wrong manifestation
1839 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1840 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1842 def test_update_from_another_object(self):
1843 self.client.create_container('κουβάς')
1844 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1845 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1846 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1847 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1850 self.client.retrieve_object('κουβάς', 'νέο'),
1851 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1853 class TestPermissions(BaseTestCase):
1855 BaseTestCase.setUp(self)
1856 #keep track of initial account groups
1857 self.initial_groups = self.client.retrieve_account_groups()
1858 #keep track of initial account meta
1859 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1862 self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1863 groups = {'pithosdev':','.join(self.authorized)}
1864 self.client.set_account_groups(**groups)
1867 #delete additionally created meta
1869 for m in self.client.retrieve_account_metadata(restricted=True):
1870 if m not in self.initial_meta:
1872 self.client.delete_account_metadata(l)
1874 #delete additionally created groups
1876 for g in self.client.retrieve_account_groups():
1877 if g not in self.initial_groups:
1879 self.client.unset_account_groups(l)
1881 BaseTestCase.tearDown(self)
1883 def test_read_access(self):
1884 self.client.create_container('c')
1885 o = self.upload_random_data('c', 'o')
1886 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1887 for token, account in OTHER_ACCOUNTS.items():
1888 cl = Pithos_Client(get_server(), token, account, get_api())
1889 if account in self.authorized:
1890 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1891 'c', 'o', account=get_user())
1893 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1894 'c', 'o', account=get_user())
1897 def test_write_access(self):
1898 self.client.create_container('c')
1899 o = self.upload_random_data('c', 'o')
1900 self.client.share_object('c', 'o', ['chazapis'], read=False)
1901 for token, account in OTHER_ACCOUNTS.items():
1902 cl = Pithos_Client(get_server(), token, account, get_api())
1903 new_data = get_random_data()
1904 if account == 'chazapis':
1905 self.assert_not_raises_fault(401, cl.update_object,
1906 'c', 'o', StringIO(new_data),
1908 server_data = self.client.retrieve_object('c', 'o')
1909 self.assertEqual(o['data'], server_data[:len(o['data'])])
1910 self.assertEqual(new_data, server_data[len(o['data']):])
1912 self.assert_raises_fault(401, cl.update_object,
1913 'c', 'o', StringIO(new_data),
1916 class AssertMappingInvariant(object):
1917 def __init__(self, callable, *args, **kwargs):
1918 self.callable = callable
1920 self.kwargs = kwargs
1922 def __enter__(self):
1923 self.map = self.callable(*self.args, **self.kwargs)
1926 def __exit__(self, type, value, tb):
1927 map = self.callable(*self.args, **self.kwargs)
1928 for k in self.map.keys():
1929 if is_date(self.map[k]):
1931 assert map[k] == self.map[k]
1933 class AssertContentInvariant(object):
1934 def __init__(self, callable, *args, **kwargs):
1935 self.callable = callable
1937 self.kwargs = kwargs
1939 def __enter__(self):
1940 self.content = self.callable(*self.args, **self.kwargs)[2]
1943 def __exit__(self, type, value, tb):
1944 content = self.callable(*self.args, **self.kwargs)[2]
1945 assert self.content == content
1947 def get_content_splitted(response):
1949 return response.content.split('\n')
1951 def compute_md5_hash(data):
1955 return md5.hexdigest().lower()
1957 def compute_block_hash(data, algorithm):
1958 h = hashlib.new(algorithm)
1959 h.update(data.rstrip('\x00'))
1960 return h.hexdigest()
1962 def get_random_data(length=500):
1963 char_set = string.ascii_uppercase + string.digits
1964 return ''.join(random.choice(char_set) for x in range(length))
1967 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
1968 __D = r'(?P<day>\d{2})'
1969 __D2 = r'(?P<day>[ \d]\d)'
1970 __M = r'(?P<mon>\w{3})'
1971 __Y = r'(?P<year>\d{4})'
1972 __Y2 = r'(?P<year>\d{2})'
1973 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
1974 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
1975 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
1976 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
1977 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
1978 m = regex.match(date)
1983 o_names = ['kate.jpg',
1984 'kate_beckinsale.jpg',
1985 'How To Win Friends And Influence People.pdf',
1986 'moms_birthday.jpg',
1988 'Disturbed - Down With The Sickness.mp3',
1989 'army_of_darkness.avi',
1991 'photos/animals/dogs/poodle.jpg',
1992 'photos/animals/dogs/terrier.jpg',
1993 'photos/animals/cats/persian.jpg',
1994 'photos/animals/cats/siamese.jpg',
1995 'photos/plants/fern.jpg',
1996 'photos/plants/rose.jpg',
1999 if __name__ == "__main__":