4 # Copyright 2011 GRNET S.A. All rights reserved.
6 # Redistribution and use in source and binary forms, with or
7 # without modification, are permitted provided that the following
10 # 1. Redistributions of source code must retain the above
11 # copyright notice, this list of conditions and the following
14 # 2. Redistributions in binary form must reproduce the above
15 # copyright notice, this list of conditions and the following
16 # disclaimer in the documentation and/or other materials
17 # provided with the distribution.
19 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
32 # The views and conclusions contained in the software and
33 # documentation are those of the authors and should not be
34 # interpreted as representing official policies, either expressed
35 # or implied, of GRNET S.A.
37 from lib.client import Pithos_Client, Fault
38 from lib.util import get_user, get_auth, get_server, get_api
39 from xml.dom import minidom
40 from StringIO import StringIO
52 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
53 "%A, %d-%b-%y %H:%M:%S GMT",
54 "%a, %d %b %Y %H:%M:%S GMT"]
67 class BaseTestCase(unittest.TestCase):
68 #TODO unauthorized request
70 self.client = Pithos_Client(get_server(), get_auth(), get_user(),
73 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
76 # 'account': ('x-account-container-count',
77 # 'x-account-bytes-used',
89 # 'x-object-manifest',
91 # 'x-object-modified-by',
93 # 'x-object-version-timestamp',
95 # 'container': ('x-container-object-count',
96 # 'x-container-bytes-used',
101 # 'x-container-block-size',
102 # 'x-container-block-hash',
103 # 'x-container-policy-quota',
104 # 'x-container-policy-versioning',
106 # 'x-container-object-meta',
107 # 'x-container-policy-versioning',
110 #self.contentTypes = {'xml':'application/xml',
111 # 'json':'application/json',
119 'x_container_policy_quota',
120 'x_container_policy_versioning',),
128 self.return_codes = (400, 401, 403, 404, 503,)
131 self._clean_account()
133 def _clean_account(self):
134 for c in self.client.list_containers():
136 #list objects returns at most 10000 objects
137 #so repeat until there are no more objects
138 objects = self.client.list_objects(c)
142 self.client.delete_object(c, o)
143 self.client.delete_container(c)
145 def assert_status(self, status, codes):
146 l = [elem for elem in self.return_codes]
147 if type(codes) == types.ListType:
151 self.assertTrue(status in l)
153 #def assert_headers(self, headers, type, **exp_meta):
154 # prefix = 'x-%s-meta-' %type
155 # system_headers = [h for h in headers if not h.startswith(prefix)]
156 # for k,v in headers.items():
157 # if k in system_headers:
158 # self.assertTrue(k in headers[type])
160 # k = k.split(prefix)[-1]
161 # self.assertEqual(v, exp_meta[k])
163 def assert_extended(self, data, format, type, size=10000):
165 self._assert_xml(data, type, size)
166 elif format == 'json':
167 self._assert_json(data, type, size)
169 def _assert_json(self, data, type, size):
170 convert = lambda s: s.lower()
171 info = [convert(elem) for elem in self.extended[type]]
172 self.assertTrue(len(data) <= size)
175 if 'subdir' in i.keys():
177 self.assertTrue(item in i.keys())
179 def _assert_xml(self, data, type, size):
180 convert = lambda s: s.lower()
181 info = [convert(elem) for elem in self.extended[type]]
183 info.remove('content_encoding')
187 entities = xml.getElementsByTagName(type)
188 self.assertTrue(len(entities) <= size)
191 self.assertTrue(e.getElementsByTagName(item))
193 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
195 asserts that a Fault with a specific status is raised
196 when callableObj is called with the specific arguments
199 r = callableObj(*args, **kwargs)
200 self.fail('Should never reach here')
202 if type(status) == types.ListType:
203 self.failUnless(f.status in status)
205 self.failUnless(f.status == status)
207 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
209 asserts that a Fault with a specific status is not raised
210 when callableObj is called with the specific arguments
213 r = callableObj(*args, **kwargs)
215 self.failIfEqual(f.status, status)
217 def assert_container_exists(self, container):
219 asserts the existence of a container
222 self.client.retrieve_container_metadata(container)
224 self.failIf(f.status == 404)
226 def assert_container_not_exists(self, container):
228 asserts there is no such a container
230 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
233 def assert_object_exists(self, container, object):
235 asserts the existence of an object
238 self.client.retrieve_object_metadata(container, object)
240 self.failIf(f.status == 404)
242 def assert_object_not_exists(self, container, object):
244 asserts there is no such an object
246 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
249 def assert_versionlist_structure(self, versionlist):
250 self.assertTrue(type(versionlist) == types.ListType)
251 for elem in versionlist:
252 self.assertTrue(type(elem) == types.ListType)
253 self.assertEqual(len(elem), 2)
255 def upload_random_data(self, container, name, length=1024, type=None,
257 data = get_random_data(length)
258 return self.upload_data(container, name, data, type, enc, **meta)
260 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
266 obj['hash'] = compute_md5_hash(obj['data'])
269 args['etag'] = etag if etag else obj['hash']
272 guess = mimetypes.guess_type(name)
273 type = type if type else guess[0]
274 enc = enc if enc else guess[1]
277 args['content_type'] = type if type else 'plain/text'
278 args['content_encoding'] = enc if enc else None
282 path = '/%s/%s' % (container, name)
283 self.client.create_object(container, name, f=StringIO(obj['data']),
290 class AccountHead(BaseTestCase):
292 BaseTestCase.setUp(self)
293 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
294 for item in self.containers:
295 self.client.create_container(item)
297 #keep track of initial account groups
298 self.initial_groups = self.client.retrieve_account_groups()
300 #keep track of initial account meta
301 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
304 self.client.update_account_metadata(**meta)
305 self.updated_meta = self.initial_meta.update(meta)
308 #delete additionally created meta
310 for m in self.client.retrieve_account_metadata(restricted=True):
311 if m not in self.initial_meta:
313 self.client.delete_account_metadata(l)
315 #delete additionally created groups
317 for g in self.client.retrieve_account_groups():
318 if g not in self.initial_groups:
320 self.client.unset_account_groups(l)
322 BaseTestCase.tearDown(self)
324 def test_get_account_meta(self):
325 meta = self.client.retrieve_account_metadata()
327 containers = self.client.list_containers()
328 l = str(len(containers))
329 self.assertEqual(meta['x-account-container-count'], l)
332 m = self.client.retrieve_container_metadata(c)
333 size = size + int(m['x-container-bytes-used'])
334 self.assertEqual(meta['x-account-bytes-used'], str(size))
336 def test_get_account_403(self):
337 self.assert_raises_fault(403,
338 self.invalid_client.retrieve_account_metadata)
340 def test_get_account_meta_until(self):
341 t = datetime.datetime.utcnow()
342 past = t - datetime.timedelta(minutes=-15)
343 past = int(_time.mktime(past.timetuple()))
345 meta = {'premium':True}
346 self.client.update_account_metadata(**meta)
347 meta = self.client.retrieve_account_metadata(restricted=True,
349 self.assertTrue('premium' not in meta)
351 meta = self.client.retrieve_account_metadata(restricted=True)
352 self.assertTrue('premium' in meta)
354 def test_get_account_meta_until_invalid_date(self):
355 meta = {'premium':True}
356 self.client.update_account_metadata(**meta)
357 meta = self.client.retrieve_account_metadata(restricted=True,
359 self.assertTrue('premium' in meta)
361 class AccountGet(BaseTestCase):
363 BaseTestCase.setUp(self)
364 #create some containers
365 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
366 for item in self.containers:
367 self.client.create_container(item)
371 containers = self.client.list_containers()
372 self.assertEquals(self.containers, containers)
374 def test_list_403(self):
375 self.assert_raises_fault(403, self.invalid_client.list_containers)
377 def test_list_with_limit(self):
379 containers = self.client.list_containers(limit=limit)
380 self.assertEquals(len(containers), limit)
381 self.assertEquals(self.containers[:2], containers)
383 def test_list_with_marker(self):
386 containers = self.client.list_containers(limit=l, marker=m)
387 i = self.containers.index(m) + 1
388 self.assertEquals(self.containers[i:(i+l)], containers)
391 containers = self.client.list_containers(limit=l, marker=m)
392 i = self.containers.index(m) + 1
393 self.assertEquals(self.containers[i:(i+l)], containers)
395 def test_list_json_with_marker(self):
398 containers = self.client.list_containers(limit=l, marker=m, format='json')
399 self.assert_extended(containers, 'json', 'container', l)
400 self.assertEqual(containers[0]['name'], 'kiwis')
401 self.assertEqual(containers[1]['name'], 'oranges')
403 def test_list_xml_with_marker(self):
406 xml = self.client.list_containers(limit=l, marker=m, format='xml')
407 self.assert_extended(xml, 'xml', 'container', l)
408 nodes = xml.getElementsByTagName('name')
409 self.assertEqual(len(nodes), 1)
410 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
412 def test_if_modified_since(self):
413 t = datetime.datetime.utcnow()
414 t2 = t - datetime.timedelta(minutes=10)
417 self.client.create_container('dummy')
419 for f in DATE_FORMATS:
420 past = t2.strftime(f)
422 c = self.client.list_containers(if_modified_since=past)
423 self.assertEqual(len(c), len(self.containers) + 1)
425 self.failIf(f.status == 304) #fail if not modified
427 def test_if_modified_since_invalid_date(self):
428 c = self.client.list_containers(if_modified_since='')
429 self.assertEqual(len(c), len(self.containers))
431 def test_if_not_modified_since(self):
432 now = datetime.datetime.utcnow()
433 since = now + datetime.timedelta(1)
435 for f in DATE_FORMATS:
436 args = {'if_modified_since':'%s' %since.strftime(f)}
439 self.assert_raises_fault(304, self.client.list_containers, **args)
441 def test_if_unmodified_since(self):
442 now = datetime.datetime.utcnow()
443 since = now + datetime.timedelta(1)
445 for f in DATE_FORMATS:
446 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
449 self.assertEqual(self.containers, c)
451 def test_if_unmodified_since_precondition_failed(self):
452 t = datetime.datetime.utcnow()
453 t2 = t - datetime.timedelta(minutes=10)
456 self.client.create_container('dummy')
458 for f in DATE_FORMATS:
459 past = t2.strftime(f)
461 args = {'if_unmodified_since':'%s' %past}
463 #assert precondition failed
464 self.assert_raises_fault(412, self.client.list_containers, **args)
466 class AccountPost(BaseTestCase):
468 BaseTestCase.setUp(self)
469 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
470 for item in self.containers:
471 self.client.create_container(item)
473 #keep track of initial account groups
474 self.initial_groups = self.client.retrieve_account_groups()
476 #keep track of initial account meta
477 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
480 self.client.update_account_metadata(**meta)
481 self.updated_meta = self.initial_meta.update(meta)
484 #delete additionally created meta
486 for m in self.client.retrieve_account_metadata(restricted=True):
487 if m not in self.initial_meta:
489 self.client.delete_account_metadata(l)
491 #delete additionally created groups
493 for g in self.client.retrieve_account_groups():
494 if g not in self.initial_groups:
496 self.client.unset_account_groups(l)
498 BaseTestCase.tearDown(self)
500 def test_update_meta(self):
501 with AssertMappingInvariant(self.client.retrieve_account_groups):
502 meta = {'test':'test', 'tost':'tost'}
503 self.client.update_account_metadata(**meta)
505 meta.update(self.initial_meta)
506 self.assertEqual(meta,
507 self.client.retrieve_account_metadata(
510 def test_invalid_account_update_meta(self):
511 meta = {'test':'test', 'tost':'tost'}
512 self.assert_raises_fault(403,
513 self.invalid_client.update_account_metadata,
516 def test_reset_meta(self):
517 with AssertMappingInvariant(self.client.retrieve_account_groups):
518 meta = {'test':'test', 'tost':'tost'}
519 self.client.update_account_metadata(**meta)
521 meta = {'test':'test33'}
522 self.client.reset_account_metadata(**meta)
524 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
526 def test_delete_meta(self):
527 with AssertMappingInvariant(self.client.retrieve_account_groups):
528 meta = {'test':'test', 'tost':'tost'}
529 self.client.update_account_metadata(**meta)
531 self.client.delete_account_metadata(meta.keys())
533 account_meta = self.client.retrieve_account_metadata(restricted=True)
535 self.assertTrue(m not in account_meta.keys())
537 def test_set_account_groups(self):
538 with AssertMappingInvariant(self.client.retrieve_account_metadata):
539 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
540 self.client.set_account_groups(**groups)
542 self.assertEqual(set(groups['pithosdev']),
543 set(self.client.retrieve_account_groups()['pithosdev']))
545 more_groups = {'clientsdev':'pkanavos,mvasilak'}
546 self.client.set_account_groups(**more_groups)
548 groups.update(more_groups)
549 self.assertEqual(set(groups['clientsdev']),
550 set(self.client.retrieve_account_groups()['clientsdev']))
552 def test_reset_account_groups(self):
553 with AssertMappingInvariant(self.client.retrieve_account_metadata):
554 groups = {'pithosdev':'verigak,gtsouk,chazapis',
555 'clientsdev':'pkanavos,mvasilak'}
556 self.client.set_account_groups(**groups)
558 self.assertEqual(set(groups['pithosdev'].split(',')),
559 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
560 self.assertEqual(set(groups['clientsdev'].split(',')),
561 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
563 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
564 self.client.reset_account_groups(**groups)
566 self.assertEqual(set(groups['pithosdev'].split(',')),
567 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
569 def test_delete_account_groups(self):
570 with AssertMappingInvariant(self.client.retrieve_account_metadata):
571 groups = {'pithosdev':'verigak,gtsouk,chazapis',
572 'clientsdev':'pkanavos,mvasilak'}
573 self.client.set_account_groups(**groups)
575 self.client.unset_account_groups(groups.keys())
577 self.assertEqual({}, self.client.retrieve_account_groups())
579 class ContainerHead(BaseTestCase):
581 BaseTestCase.setUp(self)
582 self.container = 'apples'
583 self.client.create_container(self.container)
585 def test_get_meta(self):
586 meta = {'trash':'true'}
587 t1 = datetime.datetime.utcnow()
588 o = self.upload_random_data(self.container, o_names[0], **meta)
590 headers = self.client.retrieve_container_metadata(self.container)
591 self.assertEqual(headers['x-container-object-count'], '1')
592 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
593 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
595 threashold = datetime.timedelta(seconds=1)
596 self.assertTrue(delta < threashold)
597 self.assertTrue(headers['x-container-object-meta'])
598 self.assertTrue('Trash' in headers['x-container-object-meta'])
600 class ContainerGet(BaseTestCase):
602 BaseTestCase.setUp(self)
603 self.container = ['pears', 'apples']
604 for c in self.container:
605 self.client.create_container(c)
607 for o in o_names[:8]:
608 self.obj.append(self.upload_random_data(self.container[0], o))
609 for o in o_names[8:]:
610 self.obj.append(self.upload_random_data(self.container[1], o))
612 def test_list_objects(self):
613 objects = self.client.list_objects(self.container[0])
614 l = [elem['name'] for elem in self.obj[:8]]
616 self.assertEqual(objects, l)
618 def test_list_objects_containing_slash(self):
619 self.client.create_container('test')
620 self.upload_random_data('test', '/objectname')
622 objects = self.client.list_objects('test')
623 self.assertEqual(objects, ['/objectname'])
625 objects = self.client.list_objects('test', format='json')
626 self.assertEqual(objects[0]['name'], '/objectname')
628 objects = self.client.list_objects('test', format='xml')
629 self.assert_extended(objects, 'xml', 'object')
630 node_name = objects.getElementsByTagName('name')[0]
631 self.assertEqual(node_name.firstChild.data, '/objectname')
633 def test_list_objects_with_limit_marker(self):
634 objects = self.client.list_objects(self.container[0], limit=2)
635 l = [elem['name'] for elem in self.obj[:8]]
637 self.assertEqual(objects, l[:2])
639 markers = ['How To Win Friends And Influence People.pdf',
643 objects = self.client.list_objects(self.container[0], limit=limit,
645 l = [elem['name'] for elem in self.obj[:8]]
647 start = l.index(m) + 1
649 end = len(l) >= end and end or len(l)
650 self.assertEqual(objects, l[start:end])
653 def _test_list_limit_exceeds(self):
654 self.client.create_container('pithos')
656 for i in range(10001):
657 self.client.create_zero_length_object('pithos', i)
659 self.assertEqual(10000, len(self.client.list_objects('pithos')))
661 def test_list_empty_params(self):
662 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
664 objects = objects.strip().split('\n')
665 self.assertEqual(objects,
666 self.client.list_objects(self.container[0]))
668 def test_list_pseudo_hierarchical_folders(self):
669 objects = self.client.list_objects(self.container[1], prefix='photos',
671 self.assertEquals(['photos/animals/', 'photos/me.jpg',
672 'photos/plants/'], objects)
674 objects = self.client.list_objects(self.container[1],
675 prefix='photos/animals',
677 l = ['photos/animals/cats/', 'photos/animals/dogs/']
678 self.assertEquals(l, objects)
680 objects = self.client.list_objects(self.container[1], path='photos')
681 self.assertEquals(['photos/me.jpg'], objects)
683 def test_extended_list_json(self):
684 objects = self.client.list_objects(self.container[1], format='json',
685 limit=2, prefix='photos/animals',
687 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
688 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
690 def test_extended_list_xml(self):
691 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
692 prefix='photos', delimiter='/')
693 self.assert_extended(xml, 'xml', 'object', size=4)
694 dirs = xml.getElementsByTagName('subdir')
695 self.assertEqual(len(dirs), 2)
696 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
697 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
699 objects = xml.getElementsByTagName('name')
700 self.assertEqual(len(objects), 1)
701 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
703 def test_list_meta_double_matching(self):
704 meta = {'quality':'aaa', 'stock':'true'}
705 self.client.update_object_metadata(self.container[0],
706 self.obj[0]['name'], **meta)
707 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
708 self.assertEqual(len(obj), 1)
709 self.assertTrue(obj, self.obj[0]['name'])
711 def test_list_using_meta(self):
712 meta = {'quality':'aaa'}
713 for o in self.obj[:2]:
714 self.client.update_object_metadata(self.container[0], o['name'],
716 meta = {'stock':'true'}
717 for o in self.obj[3:5]:
718 self.client.update_object_metadata(self.container[0], o['name'],
721 obj = self.client.list_objects(self.container[0], meta='Quality')
722 self.assertEqual(len(obj), 2)
723 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
725 # test case insensitive
726 obj = self.client.list_objects(self.container[0], meta='quality')
727 self.assertEqual(len(obj), 2)
728 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
730 # test multiple matches
731 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
732 self.assertEqual(len(obj), 4)
733 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
735 # test non 1-1 multiple match
736 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
737 self.assertEqual(len(obj), 2)
738 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
740 def test_if_modified_since(self):
741 t = datetime.datetime.utcnow()
742 t2 = t - datetime.timedelta(minutes=10)
745 self.upload_random_data(self.container[0], o_names[0])
747 for f in DATE_FORMATS:
748 past = t2.strftime(f)
750 o = self.client.list_objects(self.container[0],
751 if_modified_since=past)
753 self.client.list_objects(self.container[0]))
755 self.failIf(f.status == 304) #fail if not modified
757 def test_if_modified_since_invalid_date(self):
758 headers = {'if-modified-since':''}
759 o = self.client.list_objects(self.container[0], if_modified_since='')
760 self.assertEqual(o, self.client.list_objects(self.container[0]))
762 def test_if_not_modified_since(self):
763 now = datetime.datetime.utcnow()
764 since = now + datetime.timedelta(1)
766 for f in DATE_FORMATS:
767 args = {'if_modified_since':'%s' %since.strftime(f)}
770 self.assert_raises_fault(304, self.client.list_objects,
771 self.container[0], **args)
773 def test_if_unmodified_since(self):
774 now = datetime.datetime.utcnow()
775 since = now + datetime.timedelta(1)
777 for f in DATE_FORMATS:
778 obj = self.client.list_objects(self.container[0],
779 if_unmodified_since=since.strftime(f))
782 self.assertEqual(obj, self.client.list_objects(self.container[0]))
784 def test_if_unmodified_since_precondition_failed(self):
785 t = datetime.datetime.utcnow()
786 t2 = t - datetime.timedelta(minutes=10)
789 self.client.create_container('dummy')
791 for f in DATE_FORMATS:
792 past = t2.strftime(f)
794 args = {'if_unmodified_since':'%s' %past}
796 #assert precondition failed
797 self.assert_raises_fault(412, self.client.list_objects,
798 self.container[0], **args)
800 class ContainerPut(BaseTestCase):
802 BaseTestCase.setUp(self)
803 self.containers = ['c1', 'c2']
805 def test_create(self):
806 self.client.create_container(self.containers[0])
807 containers = self.client.list_containers()
808 self.assertTrue(self.containers[0] in containers)
809 self.assert_container_exists(self.containers[0])
811 def test_create_twice(self):
812 self.client.create_container(self.containers[0])
813 self.assertTrue(not self.client.create_container(self.containers[0]))
815 def test_quota(self):
816 self.client.create_container(self.containers[0])
818 policy = {'quota':100}
819 self.client.set_container_policies('c1', **policy)
821 meta = self.client.retrieve_container_metadata('c1')
822 self.assertTrue('x-container-policy-quota' in meta)
823 self.assertEqual(meta['x-container-policy-quota'], '100')
826 kwargs = {'length':101}
827 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
831 self.client.set_container_policies('c1', **policy)
833 class ContainerPost(BaseTestCase):
835 BaseTestCase.setUp(self)
836 self.container = 'apples'
837 self.client.create_container(self.container)
839 def test_update_meta(self):
840 meta = {'test':'test33',
842 self.client.update_container_metadata(self.container, **meta)
843 headers = self.client.retrieve_container_metadata(self.container)
844 for k,v in meta.items():
845 k = 'x-container-meta-%s' % k
846 self.assertTrue(headers[k])
847 self.assertEqual(headers[k], v)
849 class ContainerDelete(BaseTestCase):
851 BaseTestCase.setUp(self)
852 self.containers = ['c1', 'c2']
853 for c in self.containers:
854 self.client.create_container(c)
856 def test_delete(self):
857 status = self.client.delete_container(self.containers[0])[0]
858 self.assertEqual(status, 204)
860 def test_delete_non_empty(self):
861 self.upload_random_data(self.containers[1], o_names[0])
862 self.assert_raises_fault(409, self.client.delete_container,
865 def test_delete_invalid(self):
866 self.assert_raises_fault(404, self.client.delete_container, 'c3')
868 class ObjectHead(BaseTestCase):
871 class ObjectGet(BaseTestCase):
873 BaseTestCase.setUp(self)
874 self.containers = ['c1', 'c2']
875 #create some containers
876 for c in self.containers:
877 self.client.create_container(c)
880 names = ('obj1', 'obj2')
883 self.objects.append(self.upload_random_data(self.containers[1], n))
885 def test_versions(self):
886 c = self.containers[1]
888 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
889 self.assert_versionlist_structure(b)
892 meta = {'quality':'AAA', 'stock':True}
893 self.client.update_object_metadata(c, o['name'], **meta)
895 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
896 self.assert_versionlist_structure(a)
897 self.assertEqual(len(b)+1, len(a))
898 self.assertEqual(b, a[:-1])
900 #get exact previous version metadata
902 v_meta = self.client.retrieve_object_metadata(c, o['name'],
905 for k in meta.keys():
906 self.assertTrue(k not in v_meta)
909 data = get_random_data()
910 self.client.update_object(c, o['name'], StringIO(data))
912 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
913 self.assert_versionlist_structure(aa)
914 self.assertEqual(len(a)+1, len(aa))
915 self.assertEqual(a, aa[:-1])
917 #get exact previous version
919 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
920 self.assertEqual(o['data'], v_data)
921 self.assertEqual(self.client.retrieve_object(c, o['name']),
922 '%s%s' %(v_data, data))
926 o = self.client.retrieve_object(self.containers[1],
927 self.objects[0]['name'],
928 self.objects[0]['meta'])
929 self.assertEqual(o, self.objects[0]['data'])
931 def test_objects_with_trailing_spaces(self):
932 self.client.create_container('test')
934 self.upload_random_data('test', 'a')
935 #look for 'a ' object
936 self.assert_raises_fault(404, self.client.retrieve_object,
940 self.client.delete_object('test', 'a')
941 self.assert_raises_fault(404, self.client.retrieve_object,
945 self.upload_random_data('test', 'a ')
947 self.assert_raises_fault(404, self.client.retrieve_object,
950 def test_get_invalid(self):
951 self.assert_raises_fault(404, self.client.retrieve_object,
952 self.containers[0], self.objects[0]['name'])
954 def test_get_partial(self):
955 #perform get with range
956 status, headers, data = self.client.request_object(self.containers[1],
957 self.objects[0]['name'],
960 #assert successful partial content
961 self.assertEqual(status, 206)
964 self.assertEqual(headers['content-type'],
965 self.objects[0]['meta']['content_type'])
967 #assert content length
968 self.assertEqual(int(headers['content-length']), 500)
971 self.assertEqual(self.objects[0]['data'][:500], data)
973 def test_get_final_500(self):
974 #perform get with range
975 headers = {'range':'bytes=-500'}
976 status, headers, data = self.client.request_object(self.containers[1],
977 self.objects[0]['name'],
980 #assert successful partial content
981 self.assertEqual(status, 206)
984 self.assertEqual(headers['content-type'],
985 self.objects[0]['meta']['content_type'])
987 #assert content length
988 self.assertEqual(int(headers['content-length']), 500)
991 self.assertTrue(self.objects[0]['data'][-500:], data)
993 def test_get_rest(self):
994 #perform get with range
995 offset = len(self.objects[0]['data']) - 500
996 status, headers, data = self.client.request_object(self.containers[1],
997 self.objects[0]['name'],
998 range='bytes=%s-' %offset)
1000 #assert successful partial content
1001 self.assertEqual(status, 206)
1003 #assert content-type
1004 self.assertEqual(headers['content-type'],
1005 self.objects[0]['meta']['content_type'])
1007 #assert content length
1008 self.assertEqual(int(headers['content-length']), 500)
1011 self.assertTrue(self.objects[0]['data'][-500:], data)
1013 def test_get_range_not_satisfiable(self):
1014 #perform get with range
1015 offset = len(self.objects[0]['data']) + 1
1017 #assert range not satisfiable
1018 self.assert_raises_fault(416, self.client.retrieve_object,
1019 self.containers[1], self.objects[0]['name'],
1020 range='bytes=0-%s' %offset)
1022 def test_multiple_range(self):
1023 #perform get with multiple range
1024 ranges = ['0-499', '-500', '1000-']
1025 bytes = 'bytes=%s' % ','.join(ranges)
1026 status, headers, data = self.client.request_object(self.containers[1],
1027 self.objects[0]['name'],
1030 # assert partial content
1031 self.assertEqual(status, 206)
1033 # assert Content-Type of the reply will be multipart/byteranges
1034 self.assertTrue(headers['content-type'])
1035 content_type_parts = headers['content-type'].split()
1036 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1038 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1039 cparts = data.split(boundary)[1:-1]
1041 # assert content parts are exactly 2
1042 self.assertEqual(len(cparts), len(ranges))
1044 # for each content part assert headers
1046 for cpart in cparts:
1047 content = cpart.split('\r\n')
1048 headers = content[1:3]
1049 content_range = headers[0].split(': ')
1050 self.assertEqual(content_range[0], 'Content-Range')
1052 r = ranges[i].split('-')
1053 if not r[0] and not r[1]:
1056 start = len(self.objects[0]['data']) - int(r[1])
1057 end = len(self.objects[0]['data'])
1060 end = len(self.objects[0]['data'])
1064 fdata = self.objects[0]['data'][start:end]
1065 sdata = '\r\n'.join(content[4:-1])
1066 self.assertEqual(len(fdata), len(sdata))
1067 self.assertEquals(fdata, sdata)
1070 def test_multiple_range_not_satisfiable(self):
1071 #perform get with multiple range
1072 out_of_range = len(self.objects[0]['data']) + 1
1073 ranges = ['0-499', '-500', '%d-' %out_of_range]
1074 bytes = 'bytes=%s' % ','.join(ranges)
1076 # assert partial content
1077 self.assert_raises_fault(416, self.client.retrieve_object,
1079 self.objects[0]['name'], range=bytes)
1081 def test_get_with_if_match(self):
1082 #perform get with If-Match
1083 etag = self.objects[0]['hash']
1084 status, headers, data = self.client.request_object(self.containers[1],
1085 self.objects[0]['name'],
1088 self.assertEqual(status, 200)
1090 #assert content-type
1091 self.assertEqual(headers['content-type'],
1092 self.objects[0]['meta']['content_type'])
1094 #assert response content
1095 self.assertEqual(self.objects[0]['data'], data)
1097 def test_get_with_if_match_star(self):
1098 #perform get with If-Match *
1099 headers = {'if-match':'*'}
1100 status, headers, data = self.client.request_object(self.containers[1],
1101 self.objects[0]['name'],
1104 self.assertEqual(status, 200)
1106 #assert content-type
1107 self.assertEqual(headers['content-type'],
1108 self.objects[0]['meta']['content_type'])
1110 #assert response content
1111 self.assertEqual(self.objects[0]['data'], data)
1113 def test_get_with_multiple_if_match(self):
1114 #perform get with If-Match
1115 etags = [i['hash'] for i in self.objects if i]
1116 etags = ','.join('"%s"' % etag for etag in etags)
1117 status, headers, data = self.client.request_object(self.containers[1],
1118 self.objects[0]['name'],
1121 self.assertEqual(status, 200)
1123 #assert content-type
1124 self.assertEqual(headers['content-type'],
1125 self.objects[0]['meta']['content_type'])
1127 #assert content-type
1128 self.assertEqual(headers['content-type'],
1129 self.objects[0]['meta']['content_type'])
1131 #assert response content
1132 self.assertEqual(self.objects[0]['data'], data)
1134 def test_if_match_precondition_failed(self):
1135 #assert precondition failed
1136 self.assert_raises_fault(412, self.client.retrieve_object,
1138 self.objects[0]['name'], if_match='123')
1140 def test_if_none_match(self):
1141 #perform get with If-None-Match
1142 status, headers, data = self.client.request_object(self.containers[1],
1143 self.objects[0]['name'],
1144 if_none_match='123')
1147 self.assertEqual(status, 200)
1149 #assert content-type
1150 self.assertEqual(headers['content_type'],
1151 self.objects[0]['meta']['content_type'])
1153 def test_if_none_match(self):
1154 #perform get with If-None-Match * and assert not modified
1155 self.assert_raises_fault(304, self.client.retrieve_object,
1157 self.objects[0]['name'],
1160 def test_if_none_match_not_modified(self):
1161 #perform get with If-None-Match and assert not modified
1162 self.assert_raises_fault(304, self.client.retrieve_object,
1164 self.objects[0]['name'],
1165 if_none_match=self.objects[0]['hash'])
1167 meta = self.client.retrieve_object_metadata(self.containers[1],
1168 self.objects[0]['name'])
1169 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1171 def test_if_modified_since(self):
1172 t = datetime.datetime.utcnow()
1173 t2 = t - datetime.timedelta(minutes=10)
1176 self.upload_data(self.containers[1],
1177 self.objects[0]['name'],
1178 self.objects[0]['data'][:200])
1180 for f in DATE_FORMATS:
1181 past = t2.strftime(f)
1183 headers = {'if-modified-since':'%s' %past}
1185 o = self.client.retrieve_object(self.containers[1],
1186 self.objects[0]['name'],
1187 if_modified_since=past)
1189 self.client.retrieve_object(self.containers[1],
1190 self.objects[0]['name']))
1192 self.failIf(f.status == 304)
1194 def test_if_modified_since_invalid_date(self):
1195 o = self.client.retrieve_object(self.containers[1],
1196 self.objects[0]['name'],
1197 if_modified_since='')
1198 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1199 self.objects[0]['name']))
1201 def test_if_not_modified_since(self):
1202 now = datetime.datetime.utcnow()
1203 since = now + datetime.timedelta(1)
1205 for f in DATE_FORMATS:
1206 #assert not modified
1207 self.assert_raises_fault(304, self.client.retrieve_object,
1208 self.containers[1], self.objects[0]['name'],
1209 if_modified_since=since.strftime(f))
1211 def test_if_unmodified_since(self):
1212 now = datetime.datetime.utcnow()
1213 since = now + datetime.timedelta(1)
1215 for f in DATE_FORMATS:
1216 t = since.strftime(f)
1217 status, headers, data = self.client.request_object(self.containers[1],
1218 self.objects[0]['name'],
1219 if_unmodified_since=t)
1221 self.assertEqual(status, 200)
1222 self.assertEqual(self.objects[0]['data'], data)
1224 #assert content-type
1225 self.assertEqual(headers['content-type'],
1226 self.objects[0]['meta']['content_type'])
1228 def test_if_unmodified_since_precondition_failed(self):
1229 t = datetime.datetime.utcnow()
1230 t2 = t - datetime.timedelta(minutes=10)
1233 self.upload_data(self.containers[1],
1234 self.objects[0]['name'],
1235 self.objects[0]['data'][:200])
1237 for f in DATE_FORMATS:
1238 past = t2.strftime(f)
1239 #assert precondition failed
1240 self.assert_raises_fault(412, self.client.retrieve_object,
1241 self.containers[1], self.objects[0]['name'],
1242 if_unmodified_since=past)
1244 def test_hashes(self):
1247 o = self.upload_random_data(self.containers[1], fname, l)
1249 body = self.client.retrieve_object(self.containers[1], fname,
1251 hashes = body['hashes']
1252 block_size = body['block_size']
1253 block_hash = body['block_hash']
1254 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1255 self.assertTrue(len(hashes), block_num)
1258 start = i * block_size
1259 end = (i + 1) * block_size
1260 hash = compute_block_hash(o['data'][start:end], block_hash)
1261 self.assertEqual(h, hash)
1264 class ObjectPut(BaseTestCase):
1266 BaseTestCase.setUp(self)
1267 self.container = 'c1'
1268 self.client.create_container(self.container)
1270 def test_upload(self):
1272 meta = {'test':'test1'}
1273 o = self.upload_random_data(self.container, name, **meta)
1275 headers = self.client.retrieve_object_metadata(self.container,
1278 self.assertTrue('test' in headers.keys())
1279 self.assertEqual(headers['test'], meta['test'])
1281 #assert uploaded content
1282 status, h, data = self.client.request_object(self.container, name)
1283 self.assertEqual(len(o['data']), int(h['content-length']))
1284 self.assertEqual(o['data'], data)
1286 #assert content-type
1287 self.assertEqual(h['content-type'], o['meta']['content_type'])
1289 def _test_maximum_upload_size_exceeds(self):
1291 meta = {'test':'test1'}
1293 length=1024*1024*100
1294 self.assert_raises_fault(400, self.upload_random_data, self.container,
1295 name, length, **meta)
1297 def test_upload_with_name_containing_slash(self):
1298 name = '/%s' % o_names[0]
1299 meta = {'test':'test1'}
1300 o = self.upload_random_data(self.container, name, **meta)
1302 self.assertEqual(o['data'],
1303 self.client.retrieve_object(self.container, name))
1305 self.assertTrue(name in self.client.list_objects(self.container))
1307 def test_create_directory_marker(self):
1308 self.client.create_directory_marker(self.container, 'foo')
1309 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1310 self.assertEqual(meta['content-length'], '0')
1311 self.assertEqual(meta['content-type'], 'application/directory')
1313 def test_upload_unprocessable_entity(self):
1314 meta={'etag':'123', 'test':'test1'}
1316 #assert unprocessable entity
1317 self.assert_raises_fault(422, self.upload_random_data, self.container,
1320 def test_chunked_transfer(self):
1321 data = get_random_data()
1323 self.client.create_object_using_chunks(self.container, objname,
1326 uploaded_data = self.client.retrieve_object(self.container, objname)
1327 self.assertEqual(data, uploaded_data)
1329 def test_manifestation(self):
1330 prefix = 'myobject/'
1333 part = '%s%d' %(prefix, i)
1334 o = self.upload_random_data(self.container, part)
1337 manifest = '%s/%s' %(self.container, prefix)
1338 self.client.create_manifestation(self.container, 'large-object', manifest)
1340 self.assert_object_exists(self.container, 'large-object')
1341 self.assertEqual(data, self.client.retrieve_object(self.container,
1344 #wrong manifestation
1345 self.client.create_manifestation(self.container, 'large-object',
1346 '%s/invalid' % self.container)
1347 self.assertEqual('', self.client.retrieve_object(self.container,
1350 def test_create_zero_length_object(self):
1353 zero = self.client.create_zero_length_object(c, o)
1354 zero_meta = self.client.retrieve_object_metadata(c, o)
1355 zero_hash = self.client.retrieve_object_hashmap(c, o)
1356 zero_data = self.client.retrieve_object(c, o)
1358 self.assertEqual(int(zero_meta['content-length']), 0)
1359 self.assertEqual(zero_hash, [])
1360 self.assertEqual(zero_data, '')
1362 def test_create_object_by_hashmap(self):
1365 self.upload_random_data(c, o)
1366 hashmap = self.client.retrieve_object(c, o, format='json')
1368 self.client.create_object_by_hashmap(c, o2, hashmap)
1369 self.assertEqual(self.client.retrieve_object(c, o),
1370 self.client.retrieve_object(c, o))
1372 class ObjectCopy(BaseTestCase):
1374 BaseTestCase.setUp(self)
1375 self.containers = ['c1', 'c2']
1376 for c in self.containers:
1377 self.client.create_container(c)
1378 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1380 def test_copy(self):
1381 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1382 self.containers[0], self.obj['name']):
1384 meta = {'test':'testcopy'}
1385 status = self.client.copy_object(self.containers[0],
1391 #assert copy success
1392 self.assertEqual(status, 201)
1394 #assert access the new object
1395 headers = self.client.retrieve_object_metadata(self.containers[0],
1397 self.assertTrue('x-object-meta-test' in headers.keys())
1398 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1400 #assert etag is the same
1401 self.assertEqual(headers['etag'], self.obj['hash'])
1403 #assert src object still exists
1404 self.assert_object_exists(self.containers[0], self.obj['name'])
1406 def test_copy_from_different_container(self):
1407 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1408 self.containers[0], self.obj['name']):
1409 meta = {'test':'testcopy'}
1410 status = self.client.copy_object(self.containers[0],
1415 self.assertEqual(status, 201)
1417 # assert updated metadata
1418 meta = self.client.retrieve_object_metadata(self.containers[1],
1421 self.assertTrue('test' in meta.keys())
1422 self.assertTrue(meta['test'], 'testcopy')
1424 #assert src object still exists
1425 self.assert_object_exists(self.containers[0], self.obj['name'])
1427 def test_copy_invalid(self):
1428 #copy from invalid object
1429 meta = {'test':'testcopy'}
1430 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1431 'test.py', self.containers[1], 'testcopy', meta)
1433 #copy from invalid container
1434 meta = {'test':'testcopy'}
1435 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1436 self.obj['name'], self.containers[1],
1439 class ObjectMove(BaseTestCase):
1441 BaseTestCase.setUp(self)
1442 self.containers = ['c1', 'c2']
1443 for c in self.containers:
1444 self.client.create_container(c)
1445 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1447 def test_move(self):
1449 meta = {'test':'testcopy'}
1450 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1451 status = self.client.move_object(self.containers[0], self.obj['name'],
1452 self.containers[0], 'testcopy',
1455 #assert successful move
1456 self.assertEqual(status, 201)
1458 #assert updated metadata
1459 meta = self.client.retrieve_object_metadata(self.containers[0],
1462 self.assertTrue('test' in meta.keys())
1463 self.assertTrue(meta['test'], 'testcopy')
1465 #assert src object no more exists
1466 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1468 class ObjectPost(BaseTestCase):
1470 BaseTestCase.setUp(self)
1471 self.containers = ['c1', 'c2']
1472 for c in self.containers:
1473 self.client.create_container(c)
1476 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1478 def test_update_meta(self):
1479 #perform update metadata
1480 more = {'foo':'foo', 'bar':'bar'}
1481 status = self.client.update_object_metadata(self.containers[0],
1482 self.obj[0]['name'],
1484 #assert request accepted
1485 self.assertEqual(status, 202)
1487 #assert old metadata are still there
1488 headers = self.client.retrieve_object_metadata(self.containers[0],
1489 self.obj[0]['name'],
1491 #assert new metadata have been updated
1492 for k,v in more.items():
1493 self.assertTrue(k in headers.keys())
1494 self.assertTrue(headers[k], v)
1496 def test_update_object(self,
1499 instance_length = True,
1500 content_length = 500):
1501 l = len(self.obj[0]['data'])
1502 range = 'bytes %d-%d/%s' %(first_byte_pos,
1504 l if instance_length else '*')
1505 partial = last_byte_pos - first_byte_pos + 1
1506 length = first_byte_pos + partial
1507 data = get_random_data(partial)
1508 args = {'content_type':'application/octet-stream',
1509 'content_range':'%s' %range}
1511 args['content_length'] = content_length
1513 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1514 StringIO(data), **args)[0]
1516 if partial < 0 or (instance_length and l <= last_byte_pos):
1517 self.assertEqual(status, 202)
1519 self.assertEqual(status, 204)
1520 #check modified object
1521 content = self.client.retrieve_object(self.containers[0],
1522 self.obj[0]['name'])
1523 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1524 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1525 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1527 def test_update_object_lt_blocksize(self):
1528 self.test_update_object(10, 20, content_length=None)
1530 def test_update_object_gt_blocksize(self):
1531 o = self.upload_random_data(self.containers[0], o_names[1],
1532 length=4*1024*1024+5)
1533 c = self.containers[0]
1536 first_byte_pos = 4*1024*1024+1
1537 last_byte_pos = 4*1024*1024+4
1538 l = last_byte_pos - first_byte_pos + 1
1539 data = get_random_data(l)
1540 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1541 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1542 content = self.client.retrieve_object(c, o_name)
1543 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1544 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1545 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1547 def test_update_object_divided_by_blocksize(self):
1548 o = self.upload_random_data(self.containers[0], o_names[1],
1549 length=4*1024*1024+5)
1550 c = self.containers[0]
1553 first_byte_pos = 4*1024*1024
1554 last_byte_pos = 5*1024*1024
1555 l = last_byte_pos - first_byte_pos + 1
1556 data = get_random_data(l)
1557 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1558 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1559 content = self.client.retrieve_object(c, o_name)
1560 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1561 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1562 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1564 def test_update_object_no_content_length(self):
1565 self.test_update_object(content_length = None)
1567 def test_update_object_invalid_content_length(self):
1568 with AssertContentInvariant(self.client.retrieve_object,
1569 self.containers[0], self.obj[0]['name']):
1570 self.assert_raises_fault(400, self.test_update_object,
1571 content_length = 1000)
1573 def test_update_object_invalid_range(self):
1574 with AssertContentInvariant(self.client.retrieve_object,
1575 self.containers[0], self.obj[0]['name']):
1576 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1578 def test_update_object_invalid_range_and_length(self):
1579 with AssertContentInvariant(self.client.retrieve_object,
1580 self.containers[0], self.obj[0]['name']):
1581 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1584 def test_update_object_invalid_range_with_no_content_length(self):
1585 with AssertContentInvariant(self.client.retrieve_object,
1586 self.containers[0], self.obj[0]['name']):
1587 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1588 content_length = None)
1590 def test_update_object_out_of_limits(self):
1591 with AssertContentInvariant(self.client.retrieve_object,
1592 self.containers[0], self.obj[0]['name']):
1593 l = len(self.obj[0]['data'])
1594 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1596 def test_append(self):
1597 data = get_random_data(500)
1599 self.client.update_object(self.containers[0], self.obj[0]['name'],
1600 StringIO(data), content_length=500,
1601 content_type='application/octet-stream')
1603 content = self.client.retrieve_object(self.containers[0],
1604 self.obj[0]['name'])
1605 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1606 self.assertEqual(content[:-500], self.obj[0]['data'])
1608 def test_update_with_chunked_transfer(self):
1609 data = get_random_data(500)
1611 fl = len(self.obj[0]['data'])
1613 self.client.update_object_using_chunks(self.containers[0],
1614 self.obj[0]['name'],
1617 content_type='application/octet-stream')
1619 #check modified object
1620 content = self.client.retrieve_object(self.containers[0],
1621 self.obj[0]['name'])
1622 self.assertEqual(content[0:dl], data)
1623 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1625 def test_update_from_other_object(self):
1626 c = self.containers[0]
1630 source_data = self.client.retrieve_object(c, src)
1631 source_meta = self.client.retrieve_object_metadata(c, src)
1632 source_hash = self.client.retrieve_object_hashmap(c, src)
1634 #update zero length object
1635 self.client.create_zero_length_object(c, dest)
1636 source_object = '/%s/%s' % (c, src)
1637 self.client.update_from_other_source(c, dest, source_object)
1638 dest_data = self.client.retrieve_object(c, src)
1639 dest_meta = self.client.retrieve_object_metadata(c, dest)
1640 dest_hash = self.client.retrieve_object_hashmap(c, src)
1641 self.assertEqual(source_data, dest_data)
1642 self.assertEqual(source_hash, dest_hash)
1645 self.client.update_from_other_source(c, dest, source_object)
1646 content = self.client.retrieve_object(c, dest)
1647 self.assertEqual(source_data * 2, content)
1649 def test_update_range_from_other_object(self):
1650 c = self.containers[0]
1654 src = self.obj[1]['name']
1655 src_data = self.client.retrieve_object(c, src)
1657 #update zero length object
1658 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1659 source_object = '/%s/%s' % (c, src)
1660 first_byte_pos = 4*1024*1024+1
1661 last_byte_pos = 4*1024*1024+4
1662 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1663 self.client.update_from_other_source(c, dest, source_object,
1664 content_range=range)
1665 content = self.client.retrieve_object(c, dest)
1666 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1667 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1668 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1670 def test_update_hashes_from_other_object(self):
1671 c = self.containers[0]
1675 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1677 #update zero length object
1678 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1679 source_object = '/%s/%s' % (c, o_names[0])
1680 first_byte_pos = 4*1024*1024
1681 last_byte_pos = 5*1024*1024
1682 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1683 self.client.update_from_other_source(c, dest, source_object,
1684 content_range=range)
1685 content = self.client.retrieve_object(c, dest)
1686 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1687 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1688 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1691 def test_update_zero_length_object(self):
1692 c = self.containers[0]
1695 zero = self.client.create_zero_length_object(c, o)
1697 data = get_random_data()
1698 self.client.update_object(c, o, StringIO(data))
1699 self.client.create_object(c, other, StringIO(data))
1701 self.assertEqual(self.client.retrieve_object(c, o),
1702 self.client.retrieve_object(c, other))
1704 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1705 self.client.retrieve_object_hashmap(c, other))
1707 class ObjectDelete(BaseTestCase):
1709 BaseTestCase.setUp(self)
1710 self.containers = ['c1', 'c2']
1711 for c in self.containers:
1712 self.client.create_container(c)
1713 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1715 def test_delete(self):
1716 #perform delete object
1717 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1719 def test_delete_invalid(self):
1720 #assert item not found
1721 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1724 class ListSharing(BaseTestCase):
1726 BaseTestCase.setUp(self)
1728 self.client.create_container('c%s' %i)
1729 self.client.create_container('c')
1731 self.upload_random_data('c1', 'o%s' %i)
1732 accounts = OTHER_ACCOUNTS.copy()
1733 self.o1_sharing_with = accounts.popitem()
1734 self.o1_sharing = [self.o1_sharing_with[1]]
1735 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1739 l.append(accounts.popitem())
1741 def test_list_other_shared(self):
1742 self.other = Pithos_Client(get_server(),
1743 self.o1_sharing_with[0],
1744 self.o1_sharing_with[1],
1746 self.assertTrue(get_user() in self.other.list_shared_by_others())
1748 def test_list_my_shared(self):
1749 my_shared_containers = self.client.list_containers(shared=True)
1750 self.assertTrue('c1' in my_shared_containers)
1751 self.assertTrue('c2' not in my_shared_containers)
1753 my_shared_objects = self.client.list_objects('c1', shared=True)
1754 self.assertTrue('o1' in my_shared_objects)
1755 self.assertTrue('o2' not in my_shared_objects)
1757 class TestGreek(BaseTestCase):
1759 BaseTestCase.setUp(self)
1760 #keep track of initial account groups
1761 self.initial_groups = self.client.retrieve_account_groups()
1763 #keep track of initial account meta
1764 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1767 #delete additionally created meta
1769 for m in self.client.retrieve_account_metadata(restricted=True):
1770 if m not in self.initial_meta:
1772 self.client.delete_account_metadata(l)
1774 #delete additionally created groups
1776 for g in self.client.retrieve_account_groups():
1777 if g not in self.initial_groups:
1779 self.client.unset_account_groups(l)
1781 BaseTestCase.tearDown(self)
1783 def test_create_container(self):
1784 self.client.create_container('φάκελος')
1785 self.assert_container_exists('φάκελος')
1787 self.assertTrue('φάκελος' in self.client.list_containers())
1789 def test_create_object(self):
1790 self.client.create_container('φάκελος')
1791 self.upload_random_data('φάκελος', 'αντικείμενο')
1793 self.assert_object_exists('φάκελος', 'αντικείμενο')
1794 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1796 def test_copy_object(self):
1797 src_container = 'φάκελος'
1798 src_object = 'αντικείμενο'
1799 dest_container = 'αντίγραφα'
1800 dest_object = 'ασφαλές-αντίγραφο'
1802 self.client.create_container(src_container)
1803 self.upload_random_data(src_container, src_object)
1805 self.client.create_container(dest_container)
1806 self.client.copy_object(src_container, src_object, dest_container,
1809 self.assert_object_exists(src_container, src_object)
1810 self.assert_object_exists(dest_container, dest_object)
1811 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1813 def test_move_object(self):
1814 src_container = 'φάκελος'
1815 src_object = 'αντικείμενο'
1816 dest_container = 'αντίγραφα'
1817 dest_object = 'ασφαλές-αντίγραφο'
1819 self.client.create_container(src_container)
1820 self.upload_random_data(src_container, src_object)
1822 self.client.create_container(dest_container)
1823 self.client.move_object(src_container, src_object, dest_container,
1826 self.assert_object_not_exists(src_container, src_object)
1827 self.assert_object_exists(dest_container, dest_object)
1828 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1830 def test_delete_object(self):
1831 self.client.create_container('φάκελος')
1832 self.upload_random_data('φάκελος', 'αντικείμενο')
1833 self.assert_object_exists('φάκελος', 'αντικείμενο')
1835 self.client.delete_object('φάκελος', 'αντικείμενο')
1836 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1837 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1839 def test_delete_container(self):
1840 self.client.create_container('φάκελος')
1841 self.assert_container_exists('φάκελος')
1843 self.client.delete_container('φάκελος')
1844 self.assert_container_not_exists('φάκελος')
1845 self.assertTrue('φάκελος' not in self.client.list_containers())
1847 def test_account_meta(self):
1848 meta = {'ποιότητα':'ΑΑΑ'}
1849 self.client.update_account_metadata(**meta)
1850 meta = self.client.retrieve_account_metadata(restricted=True)
1851 self.assertTrue('ποιότητα' in meta.keys())
1852 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1854 def test_container_meta(self):
1855 meta = {'ποιότητα':'ΑΑΑ'}
1856 self.client.create_container('φάκελος', **meta)
1858 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1859 self.assertTrue('ποιότητα' in meta.keys())
1860 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1862 def test_object_meta(self):
1863 self.client.create_container('φάκελος')
1864 meta = {'ποιότητα':'ΑΑΑ'}
1865 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1867 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1869 self.assertTrue('ποιότητα' in meta.keys())
1870 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1872 def test_list_meta_filtering(self):
1873 self.client.create_container('φάκελος')
1874 meta = {'ποιότητα':'ΑΑΑ'}
1875 self.upload_random_data('φάκελος', 'ο1', **meta)
1876 self.upload_random_data('φάκελος', 'ο2')
1877 self.upload_random_data('φάκελος', 'ο3')
1879 meta = {'ποσότητα':'μεγάλη'}
1880 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1881 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1882 self.assertTrue('ο1' in objects)
1883 self.assertTrue('ο2' in objects)
1884 self.assertTrue('ο3' not in objects)
1886 def test_groups(self):
1888 groups = {'σεφς':'chazapis,διογένης'}
1889 self.client.set_account_groups(**groups)
1890 groups.update(self.initial_groups)
1891 self.assertEqual(groups['σεφς'],
1892 self.client.retrieve_account_groups()['σεφς'])
1895 self.client.create_container('φάκελος')
1896 o = self.upload_random_data('φάκελος', 'ο1')
1897 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1898 chef = Pithos_Client(get_server(),
1902 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1903 'φάκελος', 'ο1', account=get_user())
1906 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1907 new_data = get_random_data()
1908 self.assert_not_raises_fault(403, chef.update_object,
1909 'φάκελος', 'ο1', StringIO(new_data),
1912 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1913 self.assertEqual(server_data[:len(o['data'])], o['data'])
1914 self.assertEqual(server_data[len(o['data']):], new_data)
1916 def test_manifestation(self):
1917 self.client.create_container('κουβάς')
1921 part = '%s%d' %(prefix, i)
1922 o = self.upload_random_data('κουβάς', part)
1925 self.client.create_container('φάκελος')
1926 manifest = '%s/%s' %('κουβάς', prefix)
1927 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1929 self.assert_object_exists('φάκελος', 'άπαντα')
1930 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1933 #wrong manifestation
1934 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1935 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1937 def test_update_from_another_object(self):
1938 self.client.create_container('κουβάς')
1939 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1940 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1941 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1942 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1945 self.client.retrieve_object('κουβάς', 'νέο'),
1946 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1948 class TestPermissions(BaseTestCase):
1950 BaseTestCase.setUp(self)
1951 #keep track of initial account groups
1952 self.initial_groups = self.client.retrieve_account_groups()
1953 #keep track of initial account meta
1954 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1957 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1958 groups = {'pithosdev':','.join(self.authorized)}
1959 self.client.set_account_groups(**groups)
1962 #delete additionally created meta
1964 for m in self.client.retrieve_account_metadata(restricted=True):
1965 if m not in self.initial_meta:
1967 self.client.delete_account_metadata(l)
1969 #delete additionally created groups
1971 for g in self.client.retrieve_account_groups():
1972 if g not in self.initial_groups:
1974 self.client.unset_account_groups(l)
1976 BaseTestCase.tearDown(self)
1978 def assert_read(self, authorized=[], any=False):
1979 for token, account in OTHER_ACCOUNTS.items():
1980 cl = Pithos_Client(get_server(), token, account, get_api())
1981 if account in authorized or any:
1982 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1983 'c', 'o', account=get_user())
1985 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1986 'c', 'o', account=get_user())
1989 o = self.upload_random_data('c', 'o/also-shared')
1990 for token, account in OTHER_ACCOUNTS.items():
1991 cl = Pithos_Client(get_server(), token, account, get_api())
1992 if account in authorized or any:
1993 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1994 'c', 'o/also-shared', account=get_user())
1996 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1997 'c', 'o/also-shared', account=get_user())
1999 def assert_write(self, o_data, authorized=[], any=False):
2000 for token, account in OTHER_ACCOUNTS.items():
2001 cl = Pithos_Client(get_server(), token, account, get_api())
2002 new_data = get_random_data()
2003 if account in authorized or any:
2005 self.assert_not_raises_fault(403, cl.update_object,
2006 'c', 'o', StringIO(new_data),
2010 server_data = cl.retrieve_object('c', 'o', account=get_user())
2011 self.assertEqual(o_data, server_data[:len(o_data)])
2012 self.assertEqual(new_data, server_data[len(o_data):])
2013 o_data = server_data
2015 self.failIf(f.status == 403)
2017 self.assert_raises_fault(403, cl.update_object,
2018 'c', 'o', StringIO(new_data),
2022 o = self.upload_random_data('c', 'o/also-shared')
2024 for token, account in OTHER_ACCOUNTS.items():
2025 cl = Pithos_Client(get_server(), token, account, get_api())
2026 new_data = get_random_data()
2027 if account in authorized or any:
2029 self.assert_not_raises_fault(403, cl.update_object,
2034 server_data = cl.retrieve_object('c', o['name'], account=get_user())
2035 self.assertEqual(o_data, server_data[:len(o_data)])
2036 self.assertEqual(new_data, server_data[len(o_data):])
2037 o_data = server_data
2039 self.failIf(f.status == 403)
2041 self.assert_raises_fault(403, cl.update_object,
2046 def test_group_read(self):
2047 self.client.create_container('c')
2048 o = self.upload_random_data('c', 'o')
2049 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
2050 self.assert_read(authorized=self.authorized)
2052 def test_read_many(self):
2054 self.client.create_container('c')
2055 o = self.upload_random_data('c', 'o')
2056 self.client.share_object('c', 'o', self.authorized)
2057 self.assert_read(authorized=self.authorized)
2059 def test_read_by_everyone(self):
2060 self.client.create_container('c')
2061 o = self.upload_random_data('c', 'o')
2062 self.client.share_object('c', 'o', ['*'])
2063 self.assert_read(any=True)
2065 def test_group_write(self):
2066 self.client.create_container('c')
2067 o = self.upload_random_data('c', 'o')
2068 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
2069 self.assert_write(o['data'], authorized=self.authorized)
2071 def test_write_many(self):
2072 self.client.create_container('c')
2073 o = self.upload_random_data('c', 'o')
2074 self.client.share_object('c', 'o', self.authorized, read=False)
2075 self.assert_write(o['data'], authorized=self.authorized)
2077 def test_write_by_everyone(self):
2078 self.client.create_container('c')
2079 o = self.upload_random_data('c', 'o')
2080 self.client.share_object('c', 'o', ['*'], read=False)
2082 self.assert_write(o['data'], any=True)
2084 class TestPublish(BaseTestCase):
2085 def test_publish(self):
2086 self.client.create_container('c')
2087 o_data = self.upload_random_data('c', 'o')['data']
2088 self.client.publish_object('c', 'o')
2089 meta = self.client.retrieve_object_metadata('c', 'o')
2090 self.assertTrue('x-object-public' in meta)
2091 url = '/public/%s/c/o' % get_user()
2092 self.assertEqual(meta['x-object-public'], url)
2093 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2094 data = public_client.get(url)[2]
2095 self.assertEqual(o_data, data)
2097 class AssertMappingInvariant(object):
2098 def __init__(self, callable, *args, **kwargs):
2099 self.callable = callable
2101 self.kwargs = kwargs
2103 def __enter__(self):
2104 self.map = self.callable(*self.args, **self.kwargs)
2107 def __exit__(self, type, value, tb):
2108 map = self.callable(*self.args, **self.kwargs)
2109 for k in self.map.keys():
2110 if is_date(self.map[k]):
2112 assert map[k] == self.map[k]
2114 class AssertContentInvariant(object):
2115 def __init__(self, callable, *args, **kwargs):
2116 self.callable = callable
2118 self.kwargs = kwargs
2120 def __enter__(self):
2121 self.content = self.callable(*self.args, **self.kwargs)[2]
2124 def __exit__(self, type, value, tb):
2125 content = self.callable(*self.args, **self.kwargs)[2]
2126 assert self.content == content
2128 def get_content_splitted(response):
2130 return response.content.split('\n')
2132 def compute_md5_hash(data):
2136 return md5.hexdigest().lower()
2138 def compute_block_hash(data, algorithm):
2139 h = hashlib.new(algorithm)
2140 h.update(data.rstrip('\x00'))
2141 return h.hexdigest()
2143 def get_random_data(length=500):
2144 char_set = string.ascii_uppercase + string.digits
2145 return ''.join(random.choice(char_set) for x in range(length))
2148 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2149 __D = r'(?P<day>\d{2})'
2150 __D2 = r'(?P<day>[ \d]\d)'
2151 __M = r'(?P<mon>\w{3})'
2152 __Y = r'(?P<year>\d{4})'
2153 __Y2 = r'(?P<year>\d{2})'
2154 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2155 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2156 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2157 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2158 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2159 m = regex.match(date)
2164 o_names = ['kate.jpg',
2165 'kate_beckinsale.jpg',
2166 'How To Win Friends And Influence People.pdf',
2167 'moms_birthday.jpg',
2169 'Disturbed - Down With The Sickness.mp3',
2170 'army_of_darkness.avi',
2172 'photos/animals/dogs/poodle.jpg',
2173 'photos/animals/dogs/terrier.jpg',
2174 'photos/animals/cats/persian.jpg',
2175 'photos/animals/cats/siamese.jpg',
2176 'photos/plants/fern.jpg',
2177 'photos/plants/rose.jpg',
2180 if __name__ == "__main__":
2181 if get_user() == 'test':
2184 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()