summaryrefslogtreecommitdiffstats
path: root/test/functional/tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/tests.py')
-rw-r--r--test/functional/tests.py337
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()