diff options
Diffstat (limited to 'test/functional/tests.py')
-rw-r--r-- | test/functional/tests.py | 337 |
1 files changed, 336 insertions, 1 deletions
diff --git a/test/functional/tests.py b/test/functional/tests.py index e5ccda2..808208d 100644 --- a/test/functional/tests.py +++ b/test/functional/tests.py @@ -18,6 +18,8 @@ from datetime import datetime import os +import hashlib +import json import locale import random import StringIO @@ -916,7 +918,7 @@ class TestFile(Base): set_up = False def testCopy(self): - # makes sure to test encoded characters" + # makes sure to test encoded characters source_filename = 'dealde%2Fl04 011e%204c8df/flash.png' file_item = self.env.container.file(source_filename) @@ -1570,6 +1572,116 @@ class TestFileUTF8(Base2, TestFile): set_up = False +class TestDloEnv(object): + @classmethod + def setUp(cls): + cls.conn = Connection(config) + cls.conn.authenticate() + cls.account = Account(cls.conn, config.get('account', + config['username'])) + cls.account.delete_containers() + + cls.container = cls.account.container(Utils.create_name()) + + if not cls.container.create(): + raise ResponseError(cls.conn.response) + + # avoid getting a prefix that stops halfway through an encoded + # character + prefix = Utils.create_name().decode("utf-8")[:10].encode("utf-8") + cls.segment_prefix = prefix + + for letter in ('a', 'b', 'c', 'd', 'e'): + file_item = cls.container.file("%s/seg_lower%s" % (prefix, letter)) + file_item.write(letter * 10) + + file_item = cls.container.file("%s/seg_upper%s" % (prefix, letter)) + file_item.write(letter.upper() * 10) + + man1 = cls.container.file("man1") + man1.write('man1-contents', + hdrs={"X-Object-Manifest": "%s/%s/seg_lower" % + (cls.container.name, prefix)}) + + man1 = cls.container.file("man2") + man1.write('man2-contents', + hdrs={"X-Object-Manifest": "%s/%s/seg_upper" % + (cls.container.name, prefix)}) + + manall = cls.container.file("manall") + manall.write('manall-contents', + hdrs={"X-Object-Manifest": "%s/%s/seg" % + (cls.container.name, prefix)}) + + +class TestDlo(Base): + env = TestDloEnv + set_up = False + + def test_get_manifest(self): + file_item = self.env.container.file('man1') + file_contents = file_item.read() + self.assertEqual( + file_contents, + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee") + + file_item = self.env.container.file('man2') + file_contents = file_item.read() + self.assertEqual( + file_contents, + "AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE") + + file_item = self.env.container.file('manall') + file_contents = file_item.read() + self.assertEqual( + file_contents, + ("aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee" + + "AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEE")) + + def test_get_manifest_document_itself(self): + file_item = self.env.container.file('man1') + file_contents = file_item.read(parms={'multipart-manifest': 'get'}) + self.assertEqual(file_contents, "man1-contents") + + def test_get_range(self): + file_item = self.env.container.file('man1') + file_contents = file_item.read(size=25, offset=8) + self.assertEqual(file_contents, "aabbbbbbbbbbccccccccccddd") + + file_contents = file_item.read(size=1, offset=47) + self.assertEqual(file_contents, "e") + + def test_get_range_out_of_range(self): + file_item = self.env.container.file('man1') + + self.assertRaises(ResponseError, file_item.read, size=7, offset=50) + self.assert_status(416) + + def test_copy(self): + # Adding a new segment, copying the manifest, and then deleting the + # segment proves that the new object is really the concatenated + # segments and not just a manifest. + f_segment = self.env.container.file("%s/seg_lowerf" % + (self.env.segment_prefix)) + f_segment.write('ffffffffff') + try: + man1_item = self.env.container.file('man1') + man1_item.copy(self.env.container.name, "copied-man1") + finally: + # try not to leave this around for other tests to stumble over + f_segment.delete() + + file_item = self.env.container.file('copied-man1') + file_contents = file_item.read() + self.assertEqual( + file_contents, + "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeeffffffffff") + + +class TestDloUTF8(Base2, TestDlo): + set_up = False + + class TestFileComparisonEnv: @classmethod def setUp(cls): @@ -1656,5 +1768,228 @@ class TestFileComparison(Base): class TestFileComparisonUTF8(Base2, TestFileComparison): set_up = False + +class TestSloEnv(object): + slo_enabled = None # tri-state: None initially, then True/False + + @classmethod + def setUp(cls): + cls.conn = Connection(config) + cls.conn.authenticate() + cls.account = Account(cls.conn, config.get('account', + config['username'])) + cls.account.delete_containers() + + cls.container = cls.account.container(Utils.create_name()) + + if not cls.container.create(): + raise ResponseError(cls.conn.response) + + # TODO(seriously, anyone can do this): make this use the /info API once + # it lands, both for detection of SLO and for minimum segment size + if cls.slo_enabled is None: + test_file = cls.container.file(".test-slo") + try: + # If SLO is enabled, this'll raise an error since + # X-Static-Large-Object is a reserved header. + # + # If SLO is not enabled, then this will get the usual 2xx + # response. + test_file.write( + "some contents", + hdrs={'X-Static-Large-Object': 'true'}) + except ResponseError as err: + if err.status == 400: + cls.slo_enabled = True + else: + raise + else: + cls.slo_enabled = False + return + + seg_info = {} + for letter, size in (('a', 1024 * 1024), + ('b', 1024 * 1024), + ('c', 1024 * 1024), + ('d', 1024 * 1024), + ('e', 1)): + seg_name = "seg_%s" % letter + file_item = cls.container.file(seg_name) + file_item.write(letter * size) + seg_info[seg_name] = { + 'size_bytes': size, + 'etag': file_item.md5, + 'path': '/%s/%s' % (cls.container.name, seg_name)} + + file_item = cls.container.file("manifest-abcde") + file_item.write( + json.dumps([seg_info['seg_a'], seg_info['seg_b'], + seg_info['seg_c'], seg_info['seg_d'], + seg_info['seg_e']]), + parms={'multipart-manifest': 'put'}) + + file_item = cls.container.file('manifest-cd') + cd_json = json.dumps([seg_info['seg_c'], seg_info['seg_d']]) + file_item.write(cd_json, parms={'multipart-manifest': 'put'}) + cd_etag = hashlib.md5(seg_info['seg_c']['etag'] + + seg_info['seg_d']['etag']).hexdigest() + + file_item = cls.container.file("manifest-bcd-submanifest") + file_item.write( + json.dumps([seg_info['seg_b'], + {'etag': cd_etag, + 'size_bytes': (seg_info['seg_c']['size_bytes'] + + seg_info['seg_d']['size_bytes']), + 'path': '/%s/%s' % (cls.container.name, + 'manifest-cd')}]), + parms={'multipart-manifest': 'put'}) + bcd_submanifest_etag = hashlib.md5( + seg_info['seg_b']['etag'] + cd_etag).hexdigest() + + file_item = cls.container.file("manifest-abcde-submanifest") + file_item.write( + json.dumps([ + seg_info['seg_a'], + {'etag': bcd_submanifest_etag, + 'size_bytes': (seg_info['seg_b']['size_bytes'] + + seg_info['seg_c']['size_bytes'] + + seg_info['seg_d']['size_bytes']), + 'path': '/%s/%s' % (cls.container.name, + 'manifest-bcd-submanifest')}, + seg_info['seg_e']]), + parms={'multipart-manifest': 'put'}) + + +class TestSlo(Base): + env = TestSloEnv + set_up = False + + def setUp(self): + super(TestSlo, self).setUp() + if self.env.slo_enabled is False: + raise SkipTest("SLO not enabled") + elif self.env.slo_enabled is not True: + # just some sanity checking + raise Exception( + "Expected slo_enabled to be True/False, got %r" % + (self.env.slo_enabled,)) + + def test_slo_get_simple_manifest(self): + file_item = self.env.container.file('manifest-abcde') + file_contents = file_item.read() + self.assertEqual(4 * 1024 * 1024 + 1, len(file_contents)) + self.assertEqual('a', file_contents[0]) + self.assertEqual('a', file_contents[1024 * 1024 - 1]) + self.assertEqual('b', file_contents[1024 * 1024]) + self.assertEqual('d', file_contents[-2]) + self.assertEqual('e', file_contents[-1]) + + def test_slo_get_nested_manifest(self): + file_item = self.env.container.file('manifest-abcde-submanifest') + file_contents = file_item.read() + self.assertEqual(4 * 1024 * 1024 + 1, len(file_contents)) + self.assertEqual('a', file_contents[0]) + self.assertEqual('a', file_contents[1024 * 1024 - 1]) + self.assertEqual('b', file_contents[1024 * 1024]) + self.assertEqual('d', file_contents[-2]) + self.assertEqual('e', file_contents[-1]) + + def test_slo_ranged_get(self): + file_item = self.env.container.file('manifest-abcde') + file_contents = file_item.read(size=1024 * 1024 + 2, + offset=1024 * 1024 - 1) + self.assertEqual('a', file_contents[0]) + self.assertEqual('b', file_contents[1]) + self.assertEqual('b', file_contents[-2]) + self.assertEqual('c', file_contents[-1]) + + def test_slo_ranged_submanifest(self): + file_item = self.env.container.file('manifest-abcde-submanifest') + file_contents = file_item.read(size=1024 * 1024 + 2, + offset=1024 * 1024 * 2 - 1) + self.assertEqual('b', file_contents[0]) + self.assertEqual('c', file_contents[1]) + self.assertEqual('c', file_contents[-2]) + self.assertEqual('d', file_contents[-1]) + + def test_slo_etag_is_hash_of_etags(self): + expected_hash = hashlib.md5() + expected_hash.update(hashlib.md5('a' * 1024 * 1024).hexdigest()) + expected_hash.update(hashlib.md5('b' * 1024 * 1024).hexdigest()) + expected_hash.update(hashlib.md5('c' * 1024 * 1024).hexdigest()) + expected_hash.update(hashlib.md5('d' * 1024 * 1024).hexdigest()) + expected_hash.update(hashlib.md5('e').hexdigest()) + expected_etag = expected_hash.hexdigest() + + file_item = self.env.container.file('manifest-abcde') + self.assertEqual(expected_etag, file_item.info()['etag']) + + def test_slo_etag_is_hash_of_etags_submanifests(self): + + def hd(x): + return hashlib.md5(x).hexdigest() + + expected_etag = hd(hd('a' * 1024 * 1024) + + hd(hd('b' * 1024 * 1024) + + hd(hd('c' * 1024 * 1024) + + hd('d' * 1024 * 1024))) + + hd('e')) + + file_item = self.env.container.file('manifest-abcde-submanifest') + self.assertEqual(expected_etag, file_item.info()['etag']) + + def test_slo_etag_mismatch(self): + file_item = self.env.container.file("manifest-a-bad-etag") + try: + file_item.write( + json.dumps([{ + 'size_bytes': 1024 * 1024, + 'etag': 'not it', + 'path': '/%s/%s' % (self.env.container.name, 'seg_a')}]), + parms={'multipart-manifest': 'put'}) + except ResponseError as err: + self.assertEqual(400, err.status) + else: + self.fail("Expected ResponseError but didn't get it") + + def test_slo_size_mismatch(self): + file_item = self.env.container.file("manifest-a-bad-size") + try: + file_item.write( + json.dumps([{ + 'size_bytes': 1024 * 1024 - 1, + 'etag': hashlib.md5('a' * 1024 * 1024).hexdigest(), + 'path': '/%s/%s' % (self.env.container.name, 'seg_a')}]), + parms={'multipart-manifest': 'put'}) + except ResponseError as err: + self.assertEqual(400, err.status) + else: + self.fail("Expected ResponseError but didn't get it") + + def test_slo_copy(self): + file_item = self.env.container.file("manifest-abcde") + file_item.copy(self.env.container.name, "copied-abcde") + + copied = self.env.container.file("copied-abcde") + copied_contents = copied.read(parms={'multipart-manifest': 'get'}) + self.assertEqual(4 * 1024 * 1024 + 1, len(copied_contents)) + + def test_slo_copy_the_manifest(self): + file_item = self.env.container.file("manifest-abcde") + file_item.copy(self.env.container.name, "copied-abcde", + parms={'multipart-manifest': 'get'}) + + copied = self.env.container.file("copied-abcde") + copied_contents = copied.read(parms={'multipart-manifest': 'get'}) + try: + json.loads(copied_contents) + except ValueError: + self.fail("COPY didn't copy the manifest (invalid json on GET)") + + +class TestSloUTF8(Base2, TestSlo): + set_up = False + + if __name__ == '__main__': unittest.main() |