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 assert_versionlist_structure(self, versionlist):
243 self.assertTrue(type(versionlist) == types.ListType)
244 for elem in versionlist:
245 self.assertTrue(type(elem) == types.ListType)
246 self.assertEqual(len(elem), 2)
248 def upload_random_data(self, container, name, length=1024, type=None,
250 data = get_random_data(length)
251 return self.upload_data(container, name, data, type, enc, **meta)
253 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
259 obj['hash'] = compute_md5_hash(obj['data'])
262 args['etag'] = etag if etag else obj['hash']
265 guess = mimetypes.guess_type(name)
266 type = type if type else guess[0]
267 enc = enc if enc else guess[1]
270 args['content_type'] = type if type else 'plain/text'
271 args['content_encoding'] = enc if enc else None
275 path = '/%s/%s' % (container, name)
276 self.client.create_object(container, name, StringIO(obj['data']),
283 class AccountHead(BaseTestCase):
285 BaseTestCase.setUp(self)
286 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
287 for item in self.containers:
288 self.client.create_container(item)
290 #keep track of initial account groups
291 self.initial_groups = self.client.retrieve_account_groups()
293 #keep track of initial account meta
294 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
297 self.client.update_account_metadata(**meta)
298 self.updated_meta = self.initial_meta.update(meta)
301 #delete additionally created meta
303 for m in self.client.retrieve_account_metadata(restricted=True):
304 if m not in self.initial_meta:
306 self.client.delete_account_metadata(l)
308 #delete additionally created groups
310 for g in self.client.retrieve_account_groups():
311 if g not in self.initial_groups:
313 self.client.unset_account_groups(l)
315 BaseTestCase.tearDown(self)
317 def test_get_account_meta(self):
318 meta = self.client.retrieve_account_metadata()
320 containers = self.client.list_containers()
321 l = str(len(containers))
322 self.assertEqual(meta['x-account-container-count'], l)
325 m = self.client.retrieve_container_metadata(c)
326 size = size + int(m['x-container-bytes-used'])
327 self.assertEqual(meta['x-account-bytes-used'], str(size))
329 def test_get_account_401(self):
330 self.assert_raises_fault(401,
331 self.invalid_client.retrieve_account_metadata)
333 def test_get_account_meta_until(self):
334 t = datetime.datetime.utcnow()
335 past = t - datetime.timedelta(minutes=-15)
336 past = int(_time.mktime(past.timetuple()))
338 meta = {'premium':True}
339 self.client.update_account_metadata(**meta)
340 meta = self.client.retrieve_account_metadata(restricted=True,
342 self.assertTrue('premium' not in meta)
344 meta = self.client.retrieve_account_metadata(restricted=True)
345 self.assertTrue('premium' in meta)
347 def test_get_account_meta_until_invalid_date(self):
348 meta = {'premium':True}
349 self.client.update_account_metadata(**meta)
350 meta = self.client.retrieve_account_metadata(restricted=True,
352 self.assertTrue('premium' in meta)
354 class AccountGet(BaseTestCase):
356 BaseTestCase.setUp(self)
357 #create some containers
358 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
359 for item in self.containers:
360 self.client.create_container(item)
364 containers = self.client.list_containers()
365 self.assertEquals(self.containers, containers)
367 def test_list_401(self):
368 self.assert_raises_fault(401, self.invalid_client.list_containers)
370 def test_list_with_limit(self):
372 containers = self.client.list_containers(limit=limit)
373 self.assertEquals(len(containers), limit)
374 self.assertEquals(self.containers[:2], containers)
376 def test_list_with_marker(self):
379 containers = self.client.list_containers(limit=l, marker=m)
380 i = self.containers.index(m) + 1
381 self.assertEquals(self.containers[i:(i+l)], containers)
384 containers = self.client.list_containers(limit=l, marker=m)
385 i = self.containers.index(m) + 1
386 self.assertEquals(self.containers[i:(i+l)], containers)
388 def test_list_json_with_marker(self):
391 containers = self.client.list_containers(limit=l, marker=m, format='json')
392 self.assert_extended(containers, 'json', 'container', l)
393 self.assertEqual(containers[0]['name'], 'kiwis')
394 self.assertEqual(containers[1]['name'], 'oranges')
396 def test_list_xml_with_marker(self):
399 xml = self.client.list_containers(limit=l, marker=m, format='xml')
400 self.assert_extended(xml, 'xml', 'container', l)
401 nodes = xml.getElementsByTagName('name')
402 self.assertEqual(len(nodes), 1)
403 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
405 def test_if_modified_since(self):
406 t = datetime.datetime.utcnow()
407 t2 = t - datetime.timedelta(minutes=10)
410 self.client.create_container('dummy')
412 for f in DATE_FORMATS:
413 past = t2.strftime(f)
415 c = self.client.list_containers(if_modified_since=past)
416 self.assertEqual(len(c), len(self.containers) + 1)
418 self.failIf(f.status == 304) #fail if not modified
420 def test_if_modified_since_invalid_date(self):
421 c = self.client.list_containers(if_modified_since='')
422 self.assertEqual(len(c), len(self.containers))
424 def test_if_not_modified_since(self):
425 now = datetime.datetime.utcnow()
426 since = now + datetime.timedelta(1)
428 for f in DATE_FORMATS:
429 args = {'if_modified_since':'%s' %since.strftime(f)}
432 self.assert_raises_fault(304, self.client.list_containers, **args)
434 def test_if_unmodified_since(self):
435 now = datetime.datetime.utcnow()
436 since = now + datetime.timedelta(1)
438 for f in DATE_FORMATS:
439 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
442 self.assertEqual(self.containers, c)
444 def test_if_unmodified_since_precondition_failed(self):
445 t = datetime.datetime.utcnow()
446 t2 = t - datetime.timedelta(minutes=10)
449 self.client.create_container('dummy')
451 for f in DATE_FORMATS:
452 past = t2.strftime(f)
454 args = {'if_unmodified_since':'%s' %past}
456 #assert precondition failed
457 self.assert_raises_fault(412, self.client.list_containers, **args)
459 class AccountPost(BaseTestCase):
461 BaseTestCase.setUp(self)
462 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
463 for item in self.containers:
464 self.client.create_container(item)
466 #keep track of initial account groups
467 self.initial_groups = self.client.retrieve_account_groups()
469 #keep track of initial account meta
470 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
473 self.client.update_account_metadata(**meta)
474 self.updated_meta = self.initial_meta.update(meta)
477 #delete additionally created meta
479 for m in self.client.retrieve_account_metadata(restricted=True):
480 if m not in self.initial_meta:
482 self.client.delete_account_metadata(l)
484 #delete additionally created groups
486 for g in self.client.retrieve_account_groups():
487 if g not in self.initial_groups:
489 self.client.unset_account_groups(l)
491 BaseTestCase.tearDown(self)
493 def test_update_meta(self):
494 with AssertMappingInvariant(self.client.retrieve_account_groups):
495 meta = {'test':'test', 'tost':'tost'}
496 self.client.update_account_metadata(**meta)
498 meta.update(self.initial_meta)
499 self.assertEqual(meta,
500 self.client.retrieve_account_metadata(
503 def test_invalid_account_update_meta(self):
504 meta = {'test':'test', 'tost':'tost'}
505 self.assert_raises_fault(401,
506 self.invalid_client.update_account_metadata,
509 def test_reset_meta(self):
510 with AssertMappingInvariant(self.client.retrieve_account_groups):
511 meta = {'test':'test', 'tost':'tost'}
512 self.client.update_account_metadata(**meta)
514 meta = {'test':'test33'}
515 self.client.reset_account_metadata(**meta)
517 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
519 def test_delete_meta(self):
520 with AssertMappingInvariant(self.client.retrieve_account_groups):
521 meta = {'test':'test', 'tost':'tost'}
522 self.client.update_account_metadata(**meta)
524 self.client.delete_account_metadata(meta.keys())
526 account_meta = self.client.retrieve_account_metadata(restricted=True)
528 self.assertTrue(m not in account_meta.keys())
530 def test_set_account_groups(self):
531 with AssertMappingInvariant(self.client.retrieve_account_metadata):
532 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
533 self.client.set_account_groups(**groups)
535 self.assertEqual(set(groups['pithosdev']),
536 set(self.client.retrieve_account_groups()['pithosdev']))
538 more_groups = {'clientsdev':'pkanavos,mvasilak'}
539 self.client.set_account_groups(**more_groups)
541 groups.update(more_groups)
542 self.assertEqual(set(groups['clientsdev']),
543 set(self.client.retrieve_account_groups()['clientsdev']))
545 def test_reset_account_groups(self):
546 with AssertMappingInvariant(self.client.retrieve_account_metadata):
547 groups = {'pithosdev':'verigak,gtsouk,chazapis',
548 'clientsdev':'pkanavos,mvasilak'}
549 self.client.set_account_groups(**groups)
551 self.assertEqual(set(groups['pithosdev'].split(',')),
552 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
553 self.assertEqual(set(groups['clientsdev'].split(',')),
554 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
556 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
557 self.client.reset_account_groups(**groups)
559 self.assertEqual(set(groups['pithosdev'].split(',')),
560 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
562 def test_delete_account_groups(self):
563 with AssertMappingInvariant(self.client.retrieve_account_metadata):
564 groups = {'pithosdev':'verigak,gtsouk,chazapis',
565 'clientsdev':'pkanavos,mvasilak'}
566 self.client.set_account_groups(**groups)
568 self.client.unset_account_groups(groups.keys())
570 self.assertEqual({}, self.client.retrieve_account_groups())
572 class ContainerHead(BaseTestCase):
574 BaseTestCase.setUp(self)
575 self.container = 'apples'
576 self.client.create_container(self.container)
578 def test_get_meta(self):
579 meta = {'trash':'true'}
580 t1 = datetime.datetime.utcnow()
581 o = self.upload_random_data(self.container, o_names[0], **meta)
583 headers = self.client.retrieve_container_metadata(self.container)
584 self.assertEqual(headers['x-container-object-count'], '1')
585 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
586 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
588 threashold = datetime.timedelta(seconds=1)
589 self.assertTrue(delta < threashold)
590 self.assertTrue(headers['x-container-object-meta'])
591 self.assertTrue('Trash' in headers['x-container-object-meta'])
593 class ContainerGet(BaseTestCase):
595 BaseTestCase.setUp(self)
596 self.container = ['pears', 'apples']
597 for c in self.container:
598 self.client.create_container(c)
600 for o in o_names[:8]:
601 self.obj.append(self.upload_random_data(self.container[0], o))
602 for o in o_names[8:]:
603 self.obj.append(self.upload_random_data(self.container[1], o))
605 def test_list_objects(self):
606 objects = self.client.list_objects(self.container[0])
607 l = [elem['name'] for elem in self.obj[:8]]
609 self.assertEqual(objects, l)
611 def test_list_objects_containing_slash(self):
612 self.client.create_container('test')
613 self.upload_random_data('test', '/objectname')
615 objects = self.client.list_objects('test')
616 self.assertEqual(objects, ['/objectname'])
618 objects = self.client.list_objects('test', format='json')
619 self.assertEqual(objects[0]['name'], '/objectname')
621 objects = self.client.list_objects('test', format='xml')
622 self.assert_extended(objects, 'xml', 'object')
623 node_name = objects.getElementsByTagName('name')[0]
624 self.assertEqual(node_name.firstChild.data, '/objectname')
626 #objects = self.client.list_objects('test', prefix='/')
627 #self.assertEqual(objects, ['/objectname'])
629 #objects = self.client.list_objects('test', path='/')
630 #self.assertEqual(objects, ['/objectname'])
632 #objects = self.client.list_objects('test', prefix='/', delimiter='n')
633 #self.assertEqual(objects, ['/object'])
635 def test_list_objects_with_limit_marker(self):
636 objects = self.client.list_objects(self.container[0], limit=2)
637 l = [elem['name'] for elem in self.obj[:8]]
639 self.assertEqual(objects, l[:2])
641 markers = ['How To Win Friends And Influence People.pdf',
645 objects = self.client.list_objects(self.container[0], limit=limit,
647 l = [elem['name'] for elem in self.obj[:8]]
649 start = l.index(m) + 1
651 end = len(l) >= end and end or len(l)
652 self.assertEqual(objects, l[start:end])
655 def _test_list_limit_exceeds(self):
656 self.client.create_container('pithos')
658 for i in range(10001):
659 self.client.create_zero_length_object('pithos', i)
661 self.assertEqual(10000, len(self.client.list_objects('pithos')))
663 def test_list_empty_params(self):
664 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
666 objects = objects.strip().split('\n')
667 self.assertEqual(objects,
668 self.client.list_objects(self.container[0]))
670 def test_list_pseudo_hierarchical_folders(self):
671 objects = self.client.list_objects(self.container[1], prefix='photos',
673 self.assertEquals(['photos/animals/', 'photos/me.jpg',
674 'photos/plants/'], objects)
676 objects = self.client.list_objects(self.container[1],
677 prefix='photos/animals',
679 l = ['photos/animals/cats/', 'photos/animals/dogs/']
680 self.assertEquals(l, objects)
682 objects = self.client.list_objects(self.container[1], path='photos')
683 self.assertEquals(['photos/me.jpg'], objects)
685 def test_extended_list_json(self):
686 objects = self.client.list_objects(self.container[1], format='json',
687 limit=2, prefix='photos/animals',
689 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
690 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
692 def test_extended_list_xml(self):
693 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
694 prefix='photos', delimiter='/')
695 self.assert_extended(xml, 'xml', 'object', size=4)
696 dirs = xml.getElementsByTagName('subdir')
697 self.assertEqual(len(dirs), 2)
698 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
699 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
701 objects = xml.getElementsByTagName('name')
702 self.assertEqual(len(objects), 1)
703 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
705 def test_list_meta_double_matching(self):
706 meta = {'quality':'aaa', 'stock':'true'}
707 self.client.update_object_metadata(self.container[0],
708 self.obj[0]['name'], **meta)
709 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
710 self.assertEqual(len(obj), 1)
711 self.assertTrue(obj, self.obj[0]['name'])
713 def test_list_using_meta(self):
714 meta = {'quality':'aaa'}
715 for o in self.obj[:2]:
716 self.client.update_object_metadata(self.container[0], o['name'],
718 meta = {'stock':'true'}
719 for o in self.obj[3:5]:
720 self.client.update_object_metadata(self.container[0], o['name'],
723 obj = self.client.list_objects(self.container[0], meta='Quality')
724 self.assertEqual(len(obj), 2)
725 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
727 # test case insensitive
728 obj = self.client.list_objects(self.container[0], meta='quality')
729 self.assertEqual(len(obj), 2)
730 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
732 # test multiple matches
733 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
734 self.assertEqual(len(obj), 4)
735 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
737 # test non 1-1 multiple match
738 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
739 self.assertEqual(len(obj), 2)
740 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
742 def test_if_modified_since(self):
743 t = datetime.datetime.utcnow()
744 t2 = t - datetime.timedelta(minutes=10)
747 self.upload_random_data(self.container[0], o_names[0])
749 for f in DATE_FORMATS:
750 past = t2.strftime(f)
752 o = self.client.list_objects(self.container[0],
753 if_modified_since=past)
755 self.client.list_objects(self.container[0]))
757 self.failIf(f.status == 304) #fail if not modified
759 def test_if_modified_since_invalid_date(self):
760 headers = {'if-modified-since':''}
761 o = self.client.list_objects(self.container[0], if_modified_since='')
762 self.assertEqual(o, self.client.list_objects(self.container[0]))
764 def test_if_not_modified_since(self):
765 now = datetime.datetime.utcnow()
766 since = now + datetime.timedelta(1)
768 for f in DATE_FORMATS:
769 args = {'if_modified_since':'%s' %since.strftime(f)}
772 self.assert_raises_fault(304, self.client.list_objects,
773 self.container[0], **args)
775 def test_if_unmodified_since(self):
776 now = datetime.datetime.utcnow()
777 since = now + datetime.timedelta(1)
779 for f in DATE_FORMATS:
780 obj = self.client.list_objects(self.container[0],
781 if_unmodified_since=since.strftime(f))
784 self.assertEqual(obj, self.client.list_objects(self.container[0]))
786 def test_if_unmodified_since_precondition_failed(self):
787 t = datetime.datetime.utcnow()
788 t2 = t - datetime.timedelta(minutes=10)
791 self.client.create_container('dummy')
793 for f in DATE_FORMATS:
794 past = t2.strftime(f)
796 args = {'if_unmodified_since':'%s' %past}
798 #assert precondition failed
799 self.assert_raises_fault(412, self.client.list_objects,
800 self.container[0], **args)
802 class ContainerPut(BaseTestCase):
804 BaseTestCase.setUp(self)
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.container = 'apples'
821 self.client.create_container(self.container)
823 def test_update_meta(self):
824 meta = {'test':'test33',
826 self.client.update_container_metadata(self.container, **meta)
827 headers = self.client.retrieve_container_metadata(self.container)
828 for k,v in meta.items():
829 k = 'x-container-meta-%s' % k
830 self.assertTrue(headers[k])
831 self.assertEqual(headers[k], v)
833 class ContainerDelete(BaseTestCase):
835 BaseTestCase.setUp(self)
836 self.containers = ['c1', 'c2']
837 for c in self.containers:
838 self.client.create_container(c)
839 self.upload_random_data(self.containers[1], o_names[0])
841 def test_delete(self):
842 status = self.client.delete_container(self.containers[0])[0]
843 self.assertEqual(status, 204)
845 def test_delete_non_empty(self):
846 self.assert_raises_fault(409, self.client.delete_container,
849 def test_delete_invalid(self):
850 self.assert_raises_fault(404, self.client.delete_container, 'c3')
852 class ObjectHead(BaseTestCase):
855 class ObjectGet(BaseTestCase):
857 BaseTestCase.setUp(self)
858 self.containers = ['c1', 'c2']
859 #create some containers
860 for c in self.containers:
861 self.client.create_container(c)
864 names = ('obj1', 'obj2')
867 self.objects.append(self.upload_random_data(self.containers[1], n))
869 def test_versions(self):
870 c = self.containers[1]
872 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
873 self.assert_versionlist_structure(b)
876 meta = {'quality':'AAA', 'stock':True}
877 self.client.update_object_metadata(c, o['name'], **meta)
879 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
880 self.assert_versionlist_structure(a)
881 self.assertEqual(len(b)+1, len(a))
882 self.assertEqual(b, a[:-1])
884 #get exact previous version metadata
886 v_meta = self.client.retrieve_object_metadata(c, o['name'],
889 for k in meta.keys():
890 self.assertTrue(k not in v_meta)
893 data = get_random_data()
894 self.client.update_object(c, o['name'], StringIO(data))
896 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
897 self.assert_versionlist_structure(aa)
898 self.assertEqual(len(a)+1, len(aa))
899 self.assertEqual(a, aa[:-1])
901 #get exact previous version
903 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
904 self.assertEqual(o['data'], v_data)
905 self.assertEqual(self.client.retrieve_object(c, o['name']),
906 '%s%s' %(v_data, data))
910 o = self.client.retrieve_object(self.containers[1],
911 self.objects[0]['name'],
912 self.objects[0]['meta'])
913 self.assertEqual(o, self.objects[0]['data'])
915 def test_get_invalid(self):
916 self.assert_raises_fault(404, self.client.retrieve_object,
917 self.containers[0], self.objects[0]['name'])
919 def test_get_partial(self):
920 #perform get with range
921 status, headers, data = self.client.request_object(self.containers[1],
922 self.objects[0]['name'],
925 #assert successful partial content
926 self.assertEqual(status, 206)
929 self.assertEqual(headers['content-type'],
930 self.objects[0]['meta']['content_type'])
932 #assert content length
933 self.assertEqual(int(headers['content-length']), 500)
936 self.assertEqual(self.objects[0]['data'][:500], data)
938 def test_get_final_500(self):
939 #perform get with range
940 headers = {'range':'bytes=-500'}
941 status, headers, data = self.client.request_object(self.containers[1],
942 self.objects[0]['name'],
945 #assert successful partial content
946 self.assertEqual(status, 206)
949 self.assertEqual(headers['content-type'],
950 self.objects[0]['meta']['content_type'])
952 #assert content length
953 self.assertEqual(int(headers['content-length']), 500)
956 self.assertTrue(self.objects[0]['data'][-500:], data)
958 def test_get_rest(self):
959 #perform get with range
960 offset = len(self.objects[0]['data']) - 500
961 status, headers, data = self.client.request_object(self.containers[1],
962 self.objects[0]['name'],
963 range='bytes=%s-' %offset)
965 #assert successful partial content
966 self.assertEqual(status, 206)
969 self.assertEqual(headers['content-type'],
970 self.objects[0]['meta']['content_type'])
972 #assert content length
973 self.assertEqual(int(headers['content-length']), 500)
976 self.assertTrue(self.objects[0]['data'][-500:], data)
978 def test_get_range_not_satisfiable(self):
979 #perform get with range
980 offset = len(self.objects[0]['data']) + 1
982 #assert range not satisfiable
983 self.assert_raises_fault(416, self.client.retrieve_object,
984 self.containers[1], self.objects[0]['name'],
985 range='bytes=0-%s' %offset)
987 def test_multiple_range(self):
988 #perform get with multiple range
989 ranges = ['0-499', '-500', '1000-']
990 bytes = 'bytes=%s' % ','.join(ranges)
991 status, headers, data = self.client.request_object(self.containers[1],
992 self.objects[0]['name'],
995 # assert partial content
996 self.assertEqual(status, 206)
998 # assert Content-Type of the reply will be multipart/byteranges
999 self.assertTrue(headers['content-type'])
1000 content_type_parts = headers['content-type'].split()
1001 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1003 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1004 cparts = data.split(boundary)[1:-1]
1006 # assert content parts are exactly 2
1007 self.assertEqual(len(cparts), len(ranges))
1009 # for each content part assert headers
1011 for cpart in cparts:
1012 content = cpart.split('\r\n')
1013 headers = content[1:3]
1014 content_range = headers[0].split(': ')
1015 self.assertEqual(content_range[0], 'Content-Range')
1017 r = ranges[i].split('-')
1018 if not r[0] and not r[1]:
1021 start = len(self.objects[0]['data']) - int(r[1])
1022 end = len(self.objects[0]['data'])
1025 end = len(self.objects[0]['data'])
1029 fdata = self.objects[0]['data'][start:end]
1030 sdata = '\r\n'.join(content[4:-1])
1031 self.assertEqual(len(fdata), len(sdata))
1032 self.assertEquals(fdata, sdata)
1035 def test_multiple_range_not_satisfiable(self):
1036 #perform get with multiple range
1037 out_of_range = len(self.objects[0]['data']) + 1
1038 ranges = ['0-499', '-500', '%d-' %out_of_range]
1039 bytes = 'bytes=%s' % ','.join(ranges)
1041 # assert partial content
1042 self.assert_raises_fault(416, self.client.retrieve_object,
1044 self.objects[0]['name'], range=bytes)
1046 def test_get_with_if_match(self):
1047 #perform get with If-Match
1048 etag = self.objects[0]['hash']
1049 status, headers, data = self.client.request_object(self.containers[1],
1050 self.objects[0]['name'],
1053 self.assertEqual(status, 200)
1055 #assert content-type
1056 self.assertEqual(headers['content-type'],
1057 self.objects[0]['meta']['content_type'])
1059 #assert response content
1060 self.assertEqual(self.objects[0]['data'], data)
1062 def test_get_with_if_match_star(self):
1063 #perform get with If-Match *
1064 headers = {'if-match':'*'}
1065 status, headers, data = self.client.request_object(self.containers[1],
1066 self.objects[0]['name'],
1069 self.assertEqual(status, 200)
1071 #assert content-type
1072 self.assertEqual(headers['content-type'],
1073 self.objects[0]['meta']['content_type'])
1075 #assert response content
1076 self.assertEqual(self.objects[0]['data'], data)
1078 def test_get_with_multiple_if_match(self):
1079 #perform get with If-Match
1080 etags = [i['hash'] for i in self.objects if i]
1081 etags = ','.join('"%s"' % etag for etag in etags)
1082 status, headers, data = self.client.request_object(self.containers[1],
1083 self.objects[0]['name'],
1086 self.assertEqual(status, 200)
1088 #assert content-type
1089 self.assertEqual(headers['content-type'],
1090 self.objects[0]['meta']['content_type'])
1092 #assert content-type
1093 self.assertEqual(headers['content-type'],
1094 self.objects[0]['meta']['content_type'])
1096 #assert response content
1097 self.assertEqual(self.objects[0]['data'], data)
1099 def test_if_match_precondition_failed(self):
1100 #assert precondition failed
1101 self.assert_raises_fault(412, self.client.retrieve_object,
1103 self.objects[0]['name'], if_match='123')
1105 def test_if_none_match(self):
1106 #perform get with If-None-Match
1107 status, headers, data = self.client.request_object(self.containers[1],
1108 self.objects[0]['name'],
1109 if_none_match='123')
1112 self.assertEqual(status, 200)
1114 #assert content-type
1115 self.assertEqual(headers['content_type'],
1116 self.objects[0]['meta']['content_type'])
1118 def test_if_none_match(self):
1119 #perform get with If-None-Match * and assert not modified
1120 self.assert_raises_fault(304, self.client.retrieve_object,
1122 self.objects[0]['name'],
1125 def test_if_none_match_not_modified(self):
1126 #perform get with If-None-Match and assert not modified
1127 self.assert_raises_fault(304, self.client.retrieve_object,
1129 self.objects[0]['name'],
1130 if_none_match=self.objects[0]['hash'])
1132 meta = self.client.retrieve_object_metadata(self.containers[1],
1133 self.objects[0]['name'])
1134 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1136 def test_if_modified_since(self):
1137 t = datetime.datetime.utcnow()
1138 t2 = t - datetime.timedelta(minutes=10)
1141 self.upload_data(self.containers[1],
1142 self.objects[0]['name'],
1143 self.objects[0]['data'][:200])
1145 for f in DATE_FORMATS:
1146 past = t2.strftime(f)
1148 headers = {'if-modified-since':'%s' %past}
1150 o = self.client.retrieve_object(self.containers[1],
1151 self.objects[0]['name'],
1152 if_modified_since=past)
1154 self.client.retrieve_object(self.containers[1],
1155 self.objects[0]['name']))
1157 self.failIf(f.status == 304)
1159 def test_if_modified_since_invalid_date(self):
1160 o = self.client.retrieve_object(self.containers[1],
1161 self.objects[0]['name'],
1162 if_modified_since='')
1163 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1164 self.objects[0]['name']))
1166 def test_if_not_modified_since(self):
1167 now = datetime.datetime.utcnow()
1168 since = now + datetime.timedelta(1)
1170 for f in DATE_FORMATS:
1171 #assert not modified
1172 self.assert_raises_fault(304, self.client.retrieve_object,
1173 self.containers[1], self.objects[0]['name'],
1174 if_modified_since=since.strftime(f))
1176 def test_if_unmodified_since(self):
1177 now = datetime.datetime.utcnow()
1178 since = now + datetime.timedelta(1)
1180 for f in DATE_FORMATS:
1181 t = since.strftime(f)
1182 status, headers, data = self.client.request_object(self.containers[1],
1183 self.objects[0]['name'],
1184 if_unmodified_since=t)
1186 self.assertEqual(status, 200)
1187 self.assertEqual(self.objects[0]['data'], data)
1189 #assert content-type
1190 self.assertEqual(headers['content-type'],
1191 self.objects[0]['meta']['content_type'])
1193 def test_if_unmodified_since_precondition_failed(self):
1194 t = datetime.datetime.utcnow()
1195 t2 = t - datetime.timedelta(minutes=10)
1198 self.upload_data(self.containers[1],
1199 self.objects[0]['name'],
1200 self.objects[0]['data'][:200])
1202 for f in DATE_FORMATS:
1203 past = t2.strftime(f)
1204 #assert precondition failed
1205 self.assert_raises_fault(412, self.client.retrieve_object,
1206 self.containers[1], self.objects[0]['name'],
1207 if_unmodified_since=past)
1209 def test_hashes(self):
1212 o = self.upload_random_data(self.containers[1], fname, l)
1214 body = self.client.retrieve_object(self.containers[1], fname,
1216 hashes = body['hashes']
1217 block_size = body['block_size']
1218 block_hash = body['block_hash']
1219 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1220 self.assertTrue(len(hashes), block_num)
1223 start = i * block_size
1224 end = (i + 1) * block_size
1225 hash = compute_block_hash(o['data'][start:end], block_hash)
1226 self.assertEqual(h, hash)
1229 class ObjectPut(BaseTestCase):
1231 BaseTestCase.setUp(self)
1232 self.container = 'c1'
1233 self.client.create_container(self.container)
1235 def test_upload(self):
1237 meta = {'test':'test1'}
1238 o = self.upload_random_data(self.container, name, **meta)
1240 headers = self.client.retrieve_object_metadata(self.container,
1243 self.assertTrue('test' in headers.keys())
1244 self.assertEqual(headers['test'], meta['test'])
1246 #assert uploaded content
1247 status, h, data = self.client.request_object(self.container, name)
1248 self.assertEqual(len(o['data']), int(h['content-length']))
1249 self.assertEqual(o['data'], data)
1251 #assert content-type
1252 self.assertEqual(h['content-type'], o['meta']['content_type'])
1254 def _test_maximum_upload_size_exceeds(self):
1256 meta = {'test':'test1'}
1258 length=1024*1024*100
1259 self.assert_raises_fault(400, self.upload_random_data, self.container,
1260 name, length, **meta)
1262 def test_upload_with_name_containing_slash(self):
1263 name = '/%s' % o_names[0]
1264 meta = {'test':'test1'}
1265 o = self.upload_random_data(self.container, name, **meta)
1267 self.assertEqual(o['data'],
1268 self.client.retrieve_object(self.container, name))
1270 self.assertTrue(name in self.client.list_objects(self.container))
1272 def test_create_directory_marker(self):
1273 self.client.create_directory_marker(self.container, 'foo')
1274 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1275 self.assertEqual(meta['content-length'], '0')
1276 self.assertEqual(meta['content-type'], 'application/directory')
1278 def test_upload_unprocessable_entity(self):
1279 meta={'etag':'123', 'test':'test1'}
1281 #assert unprocessable entity
1282 self.assert_raises_fault(422, self.upload_random_data, self.container,
1285 def test_chunked_transfer(self):
1286 data = get_random_data()
1288 self.client.create_object_using_chunks(self.container, objname,
1291 uploaded_data = self.client.retrieve_object(self.container, objname)
1292 self.assertEqual(data, uploaded_data)
1294 def test_manifestation(self):
1295 prefix = 'myobject/'
1298 part = '%s%d' %(prefix, i)
1299 o = self.upload_random_data(self.container, part)
1302 manifest = '%s/%s' %(self.container, prefix)
1303 self.client.create_manifestation(self.container, 'large-object', manifest)
1305 self.assert_object_exists(self.container, 'large-object')
1306 self.assertEqual(data, self.client.retrieve_object(self.container,
1309 #wrong manifestation
1310 self.client.create_manifestation(self.container, 'large-object',
1311 '%s/invalid' % self.container)
1312 self.assertEqual('', self.client.retrieve_object(self.container,
1315 def test_create_zero_length_object(self):
1318 zero = self.client.create_zero_length_object(c, o)
1319 zero_meta = self.client.retrieve_object_metadata(c, o)
1320 zero_hash = self.client.retrieve_object_hashmap(c, o)
1321 zero_data = self.client.retrieve_object(c, o)
1323 self.assertEqual(int(zero_meta['content-length']), 0)
1324 self.assertEqual(zero_hash, [])
1325 self.assertEqual(zero_data, '')
1327 class ObjectCopy(BaseTestCase):
1329 BaseTestCase.setUp(self)
1330 self.containers = ['c1', 'c2']
1331 for c in self.containers:
1332 self.client.create_container(c)
1333 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1338 def test_copy(self):
1339 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1340 self.containers[0], self.obj['name']):
1342 meta = {'test':'testcopy'}
1343 status = self.client.copy_object(self.containers[0],
1349 #assert copy success
1350 self.assertEqual(status, 201)
1352 #assert access the new object
1353 headers = self.client.retrieve_object_metadata(self.containers[0],
1355 self.assertTrue('x-object-meta-test' in headers.keys())
1356 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1358 #assert etag is the same
1359 self.assertEqual(headers['etag'], self.obj['hash'])
1361 #assert src object still exists
1362 self.assert_object_exists(self.containers[0], self.obj['name'])
1364 def test_copy_from_different_container(self):
1365 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1366 self.containers[0], self.obj['name']):
1367 meta = {'test':'testcopy'}
1368 status = self.client.copy_object(self.containers[0],
1373 self.assertEqual(status, 201)
1375 # assert updated metadata
1376 meta = self.client.retrieve_object_metadata(self.containers[1],
1379 self.assertTrue('test' in meta.keys())
1380 self.assertTrue(meta['test'], 'testcopy')
1382 #assert src object still exists
1383 self.assert_object_exists(self.containers[0], self.obj['name'])
1385 def test_copy_invalid(self):
1386 #copy from invalid object
1387 meta = {'test':'testcopy'}
1388 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1389 'test.py', self.containers[1], 'testcopy', meta)
1391 #copy from invalid container
1392 meta = {'test':'testcopy'}
1393 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1394 self.obj['name'], self.containers[1],
1397 class ObjectMove(BaseTestCase):
1399 BaseTestCase.setUp(self)
1400 self.containers = ['c1', 'c2']
1401 for c in self.containers:
1402 self.client.create_container(c)
1403 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1405 def test_move(self):
1407 meta = {'test':'testcopy'}
1408 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1409 status = self.client.move_object(self.containers[0], self.obj['name'],
1410 self.containers[0], 'testcopy',
1413 #assert successful move
1414 self.assertEqual(status, 201)
1416 #assert updated metadata
1417 meta = self.client.retrieve_object_metadata(self.containers[0],
1420 self.assertTrue('test' in meta.keys())
1421 self.assertTrue(meta['test'], 'testcopy')
1423 #assert src object no more exists
1424 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1426 class ObjectPost(BaseTestCase):
1428 BaseTestCase.setUp(self)
1429 self.containers = ['c1', 'c2']
1430 for c in self.containers:
1431 self.client.create_container(c)
1434 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1436 def test_update_meta(self):
1437 #perform update metadata
1438 more = {'foo':'foo', 'bar':'bar'}
1439 status = self.client.update_object_metadata(self.containers[0],
1440 self.obj[0]['name'],
1442 #assert request accepted
1443 self.assertEqual(status, 202)
1445 #assert old metadata are still there
1446 headers = self.client.retrieve_object_metadata(self.containers[0],
1447 self.obj[0]['name'],
1449 #assert new metadata have been updated
1450 for k,v in more.items():
1451 self.assertTrue(k in headers.keys())
1452 self.assertTrue(headers[k], v)
1454 def test_update_object(self,
1457 instance_length = True,
1458 content_length = 500):
1459 l = len(self.obj[0]['data'])
1460 range = 'bytes %d-%d/%s' %(first_byte_pos,
1462 l if instance_length else '*')
1463 partial = last_byte_pos - first_byte_pos + 1
1464 length = first_byte_pos + partial
1465 data = get_random_data(partial)
1466 args = {'content_type':'application/octet-stream',
1467 'content_range':'%s' %range}
1469 args['content_length'] = content_length
1471 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1472 StringIO(data), **args)[0]
1474 if partial < 0 or (instance_length and l <= last_byte_pos):
1475 self.assertEqual(status, 202)
1477 self.assertEqual(status, 204)
1478 #check modified object
1479 content = self.client.retrieve_object(self.containers[0],
1480 self.obj[0]['name'])
1481 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1482 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1483 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1485 def test_update_object_lt_blocksize(self):
1486 self.test_update_object(10, 20, content_length=None)
1488 def test_update_object_gt_blocksize(self):
1489 o = self.upload_random_data(self.containers[0], o_names[1],
1490 length=4*1024*1024+5)
1491 c = self.containers[0]
1494 first_byte_pos = 4*1024*1024+1
1495 last_byte_pos = 4*1024*1024+4
1496 l = last_byte_pos - first_byte_pos + 1
1497 data = get_random_data(l)
1498 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1499 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1500 content = self.client.retrieve_object(c, o_name)
1501 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1502 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1503 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1505 def test_update_object_divided_by_blocksize(self):
1506 o = self.upload_random_data(self.containers[0], o_names[1],
1507 length=4*1024*1024+5)
1508 c = self.containers[0]
1511 first_byte_pos = 4*1024*1024
1512 last_byte_pos = 5*1024*1024
1513 l = last_byte_pos - first_byte_pos + 1
1514 data = get_random_data(l)
1515 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1516 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1517 content = self.client.retrieve_object(c, o_name)
1518 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1519 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1520 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1522 def test_update_object_no_content_length(self):
1523 self.test_update_object(content_length = None)
1525 def test_update_object_invalid_content_length(self):
1526 with AssertContentInvariant(self.client.retrieve_object,
1527 self.containers[0], self.obj[0]['name']):
1528 self.assert_raises_fault(400, self.test_update_object,
1529 content_length = 1000)
1531 def test_update_object_invalid_range(self):
1532 with AssertContentInvariant(self.client.retrieve_object,
1533 self.containers[0], self.obj[0]['name']):
1534 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1536 def test_update_object_invalid_range_and_length(self):
1537 with AssertContentInvariant(self.client.retrieve_object,
1538 self.containers[0], self.obj[0]['name']):
1539 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1542 def test_update_object_invalid_range_with_no_content_length(self):
1543 with AssertContentInvariant(self.client.retrieve_object,
1544 self.containers[0], self.obj[0]['name']):
1545 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1546 content_length = None)
1548 def test_update_object_out_of_limits(self):
1549 with AssertContentInvariant(self.client.retrieve_object,
1550 self.containers[0], self.obj[0]['name']):
1551 l = len(self.obj[0]['data'])
1552 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1554 def test_append(self):
1555 data = get_random_data(500)
1557 self.client.update_object(self.containers[0], self.obj[0]['name'],
1558 StringIO(data), content_length=500,
1559 content_type='application/octet-stream')
1561 content = self.client.retrieve_object(self.containers[0],
1562 self.obj[0]['name'])
1563 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1564 self.assertEqual(content[:-500], self.obj[0]['data'])
1566 def test_update_with_chunked_transfer(self):
1567 data = get_random_data(500)
1569 fl = len(self.obj[0]['data'])
1571 self.client.update_object_using_chunks(self.containers[0],
1572 self.obj[0]['name'],
1575 content_type='application/octet-stream')
1577 #check modified object
1578 content = self.client.retrieve_object(self.containers[0],
1579 self.obj[0]['name'])
1580 self.assertEqual(content[0:dl], data)
1581 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1583 def test_update_from_other_object(self):
1584 c = self.containers[0]
1588 source_data = self.client.retrieve_object(c, src)
1589 source_meta = self.client.retrieve_object_metadata(c, src)
1590 source_hash = self.client.retrieve_object_hashmap(c, src)
1592 #update zero length object
1593 self.client.create_zero_length_object(c, dest)
1594 source_object = '/%s/%s' % (c, src)
1595 self.client.update_from_other_source(c, dest, source_object)
1596 dest_data = self.client.retrieve_object(c, src)
1597 dest_meta = self.client.retrieve_object_metadata(c, dest)
1598 dest_hash = self.client.retrieve_object_hashmap(c, src)
1599 self.assertEqual(source_data, dest_data)
1600 self.assertEqual(source_hash, dest_hash)
1603 self.client.update_from_other_source(c, dest, source_object)
1604 content = self.client.retrieve_object(c, dest)
1605 self.assertEqual(source_data * 2, content)
1607 def test_update_range_from_other_object(self):
1608 c = self.containers[0]
1612 src = self.obj[1]['name']
1613 src_data = self.client.retrieve_object(c, src)
1615 #update zero length object
1616 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1617 source_object = '/%s/%s' % (c, src)
1618 first_byte_pos = 4*1024*1024+1
1619 last_byte_pos = 4*1024*1024+4
1620 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1621 self.client.update_from_other_source(c, dest, source_object,
1622 content_range=range)
1623 content = self.client.retrieve_object(c, dest)
1624 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1625 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1626 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1628 def test_update_hashes_from_other_object(self):
1629 c = self.containers[0]
1633 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1635 #update zero length object
1636 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1637 source_object = '/%s/%s' % (c, o_names[0])
1638 first_byte_pos = 4*1024*1024
1639 last_byte_pos = 5*1024*1024
1640 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1641 self.client.update_from_other_source(c, dest, source_object,
1642 content_range=range)
1643 content = self.client.retrieve_object(c, dest)
1644 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1645 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1646 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1649 def test_update_zero_length_object(self):
1650 c = self.containers[0]
1653 zero = self.client.create_zero_length_object(c, o)
1655 data = get_random_data()
1656 self.client.update_object(c, o, StringIO(data))
1657 self.client.create_object(c, other, StringIO(data))
1659 self.assertEqual(self.client.retrieve_object(c, o),
1660 self.client.retrieve_object(c, other))
1662 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1663 self.client.retrieve_object_hashmap(c, other))
1665 class ObjectDelete(BaseTestCase):
1667 BaseTestCase.setUp(self)
1668 self.containers = ['c1', 'c2']
1669 for c in self.containers:
1670 self.client.create_container(c)
1671 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1673 def test_delete(self):
1674 #perform delete object
1675 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1677 def test_delete_invalid(self):
1678 #assert item not found
1679 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1682 class ListSharing(BaseTestCase):
1684 BaseTestCase.setUp(self)
1686 self.client.create_container('c%s' %i)
1687 self.client.create_container('c')
1689 self.upload_random_data('c1', 'o%s' %i)
1690 accounts = OTHER_ACCOUNTS.copy()
1691 self.o1_sharing_with = accounts.popitem()
1692 self.o1_sharing = [self.o1_sharing_with[1]]
1693 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1697 l.append(accounts.popitem())
1699 def test_list_other_shared(self):
1700 self.other = Pithos_Client(get_server(),
1701 self.o1_sharing_with[0],
1702 self.o1_sharing_with[1],
1704 self.assertTrue(get_user() in self.other.list_shared_by_others())
1706 def test_list_my_shared(self):
1707 my_shared_containers = self.client.list_containers(shared=True)
1708 self.assertTrue('c1' in my_shared_containers)
1709 self.assertTrue('c2' not in my_shared_containers)
1711 my_shared_objects = self.client.list_objects('c1', shared=True)
1712 self.assertTrue('o1' in my_shared_objects)
1713 self.assertTrue('o2' not in my_shared_objects)
1715 class TestGreek(BaseTestCase):
1717 BaseTestCase.setUp(self)
1718 #keep track of initial account groups
1719 self.initial_groups = self.client.retrieve_account_groups()
1721 #keep track of initial account meta
1722 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1725 #delete additionally created meta
1727 for m in self.client.retrieve_account_metadata(restricted=True):
1728 if m not in self.initial_meta:
1730 self.client.delete_account_metadata(l)
1732 #delete additionally created groups
1734 for g in self.client.retrieve_account_groups():
1735 if g not in self.initial_groups:
1737 self.client.unset_account_groups(l)
1739 BaseTestCase.tearDown(self)
1741 def test_create_container(self):
1742 self.client.create_container('φάκελος')
1743 self.assert_container_exists('φάκελος')
1745 self.assertTrue('φάκελος' in self.client.list_containers())
1747 def test_create_object(self):
1748 self.client.create_container('φάκελος')
1749 self.upload_random_data('φάκελος', 'αντικείμενο')
1751 self.assert_object_exists('φάκελος', 'αντικείμενο')
1752 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1754 def test_copy_object(self):
1755 src_container = 'φάκελος'
1756 src_object = 'αντικείμενο'
1757 dest_container = 'αντίγραφα'
1758 dest_object = 'ασφαλές-αντίγραφο'
1760 self.client.create_container(src_container)
1761 self.upload_random_data(src_container, src_object)
1763 self.client.create_container(dest_container)
1764 self.client.copy_object(src_container, src_object, dest_container,
1767 self.assert_object_exists(src_container, src_object)
1768 self.assert_object_exists(dest_container, dest_object)
1769 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1771 def test_move_object(self):
1772 src_container = 'φάκελος'
1773 src_object = 'αντικείμενο'
1774 dest_container = 'αντίγραφα'
1775 dest_object = 'ασφαλές-αντίγραφο'
1777 self.client.create_container(src_container)
1778 self.upload_random_data(src_container, src_object)
1780 self.client.create_container(dest_container)
1781 self.client.move_object(src_container, src_object, dest_container,
1784 self.assert_object_not_exists(src_container, src_object)
1785 self.assert_object_exists(dest_container, dest_object)
1786 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1788 def test_delete_object(self):
1789 self.client.create_container('φάκελος')
1790 self.upload_random_data('φάκελος', 'αντικείμενο')
1791 self.assert_object_exists('φάκελος', 'αντικείμενο')
1793 self.client.delete_object('φάκελος', 'αντικείμενο')
1794 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1795 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1797 def test_delete_container(self):
1798 self.client.create_container('φάκελος')
1799 self.assert_container_exists('φάκελος')
1801 self.client.delete_container('φάκελος')
1802 self.assert_container_not_exists('φάκελος')
1803 self.assertTrue('φάκελος' not in self.client.list_containers())
1805 def test_account_meta(self):
1806 meta = {'ποιότητα':'ΑΑΑ'}
1807 self.client.update_account_metadata(**meta)
1808 meta = self.client.retrieve_account_metadata(restricted=True)
1809 self.assertTrue('ποιότητα' in meta.keys())
1810 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1812 def test_container_meta(self):
1813 meta = {'ποιότητα':'ΑΑΑ'}
1814 self.client.create_container('φάκελος', **meta)
1816 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1817 self.assertTrue('ποιότητα' in meta.keys())
1818 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1820 def test_object_meta(self):
1821 self.client.create_container('φάκελος')
1822 meta = {'ποιότητα':'ΑΑΑ'}
1823 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1825 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1827 self.assertTrue('ποιότητα' in meta.keys())
1828 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1830 def test_list_meta_filtering(self):
1831 self.client.create_container('φάκελος')
1832 meta = {'ποιότητα':'ΑΑΑ'}
1833 self.upload_random_data('φάκελος', 'ο1', **meta)
1834 self.upload_random_data('φάκελος', 'ο2')
1835 self.upload_random_data('φάκελος', 'ο3')
1837 meta = {'ποσότητα':'μεγάλη'}
1838 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1839 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1840 self.assertTrue('ο1' in objects)
1841 self.assertTrue('ο2' in objects)
1842 self.assertTrue('ο3' not in objects)
1844 def test_groups(self):
1846 groups = {'σεφς':'chazapis,διογένης'}
1847 self.client.set_account_groups(**groups)
1848 groups.update(self.initial_groups)
1849 self.assertEqual(groups['σεφς'],
1850 self.client.retrieve_account_groups()['σεφς'])
1853 self.client.create_container('φάκελος')
1854 o = self.upload_random_data('φάκελος', 'ο1')
1855 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1856 chef = Pithos_Client(get_server(),
1860 self.assert_not_raises_fault(401, chef.retrieve_object_metadata,
1861 'φάκελος', 'ο1', account=get_user())
1864 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1865 new_data = get_random_data()
1866 self.assert_not_raises_fault(401, chef.update_object,
1867 'φάκελος', 'ο1', StringIO(new_data),
1870 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1871 self.assertEqual(server_data[:len(o['data'])], o['data'])
1872 self.assertEqual(server_data[len(o['data']):], new_data)
1874 def test_manifestation(self):
1875 self.client.create_container('κουβάς')
1879 part = '%s%d' %(prefix, i)
1880 o = self.upload_random_data('κουβάς', part)
1883 self.client.create_container('φάκελος')
1884 manifest = '%s/%s' %('κουβάς', prefix)
1885 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1887 self.assert_object_exists('φάκελος', 'άπαντα')
1888 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1891 #wrong manifestation
1892 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1893 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1895 def test_update_from_another_object(self):
1896 self.client.create_container('κουβάς')
1897 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1898 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1899 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1900 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1903 self.client.retrieve_object('κουβάς', 'νέο'),
1904 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1906 class TestPermissions(BaseTestCase):
1908 BaseTestCase.setUp(self)
1909 #keep track of initial account groups
1910 self.initial_groups = self.client.retrieve_account_groups()
1911 #keep track of initial account meta
1912 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
1915 self.authorized = ['chazapis', 'verigak', 'gtsouk', 'papagian']
1916 groups = {'pithosdev':','.join(self.authorized)}
1917 self.client.set_account_groups(**groups)
1920 #delete additionally created meta
1922 for m in self.client.retrieve_account_metadata(restricted=True):
1923 if m not in self.initial_meta:
1925 self.client.delete_account_metadata(l)
1927 #delete additionally created groups
1929 for g in self.client.retrieve_account_groups():
1930 if g not in self.initial_groups:
1932 self.client.unset_account_groups(l)
1934 BaseTestCase.tearDown(self)
1936 def test_read_access(self):
1937 self.client.create_container('c')
1938 o = self.upload_random_data('c', 'o')
1939 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1940 for token, account in OTHER_ACCOUNTS.items():
1941 cl = Pithos_Client(get_server(), token, account, get_api())
1942 if account in self.authorized:
1943 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1944 'c', 'o', account=get_user())
1946 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1947 'c', 'o', account=get_user())
1950 o = self.upload_random_data('c', 'o/also-shared')
1951 for token, account in OTHER_ACCOUNTS.items():
1952 cl = Pithos_Client(get_server(), token, account, get_api())
1953 if account in self.authorized:
1954 self.assert_not_raises_fault(401, cl.retrieve_object_metadata,
1955 'c', 'o/also-shared', account=get_user())
1957 self.assert_raises_fault(401, cl.retrieve_object_metadata,
1958 'c', 'o/also-shared', account=get_user())
1960 def test_write_access(self):
1961 self.client.create_container('c')
1962 o = self.upload_random_data('c', 'o')
1963 self.client.share_object('c', 'o', ['chazapis'], read=False)
1965 for token, account in OTHER_ACCOUNTS.items():
1966 cl = Pithos_Client(get_server(), token, account, get_api())
1967 new_data = get_random_data()
1968 if account in [get_user(), 'chazapis']:
1969 self.assert_not_raises_fault(401, cl.update_object,
1970 'c', 'o', StringIO(new_data),
1972 server_data = self.client.retrieve_object('c', 'o')
1973 self.assertEqual(o_data, server_data[:len(o_data)])
1974 self.assertEqual(new_data, server_data[len(o_data):])
1975 o_data = server_data
1977 self.assert_raises_fault(401, cl.update_object,
1978 'c', 'o', StringIO(new_data),
1982 o = self.upload_random_data('c', 'o/also-shared')
1984 for token, account in OTHER_ACCOUNTS.items():
1985 cl = Pithos_Client(get_server(), token, account, get_api())
1986 new_data = get_random_data()
1987 if account in [get_user(), 'chazapis']:
1988 self.assert_not_raises_fault(401, cl.update_object,
1992 server_data = self.client.retrieve_object('c', o['name'])
1993 self.assertEqual(o_data, server_data[:len(o_data)])
1994 self.assertEqual(new_data, server_data[len(o_data):])
1995 o_data = server_data
1997 self.assert_raises_fault(401, cl.update_object,
2002 class AssertMappingInvariant(object):
2003 def __init__(self, callable, *args, **kwargs):
2004 self.callable = callable
2006 self.kwargs = kwargs
2008 def __enter__(self):
2009 self.map = self.callable(*self.args, **self.kwargs)
2012 def __exit__(self, type, value, tb):
2013 map = self.callable(*self.args, **self.kwargs)
2014 for k in self.map.keys():
2015 if is_date(self.map[k]):
2017 assert map[k] == self.map[k]
2019 class AssertContentInvariant(object):
2020 def __init__(self, callable, *args, **kwargs):
2021 self.callable = callable
2023 self.kwargs = kwargs
2025 def __enter__(self):
2026 self.content = self.callable(*self.args, **self.kwargs)[2]
2029 def __exit__(self, type, value, tb):
2030 content = self.callable(*self.args, **self.kwargs)[2]
2031 assert self.content == content
2033 def get_content_splitted(response):
2035 return response.content.split('\n')
2037 def compute_md5_hash(data):
2041 return md5.hexdigest().lower()
2043 def compute_block_hash(data, algorithm):
2044 h = hashlib.new(algorithm)
2045 h.update(data.rstrip('\x00'))
2046 return h.hexdigest()
2048 def get_random_data(length=500):
2049 char_set = string.ascii_uppercase + string.digits
2050 return ''.join(random.choice(char_set) for x in range(length))
2053 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2054 __D = r'(?P<day>\d{2})'
2055 __D2 = r'(?P<day>[ \d]\d)'
2056 __M = r'(?P<mon>\w{3})'
2057 __Y = r'(?P<year>\d{4})'
2058 __Y2 = r'(?P<year>\d{2})'
2059 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2060 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2061 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2062 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2063 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2064 m = regex.match(date)
2069 o_names = ['kate.jpg',
2070 'kate_beckinsale.jpg',
2071 'How To Win Friends And Influence People.pdf',
2072 'moms_birthday.jpg',
2074 'Disturbed - Down With The Sickness.mp3',
2075 'army_of_darkness.avi',
2077 'photos/animals/dogs/poodle.jpg',
2078 'photos/animals/dogs/terrier.jpg',
2079 'photos/animals/cats/persian.jpg',
2080 'photos/animals/cats/siamese.jpg',
2081 'photos/plants/fern.jpg',
2082 'photos/plants/rose.jpg',
2085 if __name__ == "__main__":
2086 if get_user() == 'test':
2089 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()