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.tools.lib.client import Pithos_Client, Fault
38 from pithos.tools.lib.util import get_user, get_auth, get_url
40 from xml.dom import minidom
41 from StringIO import StringIO
42 from hashlib import new as newhasher
43 from binascii import hexlify
44 from httplib import HTTPConnection, HTTPSConnection
45 from urlparse import urlparse
58 DATE_FORMATS = ["%a %b %d %H:%M:%S %Y",
59 "%A, %d-%b-%y %H:%M:%S GMT",
60 "%a, %d %b %Y %H:%M:%S GMT"]
73 class BaseTestCase(unittest.TestCase):
74 #TODO unauthorized request
76 self.client = Pithos_Client(get_url(), get_auth(), get_user())
78 self.invalid_client = Pithos_Client(get_url(), 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
380 def test_if_modified_since_invalid_date(self):
381 c = self.client.list_containers(if_modified_since='')
382 self.assertEqual(len(c), len(self.containers))
384 def test_if_not_modified_since(self):
385 now = datetime.datetime.utcnow()
386 since = now + datetime.timedelta(1)
388 for f in DATE_FORMATS:
389 args = {'if_modified_since':'%s' %since.strftime(f)}
392 self.assert_raises_fault(304, self.client.list_containers, **args)
394 def test_if_unmodified_since(self):
395 now = datetime.datetime.utcnow()
396 since = now + datetime.timedelta(1)
398 for f in DATE_FORMATS:
399 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
402 self.assertEqual(self.containers, c)
404 def test_if_unmodified_since_precondition_failed(self):
405 t = datetime.datetime.utcnow()
406 t2 = t - datetime.timedelta(minutes=10)
409 self.client.create_container('dummy')
411 for f in DATE_FORMATS:
412 past = t2.strftime(f)
414 args = {'if_unmodified_since':'%s' %past}
416 #assert precondition failed
417 self.assert_raises_fault(412, self.client.list_containers, **args)
419 class AccountPost(BaseTestCase):
421 BaseTestCase.setUp(self)
422 self.containers = ['apples', 'bananas', 'kiwis', 'oranges', 'pears']
423 for item in self.containers:
424 self.client.create_container(item)
427 self.client.update_account_metadata(**meta)
428 self.updated_meta = self.initial_meta.update(meta)
430 def test_update_meta(self):
431 with AssertMappingInvariant(self.client.retrieve_account_groups):
432 meta = {'test':'test', 'tost':'tost'}
433 self.client.update_account_metadata(**meta)
435 meta.update(self.initial_meta)
436 self.assertEqual(meta,
437 self.client.retrieve_account_metadata(
440 def test_invalid_account_update_meta(self):
441 meta = {'test':'test', 'tost':'tost'}
442 self.assert_raises_fault(403,
443 self.invalid_client.update_account_metadata,
446 def test_reset_meta(self):
447 with AssertMappingInvariant(self.client.retrieve_account_groups):
448 meta = {'test':'test', 'tost':'tost'}
449 self.client.update_account_metadata(**meta)
451 meta = {'test':'test33'}
452 self.client.reset_account_metadata(**meta)
454 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
456 def test_delete_meta(self):
457 with AssertMappingInvariant(self.client.retrieve_account_groups):
458 meta = {'test':'test', 'tost':'tost'}
459 self.client.update_account_metadata(**meta)
461 self.client.delete_account_metadata(meta.keys())
463 account_meta = self.client.retrieve_account_metadata(restricted=True)
465 self.assertTrue(m not in account_meta.keys())
467 def test_set_account_groups(self):
468 with AssertMappingInvariant(self.client.retrieve_account_metadata):
469 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
470 self.client.set_account_groups(**groups)
472 self.assertEqual(set(groups['pithosdev']),
473 set(self.client.retrieve_account_groups()['pithosdev']))
475 more_groups = {'clientsdev':'pkanavos,mvasilak'}
476 self.client.set_account_groups(**more_groups)
478 groups.update(more_groups)
479 self.assertEqual(set(groups['clientsdev']),
480 set(self.client.retrieve_account_groups()['clientsdev']))
482 def test_reset_account_groups(self):
483 with AssertMappingInvariant(self.client.retrieve_account_metadata):
484 groups = {'pithosdev':'verigak,gtsouk,chazapis',
485 'clientsdev':'pkanavos,mvasilak'}
486 self.client.set_account_groups(**groups)
488 self.assertEqual(set(groups['pithosdev'].split(',')),
489 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
490 self.assertEqual(set(groups['clientsdev'].split(',')),
491 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
493 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
494 self.client.reset_account_groups(**groups)
496 self.assertEqual(set(groups['pithosdev'].split(',')),
497 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
499 def test_delete_account_groups(self):
500 with AssertMappingInvariant(self.client.retrieve_account_metadata):
501 groups = {'pithosdev':'verigak,gtsouk,chazapis',
502 'clientsdev':'pkanavos,mvasilak'}
503 self.client.set_account_groups(**groups)
505 self.client.unset_account_groups(groups.keys())
507 self.assertEqual({}, self.client.retrieve_account_groups())
509 class ContainerHead(BaseTestCase):
511 BaseTestCase.setUp(self)
512 self.container = 'apples'
513 self.client.create_container(self.container)
515 def test_get_meta(self):
516 meta = {'trash':'true'}
517 t1 = datetime.datetime.utcnow()
518 o = self.upload_random_data(self.container, o_names[0], **meta)
520 headers = self.client.retrieve_container_metadata(self.container)
521 self.assertEqual(headers['x-container-object-count'], '1')
522 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
523 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
525 threashold = datetime.timedelta(seconds=1)
526 self.assertTrue(delta < threashold)
527 self.assertTrue(headers['x-container-object-meta'])
528 self.assertTrue('Trash' in headers['x-container-object-meta'])
530 class ContainerGet(BaseTestCase):
532 BaseTestCase.setUp(self)
533 self.container = ['pears', 'apples']
534 for c in self.container:
535 self.client.create_container(c)
537 for o in o_names[:8]:
538 self.obj.append(self.upload_random_data(self.container[0], o))
539 for o in o_names[8:]:
540 self.obj.append(self.upload_random_data(self.container[1], o))
542 def test_list_shared(self):
543 self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
544 objs = self.client.list_objects(self.container[0], shared=True)
545 self.assertEqual(objs, [self.obj[0]['name']])
547 # create child object
548 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
549 objs = self.client.list_objects(self.container[0], shared=True)
550 self.assertEqual(objs, [self.obj[0]['name']])
553 self.client.create_folder(self.container[1], 'folder')
554 self.client.share_object(self.container[1], 'folder', ('*',))
555 self.upload_random_data(self.container[1], 'folder/object')
556 objs = self.client.list_objects(self.container[1], shared=True)
557 self.assertEqual(objs, ['folder', 'folder/object'])
559 def test_list_public(self):
560 self.client.publish_object(self.container[0], self.obj[0]['name'])
561 objs = self.client.list_objects(self.container[0], public=True)
562 self.assertEqual(objs, [self.obj[0]['name']])
564 # create child object
565 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
566 objs = self.client.list_objects(self.container[0], public=True)
567 self.assertEqual(objs, [self.obj[0]['name']])
570 self.client.create_folder(self.container[1], 'folder')
571 self.client.publish_object(self.container[1], 'folder')
572 self.upload_random_data(self.container[1], 'folder/object')
573 objs = self.client.list_objects(self.container[1], public=True)
574 self.assertEqual(objs, ['folder'])
576 def test_list_shared_public(self):
577 self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
578 self.client.publish_object(self.container[0], self.obj[1]['name'])
579 objs = self.client.list_objects(self.container[0], shared=True, public=True)
580 self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
582 # create child object
583 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
584 self.upload_random_data(self.container[0], strnextling(self.obj[1]['name']))
585 objs = self.client.list_objects(self.container[0], shared=True, public=True)
586 self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
589 self.client.create_folder(self.container[1], 'folder1')
590 self.client.share_object(self.container[1], 'folder1', ('*',))
591 self.upload_random_data(self.container[1], 'folder1/object')
592 self.client.create_folder(self.container[1], 'folder2')
593 self.client.publish_object(self.container[1], 'folder2')
594 o = self.upload_random_data(self.container[1], 'folder2/object')
595 objs = self.client.list_objects(self.container[1], shared=True, public=True)
596 self.assertEqual(objs, ['folder1', 'folder1/object', 'folder2'])
598 def test_list_objects(self):
599 objects = self.client.list_objects(self.container[0])
600 l = [elem['name'] for elem in self.obj[:8]]
602 self.assertEqual(objects, l)
604 def test_list_objects_containing_slash(self):
605 self.client.create_container('test')
606 self.upload_random_data('test', '/objectname')
608 objects = self.client.list_objects('test')
609 self.assertEqual(objects, ['/objectname'])
611 objects = self.client.list_objects('test', format='json')
612 self.assertEqual(objects[0]['name'], '/objectname')
614 objects = self.client.list_objects('test', format='xml')
615 self.assert_extended(objects, 'xml', 'object')
616 node_name = objects.getElementsByTagName('name')[0]
617 self.assertEqual(node_name.firstChild.data, '/objectname')
619 def test_list_objects_with_limit_marker(self):
620 objects = self.client.list_objects(self.container[0], limit=2)
621 l = [elem['name'] for elem in self.obj[:8]]
623 self.assertEqual(objects, l[:2])
625 markers = ['How To Win Friends And Influence People.pdf',
629 objects = self.client.list_objects(self.container[0], limit=limit,
631 l = [elem['name'] for elem in self.obj[:8]]
633 start = l.index(m) + 1
635 end = end if len(l) >= end else len(l)
636 self.assertEqual(objects, l[start:end])
639 def _test_list_limit_exceeds(self):
640 self.client.create_container('pithos')
642 for i in range(10001):
643 self.client.create_zero_length_object('pithos', i)
645 self.assertEqual(10000, len(self.client.list_objects('pithos')))
647 def test_list_empty_params(self):
648 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
650 objects = objects.strip().split('\n')
651 self.assertEqual(objects,
652 self.client.list_objects(self.container[0]))
654 def test_list_pseudo_hierarchical_folders(self):
655 objects = self.client.list_objects(self.container[1], prefix='photos',
657 self.assertEquals(['photos/animals/', 'photos/me.jpg',
658 'photos/plants/'], objects)
660 objects = self.client.list_objects(self.container[1],
661 prefix='photos/animals',
663 l = ['photos/animals/cats/', 'photos/animals/dogs/']
664 self.assertEquals(l, objects)
666 objects = self.client.list_objects(self.container[1], path='photos')
667 self.assertEquals(['photos/me.jpg'], objects)
669 def test_extended_list_json(self):
670 objects = self.client.list_objects(self.container[1], format='json',
671 limit=2, prefix='photos/animals',
673 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
674 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
676 def test_extended_list_xml(self):
677 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
678 prefix='photos', delimiter='/')
679 self.assert_extended(xml, 'xml', 'object', size=4)
680 dirs = xml.getElementsByTagName('subdir')
681 self.assertEqual(len(dirs), 2)
682 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
683 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
685 objects = xml.getElementsByTagName('name')
686 self.assertEqual(len(objects), 1)
687 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
689 def test_list_meta_double_matching(self):
690 meta = {'quality':'aaa', 'stock':'true'}
691 self.client.update_object_metadata(self.container[0],
692 self.obj[0]['name'], **meta)
693 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
694 self.assertEqual(len(obj), 1)
695 self.assertTrue(obj, self.obj[0]['name'])
697 def test_list_using_meta(self):
698 meta = {'quality':'aaa'}
699 for o in self.obj[:2]:
700 self.client.update_object_metadata(self.container[0], o['name'],
702 meta = {'stock':'true'}
703 for o in self.obj[3:5]:
704 self.client.update_object_metadata(self.container[0], o['name'],
707 obj = self.client.list_objects(self.container[0], meta='Quality')
708 self.assertEqual(len(obj), 2)
709 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
711 # test case insensitive
712 obj = self.client.list_objects(self.container[0], meta='quality')
713 self.assertEqual(len(obj), 2)
714 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
716 # test multiple matches
717 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
718 self.assertEqual(len(obj), 4)
719 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
721 # test non 1-1 multiple match
722 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
723 self.assertEqual(len(obj), 2)
724 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
726 def test_if_modified_since(self):
727 t = datetime.datetime.utcnow()
728 t2 = t - datetime.timedelta(minutes=10)
731 self.upload_random_data(self.container[0], o_names[0])
733 for f in DATE_FORMATS:
734 past = t2.strftime(f)
736 o = self.client.list_objects(self.container[0],
737 if_modified_since=past)
739 self.client.list_objects(self.container[0]))
741 self.failIf(f.status == 304) #fail if not modified
743 def test_if_modified_since_invalid_date(self):
744 headers = {'if-modified-since':''}
745 o = self.client.list_objects(self.container[0], if_modified_since='')
746 self.assertEqual(o, self.client.list_objects(self.container[0]))
748 def test_if_not_modified_since(self):
749 now = datetime.datetime.utcnow()
750 since = now + datetime.timedelta(1)
752 for f in DATE_FORMATS:
753 args = {'if_modified_since':'%s' %since.strftime(f)}
756 self.assert_raises_fault(304, self.client.list_objects,
757 self.container[0], **args)
759 def test_if_unmodified_since(self):
760 now = datetime.datetime.utcnow()
761 since = now + datetime.timedelta(1)
763 for f in DATE_FORMATS:
764 obj = self.client.list_objects(self.container[0],
765 if_unmodified_since=since.strftime(f))
768 self.assertEqual(obj, self.client.list_objects(self.container[0]))
770 def test_if_unmodified_since_precondition_failed(self):
771 t = datetime.datetime.utcnow()
772 t2 = t - datetime.timedelta(minutes=10)
775 self.client.create_container('dummy')
777 for f in DATE_FORMATS:
778 past = t2.strftime(f)
780 args = {'if_unmodified_since':'%s' %past}
782 #assert precondition failed
783 self.assert_raises_fault(412, self.client.list_objects,
784 self.container[0], **args)
786 class ContainerPut(BaseTestCase):
788 BaseTestCase.setUp(self)
789 self.containers = ['c1', 'c2']
791 def test_create(self):
792 self.client.create_container(self.containers[0])
793 containers = self.client.list_containers()
794 self.assertTrue(self.containers[0] in containers)
795 self.assert_container_exists(self.containers[0])
797 def test_create_twice(self):
798 self.client.create_container(self.containers[0])
799 self.assertTrue(not self.client.create_container(self.containers[0]))
801 def test_quota(self):
802 self.client.create_container(self.containers[0])
804 policy = {'quota':100}
805 self.client.set_container_policies('c1', **policy)
807 meta = self.client.retrieve_container_metadata('c1')
808 self.assertTrue('x-container-policy-quota' in meta)
809 self.assertEqual(meta['x-container-policy-quota'], '100')
812 kwargs = {'length':101}
813 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
817 self.client.set_container_policies('c1', **policy)
819 class ContainerPost(BaseTestCase):
821 BaseTestCase.setUp(self)
822 self.container = 'apples'
823 self.client.create_container(self.container)
825 def test_update_meta(self):
826 meta = {'test':'test33',
828 self.client.update_container_metadata(self.container, **meta)
829 headers = self.client.retrieve_container_metadata(self.container)
830 for k,v in meta.items():
831 k = 'x-container-meta-%s' % k
832 self.assertTrue(headers[k])
833 self.assertEqual(headers[k], v)
835 class ContainerDelete(BaseTestCase):
837 BaseTestCase.setUp(self)
838 self.containers = ['c1', 'c2']
839 for c in self.containers:
840 self.client.create_container(c)
842 def test_delete(self):
843 status = self.client.delete_container(self.containers[0])[0]
844 self.assertEqual(status, 204)
846 def test_delete_non_empty(self):
847 self.upload_random_data(self.containers[1], o_names[0])
848 self.assert_raises_fault(409, self.client.delete_container,
851 def test_delete_invalid(self):
852 self.assert_raises_fault(404, self.client.delete_container, 'c3')
854 class ObjectGet(BaseTestCase):
856 BaseTestCase.setUp(self)
857 self.containers = ['c1', 'c2']
858 #create some containers
859 for c in self.containers:
860 self.client.create_container(c)
863 names = ('obj1', 'obj2')
866 self.objects.append(self.upload_random_data(self.containers[1], n))
868 def test_versions(self):
869 c = self.containers[1]
871 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
872 self.assert_versionlist_structure(b)
875 meta = {'quality':'AAA', 'stock':True}
876 self.client.update_object_metadata(c, o['name'], **meta)
878 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
879 self.assert_versionlist_structure(a)
880 self.assertEqual(len(b)+1, len(a))
881 self.assertEqual(b, a[:-1])
883 #get exact previous version metadata
885 v_meta = self.client.retrieve_object_metadata(c, o['name'],
888 for k in meta.keys():
889 self.assertTrue(k not in v_meta)
892 data = get_random_data()
893 self.client.update_object(c, o['name'], StringIO(data))
895 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
896 self.assert_versionlist_structure(aa)
897 self.assertEqual(len(a)+1, len(aa))
898 self.assertEqual(a, aa[:-1])
900 #get exact previous version
902 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
903 self.assertEqual(o['data'], v_data)
904 self.assertEqual(self.client.retrieve_object(c, o['name']),
905 '%s%s' %(v_data, data))
909 o = self.client.retrieve_object(self.containers[1],
910 self.objects[0]['name'],
911 self.objects[0]['meta'])
912 self.assertEqual(o, self.objects[0]['data'])
914 def test_objects_with_trailing_spaces(self):
915 self.client.create_container('test')
917 self.upload_random_data('test', 'a')
918 #look for 'a ' object
919 self.assert_raises_fault(404, self.client.retrieve_object,
923 self.client.delete_object('test', 'a')
924 self.assert_raises_fault(404, self.client.retrieve_object,
928 self.upload_random_data('test', 'a ')
930 self.assert_raises_fault(404, self.client.retrieve_object,
933 def test_get_invalid(self):
934 self.assert_raises_fault(404, self.client.retrieve_object,
935 self.containers[0], self.objects[0]['name'])
937 def test_get_partial(self):
938 #perform get with range
939 status, headers, data = self.client.request_object(self.containers[1],
940 self.objects[0]['name'],
943 #assert successful partial content
944 self.assertEqual(status, 206)
947 self.assertEqual(headers['content-type'],
948 self.objects[0]['meta']['content_type'])
950 #assert content length
951 self.assertEqual(int(headers['content-length']), 500)
954 self.assertEqual(self.objects[0]['data'][:500], data)
956 def test_get_final_500(self):
957 #perform get with range
958 headers = {'range':'bytes=-500'}
959 status, headers, data = self.client.request_object(self.containers[1],
960 self.objects[0]['name'],
963 #assert successful partial content
964 self.assertEqual(status, 206)
967 self.assertEqual(headers['content-type'],
968 self.objects[0]['meta']['content_type'])
970 #assert content length
971 self.assertEqual(int(headers['content-length']), 500)
974 self.assertTrue(self.objects[0]['data'][-500:], data)
976 def test_get_rest(self):
977 #perform get with range
978 offset = len(self.objects[0]['data']) - 500
979 status, headers, data = self.client.request_object(self.containers[1],
980 self.objects[0]['name'],
981 range='bytes=%s-' %offset)
983 #assert successful partial content
984 self.assertEqual(status, 206)
987 self.assertEqual(headers['content-type'],
988 self.objects[0]['meta']['content_type'])
990 #assert content length
991 self.assertEqual(int(headers['content-length']), 500)
994 self.assertTrue(self.objects[0]['data'][-500:], data)
996 def test_get_range_not_satisfiable(self):
997 #perform get with range
998 offset = len(self.objects[0]['data']) + 1
1000 #assert range not satisfiable
1001 self.assert_raises_fault(416, self.client.retrieve_object,
1002 self.containers[1], self.objects[0]['name'],
1003 range='bytes=0-%s' %offset)
1005 def test_multiple_range(self):
1006 #perform get with multiple range
1007 ranges = ['0-499', '-500', '1000-']
1008 bytes = 'bytes=%s' % ','.join(ranges)
1009 status, headers, data = self.client.request_object(self.containers[1],
1010 self.objects[0]['name'],
1013 # assert partial content
1014 self.assertEqual(status, 206)
1016 # assert Content-Type of the reply will be multipart/byteranges
1017 self.assertTrue(headers['content-type'])
1018 content_type_parts = headers['content-type'].split()
1019 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1021 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1022 cparts = data.split(boundary)[1:-1]
1024 # assert content parts are exactly 2
1025 self.assertEqual(len(cparts), len(ranges))
1027 # for each content part assert headers
1029 for cpart in cparts:
1030 content = cpart.split('\r\n')
1031 headers = content[1:3]
1032 content_range = headers[0].split(': ')
1033 self.assertEqual(content_range[0], 'Content-Range')
1035 r = ranges[i].split('-')
1036 if not r[0] and not r[1]:
1039 start = len(self.objects[0]['data']) - int(r[1])
1040 end = len(self.objects[0]['data'])
1043 end = len(self.objects[0]['data'])
1047 fdata = self.objects[0]['data'][start:end]
1048 sdata = '\r\n'.join(content[4:-1])
1049 self.assertEqual(len(fdata), len(sdata))
1050 self.assertEquals(fdata, sdata)
1053 def test_multiple_range_not_satisfiable(self):
1054 #perform get with multiple range
1055 out_of_range = len(self.objects[0]['data']) + 1
1056 ranges = ['0-499', '-500', '%d-' %out_of_range]
1057 bytes = 'bytes=%s' % ','.join(ranges)
1059 # assert partial content
1060 self.assert_raises_fault(416, self.client.retrieve_object,
1062 self.objects[0]['name'], range=bytes)
1064 def test_get_with_if_match(self):
1065 #perform get with If-Match
1066 etag = self.objects[0]['hash']
1067 status, headers, data = self.client.request_object(self.containers[1],
1068 self.objects[0]['name'],
1071 self.assertEqual(status, 200)
1073 #assert content-type
1074 self.assertEqual(headers['content-type'],
1075 self.objects[0]['meta']['content_type'])
1077 #assert response content
1078 self.assertEqual(self.objects[0]['data'], data)
1080 def test_get_with_if_match_star(self):
1081 #perform get with If-Match *
1082 headers = {'if-match':'*'}
1083 status, headers, data = self.client.request_object(self.containers[1],
1084 self.objects[0]['name'],
1087 self.assertEqual(status, 200)
1089 #assert content-type
1090 self.assertEqual(headers['content-type'],
1091 self.objects[0]['meta']['content_type'])
1093 #assert response content
1094 self.assertEqual(self.objects[0]['data'], data)
1096 def test_get_with_multiple_if_match(self):
1097 #perform get with If-Match
1098 etags = [i['hash'] for i in self.objects if i]
1099 etags = ','.join('"%s"' % etag for etag in etags)
1100 status, headers, data = self.client.request_object(self.containers[1],
1101 self.objects[0]['name'],
1104 self.assertEqual(status, 200)
1106 #assert content-type
1107 self.assertEqual(headers['content-type'],
1108 self.objects[0]['meta']['content_type'])
1110 #assert content-type
1111 self.assertEqual(headers['content-type'],
1112 self.objects[0]['meta']['content_type'])
1114 #assert response content
1115 self.assertEqual(self.objects[0]['data'], data)
1117 def test_if_match_precondition_failed(self):
1118 #assert precondition failed
1119 self.assert_raises_fault(412, self.client.retrieve_object,
1121 self.objects[0]['name'], if_match='123')
1123 def test_if_none_match(self):
1124 #perform get with If-None-Match
1125 status, headers, data = self.client.request_object(self.containers[1],
1126 self.objects[0]['name'],
1127 if_none_match='123')
1130 self.assertEqual(status, 200)
1132 #assert content-type
1133 self.assertEqual(headers['content_type'],
1134 self.objects[0]['meta']['content_type'])
1136 def test_if_none_match(self):
1137 #perform get with If-None-Match * and assert not modified
1138 self.assert_raises_fault(304, self.client.retrieve_object,
1140 self.objects[0]['name'],
1143 def test_if_none_match_not_modified(self):
1144 #perform get with If-None-Match and assert not modified
1145 self.assert_raises_fault(304, self.client.retrieve_object,
1147 self.objects[0]['name'],
1148 if_none_match=self.objects[0]['hash'])
1150 meta = self.client.retrieve_object_metadata(self.containers[1],
1151 self.objects[0]['name'])
1152 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1154 def test_if_modified_since(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)
1166 headers = {'if-modified-since':'%s' %past}
1168 o = self.client.retrieve_object(self.containers[1],
1169 self.objects[0]['name'],
1170 if_modified_since=past)
1172 self.client.retrieve_object(self.containers[1],
1173 self.objects[0]['name']))
1175 self.failIf(f.status == 304)
1177 def test_if_modified_since_invalid_date(self):
1178 o = self.client.retrieve_object(self.containers[1],
1179 self.objects[0]['name'],
1180 if_modified_since='')
1181 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1182 self.objects[0]['name']))
1184 def test_if_not_modified_since(self):
1185 now = datetime.datetime.utcnow()
1186 since = now + datetime.timedelta(1)
1188 for f in DATE_FORMATS:
1189 #assert not modified
1190 self.assert_raises_fault(304, self.client.retrieve_object,
1191 self.containers[1], self.objects[0]['name'],
1192 if_modified_since=since.strftime(f))
1194 def test_if_unmodified_since(self):
1195 now = datetime.datetime.utcnow()
1196 since = now + datetime.timedelta(1)
1198 for f in DATE_FORMATS:
1199 t = since.strftime(f)
1200 status, headers, data = self.client.request_object(self.containers[1],
1201 self.objects[0]['name'],
1202 if_unmodified_since=t)
1204 self.assertEqual(status, 200)
1205 self.assertEqual(self.objects[0]['data'], data)
1207 #assert content-type
1208 self.assertEqual(headers['content-type'],
1209 self.objects[0]['meta']['content_type'])
1211 def test_if_unmodified_since_precondition_failed(self):
1212 t = datetime.datetime.utcnow()
1213 t2 = t - datetime.timedelta(minutes=10)
1216 self.upload_data(self.containers[1],
1217 self.objects[0]['name'],
1218 self.objects[0]['data'][:200])
1220 for f in DATE_FORMATS:
1221 past = t2.strftime(f)
1222 #assert precondition failed
1223 self.assert_raises_fault(412, self.client.retrieve_object,
1224 self.containers[1], self.objects[0]['name'],
1225 if_unmodified_since=past)
1227 def test_hashes(self):
1230 o = self.upload_random_data(self.containers[1], fname, l)
1232 body = self.client.retrieve_object(self.containers[1], fname,
1234 hashes = body['hashes']
1235 block_size = body['block_size']
1236 block_hash = body['block_hash']
1237 block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1238 self.assertTrue(len(hashes), block_num)
1241 start = i * block_size
1242 end = (i + 1) * block_size
1243 hash = compute_block_hash(o['data'][start:end], block_hash)
1244 self.assertEqual(h, hash)
1247 class ObjectPut(BaseTestCase):
1249 BaseTestCase.setUp(self)
1250 self.container = 'c1'
1251 self.client.create_container(self.container)
1253 def test_upload(self):
1255 meta = {'test':'test1'}
1256 o = self.upload_random_data(self.container, name, **meta)
1258 headers = self.client.retrieve_object_metadata(self.container,
1261 self.assertTrue('test' in headers.keys())
1262 self.assertEqual(headers['test'], meta['test'])
1264 #assert uploaded content
1265 status, h, data = self.client.request_object(self.container, name)
1266 self.assertEqual(len(o['data']), int(h['content-length']))
1267 self.assertEqual(o['data'], data)
1269 #assert content-type
1270 self.assertEqual(h['content-type'], o['meta']['content_type'])
1272 def _test_maximum_upload_size_exceeds(self):
1274 meta = {'test':'test1'}
1276 length= 5 * (1024 * 1024 * 1024) + 1
1277 self.assert_raises_fault(400, self.upload_random_data, self.container,
1278 name, length, **meta)
1280 def test_upload_with_name_containing_slash(self):
1281 name = '/%s' % o_names[0]
1282 meta = {'test':'test1'}
1283 o = self.upload_random_data(self.container, name, **meta)
1285 self.assertEqual(o['data'],
1286 self.client.retrieve_object(self.container, name))
1288 self.assertTrue(name in self.client.list_objects(self.container))
1290 def test_create_directory_marker(self):
1291 self.client.create_directory_marker(self.container, 'foo')
1292 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1293 self.assertEqual(meta['content-length'], '0')
1294 self.assertEqual(meta['content-type'], 'application/directory')
1296 def test_upload_unprocessable_entity(self):
1297 meta={'etag':'123', 'test':'test1'}
1299 #assert unprocessable entity
1300 self.assert_raises_fault(422, self.upload_random_data, self.container,
1303 def test_chunked_transfer(self):
1304 data = get_random_data()
1306 self.client.create_object_using_chunks(self.container, objname,
1309 uploaded_data = self.client.retrieve_object(self.container, objname)
1310 self.assertEqual(data, uploaded_data)
1312 def test_manifestation(self):
1313 prefix = 'myobject/'
1316 part = '%s%d' %(prefix, i)
1317 o = self.upload_random_data(self.container, part)
1320 manifest = '%s/%s' %(self.container, prefix)
1321 self.client.create_manifestation(self.container, 'large-object', manifest)
1323 self.assert_object_exists(self.container, 'large-object')
1324 self.assertEqual(data, self.client.retrieve_object(self.container,
1327 r = self.client.retrieve_object_hashmap(self.container,'large-object')
1328 hashes = r['hashes']
1329 block_size = int(r['block_size'])
1330 block_hash = r['block_hash']
1332 block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1333 self.assertEqual(block_num, len(hashes))
1335 #wrong manifestation
1336 self.client.create_manifestation(self.container, 'large-object',
1337 '%s/invalid' % self.container)
1338 self.assertEqual('', self.client.retrieve_object(self.container,
1341 def test_create_zero_length_object(self):
1344 zero = self.client.create_zero_length_object(c, o)
1345 zero_meta = self.client.retrieve_object_metadata(c, o)
1346 zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1347 zero_data = self.client.retrieve_object(c, o)
1349 self.assertEqual(int(zero_meta['content-length']), 0)
1350 hasher = newhasher('sha256')
1352 emptyhash = hasher.digest()
1353 self.assertEqual(zero_hash, [hexlify(emptyhash)])
1354 self.assertEqual(zero_data, '')
1356 def test_create_object_by_hashmap(self):
1359 self.upload_random_data(c, o)
1360 hashmap = self.client.retrieve_object(c, o, format='json')
1362 self.client.create_object_by_hashmap(c, o2, hashmap)
1363 self.assertEqual(self.client.retrieve_object(c, o),
1364 self.client.retrieve_object(c, o))
1366 class ObjectCopy(BaseTestCase):
1368 BaseTestCase.setUp(self)
1369 self.containers = ['c1', 'c2']
1370 for c in self.containers:
1371 self.client.create_container(c)
1372 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1374 def test_copy(self):
1375 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1376 self.containers[0], self.obj['name']):
1378 meta = {'test':'testcopy'}
1379 status = self.client.copy_object(self.containers[0],
1385 #assert copy success
1386 self.assertEqual(status, 201)
1388 #assert access the new object
1389 headers = self.client.retrieve_object_metadata(self.containers[0],
1391 self.assertTrue('x-object-meta-test' in headers.keys())
1392 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1394 #assert etag is the same
1395 self.assertEqual(headers['etag'], self.obj['hash'])
1397 #assert src object still exists
1398 self.assert_object_exists(self.containers[0], self.obj['name'])
1400 def test_copy_from_different_container(self):
1401 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1402 self.containers[0], self.obj['name']):
1403 meta = {'test':'testcopy'}
1404 status = self.client.copy_object(self.containers[0],
1409 self.assertEqual(status, 201)
1411 # assert updated metadata
1412 meta = self.client.retrieve_object_metadata(self.containers[1],
1415 self.assertTrue('test' in meta.keys())
1416 self.assertTrue(meta['test'], 'testcopy')
1418 #assert src object still exists
1419 self.assert_object_exists(self.containers[0], self.obj['name'])
1421 def test_copy_invalid(self):
1422 #copy from invalid object
1423 meta = {'test':'testcopy'}
1424 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1425 'test.py', self.containers[1], 'testcopy', meta)
1427 #copy from invalid container
1428 meta = {'test':'testcopy'}
1429 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1430 self.obj['name'], self.containers[1],
1433 def test_copy_dir(self):
1434 self.client.create_folder(self.containers[0], 'dir')
1435 objects = ('object1', 'subdir/object2', 'dirs')
1436 for name in objects[:-1]:
1437 self.upload_random_data(self.containers[0], 'dir/%s' % name)
1438 self.upload_random_data(self.containers[0], 'dirs')
1440 self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1441 self.assert_object_exists(self.containers[0], 'dir')
1442 self.assert_object_not_exists(self.containers[1], 'dirs')
1443 for name in objects[:-1]:
1444 meta0 = self.client.retrieve_object_metadata(self.containers[0], 'dir/%s' % name)
1445 meta1 = self.client.retrieve_object_metadata(self.containers[1], 'dir-backup/%s' % name)
1446 t = ('content-length', 'x-object-hash', 'content-type')
1447 (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1449 class ObjectMove(BaseTestCase):
1451 BaseTestCase.setUp(self)
1452 self.containers = ['c1', 'c2']
1453 for c in self.containers:
1454 self.client.create_container(c)
1455 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1457 def test_move(self):
1458 meta = self.client.retrieve_object_metadata(self.containers[0],
1460 self.assertTrue('x-object-uuid' in meta)
1461 uuid = meta['x-object-uuid']
1464 meta = {'test':'testcopy'}
1465 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1466 status = self.client.move_object(self.containers[0], self.obj['name'],
1467 self.containers[0], 'testcopy',
1470 #assert successful move
1471 self.assertEqual(status, 201)
1473 #assert updated metadata
1474 meta = self.client.retrieve_object_metadata(self.containers[0],
1476 self.assertTrue('x-object-meta-test' in meta.keys())
1477 self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1480 self.assertTrue(meta['x-object-uuid'], uuid)
1482 #assert src object no more exists
1483 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1486 def test_move_dir(self):
1487 self.client.create_folder(self.containers[0], 'dir')
1488 objects = ('object1', 'subdir/object2', 'dirs')
1490 for name in objects[:-1]:
1491 self.upload_random_data(self.containers[0], 'dir/%s' % name)
1492 meta[name] = self.client.retrieve_object_metadata(self.containers[0], 'dir/%s' % name)
1493 self.upload_random_data(self.containers[0], 'dirs')
1495 self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/', content_type='application/folder')
1496 self.assert_object_not_exists(self.containers[0], 'dir')
1497 self.assert_object_not_exists(self.containers[1], 'dirs')
1498 for name in objects[:-1]:
1499 self.assert_object_not_exists(self.containers[0], 'dir/%s' % name)
1500 self.assert_object_exists(self.containers[1], 'dir-backup/%s' % name)
1501 meta1 = self.client.retrieve_object_metadata(self.containers[1], 'dir-backup/%s' % name)
1502 t = ('content-length', 'x-object-hash', 'content-type')
1503 (self.assertEqual(meta[name][elem], meta1[elem]) for elem in t)
1505 class ObjectPost(BaseTestCase):
1507 BaseTestCase.setUp(self)
1508 self.containers = ['c1', 'c2']
1509 for c in self.containers:
1510 self.client.create_container(c)
1513 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1515 def test_update_meta(self):
1516 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1518 self.obj[0]['name']):
1519 #perform update metadata
1520 more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1521 status = self.client.update_object_metadata(self.containers[0],
1522 self.obj[0]['name'],
1524 #assert request accepted
1525 self.assertEqual(status, 202)
1527 #assert old metadata are still there
1528 headers = self.client.retrieve_object_metadata(self.containers[0],
1529 self.obj[0]['name'],
1531 #assert new metadata have been updated
1532 for k,v in more.items():
1533 self.assertTrue(k in headers.keys())
1534 self.assertTrue(headers[k], v)
1537 more = {'f' * 114: 'b' * 257}
1538 self.assert_raises_fault(400, self.client.update_object_metadata,
1540 self.obj[0]['name'],
1543 #perform update metadata
1544 more = {'α': 'β' * 256}
1545 status = self.client.update_object_metadata(self.containers[0],
1546 self.obj[0]['name'],
1548 #assert request accepted
1549 self.assertEqual(status, 202)
1551 #assert old metadata are still there
1552 headers = self.client.retrieve_object_metadata(self.containers[0],
1553 self.obj[0]['name'],
1555 #assert new metadata have been updated
1556 for k,v in more.items():
1557 self.assertTrue(k in headers.keys())
1558 self.assertTrue(headers[k], v)
1561 more = {'α': 'β' * 257}
1562 self.assert_raises_fault(400, self.client.update_object_metadata,
1564 self.obj[0]['name'],
1567 def test_update_object(self,
1570 instance_length = True,
1571 content_length = 500):
1572 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1574 self.obj[0]['name']):
1575 l = len(self.obj[0]['data'])
1576 range = 'bytes %d-%d/%s' %(first_byte_pos,
1578 l if instance_length else '*')
1579 partial = last_byte_pos - first_byte_pos + 1
1580 length = first_byte_pos + partial
1581 data = get_random_data(partial)
1582 args = {'content_type':'application/octet-stream',
1583 'content_range':'%s' %range}
1585 args['content_length'] = content_length
1587 r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1588 StringIO(data), **args)
1591 if partial < 0 or (instance_length and l <= last_byte_pos):
1592 self.assertEqual(status, 202)
1594 self.assertEqual(status, 204)
1595 #check modified object
1596 content = self.client.retrieve_object(self.containers[0],
1597 self.obj[0]['name'])
1598 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1599 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1600 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1601 self.assertEqual(etag, compute_md5_hash(content))
1603 def test_update_object_lt_blocksize(self):
1604 self.test_update_object(10, 20, content_length=None)
1606 def test_update_object_gt_blocksize(self):
1607 o = self.upload_random_data(self.containers[0], o_names[1],
1608 length=4*1024*1024+5)
1609 c = self.containers[0]
1612 first_byte_pos = 4*1024*1024+1
1613 last_byte_pos = 4*1024*1024+4
1614 l = last_byte_pos - first_byte_pos + 1
1615 data = get_random_data(l)
1616 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1617 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1618 content = self.client.retrieve_object(c, o_name)
1619 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1620 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1621 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1623 def test_update_object_divided_by_blocksize(self):
1624 o = self.upload_random_data(self.containers[0], o_names[1],
1625 length=4*1024*1024+5)
1626 c = self.containers[0]
1629 first_byte_pos = 4*1024*1024
1630 last_byte_pos = 5*1024*1024
1631 l = last_byte_pos - first_byte_pos + 1
1632 data = get_random_data(l)
1633 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1634 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1635 content = self.client.retrieve_object(c, o_name)
1636 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1637 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1638 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1640 def test_update_object_no_content_length(self):
1641 self.test_update_object(content_length = None)
1643 def test_update_object_invalid_content_length(self):
1644 with AssertContentInvariant(self.client.retrieve_object,
1645 self.containers[0], self.obj[0]['name']):
1646 self.assert_raises_fault(400, self.test_update_object,
1647 content_length = 1000)
1649 def test_update_object_invalid_range(self):
1650 with AssertContentInvariant(self.client.retrieve_object,
1651 self.containers[0], self.obj[0]['name']):
1652 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1654 def test_update_object_invalid_range_and_length(self):
1655 with AssertContentInvariant(self.client.retrieve_object,
1656 self.containers[0], self.obj[0]['name']):
1657 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1660 def test_update_object_invalid_range_with_no_content_length(self):
1661 with AssertContentInvariant(self.client.retrieve_object,
1662 self.containers[0], self.obj[0]['name']):
1663 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1664 content_length = None)
1666 def test_update_object_out_of_limits(self):
1667 with AssertContentInvariant(self.client.retrieve_object,
1668 self.containers[0], self.obj[0]['name']):
1669 l = len(self.obj[0]['data'])
1670 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1672 def test_append(self):
1673 data = get_random_data(500)
1675 self.client.update_object(self.containers[0], self.obj[0]['name'],
1676 StringIO(data), content_length=500,
1677 content_type='application/octet-stream')
1679 content = self.client.retrieve_object(self.containers[0],
1680 self.obj[0]['name'])
1681 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1682 self.assertEqual(content[:-500], self.obj[0]['data'])
1684 def test_update_with_chunked_transfer(self):
1685 data = get_random_data(500)
1687 fl = len(self.obj[0]['data'])
1689 self.client.update_object_using_chunks(self.containers[0],
1690 self.obj[0]['name'],
1693 content_type='application/octet-stream')
1695 #check modified object
1696 content = self.client.retrieve_object(self.containers[0],
1697 self.obj[0]['name'])
1698 self.assertEqual(content[0:dl], data)
1699 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1701 def test_update_from_other_object(self):
1702 c = self.containers[0]
1706 source_data = self.client.retrieve_object(c, src)
1707 source_meta = self.client.retrieve_object_metadata(c, src)
1708 source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1710 #update zero length object
1711 self.client.create_zero_length_object(c, dest)
1712 source_object = '/%s/%s' % (c, src)
1713 self.client.update_from_other_source(c, dest, source_object)
1714 dest_data = self.client.retrieve_object(c, src)
1715 dest_meta = self.client.retrieve_object_metadata(c, dest)
1716 dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1717 self.assertEqual(source_data, dest_data)
1718 self.assertEqual(source_hash, dest_hash)
1721 self.client.update_from_other_source(c, dest, source_object)
1722 content = self.client.retrieve_object(c, dest)
1723 self.assertEqual(source_data * 2, content)
1725 def test_update_range_from_other_object(self):
1726 c = self.containers[0]
1730 src = self.obj[1]['name']
1731 src_data = self.client.retrieve_object(c, src)
1733 #update zero length object
1734 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1735 source_object = '/%s/%s' % (c, src)
1736 first_byte_pos = 4*1024*1024+1
1737 last_byte_pos = 4*1024*1024+4
1738 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1739 self.client.update_from_other_source(c, dest, source_object,
1740 content_range=range)
1741 content = self.client.retrieve_object(c, dest)
1742 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1743 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1744 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1746 def test_update_hashes_from_other_object(self):
1747 c = self.containers[0]
1751 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1753 #update zero length object
1754 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1755 source_object = '/%s/%s' % (c, o_names[0])
1756 first_byte_pos = 4*1024*1024
1757 last_byte_pos = 5*1024*1024
1758 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1759 self.client.update_from_other_source(c, dest, source_object,
1760 content_range=range)
1761 content = self.client.retrieve_object(c, dest)
1762 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1763 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1764 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1767 def test_update_zero_length_object(self):
1768 c = self.containers[0]
1771 zero = self.client.create_zero_length_object(c, o)
1773 data = get_random_data()
1774 self.client.update_object(c, o, StringIO(data))
1775 self.client.create_object(c, other, StringIO(data))
1777 self.assertEqual(self.client.retrieve_object(c, o),
1778 self.client.retrieve_object(c, other))
1780 self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1781 self.client.retrieve_object_hashmap(c, other)["hashes"])
1783 class ObjectDelete(BaseTestCase):
1785 BaseTestCase.setUp(self)
1786 self.containers = ['c1', 'c2']
1787 for c in self.containers:
1788 self.client.create_container(c)
1789 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1791 def test_delete(self):
1792 #perform delete object
1793 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1795 def test_delete_invalid(self):
1796 #assert item not found
1797 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1800 def test_delete_dir(self):
1801 self.client.create_folder(self.containers[0], 'dir')
1802 objects = ('object1', 'subdir/object2', 'dirs')
1803 for name in objects[:-1]:
1804 self.upload_random_data(self.containers[0], 'dir/%s' % name)
1805 self.upload_random_data(self.containers[0], 'dirs')
1807 self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1808 self.assert_object_not_exists(self.containers[0], 'dir')
1809 self.assert_object_exists(self.containers[0], 'dirs')
1810 for name in objects[:-1]:
1811 self.assert_object_not_exists(self.containers[0], 'dir/%s' % name)
1813 class ListSharing(BaseTestCase):
1815 BaseTestCase.setUp(self)
1817 self.client.create_container('c%s' %i)
1818 self.client.create_container('c')
1820 self.upload_random_data('c1', 'o%s' %i)
1821 accounts = OTHER_ACCOUNTS.copy()
1822 self.o1_sharing_with = accounts.popitem()
1823 self.o1_sharing = [self.o1_sharing_with[1]]
1824 self.client.share_object('c1', 'o1', self.o1_sharing, read=True)
1828 l.append(accounts.popitem())
1830 def test_list_other_shared(self):
1831 self.other = Pithos_Client(get_url(),
1832 self.o1_sharing_with[0],
1833 self.o1_sharing_with[1])
1834 self.assertTrue(get_user() in self.other.list_shared_by_others())
1836 def test_list_my_shared(self):
1837 my_shared_containers = self.client.list_containers(shared=True)
1838 self.assertTrue('c1' in my_shared_containers)
1839 self.assertTrue('c2' not in my_shared_containers)
1841 my_shared_objects = self.client.list_objects('c1', shared=True)
1842 self.assertTrue('o1' in my_shared_objects)
1843 self.assertTrue('o2' not in my_shared_objects)
1845 class List(BaseTestCase):
1847 BaseTestCase.setUp(self)
1848 for i in range(1, 5):
1850 self.client.create_container(c)
1851 for j in range(1, 3):
1853 self.upload_random_data(c, o)
1855 self.client.share_object(c, 'o1', ['papagian'], read=True)
1857 self.client.publish_object(c, 'o2')
1859 def test_shared_public(self):
1860 func, kwargs = self.client.list_containers, {'shared':True}
1862 self.assertEqual(l, ['c1', 'c2'])
1863 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1865 func, kwargs = self.client.list_containers, {'public':True}
1867 self.assertEqual(l, ['c1', 'c3'])
1868 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1870 func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1872 self.assertEqual(l, ['c1', 'c2', 'c3'])
1873 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1876 func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1877 l = func(*args, **kwargs)
1878 self.assertEqual(l, ['o1'])
1879 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1881 func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1882 l = func(*args, **kwargs)
1883 self.assertEqual(l, ['o2'])
1884 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1886 func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1887 l = func(*args, **kwargs)
1888 self.assertEqual(l, ['o1', 'o2'])
1889 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1892 func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1893 l = func(*args, **kwargs)
1894 self.assertEqual(l, ['o1'])
1895 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1897 func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1898 l = func(*args, **kwargs)
1899 self.assertEqual(l, '')
1900 self.assertEqual([], func(*args, format='json', **kwargs))
1902 func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True, 'public':True}
1903 l = func(*args, **kwargs)
1904 self.assertEqual(l, ['o1'])
1905 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1908 func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1909 l = func(*args, **kwargs)
1910 self.assertEqual(l, '')
1911 self.assertEqual([], func(*args, format='json', **kwargs))
1913 func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1914 l = func(*args, **kwargs)
1915 self.assertEqual(l, ['o2'])
1916 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1918 func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True, 'public':True}
1919 l = func(*args, **kwargs)
1920 self.assertEqual(l, ['o2'])
1921 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1924 func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
1925 l = func(*args, **kwargs)
1926 self.assertEqual(l, '')
1927 self.assertEqual([], func(*args, format='json', **kwargs))
1929 func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
1930 l = func(*args, **kwargs)
1931 self.assertEqual(l, '')
1932 self.assertEqual([], func(*args, format='json', **kwargs))
1934 func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
1935 l = func(*args, **kwargs)
1936 self.assertEqual(l, '')
1937 self.assertEqual([], func(*args, format='json', **kwargs))
1939 class TestGreek(BaseTestCase):
1940 def test_create_container(self):
1941 self.client.create_container('φάκελος')
1942 self.assert_container_exists('φάκελος')
1944 self.assertTrue('φάκελος' in self.client.list_containers())
1946 def test_create_object(self):
1947 self.client.create_container('φάκελος')
1948 self.upload_random_data('φάκελος', 'αντικείμενο')
1950 self.assert_object_exists('φάκελος', 'αντικείμενο')
1951 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1953 def test_copy_object(self):
1954 src_container = 'φάκελος'
1955 src_object = 'αντικείμενο'
1956 dest_container = 'αντίγραφα'
1957 dest_object = 'ασφαλές-αντίγραφο'
1959 self.client.create_container(src_container)
1960 self.upload_random_data(src_container, src_object)
1962 self.client.create_container(dest_container)
1963 self.client.copy_object(src_container, src_object, dest_container,
1966 self.assert_object_exists(src_container, src_object)
1967 self.assert_object_exists(dest_container, dest_object)
1968 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1970 def test_move_object(self):
1971 src_container = 'φάκελος'
1972 src_object = 'αντικείμενο'
1973 dest_container = 'αντίγραφα'
1974 dest_object = 'ασφαλές-αντίγραφο'
1976 self.client.create_container(src_container)
1977 self.upload_random_data(src_container, src_object)
1979 self.client.create_container(dest_container)
1980 self.client.move_object(src_container, src_object, dest_container,
1983 self.assert_object_not_exists(src_container, src_object)
1984 self.assert_object_exists(dest_container, dest_object)
1985 self.assertTrue(dest_object in self.client.list_objects(dest_container))
1987 def test_delete_object(self):
1988 self.client.create_container('φάκελος')
1989 self.upload_random_data('φάκελος', 'αντικείμενο')
1990 self.assert_object_exists('φάκελος', 'αντικείμενο')
1992 self.client.delete_object('φάκελος', 'αντικείμενο')
1993 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
1994 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
1996 def test_delete_container(self):
1997 self.client.create_container('φάκελος')
1998 self.assert_container_exists('φάκελος')
2000 self.client.delete_container('φάκελος')
2001 self.assert_container_not_exists('φάκελος')
2002 self.assertTrue('φάκελος' not in self.client.list_containers())
2004 def test_account_meta(self):
2005 meta = {'ποιότητα':'ΑΑΑ'}
2006 self.client.update_account_metadata(**meta)
2007 meta = self.client.retrieve_account_metadata(restricted=True)
2008 self.assertTrue('ποιότητα' in meta.keys())
2009 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2011 def test_container_meta(self):
2012 meta = {'ποιότητα':'ΑΑΑ'}
2013 self.client.create_container('φάκελος', meta=meta)
2015 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
2016 self.assertTrue('ποιότητα' in meta.keys())
2017 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2019 def test_object_meta(self):
2020 self.client.create_container('φάκελος')
2021 meta = {'ποιότητα':'ΑΑΑ'}
2022 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2024 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2026 self.assertTrue('ποιότητα' in meta.keys())
2027 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2029 def test_list_meta_filtering(self):
2030 self.client.create_container('φάκελος')
2031 meta = {'ποιότητα':'ΑΑΑ'}
2032 self.upload_random_data('φάκελος', 'ο1', **meta)
2033 self.upload_random_data('φάκελος', 'ο2')
2034 self.upload_random_data('φάκελος', 'ο3')
2036 meta = {'ποσότητα':'μεγάλη'}
2037 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2038 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2039 self.assertEquals(objects, ['ο1', 'ο2'])
2041 objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
2042 self.assertEquals(objects, ['ο2', 'ο3'])
2044 objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
2045 self.assertEquals(objects, ['ο3'])
2047 meta = {'ποιότητα':'ΑΒ'}
2048 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2049 objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2050 self.assertEquals(objects, ['ο1'])
2051 objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2052 self.assertEquals(objects, ['ο2'])
2054 meta = {'έτος':'2011'}
2055 self.client.update_object_metadata('φάκελος', 'ο3', **meta)
2056 meta = {'έτος':'2012'}
2057 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2058 objects = self.client.list_objects('φάκελος', meta='έτος<2012')
2059 self.assertEquals(objects, ['ο3'])
2060 objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
2061 self.assertEquals(objects, ['ο2', 'ο3'])
2062 objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
2063 self.assertEquals(objects, '')
2065 def test_groups(self):
2067 groups = {'σεφς':'chazapis,διογένης'}
2068 self.client.set_account_groups(**groups)
2069 groups.update(self.initial_groups)
2070 self.assertEqual(groups['σεφς'],
2071 self.client.retrieve_account_groups()['σεφς'])
2074 self.client.create_container('φάκελος')
2075 o = self.upload_random_data('φάκελος', 'ο1')
2076 self.client.share_object('φάκελος', 'ο1', ['%s:σεφς' % get_user()])
2077 chef = Pithos_Client(get_url(),
2080 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2081 'φάκελος', 'ο1', account=get_user())
2084 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
2085 new_data = get_random_data()
2086 self.assert_not_raises_fault(403, chef.update_object,
2087 'φάκελος', 'ο1', StringIO(new_data),
2090 server_data = self.client.retrieve_object('φάκελος', 'ο1')
2091 self.assertEqual(server_data[:len(o['data'])], o['data'])
2092 self.assertEqual(server_data[len(o['data']):], new_data)
2094 def test_manifestation(self):
2095 self.client.create_container('κουβάς')
2099 part = '%s%d' %(prefix, i)
2100 o = self.upload_random_data('κουβάς', part)
2103 self.client.create_container('φάκελος')
2104 manifest = '%s/%s' %('κουβάς', prefix)
2105 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2107 self.assert_object_exists('φάκελος', 'άπαντα')
2108 self.assertEqual(data, self.client.retrieve_object('φάκελος',
2111 #wrong manifestation
2112 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2113 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2115 def test_update_from_another_object(self):
2116 self.client.create_container('κουβάς')
2117 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2118 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2119 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2120 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2123 self.client.retrieve_object('κουβάς', 'νέο'),
2124 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2126 class TestPermissions(BaseTestCase):
2128 BaseTestCase.setUp(self)
2131 self.authorized = ['chazapis', 'verigak', 'gtsouk']
2132 groups = {'pithosdev':','.join(self.authorized)}
2133 self.client.set_account_groups(**groups)
2135 self.container = 'c'
2137 self.client.create_container(self.container)
2138 self.upload_random_data(self.container, self.object)
2139 self.upload_random_data(self.container, self.object+'/')
2140 self.upload_random_data(self.container, self.object+'/a')
2141 self.upload_random_data(self.container, self.object+'a')
2142 self.upload_random_data(self.container, self.object+'a/')
2143 self.dir_content_types = ('application/directory', 'application/folder')
2145 def assert_read(self, authorized=[], any=False, depth=0):
2146 for token, account in OTHER_ACCOUNTS.items():
2147 cl = Pithos_Client(get_url(), token, account)
2148 if account in authorized or any:
2149 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2150 self.container, self.object,
2153 self.assert_raises_fault(403, cl.retrieve_object_metadata,
2154 self.container, self.object,
2158 meta = self.client.retrieve_object_metadata(self.container, self.object)
2159 type = meta['content-type']
2160 derivatives = self.client.list_objects(self.container, prefix=self.object)
2161 #exclude the self.object
2162 del derivatives[derivatives.index(self.object)]
2163 for o in derivatives:
2164 for token, account in OTHER_ACCOUNTS.items():
2165 cl = Pithos_Client(get_url(), token, account)
2166 prefix = self.object if self.object.endswith('/') else self.object+'/'
2167 if (account in authorized or any) and \
2168 (type in self.dir_content_types) and \
2169 o.startswith(prefix):
2170 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2171 self.container, o, account=get_user())
2173 self.assert_raises_fault(403, cl.retrieve_object_metadata,
2174 self.container, o, account=get_user())
2176 def assert_write(self, authorized=[], any=False):
2177 o_data = self.client.retrieve_object(self.container, self.object)
2178 for token, account in OTHER_ACCOUNTS.items():
2179 cl = Pithos_Client(get_url(), token, account)
2180 new_data = get_random_data()
2181 if account in authorized or any:
2183 self.assert_not_raises_fault(403, cl.update_object,
2184 self.container, self.object, StringIO(new_data),
2188 server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2189 self.assertEqual(o_data, server_data[:len(o_data)])
2190 self.assertEqual(new_data, server_data[len(o_data):])
2191 o_data = server_data
2193 self.failIf(f.status == 403)
2195 self.assert_raises_fault(403, cl.update_object,
2196 self.container, self.object, StringIO(new_data),
2199 meta = self.client.retrieve_object_metadata(self.container, self.object)
2200 type = meta['content-type']
2201 derivatives = self.client.list_objects(self.container, prefix=self.object)
2203 del derivatives[derivatives.index(self.object)]
2204 for o in derivatives:
2205 for token, account in OTHER_ACCOUNTS.items():
2206 prefix = self.object if self.object.endswith('/') else self.object+'/'
2207 cl = Pithos_Client(get_url(), token, account)
2208 new_data = get_random_data()
2209 if (account in authorized or any) and \
2210 (type in self.dir_content_types) and \
2211 o.startswith(prefix):
2213 self.assert_not_raises_fault(403, cl.update_object,
2218 server_data = cl.retrieve_object(self.container, o, account=get_user())
2219 self.assertEqual(new_data, server_data[-len(new_data):])
2221 self.failIf(f.status == 403)
2223 self.assert_raises_fault(403, cl.update_object,
2228 def test_group_read(self):
2229 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2230 self.assert_read(authorized=self.authorized)
2232 def test_read_many(self):
2233 self.client.share_object(self.container, self.object, self.authorized)
2234 self.assert_read(authorized=self.authorized)
2236 def test_read_by_everyone(self):
2237 self.client.share_object(self.container, self.object, ['*'])
2238 self.assert_read(any=True)
2240 def test_read_directory(self):
2241 for type in self.dir_content_types:
2242 #change content type
2243 self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2244 self.client.share_object(self.container, self.object, ['*'])
2245 self.assert_read(any=True)
2246 self.client.share_object(self.container, self.object, self.authorized)
2247 self.assert_read(authorized=self.authorized)
2248 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2249 self.assert_read(authorized=self.authorized)
2251 def test_group_write(self):
2252 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2253 self.assert_write(authorized=self.authorized)
2255 def test_write_many(self):
2256 self.client.share_object(self.container, self.object, self.authorized, read=False)
2257 self.assert_write(authorized=self.authorized)
2259 def test_write_by_everyone(self):
2260 self.client.share_object(self.container, self.object, ['*'], read=False)
2261 self.assert_write(any=True)
2263 def test_write_directory(self):
2264 dir_content_types = ('application/directory', 'application/foler')
2265 for type in dir_content_types:
2266 #change content type
2267 self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2268 self.client.share_object(self.container, self.object, ['*'], read=False)
2269 self.assert_write(any=True)
2270 self.client.share_object(self.container, self.object, self.authorized, read=False)
2271 self.assert_write(authorized=self.authorized)
2272 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2273 self.assert_write(authorized=self.authorized)
2275 def test_shared_listing(self):
2276 self.client.share_object(self.container, self.object, self.authorized)
2278 my_shared_containers = self.client.list_containers(shared=True)
2279 self.assertEqual(['c'], my_shared_containers)
2280 my_shared_objects = self.client.list_objects('c', shared=True)
2281 self.assertEqual(['o'], my_shared_objects)
2283 dir_content_types = ('application/directory', 'application/foler')
2284 for type in dir_content_types:
2285 #change content type
2286 self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2287 my_shared_objects = self.client.list_objects('c', shared=True)
2288 self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2290 for token, account in OTHER_ACCOUNTS.items():
2291 if account in self.authorized:
2292 self.other = Pithos_Client(get_url(), token, account)
2293 self.assertTrue(get_user() in self.other.list_shared_by_others())
2295 class TestPublish(BaseTestCase):
2296 def test_publish(self):
2297 self.client.create_container('c')
2298 o_data = self.upload_random_data('c', 'o')['data']
2299 self.client.publish_object('c', 'o')
2300 meta = self.client.retrieve_object_metadata('c', 'o')
2301 self.assertTrue('x-object-public' in meta)
2302 url = meta['x-object-public']
2304 p = urlparse(get_url())
2305 if p.scheme == 'http':
2306 conn = HTTPConnection(p.netloc)
2307 elif p.scheme == 'https':
2308 conn = HTTPSConnection(p.netloc)
2310 raise Exception('Unknown URL scheme')
2312 conn.request('GET', url)
2313 resp = conn.getresponse()
2314 length = resp.getheader('content-length', None)
2315 data = resp.read(length)
2316 self.assertEqual(o_data, data)
2318 class TestPolicies(BaseTestCase):
2319 def test_none_versioning(self):
2320 self.client.create_container('c', policies={'versioning':'none'})
2321 o = self.upload_random_data('c', 'o')
2322 meta = self.client.retrieve_object_metadata('c', 'o')
2323 v = meta['x-object-version']
2324 more_data = get_random_data()
2325 self.client.update_object('c', 'o', StringIO(more_data))
2326 vlist = self.client.retrieve_object_versionlist('c', 'o')
2327 self.assert_raises_fault(404, self.client.retrieve_object_version,
2329 data = self.client.retrieve_object('c', 'o')
2330 end = len(o['data'])
2331 self.assertEqual(data[:end], o['data'])
2332 self.assertEqual(data[end:], more_data)
2334 def test_quota(self):
2335 self.client.create_container('c', policies={'quota':'1'})
2336 meta = self.client.retrieve_container_metadata('c')
2337 self.assertEqual(meta['x-container-policy-quota'], '1')
2338 self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2341 def test_quota_none(self):
2342 self.client.create_container('c', policies={'quota':'0'})
2343 meta = self.client.retrieve_container_metadata('c')
2344 self.assertEqual(meta['x-container-policy-quota'], '0')
2345 self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2348 class AssertUUidInvariant(object):
2349 def __init__(self, callable, *args, **kwargs):
2350 self.callable = callable
2352 self.kwargs = kwargs
2354 def __enter__(self):
2355 self.map = self.callable(*self.args, **self.kwargs)
2356 assert('x-object-uuid' in self.map)
2357 self.uuid = self.map['x-object-uuid']
2360 def __exit__(self, type, value, tb):
2361 map = self.callable(*self.args, **self.kwargs)
2362 assert('x-object-uuid' in self.map)
2363 uuid = map['x-object-uuid']
2364 assert(uuid == self.uuid)
2366 class AssertMappingInvariant(object):
2367 def __init__(self, callable, *args, **kwargs):
2368 self.callable = callable
2370 self.kwargs = kwargs
2372 def __enter__(self):
2373 self.map = self.callable(*self.args, **self.kwargs)
2376 def __exit__(self, type, value, tb):
2377 map = self.callable(*self.args, **self.kwargs)
2378 for k, v in self.map.items():
2384 class AssertContentInvariant(object):
2385 def __init__(self, callable, *args, **kwargs):
2386 self.callable = callable
2388 self.kwargs = kwargs
2390 def __enter__(self):
2391 self.content = self.callable(*self.args, **self.kwargs)[2]
2394 def __exit__(self, type, value, tb):
2395 content = self.callable(*self.args, **self.kwargs)[2]
2396 assert self.content == content
2398 def get_content_splitted(response):
2400 return response.content.split('\n')
2402 def compute_md5_hash(data):
2406 return md5.hexdigest().lower()
2408 def compute_block_hash(data, algorithm):
2409 h = hashlib.new(algorithm)
2410 h.update(data.rstrip('\x00'))
2411 return h.hexdigest()
2413 def get_random_data(length=500):
2414 char_set = string.ascii_uppercase + string.digits
2415 return ''.join(random.choice(char_set) for x in xrange(length))
2418 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2419 __D = r'(?P<day>\d{2})'
2420 __D2 = r'(?P<day>[ \d]\d)'
2421 __M = r'(?P<mon>\w{3})'
2422 __Y = r'(?P<year>\d{4})'
2423 __Y2 = r'(?P<year>\d{2})'
2424 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2425 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2426 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2427 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2428 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2429 m = regex.match(date)
2434 def strnextling(prefix):
2435 """Return the first unicode string
2436 greater than but not starting with given prefix.
2437 strnextling('hello') -> 'hellp'
2440 ## all strings start with the null string,
2441 ## therefore we have to approximate strnextling('')
2442 ## with the last unicode character supported by python
2443 ## 0x10ffff for wide (32-bit unicode) python builds
2444 ## 0x00ffff for narrow (16-bit unicode) python builds
2445 ## We will not autodetect. 0xffff is safe enough.
2446 return unichr(0xffff)
2454 o_names = ['kate.jpg',
2455 'kate_beckinsale.jpg',
2456 'How To Win Friends And Influence People.pdf',
2457 'moms_birthday.jpg',
2459 'Disturbed - Down With The Sickness.mp3',
2460 'army_of_darkness.avi',
2462 'photos/animals/dogs/poodle.jpg',
2463 'photos/animals/dogs/terrier.jpg',
2464 'photos/animals/cats/persian.jpg',
2465 'photos/animals/cats/siamese.jpg',
2466 'photos/plants/fern.jpg',
2467 'photos/plants/rose.jpg',
2472 if get_user() == 'test':
2473 unittest.main(module='pithos.tools.test')
2475 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2478 if __name__ == "__main__":