Add new common utils tests

Change-Id: I38ac7accf5fd90e402072dc6074c3ca2f2d46509
This commit is contained in:
Mike Fedosin 2017-05-25 16:09:18 +03:00
parent 4610369f28
commit b9c863e351
4 changed files with 393 additions and 30 deletions

View File

@ -50,30 +50,6 @@ LOG = logging.getLogger(__name__)
GLARE_TEST_SOCKET_FD_STR = 'GLARE_TEST_SOCKET_FD'
def chunkreadable(iter, chunk_size=65536):
"""Wrap a readable iterator with a reader yielding chunks of
a preferred size, otherwise leave iterator unchanged.
:param iter: an iter which may also be readable
:param chunk_size: maximum size of chunk
"""
return chunkiter(iter, chunk_size) if hasattr(iter, 'read') else iter
def chunkiter(fp, chunk_size=65536):
"""Return an iterator to a file-like obj which yields fixed size chunks.
:param fp: a file-like object
:param chunk_size: maximum size of chunk
"""
while True:
chunk = fp.read(chunk_size)
if chunk:
yield chunk
else:
break
def cooperative_iter(iter):
"""Return an iterator which schedules after each
iteration. This can prevent eventlet thread starvation.
@ -174,7 +150,7 @@ class CooperativeReader(object):
# check should never fire. Regardless it still worths to
# make the check, as the code may be reused somewhere else.
if len(result) >= MAX_COOP_READER_BUFFER_SIZE:
raise exception.LimitExceeded()
raise exception.RequestEntityTooLarge()
self.position += len(chunk)
else:
try:

View File

@ -13,10 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
from glare.common import exception as exc
from glare.tests.unit import base
import os
import tempfile
import glare.common.utils as utils
import mock
from OpenSSL import crypto
import six
from glare.common import exception as exc
from glare.common import utils
from glare.tests.unit import base
class TestUtils(base.BaseTestCase):
@ -39,14 +45,252 @@ class TestUtils(base.BaseTestCase):
self.assertRaises(exc.InvalidParameterValue,
utils.validate_quotes, '"The quote is not closed')
def test_no_4bytes_params(self):
@utils.no_4byte_params
def test_func(*args, **kwargs):
return args, kwargs
bad_char = u'\U0001f62a'
# params without 4bytes unicode are okay
args, kwargs = test_func('val1', param='val2')
self.assertEqual(('val1',), args)
self.assertEqual({'param': 'val2'}, kwargs)
# test various combinations with bad param
self.assertRaises(exc.BadRequest, test_func,
bad_char)
self.assertRaises(exc.BadRequest, test_func,
**{bad_char: 'val1'})
self.assertRaises(exc.BadRequest, test_func,
**{'param': bad_char})
class TestUtilsDictDiff(base.BaseTestCase):
"""tests for utils.DictDiffer class"""
def test_str_repr(self):
past_dict = {"a": 1, "b": 2, "d": 4}
current_dic = {"b": 2, "d": "different value!", "e": "new!"}
dict_diff = utils.DictDiffer(current_dic, past_dict)
self.assertEqual({'a'}, dict_diff.removed())
self.assertEqual({'b'}, dict_diff.unchanged())
self.assertEqual({'d'}, dict_diff.changed())
self.assertEqual({'e'}, dict_diff.added())
expected_dict_str = "\nResult output:\n\tAdded keys: " \
"e\n\tRemoved keys:" \
" a\n\tChanged keys: d\n\tUnchanged keys: b\n"
self.assertEqual(str(dict_diff), expected_dict_str)
class TestReaders(base.BaseTestCase):
"""Test various readers in glare.common.utils"""
def test_cooperative_reader_iterator(self):
"""Ensure cooperative reader class accesses all bytes of file"""
BYTES = 1024
bytes_read = 0
with tempfile.TemporaryFile('w+') as tmp_fd:
tmp_fd.write('*' * BYTES)
tmp_fd.seek(0)
for chunk in utils.CooperativeReader(tmp_fd):
bytes_read += len(chunk)
self.assertEqual(BYTES, bytes_read)
def test_cooperative_reader_explicit_read(self):
BYTES = 1024
bytes_read = 0
with tempfile.TemporaryFile('w+') as tmp_fd:
tmp_fd.write('*' * BYTES)
tmp_fd.seek(0)
reader = utils.CooperativeReader(tmp_fd)
byte = reader.read(1)
while len(byte) != 0:
bytes_read += 1
byte = reader.read(1)
self.assertEqual(BYTES, bytes_read)
def test_cooperative_reader_no_read_method(self):
BYTES = 1024
stream = [b'*'] * BYTES
reader = utils.CooperativeReader(stream)
bytes_read = 0
byte = reader.read()
while len(byte) != 0:
bytes_read += 1
byte = reader.read()
self.assertEqual(BYTES, bytes_read)
# some data may be left in the buffer
reader = utils.CooperativeReader(stream)
reader.buffer = 'some data'
buffer_string = reader.read()
self.assertEqual('some data', buffer_string)
def test_cooperative_reader_no_read_method_buffer_size(self):
# Decrease buffer size to 1000 bytes to test its overflow
with mock.patch('glare.common.utils.MAX_COOP_READER_BUFFER_SIZE',
1000):
BYTES = 1024
stream = [b'*'] * BYTES
reader = utils.CooperativeReader(stream)
# Reading 1001 bytes to the buffer leads to 413 error
self.assertRaises(exc.RequestEntityTooLarge, reader.read, 1001)
def test_cooperative_reader_of_iterator(self):
"""Ensure cooperative reader supports iterator backends too"""
data = b'abcdefgh'
data_list = [data[i:i + 1] * 3 for i in range(len(data))]
reader = utils.CooperativeReader(data_list)
chunks = []
while True:
chunks.append(reader.read(3))
if chunks[-1] == b'':
break
meat = b''.join(chunks)
self.assertEqual(b'aaabbbcccdddeeefffggghhh', meat)
def test_cooperative_reader_of_iterator_stop_iteration_err(self):
"""Ensure cooperative reader supports iterator backends too"""
reader = utils.CooperativeReader([l * 3 for l in ''])
chunks = []
while True:
chunks.append(reader.read(3))
if chunks[-1] == b'':
break
meat = b''.join(chunks)
self.assertEqual(b'', meat)
def _create_generator(self, chunk_size, max_iterations):
chars = b'abc'
iteration = 0
while True:
index = iteration % len(chars)
chunk = chars[index:index + 1] * chunk_size
yield chunk
iteration += 1
if iteration >= max_iterations:
raise StopIteration()
def _test_reader_chunked(self, chunk_size, read_size, max_iterations=5):
generator = self._create_generator(chunk_size, max_iterations)
reader = utils.CooperativeReader(generator)
result = bytearray()
while True:
data = reader.read(read_size)
if len(data) == 0:
break
self.assertLessEqual(len(data), read_size)
result += data
expected = (b'a' * chunk_size +
b'b' * chunk_size +
b'c' * chunk_size +
b'a' * chunk_size +
b'b' * chunk_size)
self.assertEqual(expected, bytes(result))
def test_cooperative_reader_preserves_size_chunk_less_then_read(self):
self._test_reader_chunked(43, 101)
def test_cooperative_reader_preserves_size_chunk_equals_read(self):
self._test_reader_chunked(1024, 1024)
def test_cooperative_reader_preserves_size_chunk_more_then_read(self):
chunk_size = 16 * 1024 * 1024 # 16 Mb, as in remote http source
read_size = 8 * 1024 # 8k, as in httplib
self._test_reader_chunked(chunk_size, read_size)
def test_limiting_reader(self):
"""Ensure limiting reader class accesses all bytes of file"""
BYTES = 1024
bytes_read = 0
data = six.BytesIO(b"*" * BYTES)
for chunk in utils.LimitingReader(data, BYTES):
bytes_read += len(chunk)
self.assertEqual(BYTES, bytes_read)
bytes_read = 0
data = six.BytesIO(b"*" * BYTES)
reader = utils.LimitingReader(data, BYTES)
byte = reader.read(1)
while len(byte) != 0:
bytes_read += 1
byte = reader.read(1)
self.assertEqual(BYTES, bytes_read)
def test_limiting_reader_fails(self):
"""Ensure limiting reader class throws exceptions if limit exceeded"""
BYTES = 1024
def _consume_all_iter():
bytes_read = 0
data = six.BytesIO(b"*" * BYTES)
for chunk in utils.LimitingReader(data, BYTES - 1):
bytes_read += len(chunk)
self.assertRaises(exc.RequestEntityTooLarge, _consume_all_iter)
def _consume_all_read():
bytes_read = 0
data = six.BytesIO(b"*" * BYTES)
reader = utils.LimitingReader(data, BYTES - 1)
byte = reader.read(1)
while len(byte) != 0:
bytes_read += 1
byte = reader.read(1)
self.assertRaises(exc.RequestEntityTooLarge, _consume_all_read)
def test_blob_iterator(self):
BYTES = 1024
bytes_read = 0
stream = [b'*'] * BYTES
for chunk in utils.BlobIterator(stream, 64):
bytes_read += len(chunk)
self.assertEqual(BYTES, bytes_read)
class TestKeyCert(base.BaseTestCase):
def test_validate_key_cert_key(self):
var_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
'../', 'var'))
keyfile = os.path.join(var_dir, 'privatekey.key')
certfile = os.path.join(var_dir, 'certificate.crt')
utils.validate_key_cert(keyfile, certfile)
def test_validate_key_cert_no_private_key(self):
with tempfile.NamedTemporaryFile('w+') as tmpf:
self.assertRaises(RuntimeError,
utils.validate_key_cert,
"/not/a/file", tmpf.name)
def test_validate_key_cert_cert_cant_read(self):
with tempfile.NamedTemporaryFile('w+') as keyf:
with tempfile.NamedTemporaryFile('w+') as certf:
os.chmod(certf.name, 0)
self.assertRaises(RuntimeError,
utils.validate_key_cert,
keyf.name, certf.name)
def test_validate_key_cert_key_cant_read(self):
with tempfile.NamedTemporaryFile('w+') as keyf:
with tempfile.NamedTemporaryFile('w+') as certf:
os.chmod(keyf.name, 0)
self.assertRaises(RuntimeError,
utils.validate_key_cert,
keyf.name, certf.name)
def test_validate_key_cert_key_crypto_error(self):
var_dir = os.path.abspath(os.path.join(os.path.dirname(__file__),
'../', 'var'))
keyfile = os.path.join(var_dir, 'privatekey.key')
certfile = os.path.join(var_dir, 'certificate.crt')
with mock.patch('OpenSSL.crypto.verify', side_effect=crypto.Error):
self.assertRaises(RuntimeError,
utils.validate_key_cert,
keyfile, certfile)

View File

@ -0,0 +1,92 @@
# > openssl x509 -in glare/tests/var/certificate.crt -noout -text
# Certificate:
# Data:
# Version: 1 (0x0)
# Serial Number: 1 (0x1)
# Signature Algorithm: sha1WithRSAEncryption
# Issuer: C=AU, ST=Some-State, O=OpenStack, OU=Glare, CN=Glare CA
# Validity
# Not Before: Feb 2 20:22:13 2015 GMT
# Not After : Jan 31 20:22:13 2024 GMT
# Subject: C=AU, ST=Some-State, O=OpenStack, OU=Glare, CN=127.0.0.1
# Subject Public Key Info:
# Public Key Algorithm: rsaEncryption
# RSA Public Key: (4096 bit)
# Modulus (4096 bit):
# 00:9f:44:13:51:de:e9:5a:f7:ac:33:2a:1a:4c:91:
# a1:73:bc:f3:a6:d3:e6:59:ae:e8:e2:34:68:3e:f4:
# 40:c1:a1:1a:65:9a:a3:67:e9:2c:b9:79:9c:00:b1:
# 7c:c1:e6:9e:de:47:bf:f1:cb:f2:73:d4:c3:62:fe:
# 82:90:6f:b4:75:ca:7e:56:8f:99:3d:06:51:3c:40:
# f4:ff:74:97:4f:0d:d2:e6:66:76:8d:97:bf:89:ce:
# fe:b2:d7:89:71:f2:a0:d9:f5:26:7c:1a:7a:bf:2b:
# 8f:72:80:e7:1f:4d:4a:40:a3:b9:9e:33:f6:55:e0:
# 40:2b:1e:49:e4:8c:71:9d:11:32:cf:21:41:e1:13:
# 28:c6:d6:f6:e0:b3:26:10:6d:5b:63:1d:c3:ee:d0:
# c4:66:63:38:89:6b:8f:2a:c2:bd:4f:e4:bc:03:8f:
# a2:f2:5c:1d:73:11:9c:7b:93:3d:d6:a3:d1:2d:cd:
# 64:23:24:bc:65:3c:71:20:28:60:a0:ea:fe:77:0e:
# 1d:95:36:76:ad:e7:2f:1c:27:62:55:e3:9d:11:c1:
# fb:43:3e:e5:21:ac:fd:0e:7e:3d:c9:44:d2:bd:6f:
# 89:7e:0f:cb:88:54:57:fd:8d:21:c8:34:e1:47:01:
# 28:0f:45:a1:7e:60:1a:9c:4c:0c:b8:c1:37:2d:46:
# ab:18:9e:ca:49:d3:77:b7:92:3a:d2:7f:ca:d5:02:
# f1:75:81:66:39:51:aa:bc:d7:f0:91:23:69:e8:71:
# ae:44:76:5e:87:54:eb:72:fc:ac:fd:60:22:e0:6a:
# e4:ad:37:b7:f6:e5:24:b4:95:2c:26:0e:75:a0:e9:
# ed:57:be:37:42:64:1f:02:49:0c:bd:5d:74:6d:e6:
# f2:da:5c:54:82:fa:fc:ff:3a:e4:1a:7a:a9:3c:3d:
# ee:b5:df:09:0c:69:c3:51:92:67:80:71:9b:10:8b:
# 20:ff:a2:5e:c5:f2:86:a0:06:65:1c:42:f9:91:24:
# 54:29:ed:7e:ec:db:4c:7b:54:ee:b1:25:1b:38:53:
# ae:01:b6:c5:93:1e:a3:4d:1b:e8:73:47:50:57:e8:
# ec:a0:80:53:b1:34:74:37:9a:c1:8c:14:64:2e:16:
# dd:a1:2e:d3:45:3e:2c:46:62:20:2a:93:7a:92:4c:
# b2:cc:64:47:ad:63:32:0b:68:0c:24:98:20:83:08:
# 35:74:a7:68:7a:ef:d6:84:07:d1:5e:d7:c0:6c:3f:
# a7:4a:78:62:a8:70:75:37:fb:ce:1f:09:1e:7c:11:
# 35:cc:b3:5a:a3:cc:3f:35:c9:ee:24:6f:63:f8:54:
# 6f:7c:5b:b4:76:3d:f2:81:6d:ad:64:66:10:d0:c4:
# 0b:2c:2f
# Exponent: 65537 (0x10001)
# Signature Algorithm: sha1WithRSAEncryption
# 5f:e8:a8:93:20:6c:0f:12:90:a6:e2:64:21:ed:63:0e:8c:e0:
# 0f:d5:04:13:4d:2a:e9:a5:91:b7:e4:51:94:bd:0a:70:4b:94:
# c7:1c:94:ed:d7:64:95:07:6b:a1:4a:bc:0b:53:b5:1a:7e:f1:
# 9c:12:59:24:5f:36:72:34:ca:33:ee:28:46:fd:21:e6:52:19:
# 0c:3d:94:6b:bd:cb:76:a1:45:7f:30:7b:71:f1:84:b6:3c:e0:
# ac:af:13:81:9c:0e:6e:3c:9b:89:19:95:de:8e:9c:ef:70:ac:
# 07:ae:74:42:47:35:50:88:36:ec:32:1a:55:24:08:f2:44:57:
# 67:fe:0a:bb:6b:a7:bd:bc:af:bf:2a:e4:dd:53:84:6b:de:1d:
# 2a:28:21:38:06:7a:5b:d8:83:15:65:31:6d:61:67:00:9e:1a:
# 61:85:15:a2:4c:9a:eb:6d:59:8e:34:ac:2c:d5:24:4e:00:ff:
# 30:4d:a3:d5:80:63:17:52:65:ac:7f:f4:0a:8e:56:a4:97:51:
# 39:81:ae:e8:cb:52:09:b3:47:b4:fd:1b:e2:04:f9:f2:76:e3:
# 63:ef:90:aa:54:98:96:05:05:a9:91:76:18:ed:5d:9e:6e:88:
# 50:9a:f7:2c:ce:5e:54:ba:15:ec:62:ff:5d:be:af:35:03:b1:
# 3f:32:3e:0e
-----BEGIN CERTIFICATE-----
MIIEKjCCAxICAQEwDQYJKoZIhvcNAQEFBQAwWzELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxEjAQBgNVBAoMCU9wZW5TdGFjazEPMA0GA1UECwwGR2xh
bmNlMRIwEAYDVQQDDAlHbGFuY2UgQ0EwHhcNMTUwMjAyMjAyMjEzWhcNMjQwMTMx
MjAyMjEzWjBbMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTESMBAG
A1UEChMJT3BlblN0YWNrMQ8wDQYDVQQLEwZHbGFuY2UxEjAQBgNVBAMTCTEyNy4w
LjAuMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ9EE1He6Vr3rDMq
GkyRoXO886bT5lmu6OI0aD70QMGhGmWao2fpLLl5nACxfMHmnt5Hv/HL8nPUw2L+
gpBvtHXKflaPmT0GUTxA9P90l08N0uZmdo2Xv4nO/rLXiXHyoNn1Jnwaer8rj3KA
5x9NSkCjuZ4z9lXgQCseSeSMcZ0RMs8hQeETKMbW9uCzJhBtW2Mdw+7QxGZjOIlr
jyrCvU/kvAOPovJcHXMRnHuTPdaj0S3NZCMkvGU8cSAoYKDq/ncOHZU2dq3nLxwn
YlXjnRHB+0M+5SGs/Q5+PclE0r1viX4Py4hUV/2NIcg04UcBKA9FoX5gGpxMDLjB
Ny1GqxieyknTd7eSOtJ/ytUC8XWBZjlRqrzX8JEjaehxrkR2XodU63L8rP1gIuBq
5K03t/blJLSVLCYOdaDp7Ve+N0JkHwJJDL1ddG3m8tpcVIL6/P865Bp6qTw97rXf
CQxpw1GSZ4BxmxCLIP+iXsXyhqAGZRxC+ZEkVCntfuzbTHtU7rElGzhTrgG2xZMe
o00b6HNHUFfo7KCAU7E0dDeawYwUZC4W3aEu00U+LEZiICqTepJMssxkR61jMgto
DCSYIIMINXSnaHrv1oQH0V7XwGw/p0p4YqhwdTf7zh8JHnwRNcyzWqPMPzXJ7iRv
Y/hUb3xbtHY98oFtrWRmENDECywvAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAF/o
qJMgbA8SkKbiZCHtYw6M4A/VBBNNKumlkbfkUZS9CnBLlMcclO3XZJUHa6FKvAtT
tRp+8ZwSWSRfNnI0yjPuKEb9IeZSGQw9lGu9y3ahRX8we3HxhLY84KyvE4GcDm48
m4kZld6OnO9wrAeudEJHNVCINuwyGlUkCPJEV2f+Crtrp728r78q5N1ThGveHSoo
ITgGelvYgxVlMW1hZwCeGmGFFaJMmuttWY40rCzVJE4A/zBNo9WAYxdSZax/9AqO
VqSXUTmBrujLUgmzR7T9G+IE+fJ242PvkKpUmJYFBamRdhjtXZ5uiFCa9yzOXlS6
Fexi/12+rzUDsT8yPg4=
-----END CERTIFICATE-----

View File

@ -0,0 +1,51 @@
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----