4 # Copyright 2011-2012 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 pithos.lib.client import Pithos_Client, Fault
38 from pithos.lib.util import get_user, get_auth, get_server, get_api
40 from xml.dom import minidom
41 from StringIO import StringIO
42 from hashlib import new as newhasher
43 from binascii import hexlify
56 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
57 "%A, %d-%b-%y %H:%M:%S GMT",
58 "%a, %d %b %Y %H:%M:%S GMT"]
71 class BaseTestCase(unittest.TestCase):
72 #TODO unauthorized request
74 self.client = Pithos_Client(get_server(), get_auth(), get_user(),
77 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
80 #keep track of initial account groups
81 self.initial_groups = self.client.retrieve_account_groups()
83 #keep track of initial account meta
84 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
92 'x_container_policy'),
100 self.return_codes = (400, 401, 403, 404, 503,)
103 #delete additionally created meta
105 for m in self.client.retrieve_account_metadata(restricted=True):
106 if m not in self.initial_meta:
108 self.client.delete_account_metadata(l)
110 #delete additionally created groups
112 for g in self.client.retrieve_account_groups():
113 if g not in self.initial_groups:
115 self.client.unset_account_groups(l)
116 self._clean_account()
118 def _clean_account(self):
119 for c in self.client.list_containers():
121 #list objects returns at most 10000 objects
122 #so repeat until there are no more objects
123 objects = self.client.list_objects(c)
127 self.client.delete_object(c, o)
128 self.client.delete_container(c)
130 def assert_status(self, status, codes):
131 l = [elem for elem in self.return_codes]
132 if type(codes) == types.ListType:
136 self.assertTrue(status in l)
138 def assert_extended(self, data, format, type, size=10000):
140 self._assert_xml(data, type, size)
141 elif format == 'json':
142 self._assert_json(data, type, size)
144 def _assert_json(self, data, type, size):
145 convert = lambda s: s.lower()
146 info = [convert(elem) for elem in self.extended[type]]
147 self.assertTrue(len(data) <= size)
150 if 'subdir' in i.keys():
152 self.assertTrue(item in i.keys())
154 def _assert_xml(self, data, type, size):
155 convert = lambda s: s.lower()
156 info = [convert(elem) for elem in self.extended[type]]
158 info.remove('content_encoding')
162 entities = xml.getElementsByTagName(type)
163 self.assertTrue(len(entities) <= size)
166 self.assertTrue(e.getElementsByTagName(item))
168 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
170 asserts that a Fault with a specific status is raised
171 when callableObj is called with the specific arguments
174 r = callableObj(*args, **kwargs)
175 self.fail('Should never reach here')
177 if type(status) == types.ListType:
178 self.failUnless(f.status in status)
180 self.failUnless(f.status == status)
182 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
184 asserts that a Fault with a specific status is not raised
185 when callableObj is called with the specific arguments
188 r = callableObj(*args, **kwargs)
190 self.failIfEqual(f.status, status)
192 def assert_container_exists(self, container):
194 asserts the existence of a container
197 self.client.retrieve_container_metadata(container)
199 self.failIf(f.status == 404)
201 def assert_container_not_exists(self, container):
203 asserts there is no such a container
205 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
208 def assert_object_exists(self, container, object):
210 asserts the existence of an object
213 self.client.retrieve_object_metadata(container, object)
215 self.failIf(f.status == 404)
217 def assert_object_not_exists(self, container, object):
219 asserts there is no such an object
221 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
224 def assert_versionlist_structure(self, versionlist):
225 self.assertTrue(type(versionlist) == types.ListType)
226 for elem in versionlist:
227 self.assertTrue(type(elem) == types.ListType)
228 self.assertEqual(len(elem), 2)
230 def upload_random_data(self, container, name, length=1024, type=None,
232 data = get_random_data(length)
233 return self.upload_data(container, name, data, type, enc, **meta)
235 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
241 obj['hash'] = compute_md5_hash(obj['data'])
244 args['etag'] = etag if etag else obj['hash']
247 guess = mimetypes.guess_type(name)
248 type = type if type else guess[0]
249 enc = enc if enc else guess[1]
252 args['content_type'] = type if type else 'plain/text'
253 args['content_encoding'] = enc if enc else None
257 path = '/%s/%s' % (container, name)
258 self.client.create_object(container, name, f=StringIO(obj['data']),
265 class AccountHead(BaseTestCase):
267 BaseTestCase.setUp(self)
268 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
269 for item in self.containers:
270 self.client.create_container(item)
273 self.client.update_account_metadata(**meta)
274 #self.updated_meta = self.initial_meta.update(meta)
276 def test_get_account_meta(self):
277 meta = self.client.retrieve_account_metadata()
279 containers = self.client.list_containers()
280 l = str(len(containers))
281 self.assertEqual(meta['x-account-container-count'], l)
284 m = self.client.retrieve_container_metadata(c)
285 size = size + int(m['x-container-bytes-used'])
286 self.assertEqual(meta['x-account-bytes-used'], str(size))
288 def test_get_account_403(self):
289 self.assert_raises_fault(403,
290 self.invalid_client.retrieve_account_metadata)
292 def test_get_account_meta_until(self):
293 t = datetime.datetime.utcnow()
294 past = t - datetime.timedelta(minutes=-15)
295 past = int(_time.mktime(past.timetuple()))
297 meta = {'premium':True}
298 self.client.update_account_metadata(**meta)
299 meta = self.client.retrieve_account_metadata(restricted=True,
301 self.assertTrue('premium' not in meta)
303 meta = self.client.retrieve_account_metadata(restricted=True)
304 self.assertTrue('premium' in meta)
306 def test_get_account_meta_until_invalid_date(self):
307 meta = {'premium':True}
308 self.client.update_account_metadata(**meta)
309 meta = self.client.retrieve_account_metadata(restricted=True,
311 self.assertTrue('premium' in meta)
313 class AccountGet(BaseTestCase):
315 BaseTestCase.setUp(self)
316 #create some containers
317 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
318 for item in self.containers:
319 self.client.create_container(item)
323 containers = self.client.list_containers()
324 self.assertEquals(self.containers, containers)
326 def test_list_403(self):
327 self.assert_raises_fault(403, self.invalid_client.list_containers)
329 def test_list_with_limit(self):
331 containers = self.client.list_containers(limit=limit)
332 self.assertEquals(len(containers), limit)
333 self.assertEquals(self.containers[:2], containers)
335 def test_list_with_marker(self):
338 containers = self.client.list_containers(limit=l, marker=m)
339 i = self.containers.index(m) + 1
340 self.assertEquals(self.containers[i:(i+l)], containers)
343 containers = self.client.list_containers(limit=l, marker=m)
344 i = self.containers.index(m) + 1
345 self.assertEquals(self.containers[i:(i+l)], containers)
347 def test_list_json_with_marker(self):
350 containers = self.client.list_containers(limit=l, marker=m, format='json')
351 self.assert_extended(containers, 'json', 'container', l)
352 self.assertEqual(containers[0]['name'], 'kiwis')
353 self.assertEqual(containers[1]['name'], 'oranges')
355 def test_list_xml_with_marker(self):
358 xml = self.client.list_containers(limit=l, marker=m, format='xml')
359 self.assert_extended(xml, 'xml', 'container', l)
360 nodes = xml.getElementsByTagName('name')
361 self.assertEqual(len(nodes), 1)
362 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
364 def test_if_modified_since(self):
365 t = datetime.datetime.utcnow()
366 t2 = t - datetime.timedelta(minutes=10)
369 self.client.create_container('dummy')
371 for f in DATE_FORMATS:
372 past = t2.strftime(f)
374 c = self.client.list_containers(if_modified_since=past)
375 self.assertEqual(len(c), len(self.containers) + 1)
377 self.failIf(f.status == 304) #fail if not modified
379 def test_if_modified_since_invalid_date(self):
380 c = self.client.list_containers(if_modified_since='')
381 self.assertEqual(len(c), len(self.containers))
383 def test_if_not_modified_since(self):
384 now = datetime.datetime.utcnow()
385 since = now + datetime.timedelta(1)
387 for f in DATE_FORMATS:
388 args = {'if_modified_since':'%s' %since.strftime(f)}
391 self.assert_raises_fault(304, self.client.list_containers, **args)
393 def test_if_unmodified_since(self):
394 now = datetime.datetime.utcnow()
395 since = now + datetime.timedelta(1)
397 for f in DATE_FORMATS:
398 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
401 self.assertEqual(self.containers, c)
403 def test_if_unmodified_since_precondition_failed(self):
404 t = datetime.datetime.utcnow()
405 t2 = t - datetime.timedelta(minutes=10)
408 self.client.create_container('dummy')
410 for f in DATE_FORMATS:
411 past = t2.strftime(f)
413 args = {'if_unmodified_since':'%s' %past}
415 #assert precondition failed
416 self.assert_raises_fault(412, self.client.list_containers, **args)
418 class AccountPost(BaseTestCase):
420 BaseTestCase.setUp(self)
421 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
422 for item in self.containers:
423 self.client.create_container(item)
426 self.client.update_account_metadata(**meta)
427 self.updated_meta = self.initial_meta.update(meta)
429 def test_update_meta(self):
430 with AssertMappingInvariant(self.client.retrieve_account_groups):
431 meta = {'test':'test', 'tost':'tost'}
432 self.client.update_account_metadata(**meta)
434 meta.update(self.initial_meta)
435 self.assertEqual(meta,
436 self.client.retrieve_account_metadata(
439 def test_invalid_account_update_meta(self):
440 meta = {'test':'test', 'tost':'tost'}
441 self.assert_raises_fault(403,
442 self.invalid_client.update_account_metadata,
445 def test_reset_meta(self):
446 with AssertMappingInvariant(self.client.retrieve_account_groups):
447 meta = {'test':'test', 'tost':'tost'}
448 self.client.update_account_metadata(**meta)
450 meta = {'test':'test33'}
451 self.client.reset_account_metadata(**meta)
453 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
455 def test_delete_meta(self):
456 with AssertMappingInvariant(self.client.retrieve_account_groups):
457 meta = {'test':'test', 'tost':'tost'}
458 self.client.update_account_metadata(**meta)
460 self.client.delete_account_metadata(meta.keys())
462 account_meta = self.client.retrieve_account_metadata(restricted=True)
464 self.assertTrue(m not in account_meta.keys())
466 def test_set_account_groups(self):
467 with AssertMappingInvariant(self.client.retrieve_account_metadata):
468 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
469 self.client.set_account_groups(**groups)
471 self.assertEqual(set(groups['pithosdev']),
472 set(self.client.retrieve_account_groups()['pithosdev']))
474 more_groups = {'clientsdev':'pkanavos,mvasilak'}
475 self.client.set_account_groups(**more_groups)
477 groups.update(more_groups)
478 self.assertEqual(set(groups['clientsdev']),
479 set(self.client.retrieve_account_groups()['clientsdev']))
481 def test_reset_account_groups(self):
482 with AssertMappingInvariant(self.client.retrieve_account_metadata):
483 groups = {'pithosdev':'verigak,gtsouk,chazapis',
484 'clientsdev':'pkanavos,mvasilak'}
485 self.client.set_account_groups(**groups)
487 self.assertEqual(set(groups['pithosdev'].split(',')),
488 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
489 self.assertEqual(set(groups['clientsdev'].split(',')),
490 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
492 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
493 self.client.reset_account_groups(**groups)
495 self.assertEqual(set(groups['pithosdev'].split(',')),
496 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
498 def test_delete_account_groups(self):
499 with AssertMappingInvariant(self.client.retrieve_account_metadata):
500 groups = {'pithosdev':'verigak,gtsouk,chazapis',
501 'clientsdev':'pkanavos,mvasilak'}
502 self.client.set_account_groups(**groups)
504 self.client.unset_account_groups(groups.keys())
506 self.assertEqual({}, self.client.retrieve_account_groups())
508 class ContainerHead(BaseTestCase):
510 BaseTestCase.setUp(self)
511 self.container = 'apples'
512 self.client.create_container(self.container)
514 def test_get_meta(self):
515 meta = {'trash':'true'}
516 t1 = datetime.datetime.utcnow()
517 o = self.upload_random_data(self.container, o_names[0], **meta)
519 headers = self.client.retrieve_container_metadata(self.container)
520 self.assertEqual(headers['x-container-object-count'], '1')
521 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
522 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
524 threashold = datetime.timedelta(seconds=1)
525 self.assertTrue(delta < threashold)
526 self.assertTrue(headers['x-container-object-meta'])
527 self.assertTrue('Trash' in headers['x-container-object-meta'])
529 class ContainerGet(BaseTestCase):
531 BaseTestCase.setUp(self)
532 self.container = ['pears', 'apples']
533 for c in self.container:
534 self.client.create_container(c)
536 for o in o_names[:8]:
537 self.obj.append(self.upload_random_data(self.container[0], o))
538 for o in o_names[8:]:
539 self.obj.append(self.upload_random_data(self.container[1], o))
541 def test_list_objects(self):
542 objects = self.client.list_objects(self.container[0])
543 l = [elem['name'] for elem in self.obj[:8]]
545 self.assertEqual(objects, l)
547 def test_list_objects_containing_slash(self):
548 self.client.create_container('test')
549 self.upload_random_data('test', '/objectname')
551 objects = self.client.list_objects('test')
552 self.assertEqual(objects, ['/objectname'])
554 objects = self.client.list_objects('test', format='json')
555 self.assertEqual(objects[0]['name'], '/objectname')
557 objects = self.client.list_objects('test', format='xml')
558 self.assert_extended(objects, 'xml', 'object')
559 node_name = objects.getElementsByTagName('name')[0]
560 self.assertEqual(node_name.firstChild.data, '/objectname')
562 def test_list_objects_with_limit_marker(self):
563 objects = self.client.list_objects(self.container[0], limit=2)
564 l = [elem['name'] for elem in self.obj[:8]]
566 self.assertEqual(objects, l[:2])
568 markers = ['How To Win Friends And Influence People.pdf',
572 objects = self.client.list_objects(self.container[0], limit=limit,
574 l = [elem['name'] for elem in self.obj[:8]]
576 start = l.index(m) + 1
578 end = end if len(l) >= end else len(l)
579 self.assertEqual(objects, l[start:end])
582 def _test_list_limit_exceeds(self):
583 self.client.create_container('pithos')
585 for i in range(10001):
586 self.client.create_zero_length_object('pithos', i)
588 self.assertEqual(10000, len(self.client.list_objects('pithos')))
590 def test_list_empty_params(self):
591 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
593 objects = objects.strip().split('\n')
594 self.assertEqual(objects,
595 self.client.list_objects(self.container[0]))
597 def test_list_pseudo_hierarchical_folders(self):
598 objects = self.client.list_objects(self.container[1], prefix='photos',
600 self.assertEquals(['photos/animals/', 'photos/me.jpg',
601 'photos/plants/'], objects)
603 objects = self.client.list_objects(self.container[1],
604 prefix='photos/animals',
606 l = ['photos/animals/cats/', 'photos/animals/dogs/']
607 self.assertEquals(l, objects)
609 objects = self.client.list_objects(self.container[1], path='photos')
610 self.assertEquals(['photos/me.jpg'], objects)
612 def test_extended_list_json(self):
613 objects = self.client.list_objects(self.container[1], format='json',
614 limit=2, prefix='photos/animals',
616 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
617 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
619 def test_extended_list_xml(self):
620 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
621 prefix='photos', delimiter='/')
622 self.assert_extended(xml, 'xml', 'object', size=4)
623 dirs = xml.getElementsByTagName('subdir')
624 self.assertEqual(len(dirs), 2)
625 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
626 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
628 objects = xml.getElementsByTagName('name')
629 self.assertEqual(len(objects), 1)
630 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
632 def test_list_meta_double_matching(self):
633 meta = {'quality':'aaa', 'stock':'true'}
634 self.client.update_object_metadata(self.container[0],
635 self.obj[0]['name'], **meta)
636 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
637 self.assertEqual(len(obj), 1)
638 self.assertTrue(obj, self.obj[0]['name'])
640 def test_list_using_meta(self):
641 meta = {'quality':'aaa'}
642 for o in self.obj[:2]:
643 self.client.update_object_metadata(self.container[0], o['name'],
645 meta = {'stock':'true'}
646 for o in self.obj[3:5]:
647 self.client.update_object_metadata(self.container[0], o['name'],
650 obj = self.client.list_objects(self.container[0], meta='Quality')
651 self.assertEqual(len(obj), 2)
652 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
654 # test case insensitive
655 obj = self.client.list_objects(self.container[0], meta='quality')
656 self.assertEqual(len(obj), 2)
657 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
659 # test multiple matches
660 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
661 self.assertEqual(len(obj), 4)
662 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
664 # test non 1-1 multiple match
665 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
666 self.assertEqual(len(obj), 2)
667 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
669 def test_if_modified_since(self):
670 t = datetime.datetime.utcnow()
671 t2 = t - datetime.timedelta(minutes=10)
674 self.upload_random_data(self.container[0], o_names[0])
676 for f in DATE_FORMATS:
677 past = t2.strftime(f)
679 o = self.client.list_objects(self.container[0],
680 if_modified_since=past)
682 self.client.list_objects(self.container[0]))
684 self.failIf(f.status == 304) #fail if not modified
686 def test_if_modified_since_invalid_date(self):
687 headers = {'if-modified-since':''}
688 o = self.client.list_objects(self.container[0], if_modified_since='')
689 self.assertEqual(o, self.client.list_objects(self.container[0]))
691 def test_if_not_modified_since(self):
692 now = datetime.datetime.utcnow()
693 since = now + datetime.timedelta(1)
695 for f in DATE_FORMATS:
696 args = {'if_modified_since':'%s' %since.strftime(f)}
699 self.assert_raises_fault(304, self.client.list_objects,
700 self.container[0], **args)
702 def test_if_unmodified_since(self):
703 now = datetime.datetime.utcnow()
704 since = now + datetime.timedelta(1)
706 for f in DATE_FORMATS:
707 obj = self.client.list_objects(self.container[0],
708 if_unmodified_since=since.strftime(f))
711 self.assertEqual(obj, self.client.list_objects(self.container[0]))
713 def test_if_unmodified_since_precondition_failed(self):
714 t = datetime.datetime.utcnow()
715 t2 = t - datetime.timedelta(minutes=10)
718 self.client.create_container('dummy')
720 for f in DATE_FORMATS:
721 past = t2.strftime(f)
723 args = {'if_unmodified_since':'%s' %past}
725 #assert precondition failed
726 self.assert_raises_fault(412, self.client.list_objects,
727 self.container[0], **args)
729 class ContainerPut(BaseTestCase):
731 BaseTestCase.setUp(self)
732 self.containers = ['c1', 'c2']
734 def test_create(self):
735 self.client.create_container(self.containers[0])
736 containers = self.client.list_containers()
737 self.assertTrue(self.containers[0] in containers)
738 self.assert_container_exists(self.containers[0])
740 def test_create_twice(self):
741 self.client.create_container(self.containers[0])
742 self.assertTrue(not self.client.create_container(self.containers[0]))
744 def test_quota(self):
745 self.client.create_container(self.containers[0])
747 policy = {'quota':100}
748 self.client.set_container_policies('c1', **policy)
750 meta = self.client.retrieve_container_metadata('c1')
751 self.assertTrue('x-container-policy-quota' in meta)
752 self.assertEqual(meta['x-container-policy-quota'], '100')
755 kwargs = {'length':101}
756 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
760 self.client.set_container_policies('c1', **policy)
762 class ContainerPost(BaseTestCase):
764 BaseTestCase.setUp(self)
765 self.container = 'apples'
766 self.client.create_container(self.container)
768 def test_update_meta(self):
769 meta = {'test':'test33',
771 self.client.update_container_metadata(self.container, **meta)
772 headers = self.client.retrieve_container_metadata(self.container)
773 for k,v in meta.items():
774 k = 'x-container-meta-%s' % k
775 self.assertTrue(headers[k])
776 self.assertEqual(headers[k], v)
778 class ContainerDelete(BaseTestCase):
780 BaseTestCase.setUp(self)
781 self.containers = ['c1', 'c2']
782 for c in self.containers:
783 self.client.create_container(c)
785 def test_delete(self):
786 status = self.client.delete_container(self.containers[0])[0]
787 self.assertEqual(status, 204)
789 def test_delete_non_empty(self):
790 self.upload_random_data(self.containers[1], o_names[0])
791 self.assert_raises_fault(409, self.client.delete_container,
794 def test_delete_invalid(self):
795 self.assert_raises_fault(404, self.client.delete_container, 'c3')
797 class ObjectGet(BaseTestCase):
799 BaseTestCase.setUp(self)
800 self.containers = ['c1', 'c2']
801 #create some containers
802 for c in self.containers:
803 self.client.create_container(c)
806 names = ('obj1', 'obj2')
809 self.objects.append(self.upload_random_data(self.containers[1], n))
811 def test_versions(self):
812 c = self.containers[1]
814 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
815 self.assert_versionlist_structure(b)
818 meta = {'quality':'AAA', 'stock':True}
819 self.client.update_object_metadata(c, o['name'], **meta)
821 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
822 self.assert_versionlist_structure(a)
823 self.assertEqual(len(b)+1, len(a))
824 self.assertEqual(b, a[:-1])
826 #get exact previous version metadata
828 v_meta = self.client.retrieve_object_metadata(c, o['name'],
831 for k in meta.keys():
832 self.assertTrue(k not in v_meta)
835 data = get_random_data()
836 self.client.update_object(c, o['name'], StringIO(data))
838 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
839 self.assert_versionlist_structure(aa)
840 self.assertEqual(len(a)+1, len(aa))
841 self.assertEqual(a, aa[:-1])
843 #get exact previous version
845 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
846 self.assertEqual(o['data'], v_data)
847 self.assertEqual(self.client.retrieve_object(c, o['name']),
848 '%s%s' %(v_data, data))
852 o = self.client.retrieve_object(self.containers[1],
853 self.objects[0]['name'],
854 self.objects[0]['meta'])
855 self.assertEqual(o, self.objects[0]['data'])
857 def test_objects_with_trailing_spaces(self):
858 self.client.create_container('test')
860 self.upload_random_data('test', 'a')
861 #look for 'a ' object
862 self.assert_raises_fault(404, self.client.retrieve_object,
866 self.client.delete_object('test', 'a')
867 self.assert_raises_fault(404, self.client.retrieve_object,
871 self.upload_random_data('test', 'a ')
873 self.assert_raises_fault(404, self.client.retrieve_object,
876 def test_get_invalid(self):
877 self.assert_raises_fault(404, self.client.retrieve_object,
878 self.containers[0], self.objects[0]['name'])
880 def test_get_partial(self):
881 #perform get with range
882 status, headers, data = self.client.request_object(self.containers[1],
883 self.objects[0]['name'],
886 #assert successful partial content
887 self.assertEqual(status, 206)
890 self.assertEqual(headers['content-type'],
891 self.objects[0]['meta']['content_type'])
893 #assert content length
894 self.assertEqual(int(headers['content-length']), 500)
897 self.assertEqual(self.objects[0]['data'][:500], data)
899 def test_get_final_500(self):
900 #perform get with range
901 headers = {'range':'bytes=-500'}
902 status, headers, data = self.client.request_object(self.containers[1],
903 self.objects[0]['name'],
906 #assert successful partial content
907 self.assertEqual(status, 206)
910 self.assertEqual(headers['content-type'],
911 self.objects[0]['meta']['content_type'])
913 #assert content length
914 self.assertEqual(int(headers['content-length']), 500)
917 self.assertTrue(self.objects[0]['data'][-500:], data)
919 def test_get_rest(self):
920 #perform get with range
921 offset = len(self.objects[0]['data']) - 500
922 status, headers, data = self.client.request_object(self.containers[1],
923 self.objects[0]['name'],
924 range='bytes=%s-' %offset)
926 #assert successful partial content
927 self.assertEqual(status, 206)
930 self.assertEqual(headers['content-type'],
931 self.objects[0]['meta']['content_type'])
933 #assert content length
934 self.assertEqual(int(headers['content-length']), 500)
937 self.assertTrue(self.objects[0]['data'][-500:], data)
939 def test_get_range_not_satisfiable(self):
940 #perform get with range
941 offset = len(self.objects[0]['data']) + 1
943 #assert range not satisfiable
944 self.assert_raises_fault(416, self.client.retrieve_object,
945 self.containers[1], self.objects[0]['name'],
946 range='bytes=0-%s' %offset)
948 def test_multiple_range(self):
949 #perform get with multiple range
950 ranges = ['0-499', '-500', '1000-']
951 bytes = 'bytes=%s' % ','.join(ranges)
952 status, headers, data = self.client.request_object(self.containers[1],
953 self.objects[0]['name'],
956 # assert partial content
957 self.assertEqual(status, 206)
959 # assert Content-Type of the reply will be multipart/byteranges
960 self.assertTrue(headers['content-type'])
961 content_type_parts = headers['content-type'].split()
962 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
964 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
965 cparts = data.split(boundary)[1:-1]
967 # assert content parts are exactly 2
968 self.assertEqual(len(cparts), len(ranges))
970 # for each content part assert headers
973 content = cpart.split('\r\n')
974 headers = content[1:3]
975 content_range = headers[0].split(': ')
976 self.assertEqual(content_range[0], 'Content-Range')
978 r = ranges[i].split('-')
979 if not r[0] and not r[1]:
982 start = len(self.objects[0]['data']) - int(r[1])
983 end = len(self.objects[0]['data'])
986 end = len(self.objects[0]['data'])
990 fdata = self.objects[0]['data'][start:end]
991 sdata = '\r\n'.join(content[4:-1])
992 self.assertEqual(len(fdata), len(sdata))
993 self.assertEquals(fdata, sdata)
996 def test_multiple_range_not_satisfiable(self):
997 #perform get with multiple range
998 out_of_range = len(self.objects[0]['data']) + 1
999 ranges = ['0-499', '-500', '%d-' %out_of_range]
1000 bytes = 'bytes=%s' % ','.join(ranges)
1002 # assert partial content
1003 self.assert_raises_fault(416, self.client.retrieve_object,
1005 self.objects[0]['name'], range=bytes)
1007 def test_get_with_if_match(self):
1008 #perform get with If-Match
1009 etag = self.objects[0]['hash']
1010 status, headers, data = self.client.request_object(self.containers[1],
1011 self.objects[0]['name'],
1014 self.assertEqual(status, 200)
1016 #assert content-type
1017 self.assertEqual(headers['content-type'],
1018 self.objects[0]['meta']['content_type'])
1020 #assert response content
1021 self.assertEqual(self.objects[0]['data'], data)
1023 def test_get_with_if_match_star(self):
1024 #perform get with If-Match *
1025 headers = {'if-match':'*'}
1026 status, headers, data = self.client.request_object(self.containers[1],
1027 self.objects[0]['name'],
1030 self.assertEqual(status, 200)
1032 #assert content-type
1033 self.assertEqual(headers['content-type'],
1034 self.objects[0]['meta']['content_type'])
1036 #assert response content
1037 self.assertEqual(self.objects[0]['data'], data)
1039 def test_get_with_multiple_if_match(self):
1040 #perform get with If-Match
1041 etags = [i['hash'] for i in self.objects if i]
1042 etags = ','.join('"%s"' % etag for etag in etags)
1043 status, headers, data = self.client.request_object(self.containers[1],
1044 self.objects[0]['name'],
1047 self.assertEqual(status, 200)
1049 #assert content-type
1050 self.assertEqual(headers['content-type'],
1051 self.objects[0]['meta']['content_type'])
1053 #assert content-type
1054 self.assertEqual(headers['content-type'],
1055 self.objects[0]['meta']['content_type'])
1057 #assert response content
1058 self.assertEqual(self.objects[0]['data'], data)
1060 def test_if_match_precondition_failed(self):
1061 #assert precondition failed
1062 self.assert_raises_fault(412, self.client.retrieve_object,
1064 self.objects[0]['name'], if_match='123')
1066 def test_if_none_match(self):
1067 #perform get with If-None-Match
1068 status, headers, data = self.client.request_object(self.containers[1],
1069 self.objects[0]['name'],
1070 if_none_match='123')
1073 self.assertEqual(status, 200)
1075 #assert content-type
1076 self.assertEqual(headers['content_type'],
1077 self.objects[0]['meta']['content_type'])
1079 def test_if_none_match(self):
1080 #perform get with If-None-Match * and assert not modified
1081 self.assert_raises_fault(304, self.client.retrieve_object,
1083 self.objects[0]['name'],
1086 def test_if_none_match_not_modified(self):
1087 #perform get with If-None-Match and assert not modified
1088 self.assert_raises_fault(304, self.client.retrieve_object,
1090 self.objects[0]['name'],
1091 if_none_match=self.objects[0]['hash'])
1093 meta = self.client.retrieve_object_metadata(self.containers[1],
1094 self.objects[0]['name'])
1095 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1097 def test_if_modified_since(self):
1098 t = datetime.datetime.utcnow()
1099 t2 = t - datetime.timedelta(minutes=10)
1102 self.upload_data(self.containers[1],
1103 self.objects[0]['name'],
1104 self.objects[0]['data'][:200])
1106 for f in DATE_FORMATS:
1107 past = t2.strftime(f)
1109 headers = {'if-modified-since':'%s' %past}
1111 o = self.client.retrieve_object(self.containers[1],
1112 self.objects[0]['name'],
1113 if_modified_since=past)
1115 self.client.retrieve_object(self.containers[1],
1116 self.objects[0]['name']))
1118 self.failIf(f.status == 304)
1120 def test_if_modified_since_invalid_date(self):
1121 o = self.client.retrieve_object(self.containers[1],
1122 self.objects[0]['name'],
1123 if_modified_since='')
1124 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1125 self.objects[0]['name']))
1127 def test_if_not_modified_since(self):
1128 now = datetime.datetime.utcnow()
1129 since = now + datetime.timedelta(1)
1131 for f in DATE_FORMATS:
1132 #assert not modified
1133 self.assert_raises_fault(304, self.client.retrieve_object,
1134 self.containers[1], self.objects[0]['name'],
1135 if_modified_since=since.strftime(f))
1137 def test_if_unmodified_since(self):
1138 now = datetime.datetime.utcnow()
1139 since = now + datetime.timedelta(1)
1141 for f in DATE_FORMATS:
1142 t = since.strftime(f)
1143 status, headers, data = self.client.request_object(self.containers[1],
1144 self.objects[0]['name'],
1145 if_unmodified_since=t)
1147 self.assertEqual(status, 200)
1148 self.assertEqual(self.objects[0]['data'], data)
1150 #assert content-type
1151 self.assertEqual(headers['content-type'],
1152 self.objects[0]['meta']['content_type'])
1154 def test_if_unmodified_since_precondition_failed(self):
1155 t = datetime.datetime.utcnow()
1156 t2 = t - datetime.timedelta(minutes=10)
1159 self.upload_data(self.containers[1],
1160 self.objects[0]['name'],
1161 self.objects[0]['data'][:200])
1163 for f in DATE_FORMATS:
1164 past = t2.strftime(f)
1165 #assert precondition failed
1166 self.assert_raises_fault(412, self.client.retrieve_object,
1167 self.containers[1], self.objects[0]['name'],
1168 if_unmodified_since=past)
1170 def test_hashes(self):
1173 o = self.upload_random_data(self.containers[1], fname, l)
1175 body = self.client.retrieve_object(self.containers[1], fname,
1177 hashes = body['hashes']
1178 block_size = body['block_size']
1179 block_hash = body['block_hash']
1180 block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1181 self.assertTrue(len(hashes), block_num)
1184 start = i * block_size
1185 end = (i + 1) * block_size
1186 hash = compute_block_hash(o['data'][start:end], block_hash)
1187 self.assertEqual(h, hash)
1190 class ObjectPut(BaseTestCase):
1192 BaseTestCase.setUp(self)
1193 self.container = 'c1'
1194 self.client.create_container(self.container)
1196 def test_upload(self):
1198 meta = {'test':'test1'}
1199 o = self.upload_random_data(self.container, name, **meta)
1201 headers = self.client.retrieve_object_metadata(self.container,
1204 self.assertTrue('test' in headers.keys())
1205 self.assertEqual(headers['test'], meta['test'])
1207 #assert uploaded content
1208 status, h, data = self.client.request_object(self.container, name)
1209 self.assertEqual(len(o['data']), int(h['content-length']))
1210 self.assertEqual(o['data'], data)
1212 #assert content-type
1213 self.assertEqual(h['content-type'], o['meta']['content_type'])
1215 def _test_maximum_upload_size_exceeds(self):
1217 meta = {'test':'test1'}
1219 length=1024*1024*100
1220 self.assert_raises_fault(400, self.upload_random_data, self.container,
1221 name, length, **meta)
1223 def test_upload_with_name_containing_slash(self):
1224 name = '/%s' % o_names[0]
1225 meta = {'test':'test1'}
1226 o = self.upload_random_data(self.container, name, **meta)
1228 self.assertEqual(o['data'],
1229 self.client.retrieve_object(self.container, name))
1231 self.assertTrue(name in self.client.list_objects(self.container))
1233 def test_create_directory_marker(self):
1234 self.client.create_directory_marker(self.container, 'foo')
1235 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1236 self.assertEqual(meta['content-length'], '0')
1237 self.assertEqual(meta['content-type'], 'application/directory')
1239 def test_upload_unprocessable_entity(self):
1240 meta={'etag':'123', 'test':'test1'}
1242 #assert unprocessable entity
1243 self.assert_raises_fault(422, self.upload_random_data, self.container,
1246 def test_chunked_transfer(self):
1247 data = get_random_data()
1249 self.client.create_object_using_chunks(self.container, objname,
1252 uploaded_data = self.client.retrieve_object(self.container, objname)
1253 self.assertEqual(data, uploaded_data)
1255 def test_manifestation(self):
1256 prefix = 'myobject/'
1259 part = '%s%d' %(prefix, i)
1260 o = self.upload_random_data(self.container, part)
1263 manifest = '%s/%s' %(self.container, prefix)
1264 self.client.create_manifestation(self.container, 'large-object', manifest)
1266 self.assert_object_exists(self.container, 'large-object')
1267 self.assertEqual(data, self.client.retrieve_object(self.container,
1270 #wrong manifestation
1271 self.client.create_manifestation(self.container, 'large-object',
1272 '%s/invalid' % self.container)
1273 self.assertEqual('', self.client.retrieve_object(self.container,
1276 def test_create_zero_length_object(self):
1279 zero = self.client.create_zero_length_object(c, o)
1280 zero_meta = self.client.retrieve_object_metadata(c, o)
1281 zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1282 zero_data = self.client.retrieve_object(c, o)
1284 self.assertEqual(int(zero_meta['content-length']), 0)
1285 hasher = newhasher('sha256')
1287 emptyhash = hasher.digest()
1288 self.assertEqual(zero_hash, [hexlify(emptyhash)])
1289 self.assertEqual(zero_data, '')
1291 def test_create_object_by_hashmap(self):
1294 self.upload_random_data(c, o)
1295 hashmap = self.client.retrieve_object(c, o, format='json')
1297 self.client.create_object_by_hashmap(c, o2, hashmap)
1298 self.assertEqual(self.client.retrieve_object(c, o),
1299 self.client.retrieve_object(c, o))
1301 class ObjectCopy(BaseTestCase):
1303 BaseTestCase.setUp(self)
1304 self.containers = ['c1', 'c2']
1305 for c in self.containers:
1306 self.client.create_container(c)
1307 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1309 def test_copy(self):
1310 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1311 self.containers[0], self.obj['name']):
1313 meta = {'test':'testcopy'}
1314 status = self.client.copy_object(self.containers[0],
1320 #assert copy success
1321 self.assertEqual(status, 201)
1323 #assert access the new object
1324 headers = self.client.retrieve_object_metadata(self.containers[0],
1326 self.assertTrue('x-object-meta-test' in headers.keys())
1327 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1329 #assert etag is the same
1330 self.assertEqual(headers['etag'], self.obj['hash'])
1332 #assert src object still exists
1333 self.assert_object_exists(self.containers[0], self.obj['name'])
1335 def test_copy_from_different_container(self):
1336 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1337 self.containers[0], self.obj['name']):
1338 meta = {'test':'testcopy'}
1339 status = self.client.copy_object(self.containers[0],
1344 self.assertEqual(status, 201)
1346 # assert updated metadata
1347 meta = self.client.retrieve_object_metadata(self.containers[1],
1350 self.assertTrue('test' in meta.keys())
1351 self.assertTrue(meta['test'], 'testcopy')
1353 #assert src object still exists
1354 self.assert_object_exists(self.containers[0], self.obj['name'])
1356 def test_copy_invalid(self):
1357 #copy from invalid object
1358 meta = {'test':'testcopy'}
1359 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1360 'test.py', self.containers[1], 'testcopy', meta)
1362 #copy from invalid container
1363 meta = {'test':'testcopy'}
1364 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1365 self.obj['name'], self.containers[1],
1368 class ObjectMove(BaseTestCase):
1370 BaseTestCase.setUp(self)
1371 self.containers = ['c1', 'c2']
1372 for c in self.containers:
1373 self.client.create_container(c)
1374 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1376 def test_move(self):
1377 meta = self.client.retrieve_object_metadata(self.containers[0],
1379 self.assertTrue('x-object-uuid' in meta)
1380 uuid = meta['x-object-uuid']
1383 meta = {'test':'testcopy'}
1384 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1385 status = self.client.move_object(self.containers[0], self.obj['name'],
1386 self.containers[0], 'testcopy',
1389 #assert successful move
1390 self.assertEqual(status, 201)
1392 #assert updated metadata
1393 meta = self.client.retrieve_object_metadata(self.containers[0],
1395 self.assertTrue('x-object-meta-test' in meta.keys())
1396 self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1399 self.assertTrue(meta['x-object-uuid'], uuid)
1401 #assert src object no more exists
1402 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1404 class ObjectPost(BaseTestCase):
1406 BaseTestCase.setUp(self)
1407 self.containers = ['c1', 'c2']
1408 for c in self.containers:
1409 self.client.create_container(c)
1412 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1414 def test_update_meta(self):
1415 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1417 self.obj[0]['name']):
1418 #perform update metadata
1419 more = {'foo':'foo', 'bar':'bar'}
1420 status = self.client.update_object_metadata(self.containers[0],
1421 self.obj[0]['name'],
1423 #assert request accepted
1424 self.assertEqual(status, 202)
1426 #assert old metadata are still there
1427 headers = self.client.retrieve_object_metadata(self.containers[0],
1428 self.obj[0]['name'],
1430 #assert new metadata have been updated
1431 for k,v in more.items():
1432 self.assertTrue(k in headers.keys())
1433 self.assertTrue(headers[k], v)
1435 def test_update_object(self,
1438 instance_length = True,
1439 content_length = 500):
1440 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1442 self.obj[0]['name']):
1443 l = len(self.obj[0]['data'])
1444 range = 'bytes %d-%d/%s' %(first_byte_pos,
1446 l if instance_length else '*')
1447 partial = last_byte_pos - first_byte_pos + 1
1448 length = first_byte_pos + partial
1449 data = get_random_data(partial)
1450 args = {'content_type':'application/octet-stream',
1451 'content_range':'%s' %range}
1453 args['content_length'] = content_length
1455 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1456 StringIO(data), **args)[0]
1458 if partial < 0 or (instance_length and l <= last_byte_pos):
1459 self.assertEqual(status, 202)
1461 self.assertEqual(status, 204)
1462 #check modified object
1463 content = self.client.retrieve_object(self.containers[0],
1464 self.obj[0]['name'])
1465 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1466 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1467 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1469 def test_update_object_lt_blocksize(self):
1470 self.test_update_object(10, 20, content_length=None)
1472 def test_update_object_gt_blocksize(self):
1473 o = self.upload_random_data(self.containers[0], o_names[1],
1474 length=4*1024*1024+5)
1475 c = self.containers[0]
1478 first_byte_pos = 4*1024*1024+1
1479 last_byte_pos = 4*1024*1024+4
1480 l = last_byte_pos - first_byte_pos + 1
1481 data = get_random_data(l)
1482 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1483 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1484 content = self.client.retrieve_object(c, o_name)
1485 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1486 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1487 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1489 def test_update_object_divided_by_blocksize(self):
1490 o = self.upload_random_data(self.containers[0], o_names[1],
1491 length=4*1024*1024+5)
1492 c = self.containers[0]
1495 first_byte_pos = 4*1024*1024
1496 last_byte_pos = 5*1024*1024
1497 l = last_byte_pos - first_byte_pos + 1
1498 data = get_random_data(l)
1499 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1500 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1501 content = self.client.retrieve_object(c, o_name)
1502 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1503 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1504 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1506 def test_update_object_no_content_length(self):
1507 self.test_update_object(content_length = None)
1509 def test_update_object_invalid_content_length(self):
1510 with AssertContentInvariant(self.client.retrieve_object,
1511 self.containers[0], self.obj[0]['name']):
1512 self.assert_raises_fault(400, self.test_update_object,
1513 content_length = 1000)
1515 def _test_update_object_invalid_range(self):
1516 with AssertContentInvariant(self.client.retrieve_object,
1517 self.containers[0], self.obj[0]['name']):
1518 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1520 def _test_update_object_invalid_range_and_length(self):
1521 with AssertContentInvariant(self.client.retrieve_object,
1522 self.containers[0], self.obj[0]['name']):
1523 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1526 def test_update_object_invalid_range_with_no_content_length(self):
1527 with AssertContentInvariant(self.client.retrieve_object,
1528 self.containers[0], self.obj[0]['name']):
1529 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1530 content_length = None)
1532 def test_update_object_out_of_limits(self):
1533 with AssertContentInvariant(self.client.retrieve_object,
1534 self.containers[0], self.obj[0]['name']):
1535 l = len(self.obj[0]['data'])
1536 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1538 def test_append(self):
1539 data = get_random_data(500)
1541 self.client.update_object(self.containers[0], self.obj[0]['name'],
1542 StringIO(data), content_length=500,
1543 content_type='application/octet-stream')
1545 content = self.client.retrieve_object(self.containers[0],
1546 self.obj[0]['name'])
1547 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1548 self.assertEqual(content[:-500], self.obj[0]['data'])
1550 def test_update_with_chunked_transfer(self):
1551 data = get_random_data(500)
1553 fl = len(self.obj[0]['data'])
1555 self.client.update_object_using_chunks(self.containers[0],
1556 self.obj[0]['name'],
1559 content_type='application/octet-stream')
1561 #check modified object
1562 content = self.client.retrieve_object(self.containers[0],
1563 self.obj[0]['name'])
1564 self.assertEqual(content[0:dl], data)
1565 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1567 def test_update_from_other_object(self):
1568 c = self.containers[0]
1572 source_data = self.client.retrieve_object(c, src)
1573 source_meta = self.client.retrieve_object_metadata(c, src)
1574 source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1576 #update zero length object
1577 self.client.create_zero_length_object(c, dest)
1578 source_object = '/%s/%s' % (c, src)
1579 self.client.update_from_other_source(c, dest, source_object)
1580 dest_data = self.client.retrieve_object(c, src)
1581 dest_meta = self.client.retrieve_object_metadata(c, dest)
1582 dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1583 self.assertEqual(source_data, dest_data)
1584 self.assertEqual(source_hash, dest_hash)
1587 self.client.update_from_other_source(c, dest, source_object)
1588 content = self.client.retrieve_object(c, dest)
1589 self.assertEqual(source_data * 2, content)
1591 def test_update_range_from_other_object(self):
1592 c = self.containers[0]
1596 src = self.obj[1]['name']
1597 src_data = self.client.retrieve_object(c, src)
1599 #update zero length object
1600 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1601 source_object = '/%s/%s' % (c, src)
1602 first_byte_pos = 4*1024*1024+1
1603 last_byte_pos = 4*1024*1024+4
1604 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1605 self.client.update_from_other_source(c, dest, source_object,
1606 content_range=range)
1607 content = self.client.retrieve_object(c, dest)
1608 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1609 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1610 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1612 def test_update_hashes_from_other_object(self):
1613 c = self.containers[0]
1617 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1619 #update zero length object
1620 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1621 source_object = '/%s/%s' % (c, o_names[0])
1622 first_byte_pos = 4*1024*1024
1623 last_byte_pos = 5*1024*1024
1624 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1625 self.client.update_from_other_source(c, dest, source_object,
1626 content_range=range)
1627 content = self.client.retrieve_object(c, dest)
1628 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1629 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1630 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1633 def test_update_zero_length_object(self):
1634 c = self.containers[0]
1637 zero = self.client.create_zero_length_object(c, o)
1639 data = get_random_data()
1640 self.client.update_object(c, o, StringIO(data))
1641 self.client.create_object(c, other, StringIO(data))
1643 self.assertEqual(self.client.retrieve_object(c, o),
1644 self.client.retrieve_object(c, other))
1646 self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1647 self.client.retrieve_object_hashmap(c, other)["hashes"])
1649 class ObjectDelete(BaseTestCase):
1651 BaseTestCase.setUp(self)
1652 self.containers = ['c1', 'c2']
1653 for c in self.containers:
1654 self.client.create_container(c)
1655 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1657 def test_delete(self):
1658 #perform delete object
1659 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1661 def test_delete_invalid(self):
1662 #assert item not found
1663 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1666 class ListSharing(BaseTestCase):
1668 BaseTestCase.setUp(self)
1670 self.client.create_container('c%s' %i)
1671 self.client.create_container('c')
1673 self.upload_random_data('c1', 'o%s' %i)
1674 accounts = OTHER_ACCOUNTS.copy()
1675 self.o1_sharing_with = accounts.popitem()
1676 self.o1_sharing = [self.o1_sharing_with[1]]
1677 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1681 l.append(accounts.popitem())
1683 def test_list_other_shared(self):
1684 self.other = Pithos_Client(get_server(),
1685 self.o1_sharing_with[0],
1686 self.o1_sharing_with[1],
1688 self.assertTrue(get_user() in self.other.list_shared_by_others())
1690 def test_list_my_shared(self):
1691 my_shared_containers = self.client.list_containers(shared=True)
1692 self.assertTrue('c1' in my_shared_containers)
1693 self.assertTrue('c2' not in my_shared_containers)
1695 my_shared_objects = self.client.list_objects('c1', shared=True)
1696 self.assertTrue('o1' in my_shared_objects)
1697 self.assertTrue('o2' not in my_shared_objects)
1699 class TestGreek(BaseTestCase):
1700 def test_create_container(self):
1701 self.client.create_container('φάκελος')
1702 self.assert_container_exists('φάκελος')
1704 self.assertTrue('φάκελος' in self.client.list_containers())
1706 def test_create_object(self):
1707 self.client.create_container('φάκελος')
1708 self.upload_random_data('φάκελος', 'αντικείμενο')
1710 self.assert_object_exists('φάκελος', 'αντικείμενο')
1711 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1713 def test_copy_object(self):
1714 src_container = 'φάκελος'
1715 src_object = 'αντικείμενο'
1716 dest_container = 'αντίγραφα'
1717 dest_object = 'ασφαλές-αντίγραφο'
1719 self.client.create_container(src_container)
1720 self.upload_random_data(src_container, src_object)
1722 self.client.create_container(dest_container)
1723 self.client.copy_object(src_container, src_object, dest_container,
1726 self.assert_object_exists(src_container, src_object)
1727 self.assert_object_exists(dest_container, dest_object)
1728 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1730 def test_move_object(self):
1731 src_container = 'φάκελος'
1732 src_object = 'αντικείμενο'
1733 dest_container = 'αντίγραφα'
1734 dest_object = 'ασφαλές-αντίγραφο'
1736 self.client.create_container(src_container)
1737 self.upload_random_data(src_container, src_object)
1739 self.client.create_container(dest_container)
1740 self.client.move_object(src_container, src_object, dest_container,
1743 self.assert_object_not_exists(src_container, src_object)
1744 self.assert_object_exists(dest_container, dest_object)
1745 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1747 def test_delete_object(self):
1748 self.client.create_container('φάκελος')
1749 self.upload_random_data('φάκελος', 'αντικείμενο')
1750 self.assert_object_exists('φάκελος', 'αντικείμενο')
1752 self.client.delete_object('φάκελος', 'αντικείμενο')
1753 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1754 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1756 def test_delete_container(self):
1757 self.client.create_container('φάκελος')
1758 self.assert_container_exists('φάκελος')
1760 self.client.delete_container('φάκελος')
1761 self.assert_container_not_exists('φάκελος')
1762 self.assertTrue('φάκελος' not in self.client.list_containers())
1764 def test_account_meta(self):
1765 meta = {'ποιότητα':'ΑΑΑ'}
1766 self.client.update_account_metadata(**meta)
1767 meta = self.client.retrieve_account_metadata(restricted=True)
1768 self.assertTrue('ποιότητα' in meta.keys())
1769 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1771 def test_container_meta(self):
1772 meta = {'ποιότητα':'ΑΑΑ'}
1773 self.client.create_container('φάκελος', **meta)
1775 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1776 self.assertTrue('ποιότητα' in meta.keys())
1777 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1779 def test_object_meta(self):
1780 self.client.create_container('φάκελος')
1781 meta = {'ποιότητα':'ΑΑΑ'}
1782 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1784 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1786 self.assertTrue('ποιότητα' in meta.keys())
1787 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1789 def test_list_meta_filtering(self):
1790 self.client.create_container('φάκελος')
1791 meta = {'ποιότητα':'ΑΑΑ'}
1792 self.upload_random_data('φάκελος', 'ο1', **meta)
1793 self.upload_random_data('φάκελος', 'ο2')
1794 self.upload_random_data('φάκελος', 'ο3')
1796 meta = {'ποσότητα':'μεγάλη'}
1797 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1798 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1799 self.assertEquals(objects, ['ο1', 'ο2'])
1801 objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
1802 self.assertEquals(objects, ['ο2', 'ο3'])
1804 objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
1805 self.assertEquals(objects, ['ο3'])
1807 meta = {'ποιότητα':'ΑΒ'}
1808 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1809 objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
1810 self.assertEquals(objects, ['ο1'])
1811 objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
1812 self.assertEquals(objects, ['ο2'])
1814 meta = {'έτος':'2011'}
1815 self.client.update_object_metadata('φάκελος', 'ο3', **meta)
1816 meta = {'έτος':'2012'}
1817 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1818 objects = self.client.list_objects('φάκελος', meta='έτος<2012')
1819 self.assertEquals(objects, ['ο3'])
1820 objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
1821 self.assertEquals(objects, ['ο2', 'ο3'])
1822 objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
1823 self.assertEquals(objects, '')
1825 def test_groups(self):
1827 groups = {'σεφς':'chazapis,διογένης'}
1828 self.client.set_account_groups(**groups)
1829 groups.update(self.initial_groups)
1830 self.assertEqual(groups['σεφς'],
1831 self.client.retrieve_account_groups()['σεφς'])
1834 self.client.create_container('φάκελος')
1835 o = self.upload_random_data('φάκελος', 'ο1')
1836 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1837 chef = Pithos_Client(get_server(),
1841 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1842 'φάκελος', 'ο1', account=get_user())
1845 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1846 new_data = get_random_data()
1847 self.assert_not_raises_fault(403, chef.update_object,
1848 'φάκελος', 'ο1', StringIO(new_data),
1851 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1852 self.assertEqual(server_data[:len(o['data'])], o['data'])
1853 self.assertEqual(server_data[len(o['data']):], new_data)
1855 def test_manifestation(self):
1856 self.client.create_container('κουβάς')
1860 part = '%s%d' %(prefix, i)
1861 o = self.upload_random_data('κουβάς', part)
1864 self.client.create_container('φάκελος')
1865 manifest = '%s/%s' %('κουβάς', prefix)
1866 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1868 self.assert_object_exists('φάκελος', 'άπαντα')
1869 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1872 #wrong manifestation
1873 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1874 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1876 def test_update_from_another_object(self):
1877 self.client.create_container('κουβάς')
1878 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1879 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1880 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1881 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1884 self.client.retrieve_object('κουβάς', 'νέο'),
1885 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1887 class TestPermissions(BaseTestCase):
1889 BaseTestCase.setUp(self)
1892 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1893 groups = {'pithosdev':','.join(self.authorized)}
1894 self.client.set_account_groups(**groups)
1896 def assert_read(self, authorized=[], any=False):
1897 for token, account in OTHER_ACCOUNTS.items():
1898 cl = Pithos_Client(get_server(), token, account, get_api())
1899 if account in authorized or any:
1900 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1901 'c', 'o', account=get_user())
1903 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1904 'c', 'o', account=get_user())
1907 o = self.upload_random_data('c', 'o/also-shared')
1908 for token, account in OTHER_ACCOUNTS.items():
1909 cl = Pithos_Client(get_server(), token, account, get_api())
1910 if account in authorized or any:
1911 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1912 'c', 'o/also-shared', account=get_user())
1914 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1915 'c', 'o/also-shared', account=get_user())
1917 def assert_write(self, o_data, authorized=[], any=False):
1918 for token, account in OTHER_ACCOUNTS.items():
1919 cl = Pithos_Client(get_server(), token, account, get_api())
1920 new_data = get_random_data()
1921 if account in authorized or any:
1923 self.assert_not_raises_fault(403, cl.update_object,
1924 'c', 'o', StringIO(new_data),
1928 server_data = cl.retrieve_object('c', 'o', account=get_user())
1929 self.assertEqual(o_data, server_data[:len(o_data)])
1930 self.assertEqual(new_data, server_data[len(o_data):])
1931 o_data = server_data
1933 self.failIf(f.status == 403)
1935 self.assert_raises_fault(403, cl.update_object,
1936 'c', 'o', StringIO(new_data),
1940 o = self.upload_random_data('c', 'o/also-shared')
1942 for token, account in OTHER_ACCOUNTS.items():
1943 cl = Pithos_Client(get_server(), token, account, get_api())
1944 new_data = get_random_data()
1945 if account in authorized or any:
1947 self.assert_not_raises_fault(403, cl.update_object,
1952 server_data = cl.retrieve_object('c', o['name'], account=get_user())
1953 self.assertEqual(o_data, server_data[:len(o_data)])
1954 self.assertEqual(new_data, server_data[len(o_data):])
1955 o_data = server_data
1957 self.failIf(f.status == 403)
1959 self.assert_raises_fault(403, cl.update_object,
1964 def test_group_read(self):
1965 self.client.create_container('c')
1966 o = self.upload_random_data('c', 'o')
1967 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1968 self.assert_read(authorized=self.authorized)
1970 def test_read_many(self):
1972 self.client.create_container('c')
1973 o = self.upload_random_data('c', 'o')
1974 self.client.share_object('c', 'o', self.authorized)
1975 self.assert_read(authorized=self.authorized)
1977 def test_read_by_everyone(self):
1978 self.client.create_container('c')
1979 o = self.upload_random_data('c', 'o')
1980 self.client.share_object('c', 'o', ['*'])
1981 self.assert_read(any=True)
1983 def test_group_write(self):
1984 self.client.create_container('c')
1985 o = self.upload_random_data('c', 'o')
1986 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1987 self.assert_write(o['data'], authorized=self.authorized)
1989 def test_write_many(self):
1990 self.client.create_container('c')
1991 o = self.upload_random_data('c', 'o')
1992 self.client.share_object('c', 'o', self.authorized, read=False)
1993 self.assert_write(o['data'], authorized=self.authorized)
1995 def test_write_by_everyone(self):
1996 self.client.create_container('c')
1997 o = self.upload_random_data('c', 'o')
1998 self.client.share_object('c', 'o', ['*'], read=False)
2000 self.assert_write(o['data'], any=True)
2002 class TestPublish(BaseTestCase):
2003 def test_publish(self):
2004 self.client.create_container('c')
2005 o_data = self.upload_random_data('c', 'o')['data']
2006 self.client.publish_object('c', 'o')
2007 meta = self.client.retrieve_object_metadata('c', 'o')
2008 self.assertTrue('x-object-public' in meta)
2009 url = meta['x-object-public']
2010 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
2011 data = public_client.get(url)[2]
2012 self.assertEqual(o_data, data)
2014 class AssertUUidInvariant(object):
2015 def __init__(self, callable, *args, **kwargs):
2016 self.callable = callable
2018 self.kwargs = kwargs
2020 def __enter__(self):
2021 self.map = self.callable(*self.args, **self.kwargs)
2022 assert('x-object-uuid' in self.map)
2023 self.uuid = self.map['x-object-uuid']
2026 def __exit__(self, type, value, tb):
2027 map = self.callable(*self.args, **self.kwargs)
2028 assert('x-object-uuid' in self.map)
2029 uuid = map['x-object-uuid']
2030 assert(uuid == self.uuid)
2032 class AssertMappingInvariant(object):
2033 def __init__(self, callable, *args, **kwargs):
2034 self.callable = callable
2036 self.kwargs = kwargs
2038 def __enter__(self):
2039 self.map = self.callable(*self.args, **self.kwargs)
2042 def __exit__(self, type, value, tb):
2043 map = self.callable(*self.args, **self.kwargs)
2044 for k, v in self.map.items():
2047 #print '#', k, v, map[k]
2051 class AssertContentInvariant(object):
2052 def __init__(self, callable, *args, **kwargs):
2053 self.callable = callable
2055 self.kwargs = kwargs
2057 def __enter__(self):
2058 self.content = self.callable(*self.args, **self.kwargs)[2]
2061 def __exit__(self, type, value, tb):
2062 content = self.callable(*self.args, **self.kwargs)[2]
2063 assert self.content == content
2065 def get_content_splitted(response):
2067 return response.content.split('\n')
2069 def compute_md5_hash(data):
2073 return md5.hexdigest().lower()
2075 def compute_block_hash(data, algorithm):
2076 h = hashlib.new(algorithm)
2077 h.update(data.rstrip('\x00'))
2078 return h.hexdigest()
2080 def get_random_data(length=500):
2081 char_set = string.ascii_uppercase + string.digits
2082 return ''.join(random.choice(char_set) for x in range(length))
2085 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2086 __D = r'(?P<day>\d{2})'
2087 __D2 = r'(?P<day>[ \d]\d)'
2088 __M = r'(?P<mon>\w{3})'
2089 __Y = r'(?P<year>\d{4})'
2090 __Y2 = r'(?P<year>\d{2})'
2091 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2092 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2093 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2094 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2095 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2096 m = regex.match(date)
2101 o_names = ['kate.jpg',
2102 'kate_beckinsale.jpg',
2103 'How To Win Friends And Influence People.pdf',
2104 'moms_birthday.jpg',
2106 'Disturbed - Down With The Sickness.mp3',
2107 'army_of_darkness.avi',
2109 'photos/animals/dogs/poodle.jpg',
2110 'photos/animals/dogs/terrier.jpg',
2111 'photos/animals/cats/persian.jpg',
2112 'photos/animals/cats/siamese.jpg',
2113 'photos/plants/fern.jpg',
2114 'photos/plants/rose.jpg',
2119 if get_user() == 'test':
2122 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2125 if __name__ == "__main__":