diff --git a/.travis.yml b/.travis.yml index 2e4b88ed0..b23b97e70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,8 @@ matrix: env: TOXENV=bandit - python: 3.6 env: TOXENV=doc8 + - python: 3.6 + env: TOXENV=docs - python: 3.6 env: TOXENV=readme # pending reorg of deserialize_header @@ -21,9 +23,8 @@ matrix: # env: TOXENV=flake8 # - python: 3.6 # env: TOXENV=pylint -# pending test-vectors refactor -# - python: 3.6 -# env: TOXENV=flake8-tests + - python: 3.6 + env: TOXENV=flake8-tests - python: 3.6 env: TOXENV=pylint-tests - python: 3.6 @@ -33,4 +34,4 @@ matrix: - python: 3.6 env: TOXENV=pylint-examples install: pip install tox -script: tox \ No newline at end of file +script: tox diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 13b156c42..c978933a6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,20 @@ Changelog ********* +1.3.3 +===== + +Bugfixes +-------- +* Remove use of attrs functionality deprecated in 17.3.0 + `#29 `_ + +Maintenance +----------- +* Blacklisted pytest 3.3.0 + `#32 `_ + `pytest-dev/pytest#2957 `_ + 1.3.2 ===== * Addressed `issue #13 `_ diff --git a/src/aws_encryption_sdk/identifiers.py b/src/aws_encryption_sdk/identifiers.py index e01fd5188..b853926c3 100644 --- a/src/aws_encryption_sdk/identifiers.py +++ b/src/aws_encryption_sdk/identifiers.py @@ -21,7 +21,7 @@ from aws_encryption_sdk.exceptions import InvalidAlgorithmError -__version__ = '1.3.2' +__version__ = '1.3.3' USER_AGENT_SUFFIX = 'AwsEncryptionSdkPython-KMSMasterKey/{}'.format(__version__) diff --git a/src/aws_encryption_sdk/internal/formatting/deserialize.py b/src/aws_encryption_sdk/internal/formatting/deserialize.py index 01e33592e..85ae988ff 100644 --- a/src/aws_encryption_sdk/internal/formatting/deserialize.py +++ b/src/aws_encryption_sdk/internal/formatting/deserialize.py @@ -12,6 +12,7 @@ # language governing permissions and limitations under the License. """Components for handling AWS Encryption SDK message deserialization.""" from __future__ import division + import io import logging import struct diff --git a/src/aws_encryption_sdk/key_providers/base.py b/src/aws_encryption_sdk/key_providers/base.py index 947c52a4d..d7d8010a7 100644 --- a/src/aws_encryption_sdk/key_providers/base.py +++ b/src/aws_encryption_sdk/key_providers/base.py @@ -294,7 +294,6 @@ def decrypt_data_key_from_list(self, encrypted_data_keys, algorithm, encryption_ @attr.s(hash=True) -@six.add_metaclass(abc.ABCMeta) class MasterKeyConfig(object): """Configuration object for MasterKey objects. @@ -307,23 +306,10 @@ class MasterKeyConfig(object): convert=to_bytes ) - @abc.abstractproperty - def provider_id(self): - """Utilizing ABCMeta to enable children to either set this as a class property or require it as input. - - .. note:: - Must be implemented by child classes. - - If requiring provider_id as input: - - .. code-block:: python - - provider_id = attr.ib( - hash=True, - validator=attr.validators.instance_of((six.string_types, bytes)), - convert=aws_encryption_sdk.internal.str_ops.to_str - ) - """ + def __attrs_post_init__(self): + """Verify that children of this class define a "provider_id" attribute.""" + if not hasattr(self, 'provider_id'): + raise TypeError('Instances of MasterKeyConfig must have a "provider_id" attribute defined.') @six.add_metaclass(abc.ABCMeta) @@ -338,6 +324,10 @@ class MasterKey(MasterKeyProvider): def __new__(cls, **kwargs): """Performs universal prep work for all MasterKeys.""" instance = super(MasterKey, cls).__new__(cls, **kwargs) + + if not hasattr(instance.config, 'provider_id'): + raise TypeError('MasterKey config classes must have a "provider_id" attribute defined.') + if instance.config.provider_id is not None: # Only allow override if provider_id is NOT set to non-None for the class if instance.provider_id is None: diff --git a/src/aws_encryption_sdk/streaming_client.py b/src/aws_encryption_sdk/streaming_client.py index bfb2fb745..3de3dae53 100644 --- a/src/aws_encryption_sdk/streaming_client.py +++ b/src/aws_encryption_sdk/streaming_client.py @@ -12,6 +12,7 @@ # language governing permissions and limitations under the License. """High level AWS Encryption SDK client for streaming objects.""" from __future__ import division + import abc import io import logging diff --git a/test/functional/test_f_xcompat.py b/test/functional/test_f_xcompat.py index 3bf0ba6fd..619b968fe 100644 --- a/test/functional/test_f_xcompat.py +++ b/test/functional/test_f_xcompat.py @@ -11,7 +11,6 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Functional test suite testing decryption of known good test files encrypted using static RawMasterKeyProvider.""" -from __future__ import print_function import base64 from collections import defaultdict import json @@ -78,7 +77,6 @@ def _get_raw_key(self, key_id): key_bits = int(key_bits) key_type = _KEY_TYPES_MAP[algorithm] wrapping_algorithm = _WRAPPING_ALGORITHM_MAP[algorithm][key_bits][padding_algorithm][padding_hash] - print('looking up {} {}'.format(algorithm, key_bits)) static_key = _STATIC_KEYS[algorithm][key_bits] return WrappingKey( wrapping_algorithm=wrapping_algorithm, @@ -113,7 +111,7 @@ class Scenario(object): key_ids = attr.ib(validator=attr.validators.instance_of(list)) -def _generate_test_cases(): +def _generate_test_cases(): # noqa=C901 try: root_dir = os.path.abspath(file_root()) except Exception: # pylint: disable=broad-except diff --git a/test/integration/test_i_aws_encrytion_sdk_client.py b/test/integration/test_i_aws_encrytion_sdk_client.py index a0032ddb7..91f681ea7 100644 --- a/test/integration/test_i_aws_encrytion_sdk_client.py +++ b/test/integration/test_i_aws_encrytion_sdk_client.py @@ -18,7 +18,6 @@ import aws_encryption_sdk from aws_encryption_sdk.identifiers import Algorithm - from .integration_test_utils import setup_kms_master_key_provider, SKIP_MESSAGE, skip_tests diff --git a/test/integration/test_i_xcompat_kms.py b/test/integration/test_i_xcompat_kms.py index c8d803323..fb9908de6 100644 --- a/test/integration/test_i_xcompat_kms.py +++ b/test/integration/test_i_xcompat_kms.py @@ -17,7 +17,6 @@ import pytest import aws_encryption_sdk - from .integration_test_utils import setup_kms_master_key_provider, SKIP_MESSAGE, skip_tests diff --git a/test/unit/test_crypto_authentication_signer.py b/test/unit/test_crypto_authentication_signer.py index 486e73c6a..6c8cf7d5a 100644 --- a/test/unit/test_crypto_authentication_signer.py +++ b/test/unit/test_crypto_authentication_signer.py @@ -18,7 +18,6 @@ import aws_encryption_sdk.internal.crypto.authentication from aws_encryption_sdk.internal.crypto.authentication import Signer from aws_encryption_sdk.internal.defaults import ALGORITHM - from .test_crypto import VALUES diff --git a/test/unit/test_crypto_authentication_verifier.py b/test/unit/test_crypto_authentication_verifier.py index 54e4330a2..a3a0e3037 100644 --- a/test/unit/test_crypto_authentication_verifier.py +++ b/test/unit/test_crypto_authentication_verifier.py @@ -18,7 +18,6 @@ import aws_encryption_sdk.internal.crypto.authentication from aws_encryption_sdk.internal.crypto.authentication import Verifier from aws_encryption_sdk.internal.defaults import ALGORITHM - from .test_crypto import VALUES diff --git a/test/unit/test_crypto_elliptic_curve.py b/test/unit/test_crypto_elliptic_curve.py index d52d3c02a..0c47cbe82 100644 --- a/test/unit/test_crypto_elliptic_curve.py +++ b/test/unit/test_crypto_elliptic_curve.py @@ -26,7 +26,6 @@ _ecc_public_numbers_from_compressed_point, _ecc_static_length_signature, _ECCCurveParameters, generate_ecc_signing_key ) - from .test_crypto import VALUES diff --git a/test/unit/test_crypto_wrapping_keys.py b/test/unit/test_crypto_wrapping_keys.py index 3abebf49f..eada1b78b 100644 --- a/test/unit/test_crypto_wrapping_keys.py +++ b/test/unit/test_crypto_wrapping_keys.py @@ -20,7 +20,6 @@ import aws_encryption_sdk.internal.crypto.wrapping_keys from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey from aws_encryption_sdk.internal.structures import EncryptedData - from .test_crypto import VALUES diff --git a/test/unit/test_internal_structures.py b/test/unit/test_internal_structures.py index 8e6d5a064..0fe16b50f 100644 --- a/test/unit/test_internal_structures.py +++ b/test/unit/test_internal_structures.py @@ -11,38 +11,60 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite for aws_encryption_sdk.internal.structures""" -import attr import pytest -import six from aws_encryption_sdk.internal.structures import ( EncryptedData, MessageFooter, MessageFrameBody, MessageHeaderAuthentication, MessageNoFrameBody ) +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs + + +VALID_KWARGS = { + EncryptedData: [ + dict(ciphertext=b'asjfoiwaj', iv=None, tag=None), + dict(ciphertext=b'asjfoiwaj', iv=b'ajsdhfiuaweh', tag=None), + dict(ciphertext=b'asjfoiwaj', iv=None, tag=b'aosijfoiewj'), + dict(ciphertext=b'asjfoiwaj', iv=b'ajsdhfiuaweh', tag=b'aosijfoiewj') + ], + MessageHeaderAuthentication: [ + dict(iv=b'oasijfoaiwej', tag=b'aisudhfoaweij') + ], + MessageFrameBody: [ + dict( + iv=b'oaijefoiajew', + ciphertext=b'oasidjfaowiejf', + tag=b'ecoaiwjeconadf', + sequence_number=42523, + final_frame=False + ) + ], + MessageNoFrameBody: [ + dict( + iv=b'afioaewj', + ciphertext=b'oasjfoeiwjfio', + tag=b'asfowaeijf' + ) + ], + MessageFooter: [ + dict(signature=b'oajwefiowjaeofi') + ] +} +INVALID_KWARGS = { + EncryptedData: [ + dict(ciphertext=None, iv=None, tag=None) + ] +} -@pytest.mark.parametrize('attribute, validator_type, is_optional', ( - (EncryptedData.iv, bytes, True), - (EncryptedData.ciphertext, bytes, False), - (EncryptedData.tag, bytes, True), - (MessageHeaderAuthentication.iv, bytes, False), - (MessageHeaderAuthentication.tag, bytes, False), - (MessageFrameBody.iv, bytes, False), - (MessageFrameBody.ciphertext, bytes, False), - (MessageFrameBody.tag, bytes, False), - (MessageFrameBody.sequence_number, six.integer_types, False), - (MessageFrameBody.final_frame, bool, False), - (MessageNoFrameBody.iv, bytes, False), - (MessageNoFrameBody.ciphertext, bytes, False), - (MessageNoFrameBody.tag, bytes, False), - (MessageFooter.signature, bytes, False) -)) -def test_attributes(attribute, validator_type, is_optional): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - if is_optional: - assert attribute.validator.validator.type == validator_type - else: - assert attribute.validator.type == validator_type +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) + + +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS, INVALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): + with pytest.raises(TypeError): + cls(**kwargs) @pytest.mark.parametrize('attribute, value', ( @@ -51,82 +73,3 @@ def test_attributes(attribute, validator_type, is_optional): )) def test_static_attributes(attribute, value): assert attribute == value - - -def test_encrypted_data_fails(): - with pytest.raises(TypeError): - EncryptedData(ciphertext=None) - - -@pytest.mark.parametrize('iv, ciphertext, tag', ( - (b'iv', b'ciphertext', b'tag'), - (None, b'ciphertext', None) -)) -def test_encrypted_data_succeeds(iv, ciphertext, tag): - EncryptedData(iv=iv, ciphertext=ciphertext, tag=tag) - - -@pytest.mark.parametrize('iv, tag', ( - (None, b''), - (b'', None) -)) -def test_message_header_auth_fails(iv, tag): - with pytest.raises(TypeError): - MessageHeaderAuthentication(iv=iv, tag=tag) - - -def test_message_header_auth_succeeds(): - MessageHeaderAuthentication(iv=b'', tag=b'') - - -@pytest.mark.parametrize('iv, ciphertext, tag, sequence_number, final_frame', ( - (None, b'', b'', 1, True), - (b'', None, b'', 1, True), - (b'', b'', None, 1, True), - (b'', b'', b'', None, True), - (b'', b'', b'', 1, None) -)) -def test_message_frame_body_fails(iv, ciphertext, tag, sequence_number, final_frame): - with pytest.raises(TypeError): - MessageFrameBody( - iv=iv, - ciphertext=ciphertext, - tag=tag, - sequence_number=sequence_number, - final_frame=final_frame - ) - - -def test_message_frame_body_succeeds(): - MessageFrameBody( - iv=b'iv', - ciphertext=b'ciphertext', - tag=b'tag', - sequence_number=1, - final_frame=False - ) - - -@pytest.mark.parametrize('iv, ciphertext, tag', ( - (None, b'', b''), - (b'', None, b''), - (b'', b'', None) -)) -def test_message_no_frame_body_fails(iv, ciphertext, tag): - with pytest.raises(TypeError): - MessageNoFrameBody(iv=iv, ciphertext=ciphertext, tag=tag) - - -def test_message_no_frame_body_succeeds(): - test = MessageNoFrameBody(iv=b'', ciphertext=b'', tag=b'') - assert test.sequence_number == 1 - assert test.final_frame - - -def test_message_footer_fails(): - with pytest.raises(TypeError): - MessageFooter(signature=None) - - -def test_message_footer_succeeds(): - MessageFooter(signature=b'') diff --git a/test/unit/test_providers_base_master_key.py b/test/unit/test_providers_base_master_key.py index 0e2cdc711..cb7e484f6 100644 --- a/test/unit/test_providers_base_master_key.py +++ b/test/unit/test_providers_base_master_key.py @@ -15,6 +15,7 @@ import attr from mock import MagicMock, patch, sentinel +import pytest import six from aws_encryption_sdk.exceptions import ConfigMismatchError, IncorrectMasterKeyError, InvalidKeyIdError @@ -46,6 +47,19 @@ def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): return self.config.mock_decrypted_data_key +def test_master_key_provider_id_config_enforcement(): + class FakeConfig(object): + key_id = b'a key' + + class FakeMasterKey(MockMasterKey): + _config_class = FakeConfig + + with pytest.raises(TypeError) as excinfo: + FakeMasterKey() + + excinfo.match(r'MasterKey config classes must have a "provider_id" attribute defined.') + + class TestMasterKey(unittest.TestCase): def setUp(self): @@ -60,7 +74,7 @@ def test_parent(self): def test_provider_id_enforcement(self): class TestMasterKey(MasterKey): - def generate_data_key(self, algorithm, encryption_context): + def _generate_data_key(self, algorithm, encryption_context): pass def _encrypt_data_key(self, data_key, algorithm, encryption_context): @@ -95,7 +109,7 @@ def test_encrypt_data_key_enforcement(self): class TestMasterKey(MasterKey): provider_id = None - def generate_data_key(self, algorithm, encryption_context): + def _generate_data_key(self, algorithm, encryption_context): pass def _decrypt_data_key(self, encrypted_data_key, algorithm, encryption_context): @@ -111,7 +125,7 @@ def test_decrypt_data_key_enforcement(self): class TestMasterKey(MasterKey): provider_id = None - def generate_data_key(self, algorithm, encryption_context): + def _generate_data_key(self, algorithm, encryption_context): pass def _encrypt_data_key(self, data_key, algorithm, encryption_context): diff --git a/test/unit/test_providers_base_master_key_config.py b/test/unit/test_providers_base_master_key_config.py index 9a4305e49..57f16c046 100644 --- a/test/unit/test_providers_base_master_key_config.py +++ b/test/unit/test_providers_base_master_key_config.py @@ -11,37 +11,38 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.key_providers.base.MasterKeyConfig""" -import attr -from mock import sentinel import pytest -import six -from aws_encryption_sdk.internal.str_ops import to_bytes from aws_encryption_sdk.key_providers.base import MasterKeyConfig +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs -@pytest.mark.parametrize('attribute, validator_type, convert_function', ( - (MasterKeyConfig.key_id, (six.string_types, bytes), to_bytes), -)) -def test_attributes(attribute, validator_type, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.validator.type == validator_type - assert attribute.convert is convert_function +class FakeMasterKeyConfig(MasterKeyConfig): + provider_id = None -def test_attributes_fails(): - class TestConfig(MasterKeyConfig): - provider_id = sentinel.provider_id +VALID_KWARGS = { + FakeMasterKeyConfig: [ + dict(key_id='a key id'), + dict(key_id=b'a key id') + ] +} + + +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) + + +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): with pytest.raises(TypeError): - TestConfig(key_id=None) + cls(**kwargs) @pytest.mark.parametrize('key_id', (b'key', 'key')) def test_attributes_converts(key_id): - class TestConfig(MasterKeyConfig): - provider_id = sentinel.provider_id - test = TestConfig(key_id=key_id) + test = FakeMasterKeyConfig(key_id=key_id) assert isinstance(test.key_id, bytes) @@ -50,6 +51,6 @@ class TestConfig(MasterKeyConfig): pass with pytest.raises(TypeError) as excinfo: - TestConfig() + TestConfig(key_id='a key') - excinfo.match(r"Can't instantiate abstract class TestConfig *") + excinfo.match(r'Instances of MasterKeyConfig must have a "provider_id" attribute defined.') diff --git a/test/unit/test_providers_base_master_key_provider.py b/test/unit/test_providers_base_master_key_provider.py index bfdd8f188..3506d6a6c 100644 --- a/test/unit/test_providers_base_master_key_provider.py +++ b/test/unit/test_providers_base_master_key_provider.py @@ -21,7 +21,6 @@ DecryptKeyError, IncorrectMasterKeyError, InvalidKeyIdError, MasterKeyProviderError ) from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig - from .test_values import VALUES diff --git a/test/unit/test_providers_kms_master_key_config.py b/test/unit/test_providers_kms_master_key_config.py index 9ff75c353..bcdd00d41 100644 --- a/test/unit/test_providers_kms_master_key_config.py +++ b/test/unit/test_providers_kms_master_key_config.py @@ -11,30 +11,41 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.key_providers.kms.KMSMasterKeyConfig""" -import attr -import botocore.client -from mock import MagicMock, sentinel +import boto3 import pytest from aws_encryption_sdk.key_providers.base import MasterKeyConfig from aws_encryption_sdk.key_providers.kms import _PROVIDER_ID, KMSMasterKeyConfig +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs -def test_parent(): - assert issubclass(KMSMasterKeyConfig, MasterKeyConfig) +VALID_KWARGS = { + KMSMasterKeyConfig: [ + dict(key_id=b'a cmk', client=boto3.client('kms', region_name='us-west-2'), grant_tokens=()), + dict(key_id=b'a cmk', client=boto3.client('kms', region_name='us-west-2'), grant_tokens=[]), + dict(key_id=b'a cmk', client=boto3.client('kms', region_name='us-west-2')) + ] +} +INVALID_KWARGS = { + KMSMasterKeyConfig: [ + dict(client=None) + ] +} -@pytest.mark.parametrize('attribute, default, validator_type, convert_function', ( - (KMSMasterKeyConfig.client, attr.NOTHING, botocore.client.BaseClient, None), - (KMSMasterKeyConfig.grant_tokens, attr.Factory(tuple), tuple, tuple) -)) -def test_attributes(attribute, default, validator_type, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.default == default - assert attribute.validator.type == validator_type - if convert_function is not None: - assert attribute.convert is convert_function +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) + + +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS, INVALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): + with pytest.raises(TypeError): + cls(**kwargs) + + +def test_parent(): + assert issubclass(KMSMasterKeyConfig, MasterKeyConfig) @pytest.mark.parametrize('attribute, value', ( @@ -44,23 +55,15 @@ def test_static_attributes(attribute, value): assert attribute == value -def test_attributes_fail(): - with pytest.raises(TypeError): - KMSMasterKeyConfig(key_id='', client=None) - - def test_attributes_defaults(): test = KMSMasterKeyConfig( - key_id='', - client=MagicMock(__class__=botocore.client.BaseClient) + key_id=b'a cmk', + client=boto3.client('kms', region_name='us-west-2') ) assert test.grant_tokens == () -def test_attributes_converts(): - test = KMSMasterKeyConfig( - key_id='', - client=MagicMock(__class__=botocore.client.BaseClient), - grant_tokens=[sentinel.token_1, sentinel.token_2] - ) - assert test.grant_tokens == (sentinel.token_1, sentinel.token_2) +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_converts(cls, kwargs): + test = cls(**kwargs) + assert isinstance(test.grant_tokens, tuple) diff --git a/test/unit/test_providers_kms_master_key_provider_config.py b/test/unit/test_providers_kms_master_key_provider_config.py index c18de50db..62e24cb67 100644 --- a/test/unit/test_providers_kms_master_key_provider_config.py +++ b/test/unit/test_providers_kms_master_key_provider_config.py @@ -11,51 +11,49 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.key_providers.kms.KMSMasterKeyProviderConfig""" -import attr import botocore.session -from mock import MagicMock, sentinel import pytest from aws_encryption_sdk.key_providers.base import MasterKeyProviderConfig from aws_encryption_sdk.key_providers.kms import KMSMasterKeyProviderConfig +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs -def test_parent(): - assert issubclass(KMSMasterKeyProviderConfig, MasterKeyProviderConfig) +VALID_KWARGS = { + KMSMasterKeyProviderConfig: [ + dict(botocore_session=botocore.session.Session(), key_ids=(), region_names=()), + dict(botocore_session=botocore.session.Session(), key_ids=[], region_names=()), + dict(botocore_session=botocore.session.Session(), key_ids=(), region_names=[]), + dict(botocore_session=botocore.session.Session(), region_names=()), + dict(botocore_session=botocore.session.Session(), key_ids=()), + dict(botocore_session=botocore.session.Session()) + ] +} +INVALID_KWARGS = { + KMSMasterKeyProviderConfig: [dict(botocore_session=None)] +} -@pytest.mark.parametrize('attribute, default, validator_type, convert_function', ( - ( - KMSMasterKeyProviderConfig.botocore_session, - attr.Factory(botocore.session.Session), - botocore.session.Session, - None - ), - (KMSMasterKeyProviderConfig.key_ids, attr.Factory(tuple), tuple, tuple), - (KMSMasterKeyProviderConfig.region_names, attr.Factory(tuple), tuple, tuple) -)) -def test_attributes(attribute, default, validator_type, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.default == default - assert attribute.validator.type == validator_type - if convert_function is not None: - assert attribute.convert is convert_function +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) -def test_attributes_fails(): +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS, INVALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): with pytest.raises(TypeError): - KMSMasterKeyProviderConfig(botocore_session=None) + cls(**kwargs) + + +def test_parent(): + assert issubclass(KMSMasterKeyProviderConfig, MasterKeyProviderConfig) -def test_attributes_converts(): - test = KMSMasterKeyProviderConfig( - botocore_session=MagicMock(__class__=botocore.session.Session), - key_ids=[sentinel.key_a, sentinel.key_b], - region_names=[sentinel.region_a, sentinel.region_b] - ) - assert test.key_ids == (sentinel.key_a, sentinel.key_b) - assert test.region_names == (sentinel.region_a, sentinel.region_b) +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_converts(cls, kwargs): + test = cls(**kwargs) + assert isinstance(test.key_ids, tuple) + assert isinstance(test.region_names, tuple) def test_attributes_defaults(): diff --git a/test/unit/test_providers_raw_master_key_config.py b/test/unit/test_providers_raw_master_key_config.py index 0881ddb53..1e68cfa57 100644 --- a/test/unit/test_providers_raw_master_key_config.py +++ b/test/unit/test_providers_raw_master_key_config.py @@ -11,44 +11,44 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite to validate aws_encryption_sdk.key_providers.raw.RawMasterKeyConfig""" -import attr -from mock import MagicMock import pytest import six +from aws_encryption_sdk.identifiers import EncryptionKeyType, WrappingAlgorithm from aws_encryption_sdk.internal.crypto.wrapping_keys import WrappingKey -from aws_encryption_sdk.internal.str_ops import to_str from aws_encryption_sdk.key_providers.base import MasterKeyConfig from aws_encryption_sdk.key_providers.raw import RawMasterKeyConfig +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs +STATIC_WRAPPING_KEY = WrappingKey( + wrapping_algorithm=WrappingAlgorithm.AES_256_GCM_IV12_TAG16_NO_PADDING, + wrapping_key=b'_________a symmetric key________', + wrapping_key_type=EncryptionKeyType.SYMMETRIC +) +VALID_KWARGS = { + RawMasterKeyConfig: [ + dict(key_id=b'a raw key', provider_id='a provider', wrapping_key=STATIC_WRAPPING_KEY), + dict(key_id=b'a raw key', provider_id=b'a provider', wrapping_key=STATIC_WRAPPING_KEY) + ] +} -def test_parent(): - assert issubclass(RawMasterKeyConfig, MasterKeyConfig) +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) -@pytest.mark.parametrize('attribute, default, validator_type, convert_function', ( - (RawMasterKeyConfig.provider_id, attr.NOTHING, (six.string_types, bytes), to_str), - (RawMasterKeyConfig.wrapping_key, attr.NOTHING, WrappingKey, None) -)) -def test_attributes(attribute, default, validator_type, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.default is default - assert attribute.validator.type == validator_type - if convert_function is not None: - assert attribute.convert is convert_function - -def test_attributes_fails(): +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): with pytest.raises(TypeError): - RawMasterKeyConfig(wrapping_key=None) + cls(**kwargs) + + +def test_parent(): + assert issubclass(RawMasterKeyConfig, MasterKeyConfig) -@pytest.mark.parametrize('provider_id', ('test', b'test')) -def test_attributes_convertss(provider_id): - test = RawMasterKeyConfig( - key_id='', - provider_id=provider_id, - wrapping_key=MagicMock(__class__=WrappingKey) - ) - assert test.provider_id == 'test' +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_converts(cls, kwargs): + test = cls(**kwargs) + assert isinstance(test.provider_id, six.string_types) diff --git a/test/unit/test_streaming_client_client_config.py b/test/unit/test_streaming_client_client_config.py deleted file mode 100644 index bb0ae9c97..000000000 --- a/test/unit/test_streaming_client_client_config.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. -"""Unit test suite to validate aws_encryption_sdk.streaming_client._ClientConfig""" -import io - -import attr -from mock import MagicMock -import pytest -import six - -from aws_encryption_sdk.internal.defaults import LINE_LENGTH -from aws_encryption_sdk.internal.utils import prep_stream_data -from aws_encryption_sdk.key_providers.base import MasterKeyProvider -from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager -from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager -from aws_encryption_sdk.streaming_client import _ClientConfig - - -@pytest.mark.parametrize('attribute, default, validator_type, is_optional, convert_function', ( - (_ClientConfig.source, attr.NOTHING, None, False, prep_stream_data), - (_ClientConfig.key_provider, None, MasterKeyProvider, True, None), - (_ClientConfig.materials_manager, None, CryptoMaterialsManager, True, None), - (_ClientConfig.source_length, None, six.integer_types, True, None), - (_ClientConfig.line_length, LINE_LENGTH, six.integer_types, False, None) -)) -def test_attributes(attribute, default, validator_type, is_optional, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.default is default - if validator_type is not None: - if is_optional: - assert attribute.validator.validator.type == validator_type - else: - assert attribute.validator.type == validator_type - if convert_function is not None: - assert attribute.convert is convert_function - - -@pytest.mark.parametrize('key_provider, materials_manager, source_length', ( - (None, None, 5), - (MagicMock(__class__=MasterKeyProvider), None, 'not an int'), - (None, MagicMock(__class__=CryptoMaterialsManager), 'not an int'), - (MagicMock(__class__=MasterKeyProvider), MagicMock(__class__=CryptoMaterialsManager), 5) -)) -def test_attributes_fail(key_provider, materials_manager, source_length): - with pytest.raises(TypeError): - _ClientConfig( - source='', - key_provider=key_provider, - materials_manager=materials_manager, - source_length=source_length - ) - - -def test_attributes_defaults(): - test = _ClientConfig( - source='', - key_provider=MagicMock(__class__=MasterKeyProvider) - ) - assert test.source_length is None - assert test.line_length == LINE_LENGTH - - -def test_attributes_converts(): - test = _ClientConfig( - source='', - key_provider=MagicMock(__class__=MasterKeyProvider) - ) - assert isinstance(test.source, io.BytesIO) - assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) - assert test.materials_manager.master_key_provider is test.key_provider diff --git a/test/unit/test_streaming_client_configs.py b/test/unit/test_streaming_client_configs.py new file mode 100644 index 000000000..674ef0d7e --- /dev/null +++ b/test/unit/test_streaming_client_configs.py @@ -0,0 +1,121 @@ +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Unit test suite to validate aws_encryption_sdk.streaming_client config classes.""" +import io + +import pytest +import six + +from aws_encryption_sdk.internal.defaults import ALGORITHM, FRAME_LENGTH, LINE_LENGTH +from aws_encryption_sdk.key_providers.base import MasterKeyProvider, MasterKeyProviderConfig +from aws_encryption_sdk.materials_managers.base import CryptoMaterialsManager +from aws_encryption_sdk.materials_managers.default import DefaultCryptoMaterialsManager +from aws_encryption_sdk.streaming_client import _ClientConfig, DecryptorConfig, EncryptorConfig +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs, build_valid_kwargs_list + + +class FakeCryptoMaterialsManager(CryptoMaterialsManager): + + def get_encryption_materials(self, request): + return + + def decrypt_materials(self, request): + return + + +class FakeMasterKeyProvider(MasterKeyProvider): + _config_class = MasterKeyProviderConfig + provider_id = 'fake provider' + + def _new_master_key(self, key_id): + return + + +BASE_KWARGS = dict(source=b'', materials_manager=FakeCryptoMaterialsManager()) +VALID_KWARGS = { + _ClientConfig: [ + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), source_length=10, line_length=10), + dict(source='', materials_manager=FakeCryptoMaterialsManager(), source_length=10, line_length=10), + dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager(), source_length=10, line_length=10), + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), source_length=10, line_length=10), + dict(source=b'', key_provider=FakeMasterKeyProvider(), source_length=10, line_length=10), + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), line_length=10), + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), source_length=10), + dict(source=b'', materials_manager=FakeCryptoMaterialsManager()) + ], + EncryptorConfig: build_valid_kwargs_list( + BASE_KWARGS, + dict(encryption_context={}, algorithm=ALGORITHM, frame_length=8192) + ), + DecryptorConfig: build_valid_kwargs_list(BASE_KWARGS, dict(max_body_length=10)) +} +INVALID_KWARGS = { + _ClientConfig: [ + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), key_provider=FakeMasterKeyProvider()) + ], + EncryptorConfig: [ + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), encryption_context=None), + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), frame_length=None) + ], + DecryptorConfig: [ + dict(source=b'', materials_manager=FakeCryptoMaterialsManager(), max_body_length='not an int') + ] +} + + +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) + + +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS, INVALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): + with pytest.raises(TypeError): + cls(**kwargs) + + +@pytest.mark.parametrize('cls', (EncryptorConfig, DecryptorConfig)) +def test_parents(cls): + assert issubclass(cls, _ClientConfig) + + +def test_client_config_defaults(): + test = _ClientConfig(**BASE_KWARGS) + assert test.source_length is None + assert test.line_length == LINE_LENGTH + + +def test_encryptor_config_defaults(): + test = EncryptorConfig(**BASE_KWARGS) + assert test.encryption_context == {} + assert test.algorithm is None + assert test.frame_length == FRAME_LENGTH + + +def test_decryptor_config_defautls(): + test = DecryptorConfig(**BASE_KWARGS) + assert test.max_body_length is None + + +@pytest.mark.parametrize('kwargs, stream_type', ( + (dict(source=b'', materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=b'', key_provider=FakeMasterKeyProvider()), io.BytesIO), + (dict(source='', materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=io.BytesIO(), materials_manager=FakeCryptoMaterialsManager()), io.BytesIO), + (dict(source=six.StringIO(), materials_manager=FakeCryptoMaterialsManager()), six.StringIO) +)) +def test_client_config_converts(kwargs, stream_type): + test = _ClientConfig(**kwargs) + assert isinstance(test.source, stream_type) + if test.key_provider is not None: + assert isinstance(test.materials_manager, DefaultCryptoMaterialsManager) diff --git a/test/unit/test_streaming_client_decryptor_config.py b/test/unit/test_streaming_client_decryptor_config.py deleted file mode 100644 index 79917e7fb..000000000 --- a/test/unit/test_streaming_client_decryptor_config.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. -"""Unit test suite to validate aws_encryption_sdk.streaming_client.DecryptorConfig""" -import attr -from mock import MagicMock -import pytest -import six - -from aws_encryption_sdk.key_providers.base import MasterKeyProvider -from aws_encryption_sdk.streaming_client import _ClientConfig, DecryptorConfig - - -def test_parent(): - assert issubclass(DecryptorConfig, _ClientConfig) - - -@pytest.mark.parametrize('attribute, default, validator_type, is_optional', ( - (DecryptorConfig.max_body_length, None, six.integer_types, True), -)) -def test_attributes(attribute, default, validator_type, is_optional): - assert isinstance(attribute, attr.Attribute) - assert attribute.hash - assert attribute.default == default - if is_optional: - assert attribute.validator.validator.type == validator_type - else: - assert attribute.validator.type == validator_type - - -def test_attributes_fails(): - with pytest.raises(TypeError): - DecryptorConfig( - source='', - key_provider=MagicMock(__class__=MasterKeyProvider), - max_body_length='not an int' - ) - - -def test_attributes_defaults(): - test = DecryptorConfig( - source='', - key_provider=MagicMock(__class__=MasterKeyProvider) - ) - assert test.max_body_length is None diff --git a/test/unit/test_streaming_client_encryptor_config.py b/test/unit/test_streaming_client_encryptor_config.py deleted file mode 100644 index 9cfbaa087..000000000 --- a/test/unit/test_streaming_client_encryptor_config.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. -"""Unit test suite to validate aws_encryption_sdk.streaming_client.EncryptorConfig""" -import attr -from mock import MagicMock -import pytest -import six - -from aws_encryption_sdk.identifiers import Algorithm -from aws_encryption_sdk.internal.defaults import FRAME_LENGTH -from aws_encryption_sdk.key_providers.base import MasterKey -from aws_encryption_sdk.streaming_client import _ClientConfig, EncryptorConfig - - -def test_parent(): - assert issubclass(EncryptorConfig, _ClientConfig) - - -@pytest.mark.parametrize('attribute, default, validator_type, in_hash, is_optional', ( - (EncryptorConfig.encryption_context, attr.Factory(dict), dict, False, False), - (EncryptorConfig.algorithm, None, Algorithm, True, True), - (EncryptorConfig.frame_length, FRAME_LENGTH, six.integer_types, True, False) -)) -def test_attributes(attribute, default, validator_type, in_hash, is_optional): - assert isinstance(attribute, attr.Attribute) - assert attribute.default == default - if is_optional: - assert attribute.validator.validator.type == validator_type - else: - assert attribute.validator.type == validator_type - if in_hash: - assert attribute.hash - else: - assert not attribute.hash - - -@pytest.mark.parametrize('encryption_context, frame_length', ( - (None, 5), - ({}, None) -)) -def test_attributes_fail(encryption_context, frame_length): - with pytest.raises(TypeError): - EncryptorConfig( - source='', - key_provider=MagicMock(__class__=MasterKey), - encryption_context=encryption_context, - algorithm=MagicMock(__class__=Algorithm), - frame_length=frame_length - ) - - -def test_attributes_defaults(): - test = EncryptorConfig(source='', key_provider=MagicMock(__class__=MasterKey)) - assert test.encryption_context == {} - assert test.algorithm is None - assert test.frame_length == FRAME_LENGTH diff --git a/test/unit/test_structures.py b/test/unit/test_structures.py index b08ca63d0..77fc85165 100644 --- a/test/unit/test_structures.py +++ b/test/unit/test_structures.py @@ -11,304 +11,70 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. """Unit test suite for aws_encryption_sdk.structures""" -import attr -from mock import MagicMock import pytest -import six from aws_encryption_sdk.identifiers import ( Algorithm, ContentType, ObjectType, SerializationVersion ) -from aws_encryption_sdk.internal.str_ops import to_bytes, to_str from aws_encryption_sdk.structures import ( DataKey, EncryptedDataKey, MasterKeyInfo, MessageHeader, RawDataKey ) +from .unit_test_utils import all_invalid_kwargs, all_valid_kwargs -@pytest.mark.parametrize('attribute, validator_type, convert_function', ( - (MessageHeader.version, SerializationVersion, None), - (MessageHeader.type, ObjectType, None), - (MessageHeader.algorithm, Algorithm, None), - (MessageHeader.message_id, bytes, None), - (MessageHeader.encryption_context, dict, None), - (MessageHeader.encrypted_data_keys, set, None), - (MessageHeader.content_type, ContentType, None), - (MessageHeader.content_aad_length, six.integer_types, None), - (MessageHeader.header_iv_length, six.integer_types, None), - (MessageHeader.frame_length, six.integer_types, None), - (MasterKeyInfo.provider_id, (six.string_types, bytes), to_str), - (MasterKeyInfo.key_info, (six.string_types, bytes), to_bytes), - (RawDataKey.key_provider, MasterKeyInfo, None), - (RawDataKey.data_key, bytes, None), - (DataKey.key_provider, MasterKeyInfo, None), - (DataKey.data_key, bytes, None), - (DataKey.encrypted_data_key, bytes, None), - (EncryptedDataKey.key_provider, MasterKeyInfo, None), - (EncryptedDataKey.encrypted_data_key, bytes, None) -)) -def test_attributes(attribute, validator_type, convert_function): - assert isinstance(attribute, attr.Attribute) - assert attribute.validator.type == validator_type - assert attribute.hash - if convert_function is not None: - assert attribute.convert is convert_function - - -@pytest.mark.parametrize( - ( - 'version,' - 'message_type,' - 'algorithm,' - 'message_id,' - 'encryption_context,' - 'encrypted_data_keys,' - 'content_type,' - 'content_aad_length,' - 'header_iv_length,' - 'frame_length' - ), - ( - ( - None, - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - None, - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - None, - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - None, - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - None, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - None, - MagicMock(__class__=ContentType), - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - None, - 5, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - None, - 5, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - None, - 5 - ), - ( - MagicMock(__class__=SerializationVersion), - MagicMock(__class__=ObjectType), - MagicMock(__class__=Algorithm), - b'', - {}, - set([]), - MagicMock(__class__=ContentType), - 5, - 5, - None - ) - ) -) -def test_message_header_attributes_fails( - version, - message_type, - algorithm, - message_id, - encryption_context, - encrypted_data_keys, - content_type, - content_aad_length, - header_iv_length, - frame_length -): - with pytest.raises(TypeError): - MessageHeader( - version=version, - type=message_type, - algorithm=algorithm, - message_id=message_id, - encryption_context=encryption_context, - encrypted_data_keys=encrypted_data_keys, - content_type=content_type, - content_aad_length=content_aad_length, - header_iv_length=header_iv_length, - frame_length=frame_length - ) - - -def test_message_header_attributes_succeeds(): - MessageHeader( - version=MagicMock(__class__=SerializationVersion), - type=MagicMock(__class__=ObjectType), - algorithm=MagicMock(__class__=Algorithm), - message_id=b'', +VALID_KWARGS = { + MessageHeader: [dict( + version=SerializationVersion.V1, + type=ObjectType.CUSTOMER_AE_DATA, + algorithm=Algorithm.AES_256_GCM_IV12_TAG16_HKDF_SHA384_ECDSA_P384, + message_id=b'aosiejfoaiwej', encryption_context={}, encrypted_data_keys=set([]), - content_type=MagicMock(__class__=ContentType), - content_aad_length=5, - header_iv_length=5, - frame_length=5 - ) - - -@pytest.mark.parametrize('provider_id, key_info', ( - (None, 'key'), - ('provider', None) -)) -def test_master_key_info_attributes_fails(provider_id, key_info): + content_type=ContentType.FRAMED_DATA, + content_aad_length=32456, + header_iv_length=32456, + frame_length=234567 + )], + MasterKeyInfo: [ + dict(provider_id='fawnofijawef', key_info='ajsnoiajerofi'), + dict(provider_id=b'fawnofijawef', key_info='ajsnoiajerofi'), + dict(provider_id='fawnofijawef', key_info=b'ajsnoiajerofi'), + dict(provider_id=b'fawnofijawef', key_info=b'ajsnoiajerofi') + ], + RawDataKey: [dict( + key_provider=MasterKeyInfo(provider_id='asjnoa', key_info=b'aosjfoaiwej'), + data_key=b'aosijfoewaijf' + )], + DataKey: [dict( + key_provider=MasterKeyInfo(provider_id='asjnoa', key_info=b'aosjfoaiwej'), + data_key=b'oaijefoawiejf', + encrypted_data_key=b'aisofiawjef' + )], + EncryptedDataKey: [dict( + key_provider=MasterKeyInfo(provider_id='asjnoa', key_info=b'aosjfoaiwej'), + encrypted_data_key=b'aisofiawjef' + )] +} + + +@pytest.mark.parametrize('cls, kwargs', all_valid_kwargs(VALID_KWARGS)) +def test_attributes_valid_kwargs(cls, kwargs): + cls(**kwargs) + + +@pytest.mark.parametrize('cls, kwargs', all_invalid_kwargs(VALID_KWARGS)) +def test_attributes_invalid_kwargs(cls, kwargs): with pytest.raises(TypeError): - MasterKeyInfo(provider_id=provider_id, key_info=key_info) + cls(**kwargs) -@pytest.mark.parametrize('provider_id, key_info', ( - ('provider', 'key'), - (b'provider', b'key') +@pytest.mark.parametrize('kwargs, attribute, expected_value', ( + (dict(provider_id='asfoijwae', key_info=b'oaiejfoeiwja'), 'provider_id', 'asfoijwae'), + (dict(provider_id=b'asfoijwae', key_info=b'oaiejfoeiwja'), 'provider_id', 'asfoijwae'), + (dict(provider_id='asfoijwae', key_info='oaiejfoeiwja'), 'key_info', b'oaiejfoeiwja'), + (dict(provider_id='asfoijwae', key_info=b'oaiejfoeiwja'), 'key_info', b'oaiejfoeiwja') )) -def test_key_info_attributes_converts(provider_id, key_info): - test = MasterKeyInfo( - provider_id=provider_id, - key_info=key_info - ) - assert test.provider_id == 'provider' - assert test.key_info == b'key' - - -@pytest.mark.parametrize('key_provider, data_key', ( - (None, b''), - (MagicMock(__class__=MasterKeyInfo), None) -)) -def test_raw_data_key_attributes_fails(key_provider, data_key): - with pytest.raises(TypeError): - RawDataKey( - key_provider=key_provider, - data_key=data_key - ) - - -def test_raw_data_key_attributes_succeeds(): - RawDataKey( - key_provider=MagicMock(__class__=MasterKeyInfo), - data_key=b'' - ) - - -@pytest.mark.parametrize('key_provider, data_key, encrypted_data_key', ( - (None, b'', b''), - (MagicMock(__class__=MasterKeyInfo), None, b''), - (MagicMock(__class__=MasterKeyInfo), b'', None) -)) -def test_data_key_attributes_fails(key_provider, data_key, encrypted_data_key): - with pytest.raises(TypeError): - DataKey( - key_provider=key_provider, - data_key=data_key, - encryted_data_key=encrypted_data_key - ) - - -def test_data_key_attributes_succeeds(): - DataKey( - key_provider=MagicMock(__class__=MasterKeyInfo), - data_key=b'', - encrypted_data_key=b'' - ) - - -@pytest.mark.parametrize('key_provider, encrypted_data_key', ( - (None, b''), - (MagicMock(__class__=MasterKeyInfo), None) -)) -def test_encrypted_data_key_attributes_fails(key_provider, encrypted_data_key): - with pytest.raises(TypeError): - EncryptedDataKey( - key_provider=key_provider, - encryted_data_key=encrypted_data_key - ) - +def test_master_key_info_convert(kwargs, attribute, expected_value): + test = MasterKeyInfo(**kwargs) -def test_Encrypted_data_key_attributes_succeeds(): - EncryptedDataKey( - key_provider=MagicMock(__class__=MasterKeyInfo), - encrypted_data_key=b'' - ) + assert getattr(test, attribute) == expected_value diff --git a/test/unit/unit_test_utils.py b/test/unit/unit_test_utils.py new file mode 100644 index 000000000..5c7c1d49d --- /dev/null +++ b/test/unit/unit_test_utils.py @@ -0,0 +1,52 @@ +# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. +"""Utility functions to handle common test framework functions.""" +import copy +import itertools + + +def all_valid_kwargs(valid_kwargs): + valid = [] + for cls, kwargs_sets in valid_kwargs.items(): + for kwargs in kwargs_sets: + valid.append((cls, kwargs)) + return valid + + +def all_invalid_kwargs(valid_kwargs, invalid_kwargs=None): + if invalid_kwargs is None: + invalid_kwargs = {} + invalid = [] + for cls, kwargs_sets in valid_kwargs.items(): + if cls in invalid_kwargs: + for _kwargs in invalid_kwargs[cls]: + invalid.append((cls, _kwargs)) + continue + + kwargs = kwargs_sets[-1] + for key in kwargs: + _kwargs = copy.deepcopy(kwargs) + _kwargs.update({key: None}) + invalid.append((cls, _kwargs)) + return invalid + + +def build_valid_kwargs_list(base, optional_kwargs): + valid_kwargs = [] + options = optional_kwargs.items() + for i in range(len(optional_kwargs)): + for valid_options in itertools.combinations(options, i): + _kwargs = base.copy() + _kwargs.update(dict(valid_options)) + valid_kwargs.append(_kwargs) + return valid_kwargs diff --git a/tox.ini b/tox.ini index 795f606db..282bd5ca9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,19 +1,19 @@ [tox] envlist = - py27, py34, py35, py36, - bandit, doc8, readme, + py{27,34,35,36}, + bandit, doc8, readme, docs, flake8, pylint, flake8-tests, pylint-tests, examples, flake8-examples, pylint-examples -# Additional testing environments: +# Additional test environments: # vulture :: Runs vulture. Prone to false-positives. # linters :: Runs all linters over all source code. # linters-tests :: Runs all linters over all tests. # linters-examples :: Runs all linters over all examples and examples tests. -# Additional environments: +# Operational helper environments: # docs :: Builds Sphinx documentation. # serve-docs :: Starts local webserver to serve built documentation. # build :: Builds source and wheel dist files. @@ -34,7 +34,7 @@ passenv = sitepackages = False deps = mock - pytest + pytest!=3.3.0 pytest-catchlog pytest-cov pytest-mock @@ -58,6 +58,7 @@ deps = flake8 flake8-docstrings flake8-import-order + flake8-print>=3.0.1 commands = flake8 src/aws_encryption_sdk/ setup.py