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"]
62 from pithos.api.settings import AUTHENTICATION_USERS
63 AUTHENTICATION_USERS = AUTHENTICATION_USERS or {}
64 OTHER_ACCOUNTS = AUTHENTICATION_USERS.copy()
66 OTHER_ACCOUNTS.pop(get_auth())
70 class BaseTestCase(unittest.TestCase):
71 #TODO unauthorized request
73 self.client = Pithos_Client(get_url(), get_auth(), get_user())
75 #keep track of initial containers
76 self.initial_containers = self.client.list_containers()
77 if self.initial_containers == '':
78 self.initial_containers = []
81 self.invalid_client = Pithos_Client(get_url(), get_auth(), 'invalid')
83 #keep track of initial account groups
84 self.initial_groups = self.client.retrieve_account_groups()
86 #keep track of initial account meta
87 self.initial_meta = self.client.retrieve_account_metadata(restricted=True)
95 'x_container_policy'),
103 self.return_codes = (400, 401, 403, 404, 503,)
106 #delete additionally created meta
108 for m in self.client.retrieve_account_metadata(restricted=True):
109 if m not in self.initial_meta:
111 self.client.delete_account_metadata(l)
113 #delete additionally created groups
115 for g in self.client.retrieve_account_groups():
116 if g not in self.initial_groups:
118 self.client.unset_account_groups(l)
119 self._clean_account()
121 def _clean_account(self):
122 for c in self.client.list_containers():
123 # if c not in self.initial_containers:
124 self.client.delete_container(c, delimiter='/')
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 = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
266 self.containers.sort()
268 for item in self.containers:
269 self.client.create_container(item)
272 self.client.update_account_metadata(**meta)
273 #self.updated_meta = self.initial_meta.update(meta)
275 def test_get_account_meta(self):
276 meta = self.client.retrieve_account_metadata()
278 containers = self.client.list_containers()
279 l = str(len(containers))
280 self.assertEqual(meta['x-account-container-count'], l)
284 m = self.client.retrieve_container_metadata(c)
285 csum = sum([o['bytes'] for o in self.client.list_objects(c, format='json')])
286 self.assertEqual(int(m['x-container-bytes-used']), csum)
287 size1 += int(m['x-container-bytes-used'])
289 self.assertEqual(meta['x-account-bytes-used'], str(size1))
290 self.assertEqual(meta['x-account-bytes-used'], str(size2))
292 def test_get_account_403(self):
293 self.assert_raises_fault(403,
294 self.invalid_client.retrieve_account_metadata)
296 def test_get_account_meta_until(self):
297 t = datetime.datetime.utcnow()
298 past = t - datetime.timedelta(minutes=15)
299 past = int(_time.mktime(past.timetuple()))
301 meta = {'premium':True}
302 self.client.update_account_metadata(**meta)
303 meta = self.client.retrieve_account_metadata(restricted=True,
305 self.assertTrue('premium' not in meta)
307 meta = self.client.retrieve_account_metadata(restricted=True)
308 self.assertTrue('premium' in meta)
310 def test_get_account_meta_until_invalid_date(self):
311 meta = {'premium':True}
312 self.client.update_account_metadata(**meta)
313 meta = self.client.retrieve_account_metadata(restricted=True,
315 self.assertTrue('premium' in meta)
317 class AccountGet(BaseTestCase):
319 BaseTestCase.setUp(self)
320 #create some containers
321 self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
322 self.containers.sort()
324 for item in self.containers:
325 self.client.create_container(item)
329 containers = self.client.list_containers()
330 self.assertEquals(self.containers, containers)
332 def test_list_403(self):
333 self.assert_raises_fault(403, self.invalid_client.list_containers)
335 def test_list_with_limit(self):
337 containers = self.client.list_containers(limit=limit)
338 self.assertEquals(len(containers), limit)
339 self.assertEquals(self.containers[:2], containers)
341 def test_list_with_marker(self):
344 containers = self.client.list_containers(limit=l, marker=m)
345 i = self.containers.index(m) + 1
346 self.assertEquals(self.containers[i:(i+l)], containers)
349 containers = self.client.list_containers(limit=l, marker=m)
350 i = self.containers.index(m) + 1
351 self.assertEquals(self.containers[i:(i+l)], containers)
353 def test_list_json_with_marker(self):
356 containers = self.client.list_containers(limit=l, marker=m, format='json')
357 self.assert_extended(containers, 'json', 'container', l)
358 self.assertEqual(containers[0]['name'], 'kiwis')
359 self.assertEqual(containers[1]['name'], 'oranges')
361 def test_list_xml_with_marker(self):
364 xml = self.client.list_containers(limit=l, marker=m, format='xml')
365 self.assert_extended(xml, 'xml', 'container', l)
366 nodes = xml.getElementsByTagName('name')
367 self.assertTrue(len(nodes) <= l)
368 names = [n.childNodes[0].data for n in nodes]
369 self.assertTrue('pears' in names or 'pears' > name for name in names)
371 def test_if_modified_since(self):
372 t = datetime.datetime.utcnow()
373 t2 = t - datetime.timedelta(minutes=10)
376 self.client.create_container('dummy')
378 for f in DATE_FORMATS:
379 past = t2.strftime(f)
381 c = self.client.list_containers(if_modified_since=past)
382 self.assertEqual(len(c), len(self.containers) + 1)
384 self.failIf(f.status == 304) #fail if not modified
386 def test_if_modified_since_invalid_date(self):
387 c = self.client.list_containers(if_modified_since='')
388 self.assertEqual(len(c), len(self.containers))
390 def test_if_not_modified_since(self):
391 now = datetime.datetime.utcnow()
392 since = now + datetime.timedelta(1)
394 for f in DATE_FORMATS:
395 args = {'if_modified_since':'%s' %since.strftime(f)}
398 self.assert_raises_fault(304, self.client.list_containers, **args)
400 def test_if_unmodified_since(self):
401 now = datetime.datetime.utcnow()
402 since = now + datetime.timedelta(1)
404 for f in DATE_FORMATS:
405 c = self.client.list_containers(if_unmodified_since=since.strftime(f))
408 self.assertEqual(self.containers, c)
410 def test_if_unmodified_since_precondition_failed(self):
411 t = datetime.datetime.utcnow()
412 t2 = t - datetime.timedelta(minutes=10)
415 self.client.create_container('dummy')
417 for f in DATE_FORMATS:
418 past = t2.strftime(f)
420 args = {'if_unmodified_since':'%s' %past}
422 #assert precondition failed
423 self.assert_raises_fault(412, self.client.list_containers, **args)
425 class AccountPost(BaseTestCase):
427 BaseTestCase.setUp(self)
428 self.containers = list(set(self.initial_containers + ['apples', 'bananas', 'kiwis', 'oranges', 'pears']))
429 self.containers.sort()
431 for item in self.containers:
432 self.client.create_container(item)
435 self.client.update_account_metadata(**meta)
436 self.updated_meta = self.initial_meta.update(meta)
438 def test_update_meta(self):
439 with AssertMappingInvariant(self.client.retrieve_account_groups):
440 meta = {'test':'test', 'tost':'tost'}
441 self.client.update_account_metadata(**meta)
443 meta.update(self.initial_meta)
444 self.assertEqual(meta,
445 self.client.retrieve_account_metadata(
448 def test_invalid_account_update_meta(self):
449 meta = {'test':'test', 'tost':'tost'}
450 self.assert_raises_fault(403,
451 self.invalid_client.update_account_metadata,
454 def test_reset_meta(self):
455 with AssertMappingInvariant(self.client.retrieve_account_groups):
456 meta = {'test':'test', 'tost':'tost'}
457 self.client.update_account_metadata(**meta)
459 meta = {'test':'test33'}
460 self.client.reset_account_metadata(**meta)
462 self.assertEqual(meta, self.client.retrieve_account_metadata(restricted=True))
464 def test_delete_meta(self):
465 with AssertMappingInvariant(self.client.retrieve_account_groups):
466 meta = {'test':'test', 'tost':'tost'}
467 self.client.update_account_metadata(**meta)
469 self.client.delete_account_metadata(meta.keys())
471 account_meta = self.client.retrieve_account_metadata(restricted=True)
473 self.assertTrue(m not in account_meta.keys())
475 def test_set_account_groups(self):
476 with AssertMappingInvariant(self.client.retrieve_account_metadata):
477 groups = {'pithosdev':'verigak,gtsouk,chazapis'}
478 self.client.set_account_groups(**groups)
480 self.assertEqual(set(groups['pithosdev']),
481 set(self.client.retrieve_account_groups()['pithosdev']))
483 more_groups = {'clientsdev':'pkanavos,mvasilak'}
484 self.client.set_account_groups(**more_groups)
486 groups.update(more_groups)
487 self.assertEqual(set(groups['clientsdev']),
488 set(self.client.retrieve_account_groups()['clientsdev']))
490 def test_reset_account_groups(self):
491 with AssertMappingInvariant(self.client.retrieve_account_metadata):
492 groups = {'pithosdev':'verigak,gtsouk,chazapis',
493 'clientsdev':'pkanavos,mvasilak'}
494 self.client.set_account_groups(**groups)
496 self.assertEqual(set(groups['pithosdev'].split(',')),
497 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
498 self.assertEqual(set(groups['clientsdev'].split(',')),
499 set(self.client.retrieve_account_groups()['clientsdev'].split(',')))
501 groups = {'pithosdev':'verigak,gtsouk,chazapis,papagian'}
502 self.client.reset_account_groups(**groups)
504 self.assertEqual(set(groups['pithosdev'].split(',')),
505 set(self.client.retrieve_account_groups()['pithosdev'].split(',')))
507 def test_delete_account_groups(self):
508 with AssertMappingInvariant(self.client.retrieve_account_metadata):
509 groups = {'pithosdev':'verigak,gtsouk,chazapis',
510 'clientsdev':'pkanavos,mvasilak'}
511 self.client.set_account_groups(**groups)
513 self.client.unset_account_groups(groups.keys())
515 self.assertEqual({}, self.client.retrieve_account_groups())
517 class ContainerHead(BaseTestCase):
519 BaseTestCase.setUp(self)
520 self.container = 'apples'
521 self.client.create_container(self.container)
523 def test_get_meta(self):
524 meta = {'trash':'true'}
525 t1 = datetime.datetime.utcnow()
526 o = self.upload_random_data(self.container, o_names[0], **meta)
528 headers = self.client.retrieve_container_metadata(self.container)
529 self.assertEqual(headers['x-container-object-count'], '1')
530 self.assertEqual(headers['x-container-bytes-used'], str(len(o['data'])))
531 t2 = datetime.datetime.strptime(headers['last-modified'], DATE_FORMATS[2])
533 threashold = datetime.timedelta(seconds=1)
534 self.assertTrue(delta < threashold)
535 self.assertTrue(headers['x-container-object-meta'])
536 self.assertTrue('Trash' in headers['x-container-object-meta'])
538 class ContainerGet(BaseTestCase):
540 BaseTestCase.setUp(self)
541 self.container = ['pears', 'apples']
542 for c in self.container:
543 self.client.create_container(c)
545 for o in o_names[:8]:
546 self.obj.append(self.upload_random_data(self.container[0], o))
547 for o in o_names[8:]:
548 self.obj.append(self.upload_random_data(self.container[1], o))
550 def test_list_shared(self):
551 self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
552 objs = self.client.list_objects(self.container[0], shared=True)
553 self.assertEqual(objs, [self.obj[0]['name']])
555 # create child object
556 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
557 objs = self.client.list_objects(self.container[0], shared=True)
558 self.assertEqual(objs, [self.obj[0]['name']])
561 self.client.create_folder(self.container[1], 'folder')
562 self.client.share_object(self.container[1], 'folder', ('*',))
563 self.upload_random_data(self.container[1], 'folder/object')
564 objs = self.client.list_objects(self.container[1], shared=True)
565 self.assertEqual(objs, ['folder', 'folder/object'])
567 def test_list_public(self):
568 self.client.publish_object(self.container[0], self.obj[0]['name'])
569 objs = self.client.list_objects(self.container[0], public=True)
570 self.assertEqual(objs, [self.obj[0]['name']])
572 # create child object
573 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
574 objs = self.client.list_objects(self.container[0], public=True)
575 self.assertEqual(objs, [self.obj[0]['name']])
578 self.client.create_folder(self.container[1], 'folder')
579 self.client.publish_object(self.container[1], 'folder')
580 self.upload_random_data(self.container[1], 'folder/object')
581 objs = self.client.list_objects(self.container[1], public=True)
582 self.assertEqual(objs, ['folder'])
584 def test_list_shared_public(self):
585 self.client.share_object(self.container[0], self.obj[0]['name'], ('*',))
586 self.client.publish_object(self.container[0], self.obj[1]['name'])
587 objs = self.client.list_objects(self.container[0], shared=True, public=True)
588 self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
590 # create child object
591 self.upload_random_data(self.container[0], strnextling(self.obj[0]['name']))
592 self.upload_random_data(self.container[0], strnextling(self.obj[1]['name']))
593 objs = self.client.list_objects(self.container[0], shared=True, public=True)
594 self.assertEqual(objs, [self.obj[0]['name'], self.obj[1]['name']])
597 self.client.create_folder(self.container[1], 'folder1')
598 self.client.share_object(self.container[1], 'folder1', ('*',))
599 self.upload_random_data(self.container[1], 'folder1/object')
600 self.client.create_folder(self.container[1], 'folder2')
601 self.client.publish_object(self.container[1], 'folder2')
602 o = self.upload_random_data(self.container[1], 'folder2/object')
603 objs = self.client.list_objects(self.container[1], shared=True, public=True)
604 self.assertEqual(objs, ['folder1', 'folder1/object', 'folder2'])
606 def test_list_objects(self):
607 objects = self.client.list_objects(self.container[0])
608 l = [elem['name'] for elem in self.obj[:8]]
610 self.assertEqual(objects, l)
612 def test_list_objects_containing_slash(self):
613 self.client.create_container('test')
614 self.upload_random_data('test', '/objectname')
616 objects = self.client.list_objects('test')
617 self.assertEqual(objects, ['/objectname'])
619 objects = self.client.list_objects('test', format='json')
620 self.assertEqual(objects[0]['name'], '/objectname')
622 objects = self.client.list_objects('test', format='xml')
623 self.assert_extended(objects, 'xml', 'object')
624 node_name = objects.getElementsByTagName('name')[0]
625 self.assertEqual(node_name.firstChild.data, '/objectname')
627 def test_list_objects_with_limit_marker(self):
628 objects = self.client.list_objects(self.container[0], limit=2)
629 l = [elem['name'] for elem in self.obj[:8]]
631 self.assertEqual(objects, l[:2])
633 markers = ['How To Win Friends And Influence People.pdf',
637 objects = self.client.list_objects(self.container[0], limit=limit,
639 l = [elem['name'] for elem in self.obj[:8]]
641 start = l.index(m) + 1
643 end = end if len(l) >= end else len(l)
644 self.assertEqual(objects, l[start:end])
647 def _test_list_limit_exceeds(self):
648 self.client.create_container('pithos')
650 for i in range(10001):
651 self.client.create_zero_length_object('pithos', i)
653 self.assertEqual(10000, len(self.client.list_objects('pithos')))
655 def test_list_empty_params(self):
656 objects = self.client.get('/%s/%s' % (get_user(), self.container[0]))[2]
658 objects = objects.strip().split('\n')
659 self.assertEqual(objects,
660 self.client.list_objects(self.container[0]))
662 def test_list_pseudo_hierarchical_folders(self):
663 objects = self.client.list_objects(self.container[1], prefix='photos',
665 self.assertEquals(['photos/animals/', 'photos/me.jpg',
666 'photos/plants/'], objects)
668 objects = self.client.list_objects(self.container[1],
669 prefix='photos/animals',
671 l = ['photos/animals/cats/', 'photos/animals/dogs/']
672 self.assertEquals(l, objects)
674 objects = self.client.list_objects(self.container[1], path='photos')
675 self.assertEquals(['photos/me.jpg'], objects)
677 def test_extended_list_json(self):
678 objects = self.client.list_objects(self.container[1], format='json',
679 limit=2, prefix='photos/animals',
681 self.assertEqual(objects[0]['subdir'], 'photos/animals/cats/')
682 self.assertEqual(objects[1]['subdir'], 'photos/animals/dogs/')
684 def test_extended_list_xml(self):
685 xml = self.client.list_objects(self.container[1], format='xml', limit=4,
686 prefix='photos', delimiter='/')
687 self.assert_extended(xml, 'xml', 'object', size=4)
688 dirs = xml.getElementsByTagName('subdir')
689 self.assertEqual(len(dirs), 2)
690 self.assertEqual(dirs[0].attributes['name'].value, 'photos/animals/')
691 self.assertEqual(dirs[1].attributes['name'].value, 'photos/plants/')
693 objects = xml.getElementsByTagName('name')
694 self.assertEqual(len(objects), 1)
695 self.assertEqual(objects[0].childNodes[0].data, 'photos/me.jpg')
697 def test_list_meta_double_matching(self):
698 meta = {'quality':'aaa', 'stock':'true'}
699 self.client.update_object_metadata(self.container[0],
700 self.obj[0]['name'], **meta)
701 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
702 self.assertEqual(len(obj), 1)
703 self.assertTrue(obj, self.obj[0]['name'])
705 def test_list_using_meta(self):
706 meta = {'quality':'aaa'}
707 for o in self.obj[:2]:
708 self.client.update_object_metadata(self.container[0], o['name'],
710 meta = {'stock':'true'}
711 for o in self.obj[3:5]:
712 self.client.update_object_metadata(self.container[0], o['name'],
715 obj = self.client.list_objects(self.container[0], meta='Quality')
716 self.assertEqual(len(obj), 2)
717 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
719 # test case insensitive
720 obj = self.client.list_objects(self.container[0], meta='quality')
721 self.assertEqual(len(obj), 2)
722 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
724 # test multiple matches
725 obj = self.client.list_objects(self.container[0], meta='Quality,Stock')
726 self.assertEqual(len(obj), 4)
727 self.assertTrue(obj, [o['name'] for o in self.obj[:4]])
729 # test non 1-1 multiple match
730 obj = self.client.list_objects(self.container[0], meta='Quality,aaaa')
731 self.assertEqual(len(obj), 2)
732 self.assertTrue(obj, [o['name'] for o in self.obj[:2]])
734 def test_if_modified_since(self):
735 t = datetime.datetime.utcnow()
736 t2 = t - datetime.timedelta(minutes=10)
739 self.upload_random_data(self.container[0], o_names[0])
741 for f in DATE_FORMATS:
742 past = t2.strftime(f)
744 o = self.client.list_objects(self.container[0],
745 if_modified_since=past)
747 self.client.list_objects(self.container[0]))
749 self.failIf(f.status == 304) #fail if not modified
751 def test_if_modified_since_invalid_date(self):
752 headers = {'if-modified-since':''}
753 o = self.client.list_objects(self.container[0], if_modified_since='')
754 self.assertEqual(o, self.client.list_objects(self.container[0]))
756 def test_if_not_modified_since(self):
757 now = datetime.datetime.utcnow()
758 since = now + datetime.timedelta(1)
760 for f in DATE_FORMATS:
761 args = {'if_modified_since':'%s' %since.strftime(f)}
764 self.assert_raises_fault(304, self.client.list_objects,
765 self.container[0], **args)
767 def test_if_unmodified_since(self):
768 now = datetime.datetime.utcnow()
769 since = now + datetime.timedelta(1)
771 for f in DATE_FORMATS:
772 obj = self.client.list_objects(self.container[0],
773 if_unmodified_since=since.strftime(f))
776 self.assertEqual(obj, self.client.list_objects(self.container[0]))
778 def test_if_unmodified_since_precondition_failed(self):
779 t = datetime.datetime.utcnow()
780 t2 = t - datetime.timedelta(minutes=10)
783 self.client.create_container('dummy')
785 for f in DATE_FORMATS:
786 past = t2.strftime(f)
788 args = {'if_unmodified_since':'%s' %past}
790 #assert precondition failed
791 self.assert_raises_fault(412, self.client.list_objects,
792 self.container[0], **args)
794 class ContainerPut(BaseTestCase):
796 BaseTestCase.setUp(self)
797 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
798 self.containers.sort()
800 def test_create(self):
801 self.client.create_container(self.containers[0])
802 containers = self.client.list_containers()
803 self.assertTrue(self.containers[0] in containers)
804 self.assert_container_exists(self.containers[0])
806 def test_create_twice(self):
807 self.client.create_container(self.containers[0])
808 self.assertTrue(not self.client.create_container(self.containers[0]))
810 def test_quota(self):
811 self.client.create_container(self.containers[0])
813 policy = {'quota':100}
814 self.client.set_container_policies(self.containers[0], **policy)
816 meta = self.client.retrieve_container_metadata(self.containers[0])
817 self.assertTrue('x-container-policy-quota' in meta)
818 self.assertEqual(meta['x-container-policy-quota'], '100')
820 args = [self.containers[0], 'o1']
821 kwargs = {'length':101}
822 self.assert_raises_fault(413, self.upload_random_data, *args, **kwargs)
826 self.client.set_container_policies(self.containers[0], **policy)
828 class ContainerPost(BaseTestCase):
830 BaseTestCase.setUp(self)
831 self.container = 'apples'
832 self.client.create_container(self.container)
834 def test_update_meta(self):
835 meta = {'test':'test33',
837 self.client.update_container_metadata(self.container, **meta)
838 headers = self.client.retrieve_container_metadata(self.container)
839 for k,v in meta.items():
840 k = 'x-container-meta-%s' % k
841 self.assertTrue(headers[k])
842 self.assertEqual(headers[k], v)
844 class ContainerDelete(BaseTestCase):
846 BaseTestCase.setUp(self)
847 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
848 self.containers.sort()
850 for c in self.containers:
851 self.client.create_container(c)
853 def test_delete(self):
854 status = self.client.delete_container(self.containers[0])[0]
855 self.assertEqual(status, 204)
857 def test_delete_non_empty(self):
858 self.upload_random_data(self.containers[1], o_names[0])
859 self.assert_raises_fault(409, self.client.delete_container,
862 def test_delete_invalid(self):
863 self.assert_raises_fault(404, self.client.delete_container, 'c3')
865 def test_delete_contents(self):
866 self.client.create_folder(self.containers[0], 'folder-1')
867 self.upload_random_data(self.containers[1], 'folder-1/%s' % o_names[0])
868 self.client.create_folder(self.containers[0], 'folder-1/subfolder')
869 self.client.create_folder(self.containers[0], 'folder-2/%s' % o_names[1])
871 objects = self.client.list_objects(self.containers[0])
872 self.client.delete_container(self.containers[0], delimiter='/')
874 self.assert_object_not_exists(self.containers[0], o)
875 self.assert_container_exists(self.containers[0])
877 class ObjectGet(BaseTestCase):
879 BaseTestCase.setUp(self)
880 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
881 self.containers.sort()
883 #create some containers
884 for c in self.containers:
885 self.client.create_container(c)
888 names = ('obj1', 'obj2')
891 self.objects.append(self.upload_random_data(self.containers[1], n))
893 def test_versions(self):
894 c = self.containers[1]
896 b = self.client.retrieve_object_versionlist(c, o['name'])['versions']
897 self.assert_versionlist_structure(b)
900 meta = {'quality':'AAA', 'stock':True}
901 self.client.update_object_metadata(c, o['name'], **meta)
903 a = self.client.retrieve_object_versionlist(c, o['name'])['versions']
904 self.assert_versionlist_structure(a)
905 self.assertEqual(len(b)+1, len(a))
906 self.assertEqual(b, a[:-1])
908 #get exact previous version metadata
910 v_meta = self.client.retrieve_object_metadata(c, o['name'],
913 (self.assertTrue(k not in v_meta) for k in meta.keys())
916 data = get_random_data()
917 self.client.update_object(c, o['name'], StringIO(data))
919 aa = self.client.retrieve_object_versionlist(c, o['name'])['versions']
920 self.assert_versionlist_structure(aa)
921 self.assertEqual(len(a)+1, len(aa))
922 self.assertEqual(a, aa[:-1])
924 #get exact previous version
926 v_data = self.client.retrieve_object_version(c, o['name'], version=v)
927 self.assertEqual(o['data'], v_data)
928 self.assertEqual(self.client.retrieve_object(c, o['name']),
929 '%s%s' %(v_data, data))
933 o = self.client.retrieve_object(self.containers[1],
934 self.objects[0]['name'],
935 self.objects[0]['meta'])
936 self.assertEqual(o, self.objects[0]['data'])
938 def test_objects_with_trailing_spaces(self):
939 self.client.create_container('test')
941 self.upload_random_data('test', 'a')
942 #look for 'a ' object
943 self.assert_raises_fault(404, self.client.retrieve_object,
947 self.client.delete_object('test', 'a')
948 self.assert_raises_fault(404, self.client.retrieve_object,
952 self.upload_random_data('test', 'a ')
954 self.assert_raises_fault(404, self.client.retrieve_object,
957 def test_get_invalid(self):
958 self.assert_raises_fault(404, self.client.retrieve_object,
959 self.containers[0], self.objects[0]['name'])
961 def test_get_partial(self):
962 #perform get with range
963 status, headers, data = self.client.request_object(self.containers[1],
964 self.objects[0]['name'],
967 #assert successful partial content
968 self.assertEqual(status, 206)
971 self.assertEqual(headers['content-type'],
972 self.objects[0]['meta']['content_type'])
974 #assert content length
975 self.assertEqual(int(headers['content-length']), 500)
978 self.assertEqual(self.objects[0]['data'][:500], data)
980 def test_get_final_500(self):
981 #perform get with range
982 headers = {'range':'bytes=-500'}
983 status, headers, data = self.client.request_object(self.containers[1],
984 self.objects[0]['name'],
987 #assert successful partial content
988 self.assertEqual(status, 206)
991 self.assertEqual(headers['content-type'],
992 self.objects[0]['meta']['content_type'])
994 #assert content length
995 self.assertEqual(int(headers['content-length']), 500)
998 self.assertTrue(self.objects[0]['data'][-500:], data)
1000 def test_get_rest(self):
1001 #perform get with range
1002 offset = len(self.objects[0]['data']) - 500
1003 status, headers, data = self.client.request_object(self.containers[1],
1004 self.objects[0]['name'],
1005 range='bytes=%s-' %offset)
1007 #assert successful partial content
1008 self.assertEqual(status, 206)
1010 #assert content-type
1011 self.assertEqual(headers['content-type'],
1012 self.objects[0]['meta']['content_type'])
1014 #assert content length
1015 self.assertEqual(int(headers['content-length']), 500)
1018 self.assertTrue(self.objects[0]['data'][-500:], data)
1020 def test_get_range_not_satisfiable(self):
1021 #perform get with range
1022 offset = len(self.objects[0]['data']) + 1
1024 #assert range not satisfiable
1025 self.assert_raises_fault(416, self.client.retrieve_object,
1026 self.containers[1], self.objects[0]['name'],
1027 range='bytes=0-%s' %offset)
1029 def test_multiple_range(self):
1030 #perform get with multiple range
1031 ranges = ['0-499', '-500', '1000-']
1032 bytes = 'bytes=%s' % ','.join(ranges)
1033 status, headers, data = self.client.request_object(self.containers[1],
1034 self.objects[0]['name'],
1037 # assert partial content
1038 self.assertEqual(status, 206)
1040 # assert Content-Type of the reply will be multipart/byteranges
1041 self.assertTrue(headers['content-type'])
1042 content_type_parts = headers['content-type'].split()
1043 self.assertEqual(content_type_parts[0], ('multipart/byteranges;'))
1045 boundary = '--%s' %content_type_parts[1].split('=')[-1:][0]
1046 cparts = data.split(boundary)[1:-1]
1048 # assert content parts are exactly 2
1049 self.assertEqual(len(cparts), len(ranges))
1051 # for each content part assert headers
1053 for cpart in cparts:
1054 content = cpart.split('\r\n')
1055 headers = content[1:3]
1056 content_range = headers[0].split(': ')
1057 self.assertEqual(content_range[0], 'Content-Range')
1059 r = ranges[i].split('-')
1060 if not r[0] and not r[1]:
1063 start = len(self.objects[0]['data']) - int(r[1])
1064 end = len(self.objects[0]['data'])
1067 end = len(self.objects[0]['data'])
1071 fdata = self.objects[0]['data'][start:end]
1072 sdata = '\r\n'.join(content[4:-1])
1073 self.assertEqual(len(fdata), len(sdata))
1074 self.assertEquals(fdata, sdata)
1077 def test_multiple_range_not_satisfiable(self):
1078 #perform get with multiple range
1079 out_of_range = len(self.objects[0]['data']) + 1
1080 ranges = ['0-499', '-500', '%d-' %out_of_range]
1081 bytes = 'bytes=%s' % ','.join(ranges)
1083 # assert partial content
1084 self.assert_raises_fault(416, self.client.retrieve_object,
1086 self.objects[0]['name'], range=bytes)
1088 def test_get_with_if_match(self):
1089 #perform get with If-Match
1090 etag = self.objects[0]['hash']
1091 status, headers, data = self.client.request_object(self.containers[1],
1092 self.objects[0]['name'],
1095 self.assertEqual(status, 200)
1097 #assert content-type
1098 self.assertEqual(headers['content-type'],
1099 self.objects[0]['meta']['content_type'])
1101 #assert response content
1102 self.assertEqual(self.objects[0]['data'], data)
1104 def test_get_with_if_match_star(self):
1105 #perform get with If-Match *
1106 headers = {'if-match':'*'}
1107 status, headers, data = self.client.request_object(self.containers[1],
1108 self.objects[0]['name'],
1111 self.assertEqual(status, 200)
1113 #assert content-type
1114 self.assertEqual(headers['content-type'],
1115 self.objects[0]['meta']['content_type'])
1117 #assert response content
1118 self.assertEqual(self.objects[0]['data'], data)
1120 def test_get_with_multiple_if_match(self):
1121 #perform get with If-Match
1122 etags = [i['hash'] for i in self.objects if i]
1123 etags = ','.join('"%s"' % etag for etag in etags)
1124 status, headers, data = self.client.request_object(self.containers[1],
1125 self.objects[0]['name'],
1128 self.assertEqual(status, 200)
1130 #assert content-type
1131 self.assertEqual(headers['content-type'],
1132 self.objects[0]['meta']['content_type'])
1134 #assert content-type
1135 self.assertEqual(headers['content-type'],
1136 self.objects[0]['meta']['content_type'])
1138 #assert response content
1139 self.assertEqual(self.objects[0]['data'], data)
1141 def test_if_match_precondition_failed(self):
1142 #assert precondition failed
1143 self.assert_raises_fault(412, self.client.retrieve_object,
1145 self.objects[0]['name'], if_match='123')
1147 def test_if_none_match(self):
1148 #perform get with If-None-Match
1149 status, headers, data = self.client.request_object(self.containers[1],
1150 self.objects[0]['name'],
1151 if_none_match='123')
1154 self.assertEqual(status, 200)
1156 #assert content-type
1157 self.assertEqual(headers['content_type'],
1158 self.objects[0]['meta']['content_type'])
1160 def test_if_none_match(self):
1161 #perform get with If-None-Match * and assert not modified
1162 self.assert_raises_fault(304, self.client.retrieve_object,
1164 self.objects[0]['name'],
1167 def test_if_none_match_not_modified(self):
1168 #perform get with If-None-Match and assert not modified
1169 self.assert_raises_fault(304, self.client.retrieve_object,
1171 self.objects[0]['name'],
1172 if_none_match=self.objects[0]['hash'])
1174 meta = self.client.retrieve_object_metadata(self.containers[1],
1175 self.objects[0]['name'])
1176 self.assertEqual(meta['etag'], self.objects[0]['hash'])
1178 def test_if_modified_since(self):
1179 t = datetime.datetime.utcnow()
1180 t2 = t - datetime.timedelta(minutes=10)
1183 self.upload_data(self.containers[1],
1184 self.objects[0]['name'],
1185 self.objects[0]['data'][:200])
1187 for f in DATE_FORMATS:
1188 past = t2.strftime(f)
1190 headers = {'if-modified-since':'%s' %past}
1192 o = self.client.retrieve_object(self.containers[1],
1193 self.objects[0]['name'],
1194 if_modified_since=past)
1196 self.client.retrieve_object(self.containers[1],
1197 self.objects[0]['name']))
1199 self.failIf(f.status == 304)
1201 def test_if_modified_since_invalid_date(self):
1202 o = self.client.retrieve_object(self.containers[1],
1203 self.objects[0]['name'],
1204 if_modified_since='')
1205 self.assertEqual(o, self.client.retrieve_object(self.containers[1],
1206 self.objects[0]['name']))
1208 def test_if_not_modified_since(self):
1209 now = datetime.datetime.utcnow()
1210 since = now + datetime.timedelta(1)
1212 for f in DATE_FORMATS:
1213 #assert not modified
1214 self.assert_raises_fault(304, self.client.retrieve_object,
1215 self.containers[1], self.objects[0]['name'],
1216 if_modified_since=since.strftime(f))
1218 def test_if_unmodified_since(self):
1219 now = datetime.datetime.utcnow()
1220 since = now + datetime.timedelta(1)
1222 for f in DATE_FORMATS:
1223 t = since.strftime(f)
1224 status, headers, data = self.client.request_object(self.containers[1],
1225 self.objects[0]['name'],
1226 if_unmodified_since=t)
1228 self.assertEqual(status, 200)
1229 self.assertEqual(self.objects[0]['data'], data)
1231 #assert content-type
1232 self.assertEqual(headers['content-type'],
1233 self.objects[0]['meta']['content_type'])
1235 def test_if_unmodified_since_precondition_failed(self):
1236 t = datetime.datetime.utcnow()
1237 t2 = t - datetime.timedelta(minutes=10)
1240 self.upload_data(self.containers[1],
1241 self.objects[0]['name'],
1242 self.objects[0]['data'][:200])
1244 for f in DATE_FORMATS:
1245 past = t2.strftime(f)
1246 #assert precondition failed
1247 self.assert_raises_fault(412, self.client.retrieve_object,
1248 self.containers[1], self.objects[0]['name'],
1249 if_unmodified_since=past)
1251 def test_hashes(self):
1254 o = self.upload_random_data(self.containers[1], fname, l)
1256 body = self.client.retrieve_object(self.containers[1], fname,
1258 hashes = body['hashes']
1259 block_size = body['block_size']
1260 block_hash = body['block_hash']
1261 block_num = l/block_size if l/block_size == 0 else l/block_size + 1
1262 self.assertTrue(len(hashes), block_num)
1265 start = i * block_size
1266 end = (i + 1) * block_size
1267 hash = compute_block_hash(o['data'][start:end], block_hash)
1268 self.assertEqual(h, hash)
1271 class ObjectPut(BaseTestCase):
1273 BaseTestCase.setUp(self)
1274 self.container = 'c1'
1275 self.client.create_container(self.container)
1277 def test_upload(self):
1279 meta = {'test':'test1'}
1280 o = self.upload_random_data(self.container, name, **meta)
1282 headers = self.client.retrieve_object_metadata(self.container,
1285 self.assertTrue('test' in headers.keys())
1286 self.assertEqual(headers['test'], meta['test'])
1288 #assert uploaded content
1289 status, h, data = self.client.request_object(self.container, name)
1290 self.assertEqual(len(o['data']), int(h['content-length']))
1291 self.assertEqual(o['data'], data)
1293 #assert content-type
1294 self.assertEqual(h['content-type'], o['meta']['content_type'])
1296 def _test_maximum_upload_size_exceeds(self):
1298 meta = {'test':'test1'}
1300 length= 5 * (1024 * 1024 * 1024) + 1
1301 self.assert_raises_fault(400, self.upload_random_data, self.container,
1302 name, length, **meta)
1304 def test_upload_with_name_containing_slash(self):
1305 name = '/%s' % o_names[0]
1306 meta = {'test':'test1'}
1307 o = self.upload_random_data(self.container, name, **meta)
1309 self.assertEqual(o['data'],
1310 self.client.retrieve_object(self.container, name))
1312 self.assertTrue(name in self.client.list_objects(self.container))
1314 def test_create_directory_marker(self):
1315 self.client.create_directory_marker(self.container, 'foo')
1316 meta = self.client.retrieve_object_metadata(self.container, 'foo')
1317 self.assertEqual(meta['content-length'], '0')
1318 self.assertEqual(meta['content-type'], 'application/directory')
1320 def test_upload_unprocessable_entity(self):
1321 meta={'etag':'123', 'test':'test1'}
1323 #assert unprocessable entity
1324 self.assert_raises_fault(422, self.upload_random_data, self.container,
1327 def test_chunked_transfer(self):
1328 data = get_random_data()
1330 self.client.create_object_using_chunks(self.container, objname,
1333 uploaded_data = self.client.retrieve_object(self.container, objname)
1334 self.assertEqual(data, uploaded_data)
1336 def test_manifestation(self):
1337 prefix = 'myobject/'
1340 part = '%s%d' %(prefix, i)
1341 o = self.upload_random_data(self.container, part)
1344 manifest = '%s/%s' %(self.container, prefix)
1345 self.client.create_manifestation(self.container, 'large-object', manifest)
1347 self.assert_object_exists(self.container, 'large-object')
1348 self.assertEqual(data, self.client.retrieve_object(self.container,
1351 r = self.client.retrieve_object_hashmap(self.container,'large-object')
1352 hashes = r['hashes']
1353 block_size = int(r['block_size'])
1354 block_hash = r['block_hash']
1356 block_num = l/block_size if l/block_size != 0 else l/block_size + 1
1357 self.assertEqual(block_num, len(hashes))
1359 #wrong manifestation
1360 self.client.create_manifestation(self.container, 'large-object',
1361 '%s/invalid' % self.container)
1362 self.assertEqual('', self.client.retrieve_object(self.container,
1365 def test_create_zero_length_object(self):
1368 zero = self.client.create_zero_length_object(c, o)
1369 zero_meta = self.client.retrieve_object_metadata(c, o)
1370 zero_hash = self.client.retrieve_object_hashmap(c, o)["hashes"]
1371 zero_data = self.client.retrieve_object(c, o)
1373 self.assertEqual(int(zero_meta['content-length']), 0)
1374 hasher = newhasher('sha256')
1376 emptyhash = hasher.digest()
1377 self.assertEqual(zero_hash, [hexlify(emptyhash)])
1378 self.assertEqual(zero_data, '')
1380 def test_create_object_by_hashmap(self):
1383 self.upload_random_data(c, o)
1384 hashmap = self.client.retrieve_object(c, o, format='json')
1386 self.client.create_object_by_hashmap(c, o2, hashmap)
1387 self.assertEqual(self.client.retrieve_object(c, o),
1388 self.client.retrieve_object(c, o))
1390 class ObjectCopy(BaseTestCase):
1392 BaseTestCase.setUp(self)
1393 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1394 self.containers.sort()
1396 for c in self.containers:
1397 self.client.create_container(c)
1398 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1400 def test_copy(self):
1401 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1402 self.containers[0], self.obj['name']):
1404 meta = {'test':'testcopy'}
1405 status = self.client.copy_object(self.containers[0],
1411 #assert copy success
1412 self.assertEqual(status, 201)
1414 #assert access the new object
1415 headers = self.client.retrieve_object_metadata(self.containers[0],
1417 self.assertTrue('x-object-meta-test' in headers.keys())
1418 self.assertTrue(headers['x-object-meta-test'], 'testcopy')
1420 #assert etag is the same
1421 self.assertEqual(headers['etag'], self.obj['hash'])
1423 #assert src object still exists
1424 self.assert_object_exists(self.containers[0], self.obj['name'])
1426 def test_copy_from_different_container(self):
1427 with AssertMappingInvariant(self.client.retrieve_object_metadata,
1428 self.containers[0], self.obj['name']):
1429 meta = {'test':'testcopy'}
1430 status = self.client.copy_object(self.containers[0],
1435 self.assertEqual(status, 201)
1437 # assert updated metadata
1438 meta = self.client.retrieve_object_metadata(self.containers[1],
1441 self.assertTrue('test' in meta.keys())
1442 self.assertTrue(meta['test'], 'testcopy')
1444 #assert src object still exists
1445 self.assert_object_exists(self.containers[0], self.obj['name'])
1447 def test_copy_invalid(self):
1448 #copy from invalid object
1449 meta = {'test':'testcopy'}
1450 self.assert_raises_fault(404, self.client.copy_object, self.containers[0],
1451 'test.py', self.containers[1], 'testcopy', meta)
1453 #copy from invalid container
1454 meta = {'test':'testcopy'}
1455 self.assert_raises_fault(404, self.client.copy_object, self.containers[1],
1456 self.obj['name'], self.containers[1],
1459 def test_copy_dir(self):
1460 self.client.create_folder(self.containers[0], 'dir')
1461 self.client.create_folder(self.containers[0], 'dir/subdir')
1462 self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1463 self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1464 self.client.create_folder(self.containers[0], 'dirs')
1466 objects = self.client.list_objects(self.containers[0], prefix='dir')
1467 self.client.copy_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1468 for object in objects[:-1]:
1469 self.assert_object_exists(self.containers[0], object)
1470 self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1471 meta0 = self.client.retrieve_object_metadata(self.containers[0], object)
1472 meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1473 t = ('content-length', 'x-object-hash', 'content-type')
1474 (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1475 self.assert_object_not_exists(self.containers[1], objects[-1])
1477 class ObjectMove(BaseTestCase):
1479 BaseTestCase.setUp(self)
1480 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1481 self.containers.sort()
1483 for c in self.containers:
1484 self.client.create_container(c)
1485 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1487 def test_move(self):
1488 meta = self.client.retrieve_object_metadata(self.containers[0],
1490 self.assertTrue('x-object-uuid' in meta)
1491 uuid = meta['x-object-uuid']
1494 meta = {'test':'testcopy'}
1495 src_path = '/'.join(('/', self.containers[0], self.obj['name']))
1496 status = self.client.move_object(self.containers[0], self.obj['name'],
1497 self.containers[0], 'testcopy',
1500 #assert successful move
1501 self.assertEqual(status, 201)
1503 #assert updated metadata
1504 meta = self.client.retrieve_object_metadata(self.containers[0],
1506 self.assertTrue('x-object-meta-test' in meta.keys())
1507 self.assertTrue(meta['x-object-meta-test'], 'testcopy')
1510 self.assertTrue(meta['x-object-uuid'], uuid)
1512 #assert src object no more exists
1513 self.assert_object_not_exists(self.containers[0], self.obj['name'])
1516 def test_move_dir(self):
1518 self.client.create_folder(self.containers[0], 'dir')
1519 meta['dir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir')
1520 self.client.create_folder(self.containers[0], 'dir/subdir')
1521 meta['dir/subdir'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir')
1522 self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1523 meta['dir/object1.jpg'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/object1.jpg')
1524 self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1525 meta['dir/subdir/object2.pdf'] = self.client.retrieve_object_metadata(self.containers[0], 'dir/subdir/object2.pdf')
1526 self.client.create_folder(self.containers[0], 'dirs')
1527 meta['dirs'] = self.client.retrieve_object_metadata(self.containers[0], 'dirs')
1529 objects = self.client.list_objects(self.containers[0], prefix='dir')
1530 self.client.move_object(self.containers[0], 'dir', self.containers[1], 'dir-backup', delimiter='/')
1531 for object in objects[:-1]:
1532 self.assert_object_not_exists(self.containers[0], object)
1533 self.assert_object_exists(self.containers[1], object.replace('dir', 'dir-backup', 1))
1534 meta1 = self.client.retrieve_object_metadata(self.containers[1], object.replace('dir', 'dir-backup', 1))
1535 t = ('content-length', 'x-object-hash', 'content-type')
1536 (self.assertEqual(meta0[elem], meta1[elem]) for elem in t)
1537 self.assert_object_exists(self.containers[0], objects[-1])
1538 self.assert_object_not_exists(self.containers[1], objects[-1])
1540 class ObjectPost(BaseTestCase):
1542 BaseTestCase.setUp(self)
1543 self.containers = list(set(self.initial_containers + ['c1', 'c2']))
1544 self.containers.sort()
1546 for c in self.containers:
1547 self.client.create_container(c)
1550 self.obj.append(self.upload_random_data(self.containers[0], o_names[i]))
1552 def test_update_meta(self):
1553 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1555 self.obj[0]['name']):
1556 #perform update metadata
1557 more = {'foo': 'foo', 'bar': 'bar', 'f' * 114: 'b' * 256}
1558 status = self.client.update_object_metadata(self.containers[0],
1559 self.obj[0]['name'],
1561 #assert request accepted
1562 self.assertEqual(status, 202)
1564 #assert old metadata are still there
1565 headers = self.client.retrieve_object_metadata(self.containers[0],
1566 self.obj[0]['name'],
1568 #assert new metadata have been updated
1569 for k,v in more.items():
1570 self.assertTrue(k in headers.keys())
1571 self.assertTrue(headers[k], v)
1574 more = {'f' * 114: 'b' * 257}
1575 self.assert_raises_fault(400, self.client.update_object_metadata,
1577 self.obj[0]['name'],
1580 #perform update metadata
1581 more = {'α': 'β' * 256}
1582 status = self.client.update_object_metadata(self.containers[0],
1583 self.obj[0]['name'],
1585 #assert request accepted
1586 self.assertEqual(status, 202)
1588 #assert old metadata are still there
1589 headers = self.client.retrieve_object_metadata(self.containers[0],
1590 self.obj[0]['name'],
1592 #assert new metadata have been updated
1593 for k,v in more.items():
1594 self.assertTrue(k in headers.keys())
1595 self.assertTrue(headers[k], v)
1598 more = {'α': 'β' * 257}
1599 self.assert_raises_fault(400, self.client.update_object_metadata,
1601 self.obj[0]['name'],
1604 def test_update_object(self,
1607 instance_length = True,
1608 content_length = 500):
1609 with AssertUUidInvariant(self.client.retrieve_object_metadata,
1611 self.obj[0]['name']):
1612 l = len(self.obj[0]['data'])
1613 range = 'bytes %d-%d/%s' %(first_byte_pos,
1615 l if instance_length else '*')
1616 partial = last_byte_pos - first_byte_pos + 1
1617 length = first_byte_pos + partial
1618 data = get_random_data(partial)
1619 args = {'content_type':'application/octet-stream',
1620 'content_range':'%s' %range}
1622 args['content_length'] = content_length
1624 r = self.client.update_object(self.containers[0], self.obj[0]['name'],
1625 StringIO(data), **args)
1628 if partial < 0 or (instance_length and l <= last_byte_pos):
1629 self.assertEqual(status, 202)
1631 self.assertEqual(status, 204)
1632 #check modified object
1633 content = self.client.retrieve_object(self.containers[0],
1634 self.obj[0]['name'])
1635 self.assertEqual(content[:first_byte_pos], self.obj[0]['data'][:first_byte_pos])
1636 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1637 self.assertEqual(content[last_byte_pos+1:], self.obj[0]['data'][last_byte_pos+1:])
1638 self.assertEqual(etag, compute_md5_hash(content))
1640 def test_update_object_lt_blocksize(self):
1641 self.test_update_object(10, 20, content_length=None)
1643 def test_update_object_gt_blocksize(self):
1644 o = self.upload_random_data(self.containers[0], o_names[1],
1645 length=4*1024*1024+5)
1646 c = self.containers[0]
1649 first_byte_pos = 4*1024*1024+1
1650 last_byte_pos = 4*1024*1024+4
1651 l = last_byte_pos - first_byte_pos + 1
1652 data = get_random_data(l)
1653 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1654 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1655 content = self.client.retrieve_object(c, o_name)
1656 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1657 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1658 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1660 def test_update_object_divided_by_blocksize(self):
1661 o = self.upload_random_data(self.containers[0], o_names[1],
1662 length=4*1024*1024+5)
1663 c = self.containers[0]
1666 first_byte_pos = 4*1024*1024
1667 last_byte_pos = 5*1024*1024
1668 l = last_byte_pos - first_byte_pos + 1
1669 data = get_random_data(l)
1670 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1671 self.client.update_object(c, o_name, StringIO(data), content_range=range)
1672 content = self.client.retrieve_object(c, o_name)
1673 self.assertEqual(content[:first_byte_pos], o_data[:first_byte_pos])
1674 self.assertEqual(content[first_byte_pos:last_byte_pos+1], data)
1675 self.assertEqual(content[last_byte_pos+1:], o_data[last_byte_pos+1:])
1677 def test_update_object_no_content_length(self):
1678 self.test_update_object(content_length = None)
1680 def test_update_object_invalid_content_length(self):
1681 with AssertContentInvariant(self.client.retrieve_object,
1682 self.containers[0], self.obj[0]['name']):
1683 self.assert_raises_fault(400, self.test_update_object,
1684 content_length = 1000)
1686 def test_update_object_invalid_range(self):
1687 with AssertContentInvariant(self.client.retrieve_object,
1688 self.containers[0], self.obj[0]['name']):
1689 self.assert_raises_fault(416, self.test_update_object, 499, 0, True)
1691 def test_update_object_invalid_range_and_length(self):
1692 with AssertContentInvariant(self.client.retrieve_object,
1693 self.containers[0], self.obj[0]['name']):
1694 self.assert_raises_fault([400, 416], self.test_update_object, 499, 0, True,
1697 def test_update_object_invalid_range_with_no_content_length(self):
1698 with AssertContentInvariant(self.client.retrieve_object,
1699 self.containers[0], self.obj[0]['name']):
1700 self.assert_raises_fault(416, self.test_update_object, 499, 0, True,
1701 content_length = None)
1703 def test_update_object_out_of_limits(self):
1704 with AssertContentInvariant(self.client.retrieve_object,
1705 self.containers[0], self.obj[0]['name']):
1706 l = len(self.obj[0]['data'])
1707 self.assert_raises_fault(416, self.test_update_object, 0, l+1, True)
1709 def test_append(self):
1710 data = get_random_data(500)
1712 self.client.update_object(self.containers[0], self.obj[0]['name'],
1713 StringIO(data), content_length=500,
1714 content_type='application/octet-stream')
1716 content = self.client.retrieve_object(self.containers[0],
1717 self.obj[0]['name'])
1718 self.assertEqual(len(content), len(self.obj[0]['data']) + 500)
1719 self.assertEqual(content[:-500], self.obj[0]['data'])
1721 def test_update_with_chunked_transfer(self):
1722 data = get_random_data(500)
1724 fl = len(self.obj[0]['data'])
1726 self.client.update_object_using_chunks(self.containers[0],
1727 self.obj[0]['name'],
1730 content_type='application/octet-stream')
1732 #check modified object
1733 content = self.client.retrieve_object(self.containers[0],
1734 self.obj[0]['name'])
1735 self.assertEqual(content[0:dl], data)
1736 self.assertEqual(content[dl:fl], self.obj[0]['data'][dl:fl])
1738 def test_update_from_other_object(self):
1739 c = self.containers[0]
1743 source_data = self.client.retrieve_object(c, src)
1744 source_meta = self.client.retrieve_object_metadata(c, src)
1745 source_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1747 #update zero length object
1748 self.client.create_zero_length_object(c, dest)
1749 source_object = '/%s/%s' % (c, src)
1750 self.client.update_from_other_source(c, dest, source_object)
1751 dest_data = self.client.retrieve_object(c, src)
1752 dest_meta = self.client.retrieve_object_metadata(c, dest)
1753 dest_hash = self.client.retrieve_object_hashmap(c, src)["hashes"]
1754 self.assertEqual(source_data, dest_data)
1755 self.assertEqual(source_hash, dest_hash)
1758 self.client.update_from_other_source(c, dest, source_object)
1759 content = self.client.retrieve_object(c, dest)
1760 self.assertEqual(source_data * 2, content)
1762 def test_update_range_from_other_object(self):
1763 c = self.containers[0]
1767 src = self.obj[1]['name']
1768 src_data = self.client.retrieve_object(c, src)
1770 #update zero length object
1771 prev_data = self.upload_random_data(c, dest, length=4*1024*1024+10)['data']
1772 source_object = '/%s/%s' % (c, src)
1773 first_byte_pos = 4*1024*1024+1
1774 last_byte_pos = 4*1024*1024+4
1775 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1776 self.client.update_from_other_source(c, dest, source_object,
1777 content_range=range)
1778 content = self.client.retrieve_object(c, dest)
1779 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1780 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1781 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1783 def test_update_hashes_from_other_object(self):
1784 c = self.containers[0]
1788 src_data = self.upload_random_data(c, o_names[0], length=1024*1024+10)['data']
1790 #update zero length object
1791 prev_data = self.upload_random_data(c, dest, length=5*1024*1024+10)['data']
1792 source_object = '/%s/%s' % (c, o_names[0])
1793 first_byte_pos = 4*1024*1024
1794 last_byte_pos = 5*1024*1024
1795 range = 'bytes %d-%d/*' %(first_byte_pos, last_byte_pos)
1796 self.client.update_from_other_source(c, dest, source_object,
1797 content_range=range)
1798 content = self.client.retrieve_object(c, dest)
1799 self.assertEqual(content[:first_byte_pos], prev_data[:first_byte_pos])
1800 self.assertEqual(content[first_byte_pos:last_byte_pos+1], src_data[:last_byte_pos - first_byte_pos + 1])
1801 self.assertEqual(content[last_byte_pos+1:], prev_data[last_byte_pos+1:])
1804 def test_update_zero_length_object(self):
1805 c = self.containers[0]
1808 zero = self.client.create_zero_length_object(c, o)
1810 data = get_random_data()
1811 self.client.update_object(c, o, StringIO(data))
1812 self.client.create_object(c, other, StringIO(data))
1814 self.assertEqual(self.client.retrieve_object(c, o),
1815 self.client.retrieve_object(c, other))
1817 self.assertEqual(self.client.retrieve_object_hashmap(c, o)["hashes"],
1818 self.client.retrieve_object_hashmap(c, other)["hashes"])
1820 class ObjectDelete(BaseTestCase):
1822 BaseTestCase.setUp(self)
1823 self.containers = ['c1', 'c2']
1824 self.containers.extend(self.initial_containers)
1826 for c in self.containers:
1827 self.client.create_container(c)
1828 self.obj = self.upload_random_data(self.containers[0], o_names[0])
1830 def test_delete(self):
1831 #perform delete object
1832 self.client.delete_object(self.containers[0], self.obj['name'])[0]
1834 def test_delete_invalid(self):
1835 #assert item not found
1836 self.assert_raises_fault(404, self.client.delete_object, self.containers[1],
1839 def test_delete_dir(self):
1840 self.client.create_folder(self.containers[0], 'dir')
1841 self.client.create_folder(self.containers[0], 'dir/subdir')
1842 self.upload_random_data(self.containers[0], 'dir/object1.jpg', length=1024)
1843 self.upload_random_data(self.containers[0], 'dir/subdir/object2.pdf', length=2*1024)
1844 self.client.create_folder(self.containers[0], 'dirs')
1846 objects = self.client.list_objects(self.containers[0], prefix='dir')
1847 self.client.delete_object(self.containers[0], 'dir', delimiter='/')
1848 for object in objects[:-1]:
1849 self.assert_object_not_exists(self.containers[0], object)
1850 self.assert_object_exists(self.containers[0], objects[-1])
1852 class ListSharing(BaseTestCase):
1854 BaseTestCase.setUp(self)
1856 self.client.create_container('c%s' %i)
1857 self.client.create_container('c')
1859 self.upload_random_data('c1', 'o%s' %i)
1860 if not OTHER_ACCOUNTS:
1861 raise Warning('No other accounts avalaible for running this test.')
1862 for token, account in OTHER_ACCOUNTS.items():
1863 self.o1_sharing = token, account
1864 self.client.share_object('c1', 'o1', (account,), read=True)
1870 def test_list_other_shared(self):
1871 self.other = Pithos_Client(get_url(),
1874 self.assertTrue(get_user() in self.other.list_shared_by_others())
1876 def test_list_my_shared(self):
1877 my_shared_containers = self.client.list_containers(shared=True)
1878 self.assertTrue('c1' in my_shared_containers)
1879 self.assertTrue('c2' not in my_shared_containers)
1881 my_shared_objects = self.client.list_objects('c1', shared=True)
1882 self.assertTrue('o1' in my_shared_objects)
1883 self.assertTrue('o2' not in my_shared_objects)
1885 class List(BaseTestCase):
1887 BaseTestCase.setUp(self)
1888 for i in range(1, 5):
1890 self.client.create_container(c)
1891 for j in range(1, 3):
1893 self.upload_random_data(c, o)
1895 self.client.share_object(c, 'o1', ['papagian'], read=True)
1897 self.client.publish_object(c, 'o2')
1899 def test_shared_public(self):
1900 diff = lambda l: set(l) - set(self.initial_containers)
1902 func, kwargs = self.client.list_containers, {'shared':True}
1904 self.assertEqual(set(['c1', 'c2']), diff(l))
1905 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1907 func, kwargs = self.client.list_containers, {'public':True}
1909 self.assertEqual(set(['c1', 'c3']), diff(l))
1910 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1912 func, kwargs = self.client.list_containers, {'shared':True, 'public':True}
1914 self.assertEqual(set(['c1', 'c2', 'c3']), diff(l))
1915 self.assertEqual(l, [e['name'] for e in func(format='json', **kwargs)])
1918 func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True}
1919 l = func(*args, **kwargs)
1920 self.assertEqual(l, ['o1'])
1921 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1923 func, args, kwargs = self.client.list_objects, ['c1'], {'public':True}
1924 l = func(*args, **kwargs)
1925 self.assertEqual(l, ['o2'])
1926 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1928 func, args, kwargs = self.client.list_objects, ['c1'], {'shared':True, 'public':True}
1929 l = func(*args, **kwargs)
1930 self.assertEqual(l, ['o1', 'o2'])
1931 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1934 func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True}
1935 l = func(*args, **kwargs)
1936 self.assertEqual(l, ['o1'])
1937 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1939 func, args, kwargs = self.client.list_objects, ['c2'], {'public':True}
1940 l = func(*args, **kwargs)
1941 self.assertEqual(l, '')
1942 self.assertEqual([], func(*args, format='json', **kwargs))
1944 func, args, kwargs = self.client.list_objects, ['c2'], {'shared':True, 'public':True}
1945 l = func(*args, **kwargs)
1946 self.assertEqual(l, ['o1'])
1947 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1950 func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True}
1951 l = func(*args, **kwargs)
1952 self.assertEqual(l, '')
1953 self.assertEqual([], func(*args, format='json', **kwargs))
1955 func, args, kwargs = self.client.list_objects, ['c3'], {'public':True}
1956 l = func(*args, **kwargs)
1957 self.assertEqual(l, ['o2'])
1958 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1960 func, args, kwargs = self.client.list_objects, ['c3'], {'shared':True, 'public':True}
1961 l = func(*args, **kwargs)
1962 self.assertEqual(l, ['o2'])
1963 self.assertEqual(l, [e['name'] for e in func(*args, format='json', **kwargs)])
1966 func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True}
1967 l = func(*args, **kwargs)
1968 self.assertEqual(l, '')
1969 self.assertEqual([], func(*args, format='json', **kwargs))
1971 func, args, kwargs = self.client.list_objects, ['c4'], {'public':True}
1972 l = func(*args, **kwargs)
1973 self.assertEqual(l, '')
1974 self.assertEqual([], func(*args, format='json', **kwargs))
1976 func, args, kwargs = self.client.list_objects, ['c4'], {'shared':True, 'public':True}
1977 l = func(*args, **kwargs)
1978 self.assertEqual(l, '')
1979 self.assertEqual([], func(*args, format='json', **kwargs))
1981 class TestUTF8(BaseTestCase):
1982 def test_create_container(self):
1983 self.client.create_container('φάκελος')
1984 self.assert_container_exists('φάκελος')
1986 self.assertTrue('φάκελος' in self.client.list_containers())
1988 def test_create_object(self):
1989 self.client.create_container('φάκελος')
1990 self.upload_random_data('φάκελος', 'αντικείμενο')
1992 self.assert_object_exists('φάκελος', 'αντικείμενο')
1993 self.assertTrue('αντικείμενο' in self.client.list_objects('φάκελος'))
1995 def test_copy_object(self):
1996 src_container = 'φάκελος'
1997 src_object = 'αντικείμενο'
1998 dest_container = 'αντίγραφα'
1999 dest_object = 'ασφαλές-αντίγραφο'
2001 self.client.create_container(src_container)
2002 self.upload_random_data(src_container, src_object)
2004 self.client.create_container(dest_container)
2005 self.client.copy_object(src_container, src_object, dest_container,
2008 self.assert_object_exists(src_container, src_object)
2009 self.assert_object_exists(dest_container, dest_object)
2010 self.assertTrue(dest_object in self.client.list_objects(dest_container))
2012 def test_move_object(self):
2013 src_container = 'φάκελος'
2014 src_object = 'αντικείμενο'
2015 dest_container = 'αντίγραφα'
2016 dest_object = 'ασφαλές-αντίγραφο'
2018 self.client.create_container(src_container)
2019 self.upload_random_data(src_container, src_object)
2021 self.client.create_container(dest_container)
2022 self.client.move_object(src_container, src_object, dest_container,
2025 self.assert_object_not_exists(src_container, src_object)
2026 self.assert_object_exists(dest_container, dest_object)
2027 self.assertTrue(dest_object in self.client.list_objects(dest_container))
2029 def test_delete_object(self):
2030 self.client.create_container('φάκελος')
2031 self.upload_random_data('φάκελος', 'αντικείμενο')
2032 self.assert_object_exists('φάκελος', 'αντικείμενο')
2034 self.client.delete_object('φάκελος', 'αντικείμενο')
2035 self.assert_object_not_exists('φάκελος', 'αντικείμενο')
2036 self.assertTrue('αντικείμενο' not in self.client.list_objects('φάκελος'))
2038 def test_delete_container(self):
2039 self.client.create_container('φάκελος')
2040 self.assert_container_exists('φάκελος')
2042 self.client.delete_container('φάκελος')
2043 self.assert_container_not_exists('φάκελος')
2044 self.assertTrue('φάκελος' not in self.client.list_containers())
2046 def test_account_meta(self):
2047 meta = {'ποιότητα':'ΑΑΑ'}
2048 self.client.update_account_metadata(**meta)
2049 meta = self.client.retrieve_account_metadata(restricted=True)
2050 self.assertTrue('ποιότητα' in meta.keys())
2051 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2053 def test_container_meta(self):
2054 meta = {'ποιότητα':'ΑΑΑ'}
2055 self.client.create_container('φάκελος', meta=meta)
2057 meta = self.client.retrieve_container_metadata('φάκελος', restricted=True)
2058 self.assertTrue('ποιότητα' in meta.keys())
2059 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2061 def test_object_meta(self):
2062 self.client.create_container('φάκελος')
2063 meta = {'ποιότητα':'ΑΑΑ'}
2064 self.upload_random_data('φάκελος', 'αντικείμενο', **meta)
2066 meta = self.client.retrieve_object_metadata('φάκελος', 'αντικείμενο',
2068 self.assertTrue('ποιότητα' in meta.keys())
2069 self.assertEqual(meta['ποιότητα'], 'ΑΑΑ')
2071 def test_list_meta_filtering(self):
2072 self.client.create_container('φάκελος')
2073 meta = {'ποιότητα':'ΑΑΑ'}
2074 self.upload_random_data('φάκελος', 'ο1', **meta)
2075 self.upload_random_data('φάκελος', 'ο2')
2076 self.upload_random_data('φάκελος', 'ο3')
2078 meta = {'ποσότητα':'μεγάλη'}
2079 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2080 objects = self.client.list_objects('φάκελος', meta='ποιότητα, ποσότητα')
2081 self.assertEquals(objects, ['ο1', 'ο2'])
2083 objects = self.client.list_objects('φάκελος', meta='!ποιότητα')
2084 self.assertEquals(objects, ['ο2', 'ο3'])
2086 objects = self.client.list_objects('φάκελος', meta='!ποιότητα, !ποσότητα')
2087 self.assertEquals(objects, ['ο3'])
2089 meta = {'ποιότητα':'ΑΒ'}
2090 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2091 objects = self.client.list_objects('φάκελος', meta='ποιότητα=ΑΑΑ')
2092 self.assertEquals(objects, ['ο1'])
2093 objects = self.client.list_objects('φάκελος', meta='ποιότητα!=ΑΑΑ')
2094 self.assertEquals(objects, ['ο2'])
2096 meta = {'έτος':'2011'}
2097 self.client.update_object_metadata('φάκελος', 'ο3', **meta)
2098 meta = {'έτος':'2012'}
2099 self.client.update_object_metadata('φάκελος', 'ο2', **meta)
2100 objects = self.client.list_objects('φάκελος', meta='έτος<2012')
2101 self.assertEquals(objects, ['ο3'])
2102 objects = self.client.list_objects('φάκελος', meta='έτος<=2012')
2103 self.assertEquals(objects, ['ο2', 'ο3'])
2104 objects = self.client.list_objects('φάκελος', meta='έτος<2012,έτος!=2011')
2105 self.assertEquals(objects, '')
2107 def test_groups(self):
2109 groups = {'γκρουπ':'chazapis,διογένης'}
2110 self.client.set_account_groups(**groups)
2111 groups.update(self.initial_groups)
2112 self.assertEqual(groups['γκρουπ'],
2113 self.client.retrieve_account_groups()['γκρουπ'])
2116 self.client.create_container('φάκελος')
2117 o = self.upload_random_data('φάκελος', 'ο1')
2118 self.client.share_object('φάκελος', 'ο1', ['%s:γκρουπ' % get_user()])
2119 if 'διογένης' not in OTHER_ACCOUNTS.values():
2120 raise Warning('No such an account exists for running this test.')
2121 chef = Pithos_Client(get_url(),
2124 self.assert_not_raises_fault(403, chef.retrieve_object_metadata,
2125 'φάκελος', 'ο1', account=get_user())
2128 self.client.share_object('φάκελος', 'ο1', ['διογένης'], read=False)
2129 new_data = get_random_data()
2130 self.assert_not_raises_fault(403, chef.update_object,
2131 'φάκελος', 'ο1', StringIO(new_data),
2134 server_data = self.client.retrieve_object('φάκελος', 'ο1')
2135 self.assertEqual(server_data[:len(o['data'])], o['data'])
2136 self.assertEqual(server_data[len(o['data']):], new_data)
2138 def test_manifestation(self):
2139 self.client.create_container('κουβάς')
2143 part = '%s%d' %(prefix, i)
2144 o = self.upload_random_data('κουβάς', part)
2147 self.client.create_container('φάκελος')
2148 manifest = '%s/%s' %('κουβάς', prefix)
2149 self.client.create_manifestation('φάκελος', 'άπαντα', manifest)
2151 self.assert_object_exists('φάκελος', 'άπαντα')
2152 self.assertEqual(data, self.client.retrieve_object('φάκελος',
2155 #wrong manifestation
2156 self.client.create_manifestation('φάκελος', 'άπαντα', 'κουβάς/άκυρο')
2157 self.assertEqual('', self.client.retrieve_object('φάκελος', 'άπαντα'))
2159 def test_update_from_another_object(self):
2160 self.client.create_container('κουβάς')
2161 src_data = self.upload_random_data('κουβάς', 'πηγή')['data']
2162 initial_data = self.upload_random_data('κουβάς', 'νέο')['data']
2163 source_object = '/%s/%s' % ('κουβάς', 'πηγή')
2164 self.client.update_from_other_source('κουβάς', 'νέο', source_object)
2167 self.client.retrieve_object('κουβάς', 'νέο'),
2168 '%s%s' % (initial_data, self.client.retrieve_object('κουβάς', 'πηγή')))
2170 class TestPermissions(BaseTestCase):
2172 BaseTestCase.setUp(self)
2174 if not OTHER_ACCOUNTS:
2175 raise Warning('No other accounts avalaible for running this test.')
2178 self.authorized = ['chazapis', 'verigak', 'gtsouk']
2179 groups = {'pithosdev':','.join(self.authorized)}
2180 self.client.set_account_groups(**groups)
2182 self.container = 'c'
2184 self.client.create_container(self.container)
2185 self.upload_random_data(self.container, self.object)
2186 self.upload_random_data(self.container, self.object+'/')
2187 self.upload_random_data(self.container, self.object+'/a')
2188 self.upload_random_data(self.container, self.object+'a')
2189 self.upload_random_data(self.container, self.object+'a/')
2190 self.dir_content_types = ('application/directory', 'application/folder')
2192 def assert_read(self, authorized=[], any=False, depth=0):
2193 for token, account in OTHER_ACCOUNTS.items():
2194 cl = Pithos_Client(get_url(), token, account)
2195 if account in authorized or any:
2196 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2197 self.container, self.object,
2200 self.assert_raises_fault(403, cl.retrieve_object_metadata,
2201 self.container, self.object,
2205 meta = self.client.retrieve_object_metadata(self.container, self.object)
2206 type = meta['content-type']
2207 derivatives = self.client.list_objects(self.container, prefix=self.object)
2208 #exclude the self.object
2209 del derivatives[derivatives.index(self.object)]
2210 for o in derivatives:
2211 for token, account in OTHER_ACCOUNTS.items():
2212 cl = Pithos_Client(get_url(), token, account)
2213 prefix = self.object if self.object.endswith('/') else self.object+'/'
2214 if (account in authorized or any) and \
2215 (type in self.dir_content_types) and \
2216 o.startswith(prefix):
2217 self.assert_not_raises_fault(403, cl.retrieve_object_metadata,
2218 self.container, o, account=get_user())
2220 self.assert_raises_fault(403, cl.retrieve_object_metadata,
2221 self.container, o, account=get_user())
2223 def assert_write(self, authorized=[], any=False):
2224 o_data = self.client.retrieve_object(self.container, self.object)
2225 for token, account in OTHER_ACCOUNTS.items():
2226 cl = Pithos_Client(get_url(), token, account)
2227 new_data = get_random_data()
2228 if account in authorized or any:
2230 self.assert_not_raises_fault(403, cl.update_object,
2231 self.container, self.object, StringIO(new_data),
2235 server_data = cl.retrieve_object(self.container, self.object, account=get_user())
2236 self.assertEqual(o_data, server_data[:len(o_data)])
2237 self.assertEqual(new_data, server_data[len(o_data):])
2238 o_data = server_data
2240 self.failIf(f.status == 403)
2242 self.assert_raises_fault(403, cl.update_object,
2243 self.container, self.object, StringIO(new_data),
2246 meta = self.client.retrieve_object_metadata(self.container, self.object)
2247 type = meta['content-type']
2248 derivatives = self.client.list_objects(self.container, prefix=self.object)
2250 del derivatives[derivatives.index(self.object)]
2251 for o in derivatives:
2252 for token, account in OTHER_ACCOUNTS.items():
2253 prefix = self.object if self.object.endswith('/') else self.object+'/'
2254 cl = Pithos_Client(get_url(), token, account)
2255 new_data = get_random_data()
2256 if (account in authorized or any) and \
2257 (type in self.dir_content_types) and \
2258 o.startswith(prefix):
2260 self.assert_not_raises_fault(403, cl.update_object,
2265 server_data = cl.retrieve_object(self.container, o, account=get_user())
2266 self.assertEqual(new_data, server_data[-len(new_data):])
2268 self.failIf(f.status == 403)
2270 self.assert_raises_fault(403, cl.update_object,
2275 def test_group_read(self):
2276 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2277 self.assert_read(authorized=self.authorized)
2279 def test_read_many(self):
2280 self.client.share_object(self.container, self.object, self.authorized)
2281 self.assert_read(authorized=self.authorized)
2283 def test_read_by_everyone(self):
2284 self.client.share_object(self.container, self.object, ['*'])
2285 self.assert_read(any=True)
2287 def test_read_directory(self):
2288 for type in self.dir_content_types:
2289 #change content type
2290 self.client.move_object(self.container, self.object, self.container, self.object, content_type=type)
2291 self.client.share_object(self.container, self.object, ['*'])
2292 self.assert_read(any=True)
2293 self.client.share_object(self.container, self.object, self.authorized)
2294 self.assert_read(authorized=self.authorized)
2295 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()])
2296 self.assert_read(authorized=self.authorized)
2298 def test_group_write(self):
2299 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2300 self.assert_write(authorized=self.authorized)
2302 def test_write_many(self):
2303 self.client.share_object(self.container, self.object, self.authorized, read=False)
2304 self.assert_write(authorized=self.authorized)
2306 def test_write_by_everyone(self):
2307 self.client.share_object(self.container, self.object, ['*'], read=False)
2308 self.assert_write(any=True)
2310 def test_write_directory(self):
2311 dir_content_types = ('application/directory', 'application/foler')
2312 for type in dir_content_types:
2313 #change content type
2314 self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2315 self.client.share_object(self.container, self.object, ['*'], read=False)
2316 self.assert_write(any=True)
2317 self.client.share_object(self.container, self.object, self.authorized, read=False)
2318 self.assert_write(authorized=self.authorized)
2319 self.client.share_object(self.container, self.object, ['%s:pithosdev' % get_user()], read=False)
2320 self.assert_write(authorized=self.authorized)
2322 def test_shared_listing(self):
2323 self.client.share_object(self.container, self.object, self.authorized)
2325 my_shared_containers = self.client.list_containers(shared=True)
2326 self.assertTrue('c' in my_shared_containers)
2327 my_shared_objects = self.client.list_objects('c', shared=True)
2328 self.assertEqual(['o'], my_shared_objects)
2330 dir_content_types = ('application/directory', 'application/foler')
2331 for type in dir_content_types:
2332 #change content type
2333 self.client.move_object(self.container, self.object, self.container, self.object, content_type='application/folder')
2334 my_shared_objects = self.client.list_objects('c', shared=True)
2335 self.assertEqual(['o', 'o/', 'o/a'], my_shared_objects)
2337 for token, account in OTHER_ACCOUNTS.items():
2338 if account in self.authorized:
2339 self.other = Pithos_Client(get_url(), token, account)
2340 self.assertTrue(get_user() in self.other.list_shared_by_others())
2342 class TestPublish(BaseTestCase):
2343 def test_publish(self):
2344 self.client.create_container('c')
2345 o_data = self.upload_random_data('c', 'o')['data']
2346 self.client.publish_object('c', 'o')
2347 meta = self.client.retrieve_object_metadata('c', 'o')
2348 self.assertTrue('x-object-public' in meta)
2349 url = meta['x-object-public']
2351 p = urlparse(get_url())
2352 if p.scheme == 'http':
2353 conn = HTTPConnection(p.netloc)
2354 elif p.scheme == 'https':
2355 conn = HTTPSConnection(p.netloc)
2357 raise Exception('Unknown URL scheme')
2359 conn.request('GET', url)
2360 resp = conn.getresponse()
2361 length = resp.getheader('content-length', None)
2362 data = resp.read(length)
2363 self.assertEqual(o_data, data)
2365 class TestPolicies(BaseTestCase):
2366 def test_none_versioning(self):
2367 self.client.create_container('c', policies={'versioning':'none'})
2368 o = self.upload_random_data('c', 'o')
2369 meta = self.client.retrieve_object_metadata('c', 'o')
2370 v = meta['x-object-version']
2371 more_data = get_random_data()
2372 self.client.update_object('c', 'o', StringIO(more_data))
2373 vlist = self.client.retrieve_object_versionlist('c', 'o')
2374 self.assert_raises_fault(404, self.client.retrieve_object_version,
2376 data = self.client.retrieve_object('c', 'o')
2377 end = len(o['data'])
2378 self.assertEqual(data[:end], o['data'])
2379 self.assertEqual(data[end:], more_data)
2381 def test_quota(self):
2382 self.client.create_container('c', policies={'quota':'1'})
2383 meta = self.client.retrieve_container_metadata('c')
2384 self.assertEqual(meta['x-container-policy-quota'], '1')
2385 self.assert_raises_fault(413, self.upload_random_data, 'c', 'o',
2388 def test_quota_none(self):
2389 self.client.create_container('c', policies={'quota':'0'})
2390 meta = self.client.retrieve_container_metadata('c')
2391 self.assertEqual(meta['x-container-policy-quota'], '0')
2392 self.assert_not_raises_fault(413, self.upload_random_data, 'c', 'o',
2395 class AssertUUidInvariant(object):
2396 def __init__(self, callable, *args, **kwargs):
2397 self.callable = callable
2399 self.kwargs = kwargs
2401 def __enter__(self):
2402 self.map = self.callable(*self.args, **self.kwargs)
2403 assert('x-object-uuid' in self.map)
2404 self.uuid = self.map['x-object-uuid']
2407 def __exit__(self, type, value, tb):
2408 map = self.callable(*self.args, **self.kwargs)
2409 assert('x-object-uuid' in self.map)
2410 uuid = map['x-object-uuid']
2411 assert(uuid == self.uuid)
2413 class AssertMappingInvariant(object):
2414 def __init__(self, callable, *args, **kwargs):
2415 self.callable = callable
2417 self.kwargs = kwargs
2419 def __enter__(self):
2420 self.map = self.callable(*self.args, **self.kwargs)
2423 def __exit__(self, type, value, tb):
2424 map = self.callable(*self.args, **self.kwargs)
2425 for k, v in self.map.items():
2431 class AssertContentInvariant(object):
2432 def __init__(self, callable, *args, **kwargs):
2433 self.callable = callable
2435 self.kwargs = kwargs
2437 def __enter__(self):
2438 self.content = self.callable(*self.args, **self.kwargs)[2]
2441 def __exit__(self, type, value, tb):
2442 content = self.callable(*self.args, **self.kwargs)[2]
2443 assert self.content == content
2445 def get_content_splitted(response):
2447 return response.content.split('\n')
2449 def compute_md5_hash(data):
2453 return md5.hexdigest().lower()
2455 def compute_block_hash(data, algorithm):
2456 h = hashlib.new(algorithm)
2457 h.update(data.rstrip('\x00'))
2458 return h.hexdigest()
2460 def get_random_data(length=500):
2461 char_set = string.ascii_uppercase + string.digits
2462 return ''.join(random.choice(char_set) for x in xrange(length))
2465 MONTHS = 'jan feb mar apr may jun jul aug sep oct nov dec'.split()
2466 __D = r'(?P<day>\d{2})'
2467 __D2 = r'(?P<day>[ \d]\d)'
2468 __M = r'(?P<mon>\w{3})'
2469 __Y = r'(?P<year>\d{4})'
2470 __Y2 = r'(?P<year>\d{2})'
2471 __T = r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})'
2472 RFC1123_DATE = re.compile(r'^\w{3}, %s %s %s %s GMT$' % (__D, __M, __Y, __T))
2473 RFC850_DATE = re.compile(r'^\w{6,9}, %s-%s-%s %s GMT$' % (__D, __M, __Y2, __T))
2474 ASCTIME_DATE = re.compile(r'^\w{3} %s %s %s %s$' % (__M, __D2, __T, __Y))
2475 for regex in RFC1123_DATE, RFC850_DATE, ASCTIME_DATE:
2476 m = regex.match(date)
2481 def strnextling(prefix):
2482 """Return the first unicode string
2483 greater than but not starting with given prefix.
2484 strnextling('hello') -> 'hellp'
2487 ## all strings start with the null string,
2488 ## therefore we have to approximate strnextling('')
2489 ## with the last unicode character supported by python
2490 ## 0x10ffff for wide (32-bit unicode) python builds
2491 ## 0x00ffff for narrow (16-bit unicode) python builds
2492 ## We will not autodetect. 0xffff is safe enough.
2493 return unichr(0xffff)
2501 o_names = ['kate.jpg',
2502 'kate_beckinsale.jpg',
2503 'How To Win Friends And Influence People.pdf',
2504 'moms_birthday.jpg',
2506 'Disturbed - Down With The Sickness.mp3',
2507 'army_of_darkness.avi',
2509 'photos/animals/dogs/poodle.jpg',
2510 'photos/animals/dogs/terrier.jpg',
2511 'photos/animals/cats/persian.jpg',
2512 'photos/animals/cats/siamese.jpg',
2513 'photos/plants/fern.jpg',
2514 'photos/plants/rose.jpg',
2519 if get_user() == 'test':
2520 unittest.main(module='pithos.tools.test')
2522 print 'Will not run tests as any other user except \'test\' (current user: %s).' % get_user()
2525 if __name__ == "__main__":