4 # Copyright 2011 GRNET S.A. All rights reserved.
6 # Redistribution and use in source and binary forms, with or
7 # without modification, are permitted provided that the following
10 # 1. Redistributions of source code must retain the above
11 # copyright notice, this list of conditions and the following
14 # 2. Redistributions in binary form must reproduce the above
15 # copyright notice, this list of conditions and the following
16 # disclaimer in the documentation and/or other materials
17 # provided with the distribution.
19 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 # POSSIBILITY OF SUCH DAMAGE.
32 # The views and conclusions contained in the software and
33 # documentation are those of the authors and should not be
34 # interpreted as representing official policies, either expressed
35 # or implied, of GRNET S.A.
37 from lib.client import Pithos_Client, Fault
38 from lib.util import get_user, get_auth, get_server, get_api
39 from xml.dom import minidom
40 from StringIO import StringIO
52 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
53 "%A, %d-%b-%y %H:%M:%S GMT",
54 "%a, %d %b %Y %H:%M:%S GMT"]
67 class BaseTestCase(unittest.TestCase):
68 #TODO unauthorized request
70 self.client = Pithos_Client(get_server(), get_auth(), get_user(),
73 self.invalid_client = Pithos_Client(get_server(), get_auth(), 'invalid',
76 #keep track of initial account groups
77 self.initial_groups = self.client.retrieve_account_groups()
79 #keep track of initial account meta
80 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
88 'x_container_policy_quota',
89 'x_container_policy_versioning',),
97 self.return_codes = (400, 401, 403, 404, 503,)
100 #delete additionally created meta
102 for m in self.client.retrieve_account_metadata(restricted=True):
103 if m not in self.initial_meta:
105 self.client.delete_account_metadata(l)
107 #delete additionally created groups
109 for g in self.client.retrieve_account_groups():
110 if g not in self.initial_groups:
112 self.client.unset_account_groups(l)
113 self._clean_account()
115 def _clean_account(self):
116 for c in self.client.list_containers():
118 #list objects returns at most 10000 objects
119 #so repeat until there are no more objects
120 objects = self.client.list_objects(c)
124 self.client.delete_object(c, o)
125 self.client.delete_container(c)
127 def assert_status(self, status, codes):
128 l = [elem for elem in self.return_codes]
129 if type(codes) == types.ListType:
133 self.assertTrue(status in l)
135 def assert_extended(self, data, format, type, size=10000):
137 self._assert_xml(data, type, size)
138 elif format == 'json':
139 self._assert_json(data, type, size)
141 def _assert_json(self, data, type, size):
142 convert = lambda s: s.lower()
143 info = [convert(elem) for elem in self.extended[type]]
144 self.assertTrue(len(data) <= size)
147 if 'subdir' in i.keys():
149 self.assertTrue(item in i.keys())
151 def _assert_xml(self, data, type, size):
152 convert = lambda s: s.lower()
153 info = [convert(elem) for elem in self.extended[type]]
155 info.remove('content_encoding')
159 entities = xml.getElementsByTagName(type)
160 self.assertTrue(len(entities) <= size)
163 self.assertTrue(e.getElementsByTagName(item))
165 def assert_raises_fault(self, status, callableObj, *args, **kwargs):
167 asserts that a Fault with a specific status is raised
168 when callableObj is called with the specific arguments
171 r = callableObj(*args, **kwargs)
172 self.fail('Should never reach here')
174 if type(status) == types.ListType:
175 self.failUnless(f.status in status)
177 self.failUnless(f.status == status)
179 def assert_not_raises_fault(self, status, callableObj, *args, **kwargs):
181 asserts that a Fault with a specific status is not raised
182 when callableObj is called with the specific arguments
185 r = callableObj(*args, **kwargs)
187 self.failIfEqual(f.status, status)
189 def assert_container_exists(self, container):
191 asserts the existence of a container
194 self.client.retrieve_container_metadata(container)
196 self.failIf(f.status == 404)
198 def assert_container_not_exists(self, container):
200 asserts there is no such a container
202 self.assert_raises_fault(404, self.client.retrieve_container_metadata,
205 def assert_object_exists(self, container, object):
207 asserts the existence of an object
210 self.client.retrieve_object_metadata(container, object)
212 self.failIf(f.status == 404)
214 def assert_object_not_exists(self, container, object):
216 asserts there is no such an object
218 self.assert_raises_fault(404, self.client.retrieve_object_metadata,
221 def assert_versionlist_structure(self, versionlist):
222 self.assertTrue(type(versionlist) == types.ListType)
223 for elem in versionlist:
224 self.assertTrue(type(elem) == types.ListType)
225 self.assertEqual(len(elem), 2)
227 def upload_random_data(self, container, name, length=1024, type=None,
229 data = get_random_data(length)
230 return self.upload_data(container, name, data, type, enc, **meta)
232 def upload_data(self, container, name, data, type=None, enc=None, etag=None,
238 obj['hash'] = compute_md5_hash(obj['data'])
241 args['etag'] = etag if etag else obj['hash']
244 guess = mimetypes.guess_type(name)
245 type = type if type else guess[0]
246 enc = enc if enc else guess[1]
249 args['content_type'] = type if type else 'plain/text'
250 args['content_encoding'] = enc if enc else None
254 path = '/%s/%s' % (container, name)
255 self.client.create_object(container, name, f=StringIO(obj['data']),
262 class AccountHead(BaseTestCase):
264 BaseTestCase.setUp(self)
265 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
266 for item in self.containers:
267 self.client.create_container(item)
270 self.client.update_account_metadata(**meta)
271 #self.updated_meta = self.initial_meta.update(meta)
273 def test_get_account_meta(self):
274 meta = self.client.retrieve_account_metadata()
276 containers = self.client.list_containers()
277 l = str(len(containers))
278 self.assertEqual(meta['x-account-container-count'], l)
281 m = self.client.retrieve_container_metadata(c)
282 size = size + int(m['x-container-bytes-used'])
283 self.assertEqual(meta['x-account-bytes-used'], str(size))
285 def test_get_account_403(self):
286 self.assert_raises_fault(403,
287 self.invalid_client.retrieve_account_metadata)
289 def test_get_account_meta_until(self):
290 t = datetime.datetime.utcnow()
291 past = t - datetime.timedelta(minutes=-15)
292 past = int(_time.mktime(past.timetuple()))
294 meta = {'premium':True}
295 self.client.update_account_metadata(**meta)
296 meta = self.client.retrieve_account_metadata(restricted=True,
298 self.assertTrue('premium' not in meta)
300 meta = self.client.retrieve_account_metadata(restricted=True)
301 self.assertTrue('premium' in meta)
303 def test_get_account_meta_until_invalid_date(self):
304 meta = {'premium':True}
305 self.client.update_account_metadata(**meta)
306 meta = self.client.retrieve_account_metadata(restricted=True,
308 self.assertTrue('premium' in meta)
310 class AccountGet(BaseTestCase):
312 BaseTestCase.setUp(self)
313 #create some containers
314 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
315 for item in self.containers:
316 self.client.create_container(item)
320 containers = self.client.list_containers()
321 self.assertEquals(self.containers, containers)
323 def test_list_403(self):
324 self.assert_raises_fault(403, self.invalid_client.list_containers)
326 def test_list_with_limit(self):
328 containers = self.client.list_containers(limit=limit)
329 self.assertEquals(len(containers), limit)
330 self.assertEquals(self.containers[:2], containers)
332 def test_list_with_marker(self):
335 containers = self.client.list_containers(limit=l, marker=m)
336 i = self.containers.index(m) + 1
337 self.assertEquals(self.containers[i:(i+l)], containers)
340 containers = self.client.list_containers(limit=l, marker=m)
341 i = self.containers.index(m) + 1
342 self.assertEquals(self.containers[i:(i+l)], containers)
344 def test_list_json_with_marker(self):
347 containers = self.client.list_containers(limit=l, marker=m, format='json')
348 self.assert_extended(containers, 'json', 'container', l)
349 self.assertEqual(containers[0]['name'], 'kiwis')
350 self.assertEqual(containers[1]['name'], 'oranges')
352 def test_list_xml_with_marker(self):
355 xml = self.client.list_containers(limit=l, marker=m, format='xml')
356 self.assert_extended(xml, 'xml', 'container', l)
357 nodes = xml.getElementsByTagName('name')
358 self.assertEqual(len(nodes), 1)
359 self.assertEqual(nodes[0].childNodes[0].data, 'pears')
361 def test_if_modified_since(self):
362 t = datetime.datetime.utcnow()
363 t2 = t - datetime.timedelta(minutes=10)
366 self.client.create_container('dummy')
368 for f in DATE_FORMATS:
369 past = t2.strftime(f)
371 c = self.client.list_containers(if_modified_since=past)
372 self.assertEqual(len(c), len(self.containers) + 1)
374 self.failIf(f.status == 304) #fail if not modified
376 def test_if_modified_since_invalid_date(self):
377 c = self.client.list_containers(if_modified_since='')
378 self.assertEqual(len(c), len(self.containers))
380 def test_if_not_modified_since(self):
381 now = datetime.datetime.utcnow()
382 since = now + datetime.timedelta(1)
384 for f in DATE_FORMATS:
385 args = {'if_modified_since':'%s' %since.strftime(f)}
388 self.assert_raises_fault(304, self.client.list_containers, **args)
390 def test_if_unmodified_since(self):
391 now = datetime.datetime.utcnow()
392 since = now + datetime.timedelta(1)
394 for f in DATE_FORMATS:
395 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
398 self.assertEqual(self.containers, c)
400 def test_if_unmodified_since_precondition_failed(self):
401 t = datetime.datetime.utcnow()
402 t2 = t - datetime.timedelta(minutes=10)
405 self.client.create_container('dummy')
407 for f in DATE_FORMATS:
408 past = t2.strftime(f)
410 args = {'if_unmodified_since':'%s' %past}
412 #assert precondition failed
413 self.assert_raises_fault(412, self.client.list_containers, **args)
415 class AccountPost(BaseTestCase):
417 BaseTestCase.setUp(self)
418 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
419 for item in self.containers:
420 self.client.create_container(item)
423 self.client.update_account_metadata(**meta)
424 #self.updated_meta = self.initial_meta.update(meta)
426 def test_update_meta(self):
427 with AssertMappingInvariant(self.client.retrieve_account_groups):
428 meta = {'test':'test', 'tost':'tost'}
429 self.client.update_account_metadata(**meta)
431 meta.update(self.initial_meta)
432 self.assertEqual(meta,
433 self.client.retrieve_account_metadata(
436 def test_invalid_account_update_meta(self):
437 meta = {'test':'test', 'tost':'tost'}
438 self.assert_raises_fault(403,
439 self.invalid_client.update_account_metadata,
442 def test_reset_meta(self):
443 with AssertMappingInvariant(self.client.retrieve_account_groups):
444 meta = {'test':'test', 'tost':'tost'}
445 self.client.update_account_metadata(**meta)
447 meta = {'test':'test33'}
448 self.client.reset_account_metadata(**meta)
450 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
452 def test_delete_meta(self):
453 with AssertMappingInvariant(self.client.retrieve_account_groups):
454 meta = {'test':'test', 'tost':'tost'}
455 self.client.update_account_metadata(**meta)
457 self.client.delete_account_metadata(meta.keys())
459 account_meta = self.client.retrieve_account_metadata(restricted=True)
461 self.assertTrue(m not in account_meta.keys())
463 def test_set_account_groups(self):
464 with AssertMappingInvariant(self.client.retrieve_account_metadata):
465 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
466 self.client.set_account_groups(**groups)
468 self.assertEqual(set(groups['pithosdev']),
469 set(self.client.retrieve_account_groups()['pithosdev']))
471 more_groups = {'clientsdev':'pkanavos,mvasilak'}
472 self.client.set_account_groups(**more_groups)
474 groups.update(more_groups)
475 self.assertEqual(set(groups['clientsdev']),
476 set(self.client.retrieve_account_groups()['clientsdev']))
478 def test_reset_account_groups(self):
479 with AssertMappingInvariant(self.client.retrieve_account_metadata):
480 groups = {'pithosdev':'verigak,gtsouk,chazapis',
481 'clientsdev':'pkanavos,mvasilak'}
482 self.client.set_account_groups(**groups)
484 self.assertEqual(set(groups['pithosdev'].split(',')),
485 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
486 self.assertEqual(set(groups['clientsdev'].split(',')),
487 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
489 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
490 self.client.reset_account_groups(**groups)
492 self.assertEqual(set(groups['pithosdev'].split(',')),
493 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
495 def test_delete_account_groups(self):
496 with AssertMappingInvariant(self.client.retrieve_account_metadata):
497 groups = {'pithosdev':'verigak,gtsouk,chazapis',
498 'clientsdev':'pkanavos,mvasilak'}
499 self.client.set_account_groups(**groups)
501 self.client.unset_account_groups(groups.keys())
503 self.assertEqual({}, self.client.retrieve_account_groups())
505 class ContainerHead(BaseTestCase):
507 BaseTestCase.setUp(self)
508 self.container = 'apples'
509 self.client.create_container(self.container)
511 def test_get_meta(self):
512 meta = {'trash':'true'}
513 t1 = datetime.datetime.utcnow()
514 o = self.upload_random_data(self.container, o_names[0], **meta)
516 headers = self.client.retrieve_container_metadata(self.container)
517 self.assertEqual(headers['x-container-object-count'], '1')
518 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
519 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
521 threashold = datetime.timedelta(seconds=1)
522 self.assertTrue(delta < threashold)
523 self.assertTrue(headers['x-container-object-meta'])
524 self.assertTrue('Trash' in headers['x-container-object-meta'])
526 class ContainerGet(BaseTestCase):
528 BaseTestCase.setUp(self)
529 self.container = ['pears', 'apples']
530 for c in self.container:
531 self.client.create_container(c)
533 for o in o_names[:8]:
534 self.obj.append(self.upload_random_data(self.container[0], o))
535 for o in o_names[8:]:
536 self.obj.append(self.upload_random_data(self.container[1], o))
538 def test_list_objects(self):
539 objects = self.client.list_objects(self.container[0])
540 l = [elem['name'] for elem in self.obj[:8]]
542 self.assertEqual(objects, l)
544 def test_list_objects_containing_slash(self):
545 self.client.create_container('test')
546 self.upload_random_data('test', '/objectname')
548 objects = self.client.list_objects('test')
549 self.assertEqual(objects, ['/objectname'])
551 objects = self.client.list_objects('test', format='json')
552 self.assertEqual(objects[0]['name'], '/objectname')
554 objects = self.client.list_objects('test', format='xml')
555 self.assert_extended(objects, 'xml', 'object')
556 node_name = objects.getElementsByTagName('name')[0]
557 self.assertEqual(node_name.firstChild.data, '/objectname')
559 def test_list_objects_with_limit_marker(self):
560 objects = self.client.list_objects(self.container[0], limit=2)
561 l = [elem['name'] for elem in self.obj[:8]]
563 self.assertEqual(objects, l[:2])
565 markers = ['How To Win Friends And Influence People.pdf',
569 objects = self.client.list_objects(self.container[0], limit=limit,
571 l = [elem['name'] for elem in self.obj[:8]]
573 start = l.index(m) + 1
575 end = len(l) >= end and end or len(l)
576 self.assertEqual(objects, l[start:end])
579 def _test_list_limit_exceeds(self):
580 self.client.create_container('pithos')
582 for i in range(10001):
583 self.client.create_zero_length_object('pithos', i)
585 self.assertEqual(10000, len(self.client.list_objects('pithos')))
587 def test_list_empty_params(self):
588 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
590 objects = objects.strip().split('\n')
591 self.assertEqual(objects,
592 self.client.list_objects(self.container[0]))
594 def test_list_pseudo_hierarchical_folders(self):
595 objects = self.client.list_objects(self.container[1], prefix='photos',
597 self.assertEquals(['photos/animals/', 'photos/me.jpg',
598 'photos/plants/'], objects)
600 objects = self.client.list_objects(self.container[1],
601 prefix='photos/animals',
603 l = ['photos/animals/cats/', 'photos/animals/dogs/']
604 self.assertEquals(l, objects)
606 objects = self.client.list_objects(self.container[1], path='photos')
607 self.assertEquals(['photos/me.jpg'], objects)
609 def test_extended_list_json(self):
610 objects = self.client.list_objects(self.container[1], format='json',
611 limit=2, prefix='photos/animals',
613 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
614 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
616 def test_extended_list_xml(self):
617 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
618 prefix='photos', delimiter='/')
619 self.assert_extended(xml, 'xml', 'object', size=4)
620 dirs = xml.getElementsByTagName('subdir')
621 self.assertEqual(len(dirs), 2)
622 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
623 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
625 objects = xml.getElementsByTagName('name')
626 self.assertEqual(len(objects), 1)
627 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
629 def test_list_meta_double_matching(self):
630 meta = {'quality':'aaa', 'stock':'true'}
631 self.client.update_object_metadata(self.container[0],
632 self.obj[0]['name'], **meta)
633 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
634 self.assertEqual(len(obj), 1)
635 self.assertTrue(obj, self.obj[0]['name'])
637 def test_list_using_meta(self):
638 meta = {'quality':'aaa'}
639 for o in self.obj[:2]:
640 self.client.update_object_metadata(self.container[0], o['name'],
642 meta = {'stock':'true'}
643 for o in self.obj[3:5]:
644 self.client.update_object_metadata(self.container[0], o['name'],
647 obj = self.client.list_objects(self.container[0], meta='Quality')
648 self.assertEqual(len(obj), 2)
649 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
651 # test case insensitive
652 obj = self.client.list_objects(self.container[0], meta='quality')
653 self.assertEqual(len(obj), 2)
654 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
656 # test multiple matches
657 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
658 self.assertEqual(len(obj), 4)
659 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
661 # test non 1-1 multiple match
662 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
663 self.assertEqual(len(obj), 2)
664 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
666 def test_if_modified_since(self):
667 t = datetime.datetime.utcnow()
668 t2 = t - datetime.timedelta(minutes=10)
671 self.upload_random_data(self.container[0], o_names[0])
673 for f in DATE_FORMATS:
674 past = t2.strftime(f)
676 o = self.client.list_objects(self.container[0],
677 if_modified_since=past)
679 self.client.list_objects(self.container[0]))
681 self.failIf(f.status == 304) #fail if not modified
683 def test_if_modified_since_invalid_date(self):
684 headers = {'if-modified-since':''}
685 o = self.client.list_objects(self.container[0], if_modified_since='')
686 self.assertEqual(o, self.client.list_objects(self.container[0]))
688 def test_if_not_modified_since(self):
689 now = datetime.datetime.utcnow()
690 since = now + datetime.timedelta(1)
692 for f in DATE_FORMATS:
693 args = {'if_modified_since':'%s' %since.strftime(f)}
696 self.assert_raises_fault(304, self.client.list_objects,
697 self.container[0], **args)
699 def test_if_unmodified_since(self):
700 now = datetime.datetime.utcnow()
701 since = now + datetime.timedelta(1)
703 for f in DATE_FORMATS:
704 obj = self.client.list_objects(self.container[0],
705 if_unmodified_since=since.strftime(f))
708 self.assertEqual(obj, self.client.list_objects(self.container[0]))
710 def test_if_unmodified_since_precondition_failed(self):
711 t = datetime.datetime.utcnow()
712 t2 = t - datetime.timedelta(minutes=10)
715 self.client.create_container('dummy')
717 for f in DATE_FORMATS:
718 past = t2.strftime(f)
720 args = {'if_unmodified_since':'%s' %past}
722 #assert precondition failed
723 self.assert_raises_fault(412, self.client.list_objects,
724 self.container[0], **args)
726 class ContainerPut(BaseTestCase):
728 BaseTestCase.setUp(self)
729 self.containers = ['c1', 'c2']
731 def test_create(self):
732 self.client.create_container(self.containers[0])
733 containers = self.client.list_containers()
734 self.assertTrue(self.containers[0] in containers)
735 self.assert_container_exists(self.containers[0])
737 def test_create_twice(self):
738 self.client.create_container(self.containers[0])
739 self.assertTrue(not self.client.create_container(self.containers[0]))
741 def test_quota(self):
742 self.client.create_container(self.containers[0])
744 policy = {'quota':100}
745 self.client.set_container_policies('c1', **policy)
747 meta = self.client.retrieve_container_metadata('c1')
748 self.assertTrue('x-container-policy-quota' in meta)
749 self.assertEqual(meta['x-container-policy-quota'], '100')
752 kwargs = {'length':101}
753 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
757 self.client.set_container_policies('c1', **policy)
759 class ContainerPost(BaseTestCase):
761 BaseTestCase.setUp(self)
762 self.container = 'apples'
763 self.client.create_container(self.container)
765 def test_update_meta(self):
766 meta = {'test':'test33',
768 self.client.update_container_metadata(self.container, **meta)
769 headers = self.client.retrieve_container_metadata(self.container)
770 for k,v in meta.items():
771 k = 'x-container-meta-%s' % k
772 self.assertTrue(headers[k])
773 self.assertEqual(headers[k], v)
775 class ContainerDelete(BaseTestCase):
777 BaseTestCase.setUp(self)
778 self.containers = ['c1', 'c2']
779 for c in self.containers:
780 self.client.create_container(c)
782 def test_delete(self):
783 status = self.client.delete_container(self.containers[0])[0]
784 self.assertEqual(status, 204)
786 def test_delete_non_empty(self):
787 self.upload_random_data(self.containers[1], o_names[0])
788 self.assert_raises_fault(409, self.client.delete_container,
791 def test_delete_invalid(self):
792 self.assert_raises_fault(404, self.client.delete_container, 'c3')
794 class ObjectGet(BaseTestCase):
796 BaseTestCase.setUp(self)
797 self.containers = ['c1', 'c2']
798 #create some containers
799 for c in self.containers:
800 self.client.create_container(c)
803 names = ('obj1', 'obj2')
806 self.objects.append(self.upload_random_data(self.containers[1], n))
808 def test_versions(self):
809 c = self.containers[1]
811 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
812 self.assert_versionlist_structure(b)
815 meta = {'quality':'AAA', 'stock':True}
816 self.client.update_object_metadata(c, o['name'], **meta)
818 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
819 self.assert_versionlist_structure(a)
820 self.assertEqual(len(b)+1, len(a))
821 self.assertEqual(b, a[:-1])
823 #get exact previous version metadata
825 v_meta = self.client.retrieve_object_metadata(c, o['name'],
828 for k in meta.keys():
829 self.assertTrue(k not in v_meta)
832 data = get_random_data()
833 self.client.update_object(c, o['name'], StringIO(data))
835 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
836 self.assert_versionlist_structure(aa)
837 self.assertEqual(len(a)+1, len(aa))
838 self.assertEqual(a, aa[:-1])
840 #get exact previous version
842 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
843 self.assertEqual(o['data'], v_data)
844 self.assertEqual(self.client.retrieve_object(c, o['name']),
845 '%s%s' %(v_data, data))
849 o = self.client.retrieve_object(self.containers[1],
850 self.objects[0]['name'],
851 self.objects[0]['meta'])
852 self.assertEqual(o, self.objects[0]['data'])
854 def test_objects_with_trailing_spaces(self):
855 self.client.create_container('test')
857 self.upload_random_data('test', 'a')
858 #look for 'a ' object
859 self.assert_raises_fault(404, self.client.retrieve_object,
863 self.client.delete_object('test', 'a')
864 self.assert_raises_fault(404, self.client.retrieve_object,
868 self.upload_random_data('test', 'a ')
870 self.assert_raises_fault(404, self.client.retrieve_object,
873 def test_get_invalid(self):
874 self.assert_raises_fault(404, self.client.retrieve_object,
875 self.containers[0], self.objects[0]['name'])
877 def test_get_partial(self):
878 #perform get with range
879 status, headers, data = self.client.request_object(self.containers[1],
880 self.objects[0]['name'],
883 #assert successful partial content
884 self.assertEqual(status, 206)
887 self.assertEqual(headers['content-type'],
888 self.objects[0]['meta']['content_type'])
890 #assert content length
891 self.assertEqual(int(headers['content-length']), 500)
894 self.assertEqual(self.objects[0]['data'][:500], data)
896 def test_get_final_500(self):
897 #perform get with range
898 headers = {'range':'bytes=-500'}
899 status, headers, data = self.client.request_object(self.containers[1],
900 self.objects[0]['name'],
903 #assert successful partial content
904 self.assertEqual(status, 206)
907 self.assertEqual(headers['content-type'],
908 self.objects[0]['meta']['content_type'])
910 #assert content length
911 self.assertEqual(int(headers['content-length']), 500)
914 self.assertTrue(self.objects[0]['data'][-500:], data)
916 def test_get_rest(self):
917 #perform get with range
918 offset = len(self.objects[0]['data']) - 500
919 status, headers, data = self.client.request_object(self.containers[1],
920 self.objects[0]['name'],
921 range='bytes=%s-' %offset)
923 #assert successful partial content
924 self.assertEqual(status, 206)
927 self.assertEqual(headers['content-type'],
928 self.objects[0]['meta']['content_type'])
930 #assert content length
931 self.assertEqual(int(headers['content-length']), 500)
934 self.assertTrue(self.objects[0]['data'][-500:], data)
936 def test_get_range_not_satisfiable(self):
937 #perform get with range
938 offset = len(self.objects[0]['data']) + 1
940 #assert range not satisfiable
941 self.assert_raises_fault(416, self.client.retrieve_object,
942 self.containers[1], self.objects[0]['name'],
943 range='bytes=0-%s' %offset)
945 def test_multiple_range(self):
946 #perform get with multiple range
947 ranges = ['0-499', '-500', '1000-']
948 bytes = 'bytes=%s' % ','.join(ranges)
949 status, headers, data = self.client.request_object(self.containers[1],
950 self.objects[0]['name'],
953 # assert partial content
954 self.assertEqual(status, 206)
956 # assert Content-Type of the reply will be multipart/byteranges
957 self.assertTrue(headers['content-type'])
958 content_type_parts = headers['content-type'].split()
959 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
961 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
962 cparts = data.split(boundary)[1:-1]
964 # assert content parts are exactly 2
965 self.assertEqual(len(cparts), len(ranges))
967 # for each content part assert headers
970 content = cpart.split('\r\n')
971 headers = content[1:3]
972 content_range = headers[0].split(': ')
973 self.assertEqual(content_range[0], 'Content-Range')
975 r = ranges[i].split('-')
976 if not r[0] and not r[1]:
979 start = len(self.objects[0]['data']) - int(r[1])
980 end = len(self.objects[0]['data'])
983 end = len(self.objects[0]['data'])
987 fdata = self.objects[0]['data'][start:end]
988 sdata = '\r\n'.join(content[4:-1])
989 self.assertEqual(len(fdata), len(sdata))
990 self.assertEquals(fdata, sdata)
993 def test_multiple_range_not_satisfiable(self):
994 #perform get with multiple range
995 out_of_range = len(self.objects[0]['data']) + 1
996 ranges = ['0-499', '-500', '%d-' %out_of_range]
997 bytes = 'bytes=%s' % ','.join(ranges)
999 # assert partial content
1000 self.assert_raises_fault(416, self.client.retrieve_object,
1002 self.objects[0]['name'], range=bytes)
1004 def test_get_with_if_match(self):
1005 #perform get with If-Match
1006 etag = self.objects[0]['hash']
1007 status, headers, data = self.client.request_object(self.containers[1],
1008 self.objects[0]['name'],
1011 self.assertEqual(status, 200)
1013 #assert content-type
1014 self.assertEqual(headers['content-type'],
1015 self.objects[0]['meta']['content_type'])
1017 #assert response content
1018 self.assertEqual(self.objects[0]['data'], data)
1020 def test_get_with_if_match_star(self):
1021 #perform get with If-Match *
1022 headers = {'if-match':'*'}
1023 status, headers, data = self.client.request_object(self.containers[1],
1024 self.objects[0]['name'],
1027 self.assertEqual(status, 200)
1029 #assert content-type
1030 self.assertEqual(headers['content-type'],
1031 self.objects[0]['meta']['content_type'])
1033 #assert response content
1034 self.assertEqual(self.objects[0]['data'], data)
1036 def test_get_with_multiple_if_match(self):
1037 #perform get with If-Match
1038 etags = [i['hash'] for i in self.objects if i]
1039 etags = ','.join('"%s"' % etag for etag in etags)
1040 status, headers, data = self.client.request_object(self.containers[1],
1041 self.objects[0]['name'],
1044 self.assertEqual(status, 200)
1046 #assert content-type
1047 self.assertEqual(headers['content-type'],
1048 self.objects[0]['meta']['content_type'])
1050 #assert content-type
1051 self.assertEqual(headers['content-type'],
1052 self.objects[0]['meta']['content_type'])
1054 #assert response content
1055 self.assertEqual(self.objects[0]['data'], data)
1057 def test_if_match_precondition_failed(self):
1058 #assert precondition failed
1059 self.assert_raises_fault(412, self.client.retrieve_object,
1061 self.objects[0]['name'], if_match='123')
1063 def test_if_none_match(self):
1064 #perform get with If-None-Match
1065 status, headers, data = self.client.request_object(self.containers[1],
1066 self.objects[0]['name'],
1067 if_none_match='123')
1070 self.assertEqual(status, 200)
1072 #assert content-type
1073 self.assertEqual(headers['content_type'],
1074 self.objects[0]['meta']['content_type'])
1076 def test_if_none_match(self):
1077 #perform get with If-None-Match * and assert not modified
1078 self.assert_raises_fault(304, self.client.retrieve_object,
1080 self.objects[0]['name'],
1083 def test_if_none_match_not_modified(self):
1084 #perform get with If-None-Match and assert not modified
1085 self.assert_raises_fault(304, self.client.retrieve_object,
1087 self.objects[0]['name'],
1088 if_none_match=self.objects[0]['hash'])
1090 meta = self.client.retrieve_object_metadata(self.containers[1],
1091 self.objects[0]['name'])
1092 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1094 def test_if_modified_since(self):
1095 t = datetime.datetime.utcnow()
1096 t2 = t - datetime.timedelta(minutes=10)
1099 self.upload_data(self.containers[1],
1100 self.objects[0]['name'],
1101 self.objects[0]['data'][:200])
1103 for f in DATE_FORMATS:
1104 past = t2.strftime(f)
1106 headers = {'if-modified-since':'%s' %past}
1108 o = self.client.retrieve_object(self.containers[1],
1109 self.objects[0]['name'],
1110 if_modified_since=past)
1112 self.client.retrieve_object(self.containers[1],
1113 self.objects[0]['name']))
1115 self.failIf(f.status == 304)
1117 def test_if_modified_since_invalid_date(self):
1118 o = self.client.retrieve_object(self.containers[1],
1119 self.objects[0]['name'],
1120 if_modified_since='')
1121 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1122 self.objects[0]['name']))
1124 def test_if_not_modified_since(self):
1125 now = datetime.datetime.utcnow()
1126 since = now + datetime.timedelta(1)
1128 for f in DATE_FORMATS:
1129 #assert not modified
1130 self.assert_raises_fault(304, self.client.retrieve_object,
1131 self.containers[1], self.objects[0]['name'],
1132 if_modified_since=since.strftime(f))
1134 def test_if_unmodified_since(self):
1135 now = datetime.datetime.utcnow()
1136 since = now + datetime.timedelta(1)
1138 for f in DATE_FORMATS:
1139 t = since.strftime(f)
1140 status, headers, data = self.client.request_object(self.containers[1],
1141 self.objects[0]['name'],
1142 if_unmodified_since=t)
1144 self.assertEqual(status, 200)
1145 self.assertEqual(self.objects[0]['data'], data)
1147 #assert content-type
1148 self.assertEqual(headers['content-type'],
1149 self.objects[0]['meta']['content_type'])
1151 def test_if_unmodified_since_precondition_failed(self):
1152 t = datetime.datetime.utcnow()
1153 t2 = t - datetime.timedelta(minutes=10)
1156 self.upload_data(self.containers[1],
1157 self.objects[0]['name'],
1158 self.objects[0]['data'][:200])
1160 for f in DATE_FORMATS:
1161 past = t2.strftime(f)
1162 #assert precondition failed
1163 self.assert_raises_fault(412, self.client.retrieve_object,
1164 self.containers[1], self.objects[0]['name'],
1165 if_unmodified_since=past)
1167 def test_hashes(self):
1170 o = self.upload_random_data(self.containers[1], fname, l)
1172 body = self.client.retrieve_object(self.containers[1], fname,
1174 hashes = body['hashes']
1175 block_size = body['block_size']
1176 block_hash = body['block_hash']
1177 block_num = l/block_size == 0 and l/block_size or l/block_size + 1
1178 self.assertTrue(len(hashes), block_num)
1181 start = i * block_size
1182 end = (i + 1) * block_size
1183 hash = compute_block_hash(o['data'][start:end], block_hash)
1184 self.assertEqual(h, hash)
1187 class ObjectPut(BaseTestCase):
1189 BaseTestCase.setUp(self)
1190 self.container = 'c1'
1191 self.client.create_container(self.container)
1193 def test_upload(self):
1195 meta = {'test':'test1'}
1196 o = self.upload_random_data(self.container, name, **meta)
1198 headers = self.client.retrieve_object_metadata(self.container,
1201 self.assertTrue('test' in headers.keys())
1202 self.assertEqual(headers['test'], meta['test'])
1204 #assert uploaded content
1205 status, h, data = self.client.request_object(self.container, name)
1206 self.assertEqual(len(o['data']), int(h['content-length']))
1207 self.assertEqual(o['data'], data)
1209 #assert content-type
1210 self.assertEqual(h['content-type'], o['meta']['content_type'])
1212 def _test_maximum_upload_size_exceeds(self):
1214 meta = {'test':'test1'}
1216 length=1024*1024*100
1217 self.assert_raises_fault(400, self.upload_random_data, self.container,
1218 name, length, **meta)
1220 def test_upload_with_name_containing_slash(self):
1221 name = '/%s' % o_names[0]
1222 meta = {'test':'test1'}
1223 o = self.upload_random_data(self.container, name, **meta)
1225 self.assertEqual(o['data'],
1226 self.client.retrieve_object(self.container, name))
1228 self.assertTrue(name in self.client.list_objects(self.container))
1230 def test_create_directory_marker(self):
1231 self.client.create_directory_marker(self.container, 'foo')
1232 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1233 self.assertEqual(meta['content-length'], '0')
1234 self.assertEqual(meta['content-type'], 'application/directory')
1236 def test_upload_unprocessable_entity(self):
1237 meta={'etag':'123', 'test':'test1'}
1239 #assert unprocessable entity
1240 self.assert_raises_fault(422, self.upload_random_data, self.container,
1243 def test_chunked_transfer(self):
1244 data = get_random_data()
1246 self.client.create_object_using_chunks(self.container, objname,
1249 uploaded_data = self.client.retrieve_object(self.container, objname)
1250 self.assertEqual(data, uploaded_data)
1252 def test_manifestation(self):
1253 prefix = 'myobject/'
1256 part = '%s%d' %(prefix, i)
1257 o = self.upload_random_data(self.container, part)
1260 manifest = '%s/%s' %(self.container, prefix)
1261 self.client.create_manifestation(self.container, 'large-object', manifest)
1263 self.assert_object_exists(self.container, 'large-object')
1264 self.assertEqual(data, self.client.retrieve_object(self.container,
1267 #wrong manifestation
1268 self.client.create_manifestation(self.container, 'large-object',
1269 '%s/invalid' % self.container)
1270 self.assertEqual('', self.client.retrieve_object(self.container,
1273 def test_create_zero_length_object(self):
1276 zero = self.client.create_zero_length_object(c, o)
1277 zero_meta = self.client.retrieve_object_metadata(c, o)
1278 zero_hash = self.client.retrieve_object_hashmap(c, o)
1279 zero_data = self.client.retrieve_object(c, o)
1281 self.assertEqual(int(zero_meta['content-length']), 0)
1282 self.assertEqual(zero_hash, [])
1283 self.assertEqual(zero_data, '')
1285 def test_create_object_by_hashmap(self):
1288 self.upload_random_data(c, o)
1289 hashmap = self.client.retrieve_object(c, o, format='json')
1291 self.client.create_object_by_hashmap(c, o2, hashmap)
1292 self.assertEqual(self.client.retrieve_object(c, o),
1293 self.client.retrieve_object(c, o))
1295 class ObjectCopy(BaseTestCase):
1297 BaseTestCase.setUp(self)
1298 self.containers = ['c1', 'c2']
1299 for c in self.containers:
1300 self.client.create_container(c)
1301 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1303 def test_copy(self):
1304 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1305 self.containers[0], self.obj['name']):
1307 meta = {'test':'testcopy'}
1308 status = self.client.copy_object(self.containers[0],
1314 #assert copy success
1315 self.assertEqual(status, 201)
1317 #assert access the new object
1318 headers = self.client.retrieve_object_metadata(self.containers[0],
1320 self.assertTrue('x-object-meta-test' in headers.keys())
1321 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1323 #assert etag is the same
1324 self.assertEqual(headers['etag'], self.obj['hash'])
1326 #assert src object still exists
1327 self.assert_object_exists(self.containers[0], self.obj['name'])
1329 def test_copy_from_different_container(self):
1330 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1331 self.containers[0], self.obj['name']):
1332 meta = {'test':'testcopy'}
1333 status = self.client.copy_object(self.containers[0],
1338 self.assertEqual(status, 201)
1340 # assert updated metadata
1341 meta = self.client.retrieve_object_metadata(self.containers[1],
1344 self.assertTrue('test' in meta.keys())
1345 self.assertTrue(meta['test'], 'testcopy')
1347 #assert src object still exists
1348 self.assert_object_exists(self.containers[0], self.obj['name'])
1350 def test_copy_invalid(self):
1351 #copy from invalid object
1352 meta = {'test':'testcopy'}
1353 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1354 'test.py', self.containers[1], 'testcopy', meta)
1356 #copy from invalid container
1357 meta = {'test':'testcopy'}
1358 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1359 self.obj['name'], self.containers[1],
1362 class ObjectMove(BaseTestCase):
1364 BaseTestCase.setUp(self)
1365 self.containers = ['c1', 'c2']
1366 for c in self.containers:
1367 self.client.create_container(c)
1368 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1370 def test_move(self):
1372 meta = {'test':'testcopy'}
1373 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1374 status = self.client.move_object(self.containers[0], self.obj['name'],
1375 self.containers[0], 'testcopy',
1378 #assert successful move
1379 self.assertEqual(status, 201)
1381 #assert updated metadata
1382 meta = self.client.retrieve_object_metadata(self.containers[0],
1385 self.assertTrue('test' in meta.keys())
1386 self.assertTrue(meta['test'], 'testcopy')
1388 #assert src object no more exists
1389 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1391 class ObjectPost(BaseTestCase):
1393 BaseTestCase.setUp(self)
1394 self.containers = ['c1', 'c2']
1395 for c in self.containers:
1396 self.client.create_container(c)
1399 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1401 def test_update_meta(self):
1402 #perform update metadata
1403 more = {'foo':'foo', 'bar':'bar'}
1404 status = self.client.update_object_metadata(self.containers[0],
1405 self.obj[0]['name'],
1407 #assert request accepted
1408 self.assertEqual(status, 202)
1410 #assert old metadata are still there
1411 headers = self.client.retrieve_object_metadata(self.containers[0],
1412 self.obj[0]['name'],
1414 #assert new metadata have been updated
1415 for k,v in more.items():
1416 self.assertTrue(k in headers.keys())
1417 self.assertTrue(headers[k], v)
1419 def test_update_object(self,
1422 instance_length = True,
1423 content_length = 500):
1424 l = len(self.obj[0]['data'])
1425 range = 'bytes %d-%d/%s' %(first_byte_pos,
1427 l if instance_length else '*')
1428 partial = last_byte_pos - first_byte_pos + 1
1429 length = first_byte_pos + partial
1430 data = get_random_data(partial)
1431 args = {'content_type':'application/octet-stream',
1432 'content_range':'%s' %range}
1434 args['content_length'] = content_length
1436 status = self.client.update_object(self.containers[0], self.obj[0]['name'],
1437 StringIO(data), **args)[0]
1439 if partial < 0 or (instance_length and l <= last_byte_pos):
1440 self.assertEqual(status, 202)
1442 self.assertEqual(status, 204)
1443 #check modified object
1444 content = self.client.retrieve_object(self.containers[0],
1445 self.obj[0]['name'])
1446 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1447 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1448 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1450 def test_update_object_lt_blocksize(self):
1451 self.test_update_object(10, 20, content_length=None)
1453 def test_update_object_gt_blocksize(self):
1454 o = self.upload_random_data(self.containers[0], o_names[1],
1455 length=4*1024*1024+5)
1456 c = self.containers[0]
1459 first_byte_pos = 4*1024*1024+1
1460 last_byte_pos = 4*1024*1024+4
1461 l = last_byte_pos - first_byte_pos + 1
1462 data = get_random_data(l)
1463 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1464 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1465 content = self.client.retrieve_object(c, o_name)
1466 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1467 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1468 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1470 def test_update_object_divided_by_blocksize(self):
1471 o = self.upload_random_data(self.containers[0], o_names[1],
1472 length=4*1024*1024+5)
1473 c = self.containers[0]
1476 first_byte_pos = 4*1024*1024
1477 last_byte_pos = 5*1024*1024
1478 l = last_byte_pos - first_byte_pos + 1
1479 data = get_random_data(l)
1480 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1481 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1482 content = self.client.retrieve_object(c, o_name)
1483 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1484 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1485 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1487 def test_update_object_no_content_length(self):
1488 self.test_update_object(content_length = None)
1490 def test_update_object_invalid_content_length(self):
1491 with AssertContentInvariant(self.client.retrieve_object,
1492 self.containers[0], self.obj[0]['name']):
1493 self.assert_raises_fault(400, self.test_update_object,
1494 content_length = 1000)
1496 def _test_update_object_invalid_range(self):
1497 with AssertContentInvariant(self.client.retrieve_object,
1498 self.containers[0], self.obj[0]['name']):
1499 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1501 def _test_update_object_invalid_range_and_length(self):
1502 with AssertContentInvariant(self.client.retrieve_object,
1503 self.containers[0], self.obj[0]['name']):
1504 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1507 def test_update_object_invalid_range_with_no_content_length(self):
1508 with AssertContentInvariant(self.client.retrieve_object,
1509 self.containers[0], self.obj[0]['name']):
1510 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1511 content_length = None)
1513 def test_update_object_out_of_limits(self):
1514 with AssertContentInvariant(self.client.retrieve_object,
1515 self.containers[0], self.obj[0]['name']):
1516 l = len(self.obj[0]['data'])
1517 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1519 def test_append(self):
1520 data = get_random_data(500)
1522 self.client.update_object(self.containers[0], self.obj[0]['name'],
1523 StringIO(data), content_length=500,
1524 content_type='application/octet-stream')
1526 content = self.client.retrieve_object(self.containers[0],
1527 self.obj[0]['name'])
1528 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1529 self.assertEqual(content[:-500], self.obj[0]['data'])
1531 def test_update_with_chunked_transfer(self):
1532 data = get_random_data(500)
1534 fl = len(self.obj[0]['data'])
1536 self.client.update_object_using_chunks(self.containers[0],
1537 self.obj[0]['name'],
1540 content_type='application/octet-stream')
1542 #check modified object
1543 content = self.client.retrieve_object(self.containers[0],
1544 self.obj[0]['name'])
1545 self.assertEqual(content[0:dl], data)
1546 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1548 def test_update_from_other_object(self):
1549 c = self.containers[0]
1553 source_data = self.client.retrieve_object(c, src)
1554 source_meta = self.client.retrieve_object_metadata(c, src)
1555 source_hash = self.client.retrieve_object_hashmap(c, src)
1557 #update zero length object
1558 self.client.create_zero_length_object(c, dest)
1559 source_object = '/%s/%s' % (c, src)
1560 self.client.update_from_other_source(c, dest, source_object)
1561 dest_data = self.client.retrieve_object(c, src)
1562 dest_meta = self.client.retrieve_object_metadata(c, dest)
1563 dest_hash = self.client.retrieve_object_hashmap(c, src)
1564 self.assertEqual(source_data, dest_data)
1565 self.assertEqual(source_hash, dest_hash)
1568 self.client.update_from_other_source(c, dest, source_object)
1569 content = self.client.retrieve_object(c, dest)
1570 self.assertEqual(source_data * 2, content)
1572 def test_update_range_from_other_object(self):
1573 c = self.containers[0]
1577 src = self.obj[1]['name']
1578 src_data = self.client.retrieve_object(c, src)
1580 #update zero length object
1581 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1582 source_object = '/%s/%s' % (c, src)
1583 first_byte_pos = 4*1024*1024+1
1584 last_byte_pos = 4*1024*1024+4
1585 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1586 self.client.update_from_other_source(c, dest, source_object,
1587 content_range=range)
1588 content = self.client.retrieve_object(c, dest)
1589 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1590 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1591 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1593 def test_update_hashes_from_other_object(self):
1594 c = self.containers[0]
1598 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1600 #update zero length object
1601 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1602 source_object = '/%s/%s' % (c, o_names[0])
1603 first_byte_pos = 4*1024*1024
1604 last_byte_pos = 5*1024*1024
1605 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1606 self.client.update_from_other_source(c, dest, source_object,
1607 content_range=range)
1608 content = self.client.retrieve_object(c, dest)
1609 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1610 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1611 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1614 def test_update_zero_length_object(self):
1615 c = self.containers[0]
1618 zero = self.client.create_zero_length_object(c, o)
1620 data = get_random_data()
1621 self.client.update_object(c, o, StringIO(data))
1622 self.client.create_object(c, other, StringIO(data))
1624 self.assertEqual(self.client.retrieve_object(c, o),
1625 self.client.retrieve_object(c, other))
1627 self.assertEqual(self.client.retrieve_object_hashmap(c, o),
1628 self.client.retrieve_object_hashmap(c, other))
1630 class ObjectDelete(BaseTestCase):
1632 BaseTestCase.setUp(self)
1633 self.containers = ['c1', 'c2']
1634 for c in self.containers:
1635 self.client.create_container(c)
1636 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1638 def test_delete(self):
1639 #perform delete object
1640 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1642 def test_delete_invalid(self):
1643 #assert item not found
1644 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1647 class ListSharing(BaseTestCase):
1649 BaseTestCase.setUp(self)
1651 self.client.create_container('c%s' %i)
1652 self.client.create_container('c')
1654 self.upload_random_data('c1', 'o%s' %i)
1655 accounts = OTHER_ACCOUNTS.copy()
1656 self.o1_sharing_with = accounts.popitem()
1657 self.o1_sharing = [self.o1_sharing_with[1]]
1658 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1662 l.append(accounts.popitem())
1664 def test_list_other_shared(self):
1665 self.other = Pithos_Client(get_server(),
1666 self.o1_sharing_with[0],
1667 self.o1_sharing_with[1],
1669 self.assertTrue(get_user() in self.other.list_shared_by_others())
1671 def test_list_my_shared(self):
1672 my_shared_containers = self.client.list_containers(shared=True)
1673 self.assertTrue('c1' in my_shared_containers)
1674 self.assertTrue('c2' not in my_shared_containers)
1676 my_shared_objects = self.client.list_objects('c1', shared=True)
1677 self.assertTrue('o1' in my_shared_objects)
1678 self.assertTrue('o2' not in my_shared_objects)
1680 class TestGreek(BaseTestCase):
1681 def test_create_container(self):
1682 self.client.create_container('φάκελος')
1683 self.assert_container_exists('φάκελος')
1685 self.assertTrue('φάκελος' in self.client.list_containers())
1687 def test_create_object(self):
1688 self.client.create_container('φάκελος')
1689 self.upload_random_data('φάκελος', 'αντικείμενο')
1691 self.assert_object_exists('φάκελος', 'αντικείμενο')
1692 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1694 def test_copy_object(self):
1695 src_container = 'φάκελος'
1696 src_object = 'αντικείμενο'
1697 dest_container = 'αντίγραφα'
1698 dest_object = 'ασφαλές-αντίγραφο'
1700 self.client.create_container(src_container)
1701 self.upload_random_data(src_container, src_object)
1703 self.client.create_container(dest_container)
1704 self.client.copy_object(src_container, src_object, dest_container,
1707 self.assert_object_exists(src_container, src_object)
1708 self.assert_object_exists(dest_container, dest_object)
1709 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1711 def test_move_object(self):
1712 src_container = 'φάκελος'
1713 src_object = 'αντικείμενο'
1714 dest_container = 'αντίγραφα'
1715 dest_object = 'ασφαλές-αντίγραφο'
1717 self.client.create_container(src_container)
1718 self.upload_random_data(src_container, src_object)
1720 self.client.create_container(dest_container)
1721 self.client.move_object(src_container, src_object, dest_container,
1724 self.assert_object_not_exists(src_container, src_object)
1725 self.assert_object_exists(dest_container, dest_object)
1726 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1728 def test_delete_object(self):
1729 self.client.create_container('φάκελος')
1730 self.upload_random_data('φάκελος', 'αντικείμενο')
1731 self.assert_object_exists('φάκελος', 'αντικείμενο')
1733 self.client.delete_object('φάκελος', 'αντικείμενο')
1734 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1735 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1737 def test_delete_container(self):
1738 self.client.create_container('φάκελος')
1739 self.assert_container_exists('φάκελος')
1741 self.client.delete_container('φάκελος')
1742 self.assert_container_not_exists('φάκελος')
1743 self.assertTrue('φάκελος' not in self.client.list_containers())
1745 def test_account_meta(self):
1746 meta = {'ποιότητα':'ΑΑΑ'}
1747 self.client.update_account_metadata(**meta)
1748 meta = self.client.retrieve_account_metadata(restricted=True)
1749 self.assertTrue('ποιότητα' in meta.keys())
1750 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1752 def test_container_meta(self):
1753 meta = {'ποιότητα':'ΑΑΑ'}
1754 self.client.create_container('φάκελος', **meta)
1756 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
1757 self.assertTrue('ποιότητα' in meta.keys())
1758 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1760 def test_object_meta(self):
1761 self.client.create_container('φάκελος')
1762 meta = {'ποιότητα':'ΑΑΑ'}
1763 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
1765 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
1767 self.assertTrue('ποιότητα' in meta.keys())
1768 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
1770 def test_list_meta_filtering(self):
1771 self.client.create_container('φάκελος')
1772 meta = {'ποιότητα':'ΑΑΑ'}
1773 self.upload_random_data('φάκελος', 'ο1', **meta)
1774 self.upload_random_data('φάκελος', 'ο2')
1775 self.upload_random_data('φάκελος', 'ο3')
1777 meta = {'ποσότητα':'μεγάλη'}
1778 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
1779 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
1780 self.assertTrue('ο1' in objects)
1781 self.assertTrue('ο2' in objects)
1782 self.assertTrue('ο3' not in objects)
1784 def test_groups(self):
1786 groups = {'σεφς':'chazapis,διογένης'}
1787 self.client.set_account_groups(**groups)
1788 groups.update(self.initial_groups)
1789 self.assertEqual(groups['σεφς'],
1790 self.client.retrieve_account_groups()['σεφς'])
1793 self.client.create_container('φάκελος')
1794 o = self.upload_random_data('φάκελος', 'ο1')
1795 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
1796 chef = Pithos_Client(get_server(),
1800 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
1801 'φάκελος', 'ο1', account=get_user())
1804 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
1805 new_data = get_random_data()
1806 self.assert_not_raises_fault(403, chef.update_object,
1807 'φάκελος', 'ο1', StringIO(new_data),
1810 server_data = self.client.retrieve_object('φάκελος', 'ο1')
1811 self.assertEqual(server_data[:len(o['data'])], o['data'])
1812 self.assertEqual(server_data[len(o['data']):], new_data)
1814 def test_manifestation(self):
1815 self.client.create_container('κουβάς')
1819 part = '%s%d' %(prefix, i)
1820 o = self.upload_random_data('κουβάς', part)
1823 self.client.create_container('φάκελος')
1824 manifest = '%s/%s' %('κουβάς', prefix)
1825 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
1827 self.assert_object_exists('φάκελος', 'άπαντα')
1828 self.assertEqual(data, self.client.retrieve_object('φάκελος',
1831 #wrong manifestation
1832 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
1833 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
1835 def test_update_from_another_object(self):
1836 self.client.create_container('κουβάς')
1837 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
1838 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
1839 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
1840 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
1843 self.client.retrieve_object('κουβάς', 'νέο'),
1844 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
1846 class TestPermissions(BaseTestCase):
1848 BaseTestCase.setUp(self)
1851 self.authorized = ['chazapis', 'verigak', 'gtsouk']
1852 groups = {'pithosdev':','.join(self.authorized)}
1853 self.client.set_account_groups(**groups)
1855 def assert_read(self, authorized=[], any=False):
1856 for token, account in OTHER_ACCOUNTS.items():
1857 cl = Pithos_Client(get_server(), token, account, get_api())
1858 if account in authorized or any:
1859 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1860 'c', 'o', account=get_user())
1862 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1863 'c', 'o', account=get_user())
1866 o = self.upload_random_data('c', 'o/also-shared')
1867 for token, account in OTHER_ACCOUNTS.items():
1868 cl = Pithos_Client(get_server(), token, account, get_api())
1869 if account in authorized or any:
1870 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
1871 'c', 'o/also-shared', account=get_user())
1873 self.assert_raises_fault(403, cl.retrieve_object_metadata,
1874 'c', 'o/also-shared', account=get_user())
1876 def assert_write(self, o_data, authorized=[], any=False):
1877 for token, account in OTHER_ACCOUNTS.items():
1878 cl = Pithos_Client(get_server(), token, account, get_api())
1879 new_data = get_random_data()
1880 if account in authorized or any:
1882 self.assert_not_raises_fault(403, cl.update_object,
1883 'c', 'o', StringIO(new_data),
1887 server_data = cl.retrieve_object('c', 'o', account=get_user())
1888 self.assertEqual(o_data, server_data[:len(o_data)])
1889 self.assertEqual(new_data, server_data[len(o_data):])
1890 o_data = server_data
1892 self.failIf(f.status == 403)
1894 self.assert_raises_fault(403, cl.update_object,
1895 'c', 'o', StringIO(new_data),
1899 o = self.upload_random_data('c', 'o/also-shared')
1901 for token, account in OTHER_ACCOUNTS.items():
1902 cl = Pithos_Client(get_server(), token, account, get_api())
1903 new_data = get_random_data()
1904 if account in authorized or any:
1906 self.assert_not_raises_fault(403, cl.update_object,
1911 server_data = cl.retrieve_object('c', o['name'], account=get_user())
1912 self.assertEqual(o_data, server_data[:len(o_data)])
1913 self.assertEqual(new_data, server_data[len(o_data):])
1914 o_data = server_data
1916 self.failIf(f.status == 403)
1918 self.assert_raises_fault(403, cl.update_object,
1923 def test_group_read(self):
1924 self.client.create_container('c')
1925 o = self.upload_random_data('c', 'o')
1926 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()])
1927 self.assert_read(authorized=self.authorized)
1929 def test_read_many(self):
1931 self.client.create_container('c')
1932 o = self.upload_random_data('c', 'o')
1933 self.client.share_object('c', 'o', self.authorized)
1934 self.assert_read(authorized=self.authorized)
1936 def test_read_by_everyone(self):
1937 self.client.create_container('c')
1938 o = self.upload_random_data('c', 'o')
1939 self.client.share_object('c', 'o', ['*'])
1940 self.assert_read(any=True)
1942 def test_group_write(self):
1943 self.client.create_container('c')
1944 o = self.upload_random_data('c', 'o')
1945 self.client.share_object('c', 'o', ['%s:pithosdev' % get_user()], read=False)
1946 self.assert_write(o['data'], authorized=self.authorized)
1948 def test_write_many(self):
1949 self.client.create_container('c')
1950 o = self.upload_random_data('c', 'o')
1951 self.client.share_object('c', 'o', self.authorized, read=False)
1952 self.assert_write(o['data'], authorized=self.authorized)
1954 def test_write_by_everyone(self):
1955 self.client.create_container('c')
1956 o = self.upload_random_data('c', 'o')
1957 self.client.share_object('c', 'o', ['*'], read=False)
1959 self.assert_write(o['data'], any=True)
1961 class TestPublish(BaseTestCase):
1962 def test_publish(self):
1963 self.client.create_container('c')
1964 o_data = self.upload_random_data('c', 'o')['data']
1965 self.client.publish_object('c', 'o')
1966 meta = self.client.retrieve_object_metadata('c', 'o')
1967 self.assertTrue('x-object-public' in meta)
1968 url = '/public/%s/c/o' % get_user()
1969 self.assertEqual(meta['x-object-public'], url)
1970 public_client = Pithos_Client(get_server(), get_auth(), get_user(), api='')
1971 data = public_client.get(url)[2]
1972 self.assertEqual(o_data, data)
1974 class AssertMappingInvariant(object):
1975 def __init__(self, callable, *args, **kwargs):
1976 self.callable = callable
1978 self.kwargs = kwargs
1980 def __enter__(self):
1981 self.map = self.callable(*self.args, **self.kwargs)
1984 def __exit__(self, type, value, tb):
1985 map = self.callable(*self.args, **self.kwargs)
1986 for k in self.map.keys():
1987 if is_date(self.map[k]):
1989 assert map[k] == self.map[k]
1991 class AssertContentInvariant(object):
1992 def __init__(self, callable, *args, **kwargs):
1993 self.callable = callable
1995 self.kwargs = kwargs
1997 def __enter__(self):
1998 self.content = self.callable(*self.args, **self.kwargs)[2]
2001 def __exit__(self, type, value, tb):
2002 content = self.callable(*self.args, **self.kwargs)[2]
2003 assert self.content == content
2005 def get_content_splitted(response):
2007 return response.content.split('\n')
2009 def compute_md5_hash(data):
2013 return md5.hexdigest().lower()
2015 def compute_block_hash(data, algorithm):
2016 h = hashlib.new(algorithm)
2017 h.update(data.rstrip('\x00'))
2018 return h.hexdigest()
2020 def get_random_data(length=500):
2021 char_set = string.ascii_uppercase + string.digits
2022 return ''.join(random.choice(char_set) for x in range(length))
2025 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2026 __D = r'(?P<day>\d{2})'
2027 __D2 = r'(?P<day>[ \d]\d)'
2028 __M = r'(?P<mon>\w{3})'
2029 __Y = r'(?P<year>\d{4})'
2030 __Y2 = r'(?P<year>\d{2})'
2031 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2032 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2033 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2034 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2035 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2036 m = regex.match(date)
2041 o_names = ['kate.jpg',
2042 'kate_beckinsale.jpg',
2043 'How To Win Friends And Influence People.pdf',
2044 'moms_birthday.jpg',
2046 'Disturbed - Down With The Sickness.mp3',
2047 'army_of_darkness.avi',
2049 'photos/animals/dogs/poodle.jpg',
2050 'photos/animals/dogs/terrier.jpg',
2051 'photos/animals/cats/persian.jpg',
2052 'photos/animals/cats/siamese.jpg',
2053 'photos/plants/fern.jpg',
2054 'photos/plants/rose.jpg',
2057 if __name__ == "__main__":
2058 if get_user() == 'test':
2061 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()