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.account = 'test'
281 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
282 for item in self.containers:
283 self.client.create_container(item)
285 #keep track of initial account groups
286 self.initial_groups = self.client.retrieve_account_groups()
288 #keep track of initial account meta
289 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
292 self.client.update_account_metadata(**meta)
293 self.updated_meta = self.initial_meta.update(meta)
296 #delete additionally created meta
298 for m in self.client.retrieve_account_metadata(restricted=True):
299 if m not in self.initial_meta:
301 self.client.delete_account_metadata(l)
303 #delete additionally created groups
305 for g in self.client.retrieve_account_groups():
306 if g not in self.initial_groups:
308 self.client.unset_account_groups(l)
310 BaseTestCase.tearDown(self)
312 def test_get_account_meta(self):
313 meta = self.client.retrieve_account_metadata()
315 containers = self.client.list_containers()
316 l = str(len(containers))
317 self.assertEqual(meta['x-account-container-count'], l)
320 m = self.client.retrieve_container_metadata(c)
321 size = size + int(m['x-container-bytes-used'])
322 self.assertEqual(meta['x-account-bytes-used'], str(size))
324 def test_get_account_401(self):
325 self.assert_raises_fault(401,
326 self.invalid_client.retrieve_account_metadata)
328 def test_get_account_meta_until(self):
329 t = datetime.datetime.utcnow()
330 past = t - datetime.timedelta(minutes=-15)
331 past = int(_time.mktime(past.timetuple()))
333 meta = {'premium':True}
334 self.client.update_account_metadata(**meta)
335 meta = self.client.retrieve_account_metadata(restricted=True,
337 self.assertTrue('premium' not in meta)
339 meta = self.client.retrieve_account_metadata(restricted=True)
340 self.assertTrue('premium' in meta)
342 def test_get_account_meta_until_invalid_date(self):
343 meta = {'premium':True}
344 self.client.update_account_metadata(**meta)
345 meta = self.client.retrieve_account_metadata(restricted=True,
347 self.assertTrue('premium' in meta)
349 class AccountGet(BaseTestCase):
351 BaseTestCase.setUp(self)
352 self.account = 'test'
353 #create some containers
354 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
355 for item in self.containers:
356 self.client.create_container(item)
360 containers = self.client.list_containers()
361 self.assertEquals(self.containers, containers)
363 def test_list_401(self):
364 self.assert_raises_fault(401, self.invalid_client.list_containers)
366 def test_list_with_limit(self):
368 containers = self.client.list_containers(limit=limit)
369 self.assertEquals(len(containers), limit)
370 self.assertEquals(self.containers[:2], containers)
372 def test_list_with_marker(self):
375 containers = self.client.list_containers(limit=l, marker=m)
376 i = self.containers.index(m) + 1
377 self.assertEquals(self.containers[i:(i+l)], containers)
380 containers = self.client.list_containers(limit=l, marker=m)
381 i = self.containers.index(m) + 1
382 self.assertEquals(self.containers[i:(i+l)], containers)
384 def test_list_json_with_marker(self):
387 containers = self.client.list_containers(limit=l, marker=m, format='json')
388 self.assert_extended(containers, 'json', 'container', l)
389 self.assertEqual(containers[0]['name'], 'kiwis')
390 self.assertEqual(containers[1]['name'], 'oranges')
392 def test_list_xml_with_marker(self):
395 xml = self.client.list_containers(limit=l, marker=m, format='xml')
396 self.assert_extended(xml, 'xml', 'container', l)
397 nodes = xml.getElementsByTagName('name')
398 self.assertEqual(len(nodes), 1)
399 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
401 def test_if_modified_since(self):
402 t = datetime.datetime.utcnow()
403 t2 = t - datetime.timedelta(minutes=10)
406 self.client.create_container('dummy')
408 for f in DATE_FORMATS:
409 past = t2.strftime(f)
411 c = self.client.list_containers(if_modified_since=past)
412 self.assertEqual(len(c), len(self.containers) + 1)
414 self.failIf(f.status == 304) #fail if not modified
416 def test_if_modified_since_invalid_date(self):
417 c = self.client.list_containers(if_modified_since='')
418 self.assertEqual(len(c), len(self.containers))
420 def test_if_not_modified_since(self):
421 now = datetime.datetime.utcnow()
422 since = now + datetime.timedelta(1)
424 for f in DATE_FORMATS:
425 args = {'if_modified_since':'%s' %since.strftime(f)}
428 self.assert_raises_fault(304, self.client.list_containers, **args)
430 def test_if_unmodified_since(self):
431 now = datetime.datetime.utcnow()
432 since = now + datetime.timedelta(1)
434 for f in DATE_FORMATS:
435 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
438 self.assertEqual(self.containers, c)
440 def test_if_unmodified_since_precondition_failed(self):
441 t = datetime.datetime.utcnow()
442 t2 = t - datetime.timedelta(minutes=10)
445 self.client.create_container('dummy')
447 for f in DATE_FORMATS:
448 past = t2.strftime(f)
450 args = {'if_unmodified_since':'%s' %past}
452 #assert precondition failed
453 self.assert_raises_fault(412, self.client.list_containers, **args)
455 class AccountPost(BaseTestCase):
457 BaseTestCase.setUp(self)
458 self.account = 'test'
459 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
460 for item in self.containers:
461 self.client.create_container(item)
463 #keep track of initial account groups
464 self.initial_groups = self.client.retrieve_account_groups()
466 #keep track of initial account meta
467 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
470 self.client.update_account_metadata(**meta)
471 self.updated_meta = self.initial_meta.update(meta)
474 #delete additionally created meta
476 for m in self.client.retrieve_account_metadata(restricted=True):
477 if m not in self.initial_meta:
479 self.client.delete_account_metadata(l)
481 #delete additionally created groups
483 for g in self.client.retrieve_account_groups():
484 if g not in self.initial_groups:
486 self.client.unset_account_groups(l)
488 BaseTestCase.tearDown(self)
490 def test_update_meta(self):
491 with AssertMappingInvariant(self.client.retrieve_account_groups):
492 meta = {'test':'test', 'tost':'tost'}
493 self.client.update_account_metadata(**meta)
495 meta.update(self.initial_meta)
496 self.assertEqual(meta,
497 self.client.retrieve_account_metadata(
500 def test_invalid_account_update_meta(self):
501 meta = {'test':'test', 'tost':'tost'}
502 self.assert_raises_fault(401,
503 self.invalid_client.update_account_metadata,
506 def test_reset_meta(self):
507 with AssertMappingInvariant(self.client.retrieve_account_groups):
508 meta = {'test':'test', 'tost':'tost'}
509 self.client.update_account_metadata(**meta)
511 meta = {'test':'test33'}
512 self.client.reset_account_metadata(**meta)
514 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
516 def test_delete_meta(self):
517 with AssertMappingInvariant(self.client.retrieve_account_groups):
518 meta = {'test':'test', 'tost':'tost'}
519 self.client.update_account_metadata(**meta)
521 self.client.delete_account_metadata(meta.keys())
523 account_meta = self.client.retrieve_account_metadata(restricted=True)
525 self.assertTrue(m not in account_meta.keys())
527 def test_set_account_groups(self):
528 with AssertMappingInvariant(self.client.retrieve_account_metadata):
529 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
530 self.client.set_account_groups(**groups)
532 self.assertEqual(set(groups['pithosdev']),
533 set(self.client.retrieve_account_groups()['pithosdev']))
535 more_groups = {'clientsdev':'pkanavos,mvasilak'}
536 self.client.set_account_groups(**more_groups)
538 groups.update(more_groups)
539 self.assertEqual(set(groups['clientsdev']),
540 set(self.client.retrieve_account_groups()['clientsdev']))
542 def test_reset_account_groups(self):
543 with AssertMappingInvariant(self.client.retrieve_account_metadata):
544 groups = {'pithosdev':'verigak,gtsouk,chazapis',
545 'clientsdev':'pkanavos,mvasilak'}
546 self.client.set_account_groups(**groups)
548 self.assertEqual(set(groups['pithosdev'].split(',')),
549 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
550 self.assertEqual(set(groups['clientsdev'].split(',')),
551 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
553 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
554 self.client.reset_account_groups(**groups)
556 self.assertEqual(set(groups['pithosdev'].split(',')),
557 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
559 def test_delete_account_groups(self):
560 with AssertMappingInvariant(self.client.retrieve_account_metadata):
561 groups = {'pithosdev':'verigak,gtsouk,chazapis',
562 'clientsdev':'pkanavos,mvasilak'}
563 self.client.set_account_groups(**groups)
565 self.client.unset_account_groups(groups.keys())
567 self.assertEqual({}, self.client.retrieve_account_groups())
569 class ContainerHead(BaseTestCase):
571 BaseTestCase.setUp(self)
572 self.account = 'test'
573 self.container = 'apples'
574 self.client.create_container(self.container)
576 def test_get_meta(self):
577 meta = {'trash':'true'}
578 t1 = datetime.datetime.utcnow()
579 o = self.upload_random_data(self.container, o_names[0], **meta)
581 headers = self.client.retrieve_container_metadata(self.container)
582 self.assertEqual(headers['x-container-object-count'], '1')
583 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
584 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
586 threashold = datetime.timedelta(seconds=1)
587 self.assertTrue(delta < threashold)
588 self.assertTrue(headers['x-container-object-meta'])
589 self.assertTrue('Trash' in headers['x-container-object-meta'])
591 class ContainerGet(BaseTestCase):
593 BaseTestCase.setUp(self)
594 self.account = 'test'
595 self.container = ['pears', 'apples']
596 for c in self.container:
597 self.client.create_container(c)
599 for o in o_names[:8]:
600 self.obj.append(self.upload_random_data(self.container[0], o))
601 for o in o_names[8:]:
602 self.obj.append(self.upload_random_data(self.container[1], o))
604 def test_list_objects(self):
605 objects = self.client.list_objects(self.container[0])
606 l = [elem['name'] for elem in self.obj[:8]]
608 self.assertEqual(objects, l)
610 def test_list_objects_containing_slash(self):
611 self.client.create_container('test')
612 self.upload_random_data('test', '/objectname')
614 objects = self.client.list_objects('test')
615 self.assertEqual(objects, ['/objectname'])
617 objects = self.client.list_objects('test', format='json')
618 self.assertEqual(objects[0]['name'], '/objectname')
620 objects = self.client.list_objects('test', format='xml')
621 self.assert_extended(objects, 'xml', 'object')
622 node_name = objects.getElementsByTagName('name')[0]
623 self.assertEqual(node_name.firstChild.data, '/objectname')
625 #objects = self.client.list_objects('test', prefix='/')
626 #self.assertEqual(objects, ['/objectname'])
628 #objects = self.client.list_objects('test', path='/')
629 #self.assertEqual(objects, ['/objectname'])
631 #objects = self.client.list_objects('test', prefix='/', delimiter='n')
632 #self.assertEqual(objects, ['/object'])
634 def test_list_objects_with_limit_marker(self):
635 objects = self.client.list_objects(self.container[0], limit=2)
636 l = [elem['name'] for elem in self.obj[:8]]
638 self.assertEqual(objects, l[:2])
640 markers = ['How To Win Friends And Influence People.pdf',
644 objects = self.client.list_objects(self.container[0], limit=limit,
646 l = [elem['name'] for elem in self.obj[:8]]
648 start = l.index(m) + 1
650 end = len(l) >= end and end or len(l)
651 self.assertEqual(objects, l[start:end])
654 def _test_list_limit_exceeds(self):
655 self.client.create_container('pithos')
657 for i in range(10001):
658 self.client.create_zero_length_object('pithos', i)
660 self.assertEqual(10000, len(self.client.list_objects('pithos')))
662 def test_list_empty_params(self):
663 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
665 objects = objects.strip().split('\n')
666 self.assertEqual(objects,
667 self.client.list_objects(self.container[0]))
669 def test_list_pseudo_hierarchical_folders(self):
670 objects = self.client.list_objects(self.container[1], prefix='photos',
672 self.assertEquals(['photos/animals/', 'photos/me.jpg',
673 'photos/plants/'], objects)
675 objects = self.client.list_objects(self.container[1],
676 prefix='photos/animals',
678 l = ['photos/animals/cats/', 'photos/animals/dogs/']
679 self.assertEquals(l, objects)
681 objects = self.client.list_objects(self.container[1], path='photos')
682 self.assertEquals(['photos/me.jpg'], objects)
684 def test_extended_list_json(self):
685 objects = self.client.list_objects(self.container[1], format='json',
686 limit=2, prefix='photos/animals',
688 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
689 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
691 def test_extended_list_xml(self):
692 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
693 prefix='photos', delimiter='/')
694 self.assert_extended(xml, 'xml', 'object', size=4)
695 dirs = xml.getElementsByTagName('subdir')
696 self.assertEqual(len(dirs), 2)
697 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
698 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
700 objects = xml.getElementsByTagName('name')
701 self.assertEqual(len(objects), 1)
702 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
704 def test_list_meta_double_matching(self):
705 meta = {'quality':'aaa', 'stock':'true'}
706 self.client.update_object_metadata(self.container[0],
707 self.obj[0]['name'], **meta)
708 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
709 self.assertEqual(len(obj), 1)
710 self.assertTrue(obj, self.obj[0]['name'])
712 def test_list_using_meta(self):
713 meta = {'quality':'aaa'}
714 for o in self.obj[:2]:
715 self.client.update_object_metadata(self.container[0], o['name'],
717 meta = {'stock':'true'}
718 for o in self.obj[3:5]:
719 self.client.update_object_metadata(self.container[0], o['name'],
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 case insensitive
727 obj = self.client.list_objects(self.container[0], meta='quality')
728 self.assertEqual(len(obj), 2)
729 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
731 # test multiple matches
732 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
733 self.assertEqual(len(obj), 4)
734 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
736 # test non 1-1 multiple match
737 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
738 self.assertEqual(len(obj), 2)
739 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
741 def test_if_modified_since(self):
742 t = datetime.datetime.utcnow()
743 t2 = t - datetime.timedelta(minutes=10)
746 self.upload_random_data(self.container[0], o_names[0])
748 for f in DATE_FORMATS:
749 past = t2.strftime(f)
751 o = self.client.list_objects(self.container[0],
752 if_modified_since=past)
754 self.client.list_objects(self.container[0]))
756 self.failIf(f.status == 304) #fail if not modified
758 def test_if_modified_since_invalid_date(self):
759 headers = {'if-modified-since':''}
760 o = self.client.list_objects(self.container[0], if_modified_since='')
761 self.assertEqual(o, self.client.list_objects(self.container[0]))
763 def test_if_not_modified_since(self):
764 now = datetime.datetime.utcnow()
765 since = now + datetime.timedelta(1)
767 for f in DATE_FORMATS:
768 args = {'if_modified_since':'%s' %since.strftime(f)}
771 self.assert_raises_fault(304, self.client.list_objects,
772 self.container[0], **args)
774 def test_if_unmodified_since(self):
775 now = datetime.datetime.utcnow()
776 since = now + datetime.timedelta(1)
778 for f in DATE_FORMATS:
779 obj = self.client.list_objects(self.container[0],
780 if_unmodified_since=since.strftime(f))
783 self.assertEqual(obj, self.client.list_objects(self.container[0]))
785 def test_if_unmodified_since_precondition_failed(self):
786 t = datetime.datetime.utcnow()
787 t2 = t - datetime.timedelta(minutes=10)
790 self.client.create_container('dummy')
792 for f in DATE_FORMATS:
793 past = t2.strftime(f)
795 args = {'if_unmodified_since':'%s' %past}
797 #assert precondition failed
798 self.assert_raises_fault(412, self.client.list_objects,
799 self.container[0], **args)
801 class ContainerPut(BaseTestCase):
803 BaseTestCase.setUp(self)
804 self.account = 'test'
805 self.containers = ['c1', 'c2']
807 def test_create(self):
808 self.client.create_container(self.containers[0])
809 containers = self.client.list_containers()
810 self.assertTrue(self.containers[0] in containers)
811 self.assert_container_exists(self.containers[0])
813 def test_create_twice(self):
814 self.client.create_container(self.containers[0])
815 self.assertTrue(not self.client.create_container(self.containers[0]))
817 class ContainerPost(BaseTestCase):
819 BaseTestCase.setUp(self)
820 self.account = 'test'
821 self.container = 'apples'
822 self.client.create_container(self.container)
824 def test_update_meta(self):
825 meta = {'test':'test33',
827 self.client.update_container_metadata(self.container, **meta)
828 headers = self.client.retrieve_container_metadata(self.container)
829 for k,v in meta.items():
830 k = 'x-container-meta-%s' % k
831 self.assertTrue(headers[k])
832 self.assertEqual(headers[k], v)
834 class ContainerDelete(BaseTestCase):
836 BaseTestCase.setUp(self)
837 self.account = 'test'
838 self.containers = ['c1', 'c2']
839 for c in self.containers:
840 self.client.create_container(c)
841 self.upload_random_data(self.containers[1], o_names[0])
843 def test_delete(self):
844 status = self.client.delete_container(self.containers[0])[0]
845 self.assertEqual(status, 204)
847 def test_delete_non_empty(self):
848 self.assert_raises_fault(409, self.client.delete_container,
851 def test_delete_invalid(self):
852 self.assert_raises_fault(404, self.client.delete_container, 'c3')
854 class ObjectHead(BaseTestCase):
857 class ObjectGet(BaseTestCase):
859 BaseTestCase.setUp(self)
860 self.account = 'test'
861 self.containers = ['c1', 'c2']
862 #create some containers
863 for c in self.containers:
864 self.client.create_container(c)
867 names = ('obj1', 'obj2')
870 self.objects.append(self.upload_random_data(self.containers[1], n))
874 o = self.client.retrieve_object(self.containers[1],
875 self.objects[0]['name'],
876 self.objects[0]['meta'])
877 self.assertEqual(o, self.objects[0]['data'])
879 def test_get_invalid(self):
880 self.assert_raises_fault(404, self.client.retrieve_object,
881 self.containers[0], self.objects[0]['name'])
883 def test_get_partial(self):
884 #perform get with range
885 status, headers, data = self.client.request_object(self.containers[1],
886 self.objects[0]['name'],
889 #assert successful partial content
890 self.assertEqual(status, 206)
893 self.assertEqual(headers['content-type'],
894 self.objects[0]['meta']['content_type'])
896 #assert content length
897 self.assertEqual(int(headers['content-length']), 500)
900 self.assertEqual(self.objects[0]['data'][:500], data)
902 def test_get_final_500(self):
903 #perform get with range
904 headers = {'range':'bytes=-500'}
905 status, headers, data = self.client.request_object(self.containers[1],
906 self.objects[0]['name'],
909 #assert successful partial content
910 self.assertEqual(status, 206)
913 self.assertEqual(headers['content-type'],
914 self.objects[0]['meta']['content_type'])
916 #assert content length
917 self.assertEqual(int(headers['content-length']), 500)
920 self.assertTrue(self.objects[0]['data'][-500:], data)
922 def test_get_rest(self):
923 #perform get with range
924 offset = len(self.objects[0]['data']) - 500
925 status, headers, data = self.client.request_object(self.containers[1],
926 self.objects[0]['name'],
927 range='bytes=%s-' %offset)
929 #assert successful partial content
930 self.assertEqual(status, 206)
933 self.assertEqual(headers['content-type'],
934 self.objects[0]['meta']['content_type'])
936 #assert content length
937 self.assertEqual(int(headers['content-length']), 500)
940 self.assertTrue(self.objects[0]['data'][-500:], data)
942 def test_get_range_not_satisfiable(self):
943 #perform get with range
944 offset = len(self.objects[0]['data']) + 1
946 #assert range not satisfiable
947 self.assert_raises_fault(416, self.client.retrieve_object,
948 self.containers[1], self.objects[0]['name'],
949 range='bytes=0-%s' %offset)
951 def test_multiple_range(self):
952 #perform get with multiple range
953 ranges = ['0-499', '-500', '1000-']
954 bytes = 'bytes=%s' % ','.join(ranges)
955 status, headers, data = self.client.request_object(self.containers[1],
956 self.objects[0]['name'],
959 # assert partial content
960 self.assertEqual(status, 206)
962 # assert Content-Type of the reply will be multipart/byteranges
963 self.assertTrue(headers['content-type'])
964 content_type_parts = headers['content-type'].split()
965 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
967 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
968 cparts = data.split(boundary)[1:-1]
970 # assert content parts are exactly 2
971 self.assertEqual(len(cparts), len(ranges))
973 # for each content part assert headers
976 content = cpart.split('\r\n')
977 headers = content[1:3]
978 content_range = headers[0].split(': ')
979 self.assertEqual(content_range[0], 'Content-Range')
981 r = ranges[i].split('-')
982 if not r[0] and not r[1]:
985 start = len(self.objects[0]['data']) - int(r[1])
986 end = len(self.objects[0]['data'])
989 end = len(self.objects[0]['data'])
993 fdata = self.objects[0]['data'][start:end]
994 sdata = '\r\n'.join(content[4:-1])
995 self.assertEqual(len(fdata), len(sdata))
996 self.assertEquals(fdata, sdata)
999 def test_multiple_range_not_satisfiable(self):
1000 #perform get with multiple range
1001 out_of_range = len(self.objects[0]['data']) + 1
1002 ranges = ['0-499', '-500', '%d-' %out_of_range]
1003 bytes = 'bytes=%s' % ','.join(ranges)
1005 # assert partial content
1006 self.assert_raises_fault(416, self.client.retrieve_object,
1008 self.objects[0]['name'], range=bytes)
1010 def test_get_with_if_match(self):
1011 #perform get with If-Match
1012 etag = self.objects[0]['hash']
1013 status, headers, data = self.client.request_object(self.containers[1],
1014 self.objects[0]['name'],
1017 self.assertEqual(status, 200)
1019 #assert content-type
1020 self.assertEqual(headers['content-type'],
1021 self.objects[0]['meta']['content_type'])
1023 #assert response content
1024 self.assertEqual(self.objects[0]['data'], data)
1026 def test_get_with_if_match_star(self):
1027 #perform get with If-Match *
1028 headers = {'if-match':'*'}
1029 status, headers, data = self.client.request_object(self.containers[1],
1030 self.objects[0]['name'],
1033 self.assertEqual(status, 200)
1035 #assert content-type
1036 self.assertEqual(headers['content-type'],
1037 self.objects[0]['meta']['content_type'])
1039 #assert response content
1040 self.assertEqual(self.objects[0]['data'], data)
1042 def test_get_with_multiple_if_match(self):
1043 #perform get with If-Match
1044 etags = [i['hash'] for i in self.objects if i]
1045 etags = ','.join('"%s"' % etag for etag in etags)
1046 status, headers, data = self.client.request_object(self.containers[1],
1047 self.objects[0]['name'],
1050 self.assertEqual(status, 200)
1052 #assert content-type
1053 self.assertEqual(headers['content-type'],
1054 self.objects[0]['meta']['content_type'])
1056 #assert content-type
1057 self.assertEqual(headers['content-type'],
1058 self.objects[0]['meta']['content_type'])
1060 #assert response content
1061 self.assertEqual(self.objects[0]['data'], data)
1063 def test_if_match_precondition_failed(self):
1064 #assert precondition failed
1065 self.assert_raises_fault(412, self.client.retrieve_object,
1067 self.objects[0]['name'], if_match='123')
1069 def test_if_none_match(self):
1070 #perform get with If-None-Match
1071 status, headers, data = self.client.request_object(self.containers[1],
1072 self.objects[0]['name'],
1073 if_none_match='123')
1076 self.assertEqual(status, 200)
1078 #assert content-type
1079 self.assertEqual(headers['content_type'],
1080 self.objects[0]['meta']['content_type'])
1082 def test_if_none_match(self):
1083 #perform get with If-None-Match * and assert not modified
1084 self.assert_raises_fault(304, self.client.retrieve_object,
1086 self.objects[0]['name'],
1089 def test_if_none_match_not_modified(self):
1090 #perform get with If-None-Match and assert not modified
1091 self.assert_raises_fault(304, self.client.retrieve_object,
1093 self.objects[0]['name'],
1094 if_none_match=self.objects[0]['hash'])
1096 meta = self.client.retrieve_object_metadata(self.containers[1],
1097 self.objects[0]['name'])
1098 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1100 def test_if_modified_since(self):
1101 t = datetime.datetime.utcnow()
1102 t2 = t - datetime.timedelta(minutes=10)
1105 self.upload_data(self.containers[1],
1106 self.objects[0]['name'],
1107 self.objects[0]['data'][:200])
1109 for f in DATE_FORMATS:
1110 past = t2.strftime(f)
1112 headers = {'if-modified-since':'%s' %past}
1114 o = self.client.retrieve_object(self.containers[1],
1115 self.objects[0]['name'],
1116 if_modified_since=past)
1118 self.client.retrieve_object(self.containers[1],
1119 self.objects[0]['name']))
1121 self.failIf(f.status == 304)
1123 def test_if_modified_since_invalid_date(self):
1124 o = self.client.retrieve_object(self.containers[1],
1125 self.objects[0]['name'],
1126 if_modified_since='')
1127 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1128 self.objects[0]['name']))
1130 def test_if_not_modified_since(self):
1131 now = datetime.datetime.utcnow()
1132 since = now + datetime.timedelta(1)
1134 for f in DATE_FORMATS:
1135 #assert not modified
1136 self.assert_raises_fault(304, self.client.retrieve_object,
1137 self.containers[1], self.objects[0]['name'],
1138 if_modified_since=since.strftime(f))
1140 def test_if_unmodified_since(self):
1141 now = datetime.datetime.utcnow()
1142 since = now + datetime.timedelta(1)
1144 for f in DATE_FORMATS:
1145 t = since.strftime(f)
1146 status, headers, data = self.client.request_object(self.containers[1],
1147 self.objects[0]['name'],
1148 if_unmodified_since=t)
1150 self.assertEqual(status, 200)
1151 self.assertEqual(self.objects[0]['data'], data)
1153 #assert content-type
1154 self.assertEqual(headers['content-type'],
1155 self.objects[0]['meta']['content_type'])
1157 def test_if_unmodified_since_precondition_failed(self):
1158 t = datetime.datetime.utcnow()
1159 t2 = t - datetime.timedelta(minutes=10)
1162 self.upload_data(self.containers[1],
1163 self.objects[0]['name'],
1164 self.objects[0]['data'][:200])
1166 for f in DATE_FORMATS:
1167 past = t2.strftime(f)
1168 #assert precondition failed
1169 self.assert_raises_fault(412, self.client.retrieve_object,
1170 self.containers[1], self.objects[0]['name'],
1171 if_unmodified_since=past)
1173 def test_hashes(self):
1176 o = self.upload_random_data(self.containers[1], fname, l)
1178 body = self.client.retrieve_object(self.containers[1], fname,
1180 hashes = body['hashes']
1181 block_size = body['block_size']
1182 block_hash = body['block_hash']
1183 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1184 self.assertTrue(len(hashes), block_num)
1187 start = i * block_size
1188 end = (i + 1) * block_size
1189 hash = compute_block_hash(o['data'][start:end], block_hash)
1190 self.assertEqual(h, hash)
1193 class ObjectPut(BaseTestCase):
1195 BaseTestCase.setUp(self)
1196 self.account = 'test'
1197 self.container = 'c1'
1198 self.client.create_container(self.container)
1200 def test_upload(self):
1202 meta = {'test':'test1'}
1203 o = self.upload_random_data(self.container, name, **meta)
1205 headers = self.client.retrieve_object_metadata(self.container,
1208 self.assertTrue('test' in headers.keys())
1209 self.assertEqual(headers['test'], meta['test'])
1211 #assert uploaded content
1212 status, h, data = self.client.request_object(self.container, name)
1213 self.assertEqual(len(o['data']), int(h['content-length']))
1214 self.assertEqual(o['data'], data)
1216 #assert content-type
1217 self.assertEqual(h['content-type'], o['meta']['content_type'])
1219 def _test_maximum_upload_size_exceeds(self):
1221 meta = {'test':'test1'}
1223 length=1024*1024*100
1224 self.assert_raises_fault(400, self.upload_random_data, self.container,
1225 name, length, **meta)
1227 def test_upload_with_name_containing_slash(self):
1228 name = '/%s' % o_names[0]
1229 meta = {'test':'test1'}
1230 o = self.upload_random_data(self.container, name, **meta)
1232 self.assertEqual(o['data'],
1233 self.client.retrieve_object(self.container, name))
1235 self.assertTrue(name in self.client.list_objects(self.container))
1237 def test_create_directory_marker(self):
1238 self.client.create_directory_marker(self.container, 'foo')
1239 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1240 self.assertEqual(meta['content-length'], '0')
1241 self.assertEqual(meta['content-type'], 'application/directory')
1243 def test_upload_unprocessable_entity(self):
1244 meta={'etag':'123', 'test':'test1'}
1246 #assert unprocessable entity
1247 self.assert_raises_fault(422, self.upload_random_data, self.container,
1250 def test_chunked_transfer(self):
1251 data = get_random_data()
1253 self.client.create_object_using_chunks(self.container, objname,
1256 uploaded_data = self.client.retrieve_object(self.container, objname)
1257 self.assertEqual(data, uploaded_data)
1259 def test_manifestation(self):
1260 prefix = 'myobject/'
1263 part = '%s%d' %(prefix, i)
1264 o = self.upload_random_data(self.container, part)
1267 manifest = '%s/%s' %(self.container, prefix)
1268 self.client.create_manifestation(self.container, 'large-object', manifest)
1270 self.assert_object_exists(self.container, 'large-object')
1271 self.assertEqual(data, self.client.retrieve_object(self.container,
1274 #wrong manifestation
1275 self.client.create_manifestation(self.container, 'large-object',
1276 '%s/invalid' % self.container)
1277 self.assertEqual('', self.client.retrieve_object(self.container,
1280 def test_create_zero_length_object(self):
1283 zero = self.client.create_zero_length_object(c, o)
1284 zero_meta = self.client.retrieve_object_metadata(c, o)
1285 zero_hash = self.client.retrieve_object_hashmap(c, o)
1286 zero_data = self.client.retrieve_object(c, o)
1288 self.assertEqual(int(zero_meta['content-length']), 0)
1289 self.assertEqual(zero_hash, [])
1290 self.assertEqual(zero_data, '')
1292 class ObjectCopy(BaseTestCase):
1294 BaseTestCase.setUp(self)
1295 self.account = 'test'
1296 self.containers = ['c1', 'c2']
1297 for c in self.containers:
1298 self.client.create_container(c)
1299 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1304 def test_copy(self):
1305 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1306 self.containers[0], self.obj['name']):
1308 meta = {'test':'testcopy'}
1309 status = self.client.copy_object(self.containers[0],
1315 #assert copy success
1316 self.assertEqual(status, 201)
1318 #assert access the new object
1319 headers = self.client.retrieve_object_metadata(self.containers[0],
1321 self.assertTrue('x-object-meta-test' in headers.keys())
1322 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1324 #assert etag is the same
1325 self.assertEqual(headers['etag'], self.obj['hash'])
1327 #assert src object still exists
1328 self.assert_object_exists(self.containers[0], self.obj['name'])
1330 def test_copy_from_different_container(self):
1331 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1332 self.containers[0], self.obj['name']):
1333 meta = {'test':'testcopy'}
1334 status = self.client.copy_object(self.containers[0],
1339 self.assertEqual(status, 201)
1341 # assert updated metadata
1342 meta = self.client.retrieve_object_metadata(self.containers[1],
1345 self.assertTrue('test' in meta.keys())
1346 self.assertTrue(meta['test'], 'testcopy')
1348 #assert src object still exists
1349 self.assert_object_exists(self.containers[0], self.obj['name'])
1351 def test_copy_invalid(self):
1352 #copy from invalid object
1353 meta = {'test':'testcopy'}
1354 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1355 'test.py', self.containers[1], 'testcopy', meta)
1357 #copy from invalid container
1358 meta = {'test':'testcopy'}
1359 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1360 self.obj['name'], self.containers[1],
1363 class ObjectMove(BaseTestCase):
1365 BaseTestCase.setUp(self)
1366 self.account = 'test'
1367 self.containers = ['c1', 'c2']
1368 for c in self.containers:
1369 self.client.create_container(c)
1370 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1372 def test_move(self):
1374 meta = {'test':'testcopy'}
1375 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1376 status = self.client.move_object(self.containers[0], self.obj['name'],
1377 self.containers[0], 'testcopy',
1380 #assert successful move
1381 self.assertEqual(status, 201)
1383 #assert updated metadata
1384 meta = self.client.retrieve_object_metadata(self.containers[0],
1387 self.assertTrue('test' in meta.keys())
1388 self.assertTrue(meta['test'], 'testcopy')
1390 #assert src object no more exists
1391 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1393 class ObjectPost(BaseTestCase):
1395 BaseTestCase.setUp(self)
1396 self.account = 'test'
1397 self.containers = ['c1', 'c2']
1398 for c in self.containers:
1399 self.client.create_container(c)
1402 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1404 def test_update_meta(self):
1405 #perform update metadata
1406 more = {'foo':'foo', 'bar':'bar'}
1407 status = self.client.update_object_metadata(self.containers[0],
1408 self.obj[0]['name'],
1410 #assert request accepted
1411 self.assertEqual(status, 202)
1413 #assert old metadata are still there
1414 headers = self.client.retrieve_object_metadata(self.containers[0],
1415 self.obj[0]['name'],
1417 #assert new metadata have been updated
1418 for k,v in more.items():
1419 self.assertTrue(k in headers.keys())
1420 self.assertTrue(headers[k], v)
1422 def test_update_object(self,
1425 instance_length = True,
1426 content_length = 500):
1427 l = len(self.obj[0]['data'])
1428 range = 'bytes %d-%d/%s' %(first_byte_pos,
1430 l if instance_length else '*')
1431 partial = last_byte_pos - first_byte_pos + 1
1432 length = first_byte_pos + partial
1433 data = get_random_data(partial)
1434 args = {'content_type':'application/octet-stream',
1435 'content_range':'%s' %range}
1437 args['content_length'] = content_length
1439 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1440 StringIO(data), **args)[0]
1442 if partial < 0 or (instance_length and l <= last_byte_pos):
1443 self.assertEqual(status, 202)
1445 self.assertEqual(status, 204)
1446 #check modified object
1447 content = self.client.retrieve_object(self.containers[0],
1448 self.obj[0]['name'])
1449 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1450 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1451 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1453 def test_update_object_lt_blocksize(self):
1454 self.test_update_object(10, 20, content_length=None)
1456 def test_update_object_gt_blocksize(self):
1457 o = self.upload_random_data(self.containers[0], o_names[1],
1458 length=4*1024*1024+5)
1459 c = self.containers[0]
1462 first_byte_pos = 4*1024*1024+1
1463 last_byte_pos = 4*1024*1024+4
1464 l = last_byte_pos - first_byte_pos + 1
1465 data = get_random_data(l)
1466 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1467 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1468 content = self.client.retrieve_object(c, o_name)
1469 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1470 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1471 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1473 def test_update_object_divided_by_blocksize(self):
1474 o = self.upload_random_data(self.containers[0], o_names[1],
1475 length=4*1024*1024+5)
1476 c = self.containers[0]
1479 first_byte_pos = 4*1024*1024
1480 last_byte_pos = 5*1024*1024
1481 l = last_byte_pos - first_byte_pos + 1
1482 data = get_random_data(l)
1483 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1484 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1485 content = self.client.retrieve_object(c, o_name)
1486 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1487 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1488 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1490 def test_update_object_no_content_length(self):
1491 self.test_update_object(content_length = None)
1493 def test_update_object_invalid_content_length(self):
1494 with AssertContentInvariant(self.client.retrieve_object,
1495 self.containers[0], self.obj[0]['name']):
1496 self.assert_raises_fault(400, self.test_update_object,
1497 content_length = 1000)
1499 def test_update_object_invalid_range(self):
1500 with AssertContentInvariant(self.client.retrieve_object,
1501 self.containers[0], self.obj[0]['name']):
1502 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1504 def test_update_object_invalid_range_and_length(self):
1505 with AssertContentInvariant(self.client.retrieve_object,
1506 self.containers[0], self.obj[0]['name']):
1507 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1510 def test_update_object_invalid_range_with_no_content_length(self):
1511 with AssertContentInvariant(self.client.retrieve_object,
1512 self.containers[0], self.obj[0]['name']):
1513 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1514 content_length = None)
1516 def test_update_object_out_of_limits(self):
1517 with AssertContentInvariant(self.client.retrieve_object,
1518 self.containers[0], self.obj[0]['name']):
1519 l = len(self.obj[0]['data'])
1520 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1522 def test_append(self):
1523 data = get_random_data(500)
1525 self.client.update_object(self.containers[0], self.obj[0]['name'],
1526 StringIO(data), content_length=500,
1527 content_type='application/octet-stream')
1529 content = self.client.retrieve_object(self.containers[0],
1530 self.obj[0]['name'])
1531 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1532 self.assertEqual(content[:-500], self.obj[0]['data'])
1534 def test_update_with_chunked_transfer(self):
1535 data = get_random_data(500)
1537 fl = len(self.obj[0]['data'])
1539 self.client.update_object_using_chunks(self.containers[0],
1540 self.obj[0]['name'],
1543 content_type='application/octet-stream')
1545 #check modified object
1546 content = self.client.retrieve_object(self.containers[0],
1547 self.obj[0]['name'])
1548 self.assertEqual(content[0:dl], data)
1549 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1551 def test_update_from_other_object(self):
1552 c = self.containers[0]
1556 source_data = self.client.retrieve_object(c, src)
1557 source_meta = self.client.retrieve_object_metadata(c, src)
1558 source_hash = self.client.retrieve_object_hashmap(c, src)
1560 #update zero length object
1561 self.client.create_zero_length_object(c, dest)
1562 source_object = '/%s/%s' % (c, src)
1563 self.client.update_from_other_source(c, dest, source_object)
1564 dest_data = self.client.retrieve_object(c, src)
1565 dest_meta = self.client.retrieve_object_metadata(c, dest)
1566 dest_hash = self.client.retrieve_object_hashmap(c, src)
1567 self.assertEqual(source_data, dest_data)
1568 self.assertEqual(source_hash, dest_hash)
1571 self.client.update_from_other_source(c, dest, source_object)
1572 content = self.client.retrieve_object(c, dest)
1573 self.assertEqual(source_data * 2, content)
1575 def test_update_range_from_other_object(self):
1576 c = self.containers[0]
1580 src = self.obj[1]['name']
1581 src_data = self.client.retrieve_object(c, src)
1583 #update zero length object
1584 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1585 source_object = '/%s/%s' % (c, src)
1586 first_byte_pos = 4*1024*1024+1
1587 last_byte_pos = 4*1024*1024+4
1588 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1589 self.client.update_from_other_source(c, dest, source_object,
1590 content_range=range)
1591 content = self.client.retrieve_object(c, dest)
1592 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1593 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1594 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1596 def test_update_hashes_from_other_object(self):
1597 c = self.containers[0]
1601 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1603 #update zero length object
1604 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1605 source_object = '/%s/%s' % (c, o_names[0])
1606 first_byte_pos = 4*1024*1024
1607 last_byte_pos = 5*1024*1024
1608 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1609 self.client.update_from_other_source(c, dest, source_object,
1610 content_range=range)
1611 content = self.client.retrieve_object(c, dest)
1612 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1613 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1614 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1617 def test_update_zero_length_object(self):
1618 c = self.containers[0]
1621 zero = self.client.create_zero_length_object(c, o)
1623 data = get_random_data()
1624 self.client.update_object(c, o, StringIO(data))
1625 self.client.create_object(c, other, StringIO(data))
1627 self.assertEqual(self.client.retrieve_object(c, o),
1628 self.client.retrieve_object(c, other))
1630 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1631 self.client.retrieve_object_hashmap(c, other))
1633 class ObjectDelete(BaseTestCase):
1635 BaseTestCase.setUp(self)
1636 self.account = 'test'
1637 self.containers = ['c1', 'c2']
1638 for c in self.containers:
1639 self.client.create_container(c)
1640 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1642 def test_delete(self):
1643 #perform delete object
1644 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1646 def test_delete_invalid(self):
1647 #assert item not found
1648 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1651 class ListSharing(BaseTestCase):
1653 BaseTestCase.setUp(self)
1654 self.client.create_container('c')
1656 self.upload_random_data('c', 'o%s' %i)
1657 accounts = OTHER_ACCOUNTS.copy()
1658 self.o1_sharing_with = accounts.popitem()
1659 self.o1_sharing = [self.o1_sharing_with[1]]
1660 self.client.share_object('c', 'o1', self.o1_sharing, read=True)
1664 l.append(accounts.popitem())
1665 #self.client.set_account_groups({'pithos-dev':'chazapis,verigak,papagian'})
1666 #self.o2_sharing = 'write=%s' %
1667 #self.client.share_object('c', 'o2', self.o2_sharing)
1669 def test_listing(self):
1670 self.other = Pithos_Client(get_server(),
1671 self.o1_sharing_with[0],
1672 self.o1_sharing_with[1],
1674 self.assertTrue('test' in self.other.list_shared_by_others())
1676 class TestGreek(BaseTestCase):
1678 BaseTestCase.setUp(self)
1679 #keep track of initial account groups
1680 self.initial_groups = self.client.retrieve_account_groups()
1682 #keep track of initial account meta
1683 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1686 #delete additionally created meta
1688 for m in self.client.retrieve_account_metadata(restricted=True):
1689 if m not in self.initial_meta:
1691 self.client.delete_account_metadata(l)
1693 #delete additionally created groups
1695 for g in self.client.retrieve_account_groups():
1696 if g not in self.initial_groups:
1698 self.client.unset_account_groups(l)
1700 BaseTestCase.tearDown(self)
1702 def test_create_container(self):
1703 self.client.create_container('φάκελος')
1704 self.assert_container_exists('φάκελος')
1706 self.assertTrue('φάκελος' in self.client.list_containers())
1708 def test_create_object(self):
1709 self.client.create_container('φάκελος')
1710 self.upload_random_data('φάκελος', 'αντικείμενο')
1712 self.assert_object_exists('φάκελος', 'αντικείμενο')
1713 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1715 def test_copy_object(self):
1716 src_container = 'φάκελος'
1717 src_object = 'αντικείμενο'
1718 dest_container = 'αντίγραφα'
1719 dest_object = 'ασφαλές-αντίγραφο'
1721 self.client.create_container(src_container)
1722 self.upload_random_data(src_container, src_object)
1724 self.client.create_container(dest_container)
1725 self.client.copy_object(src_container, src_object, dest_container,
1728 self.assert_object_exists(src_container, src_object)
1729 self.assert_object_exists(dest_container, dest_object)
1730 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1732 def test_move_object(self):
1733 src_container = 'φάκελος'
1734 src_object = 'αντικείμενο'
1735 dest_container = 'αντίγραφα'
1736 dest_object = 'ασφαλές-αντίγραφο'
1738 self.client.create_container(src_container)
1739 self.upload_random_data(src_container, src_object)
1741 self.client.create_container(dest_container)
1742 self.client.move_object(src_container, src_object, dest_container,
1745 self.assert_object_not_exists(src_container, src_object)
1746 self.assert_object_exists(dest_container, dest_object)
1747 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1749 def test_delete_object(self):
1750 self.client.create_container('φάκελος')
1751 self.upload_random_data('φάκελος', 'αντικείμενο')
1752 self.assert_object_exists('φάκελος', 'αντικείμενο')
1754 self.client.delete_object('φάκελος', 'αντικείμενο')
1755 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1756 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1758 def test_delete_container(self):
1759 self.client.create_container('φάκελος')
1760 self.assert_container_exists('φάκελος')
1762 self.client.delete_container('φάκελος')
1763 self.assert_container_not_exists('φάκελος')
1764 self.assertTrue('φάκελος' not in self.client.list_containers())
1766 def test_account_meta(self):
1767 meta = {'ποιότητα':'ΑΑΑ'}
1768 self.client.update_account_metadata(**meta)
1769 meta = self.client.retrieve_account_metadata(restricted=True)
1770 self.assertTrue('ποιότητα' in meta.keys())
1771 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1773 def test_container_meta(self):
1774 meta = {'ποιότητα':'ΑΑΑ'}
1775 self.client.create_container('φάκελος', **meta)
1777 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1778 self.assertTrue('ποιότητα' in meta.keys())
1779 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1781 def test_object_meta(self):
1782 self.client.create_container('φάκελος')
1783 meta = {'ποιότητα':'ΑΑΑ'}
1784 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1786 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1788 self.assertTrue('ποιότητα' in meta.keys())
1789 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1791 def test_list_meta_filtering(self):
1792 self.client.create_container('φάκελος')
1793 meta = {'ποιότητα':'ΑΑΑ'}
1794 self.upload_random_data('φάκελος', 'ο1', **meta)
1795 self.upload_random_data('φάκελος', 'ο2')
1796 self.upload_random_data('φάκελος', 'ο3')
1798 meta = {'ποσότητα':'μεγάλη'}
1799 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1800 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1801 self.assertTrue('ο1' in objects)
1802 self.assertTrue('ο2' in objects)
1803 self.assertTrue('ο3' not in objects)
1805 def test_groups(self):
1807 groups = {'σεφς':'chazapis,διογένης'}
1808 self.client.set_account_groups(**groups)
1809 groups.update(self.initial_groups)
1810 self.assertEqual(groups['σεφς'],
1811 self.client.retrieve_account_groups()['σεφς'])
1814 self.client.create_container('φάκελος')
1815 o = self.upload_random_data('φάκελος', 'ο1')
1816 self.client.share_object('φάκελος', 'ο1', ['test:σεφς'])
1817 chef = Pithos_Client(get_server(),
1821 self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1822 'φάκελος', 'ο1', account=get_user())
1825 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1826 new_data = get_random_data()
1827 self.assert_not_raises_fault(401, chef.update_object,
1828 'φάκελος', 'ο1', StringIO(new_data),
1831 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1832 self.assertEqual(server_data[:len(o['data'])], o['data'])
1833 self.assertEqual(server_data[len(o['data']):], new_data)
1835 def test_manifestation(self):
1836 self.client.create_container('κουβάς')
1840 part = '%s%d' %(prefix, i)
1841 o = self.upload_random_data('κουβάς', part)
1844 self.client.create_container('φάκελος')
1845 manifest = '%s/%s' %('κουβάς', prefix)
1846 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1848 self.assert_object_exists('φάκελος', 'άπαντα')
1849 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1852 #wrong manifestation
1853 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1854 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1856 def test_update_from_another_object(self):
1857 self.client.create_container('κουβάς')
1858 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1859 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1860 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1861 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1864 self.client.retrieve_object('κουβάς', 'νέο'),
1865 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1867 class TestPermissions(BaseTestCase):
1869 BaseTestCase.setUp(self)
1870 #keep track of initial account groups
1871 self.initial_groups = self.client.retrieve_account_groups()
1872 #keep track of initial account meta
1873 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1876 self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1877 groups = {'pithosdev':','.join(self.authorized)}
1878 self.client.set_account_groups(**groups)
1881 #delete additionally created meta
1883 for m in self.client.retrieve_account_metadata(restricted=True):
1884 if m not in self.initial_meta:
1886 self.client.delete_account_metadata(l)
1888 #delete additionally created groups
1890 for g in self.client.retrieve_account_groups():
1891 if g not in self.initial_groups:
1893 self.client.unset_account_groups(l)
1895 BaseTestCase.tearDown(self)
1897 def test_read_access(self):
1898 self.client.create_container('c')
1899 o = self.upload_random_data('c', 'o')
1900 self.client.share_object('c', 'o', ['test:pithosdev'])
1901 for token, account in OTHER_ACCOUNTS.items():
1902 cl = Pithos_Client(get_server(), token, account, get_api())
1903 if account in self.authorized:
1904 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1905 'c', 'o', account=get_user())
1907 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1908 'c', 'o', account=get_user())
1911 def test_write_access(self):
1912 self.client.create_container('c')
1913 o = self.upload_random_data('c', 'o')
1914 self.client.share_object('c', 'o', ['chazapis'], read=False)
1915 for token, account in OTHER_ACCOUNTS.items():
1916 cl = Pithos_Client(get_server(), token, account, get_api())
1917 new_data = get_random_data()
1918 if account == 'chazapis':
1919 self.assert_not_raises_fault(401, cl.update_object,
1920 'c', 'o', StringIO(new_data),
1922 server_data = self.client.retrieve_object('c', 'o')
1923 self.assertEqual(o['data'], server_data[:len(o['data'])])
1924 self.assertEqual(new_data, server_data[len(o['data']):])
1926 self.assert_raises_fault(401, cl.update_object,
1927 'c', 'o', StringIO(new_data),
1930 class AssertMappingInvariant(object):
1931 def __init__(self, callable, *args, **kwargs):
1932 self.callable = callable
1934 self.kwargs = kwargs
1936 def __enter__(self):
1937 self.map = self.callable(*self.args, **self.kwargs)
1940 def __exit__(self, type, value, tb):
1941 map = self.callable(*self.args, **self.kwargs)
1942 for k in self.map.keys():
1943 if is_date(self.map[k]):
1945 assert map[k] == self.map[k]
1947 class AssertContentInvariant(object):
1948 def __init__(self, callable, *args, **kwargs):
1949 self.callable = callable
1951 self.kwargs = kwargs
1953 def __enter__(self):
1954 self.content = self.callable(*self.args, **self.kwargs)[2]
1957 def __exit__(self, type, value, tb):
1958 content = self.callable(*self.args, **self.kwargs)[2]
1959 assert self.content == content
1961 def get_content_splitted(response):
1963 return response.content.split('\n')
1965 def compute_md5_hash(data):
1969 return md5.hexdigest().lower()
1971 def compute_block_hash(data, algorithm):
1972 h = hashlib.new(algorithm)
1973 h.update(data.rstrip('\x00'))
1974 return h.hexdigest()
1976 def get_random_data(length=500):
1977 char_set = string.ascii_uppercase + string.digits
1978 return ''.join(random.choice(char_set) for x in range(length))
1981 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
1982 __D = r'(?P<day>\d{2})'
1983 __D2 = r'(?P<day>[ \d]\d)'
1984 __M = r'(?P<mon>\w{3})'
1985 __Y = r'(?P<year>\d{4})'
1986 __Y2 = r'(?P<year>\d{2})'
1987 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
1988 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
1989 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
1990 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
1991 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
1992 m = regex.match(date)
1997 o_names = ['kate.jpg',
1998 'kate_beckinsale.jpg',
1999 'How To Win Friends And Influence People.pdf',
2000 'moms_birthday.jpg',
2002 'Disturbed - Down With The Sickness.mp3',
2003 'army_of_darkness.avi',
2005 'photos/animals/dogs/poodle.jpg',
2006 'photos/animals/dogs/terrier.jpg',
2007 'photos/animals/cats/persian.jpg',
2008 'photos/animals/cats/siamese.jpg',
2009 'photos/plants/fern.jpg',
2010 'photos/plants/rose.jpg',
2013 if __name__ == "__main__":