"""Lambda test module.""" import copy from unittest import mock import pytest import index # noqa @pytest.fixture def event_fixture(): """S3 event fixture.""" return { 'Records': [ { 'eventVersion': '2.0', 'eventTime': '1970-01-01T00:00:00.000Z', 'requestParameters': { 'sourceIPAddress': '127.0.0.1' }, 's3': { 'configurationId': 'testConfigRule', 'object': { 'eTag': '0123456789abcdef0123456789abcdef', 'sequencer': '0A1B2C3D4E5F678901', 'key': 'HappyFace.jpg', 'size': 1024 }, 'bucket': { 'arn': 'bucketarn', 'name': 'sourcebucket', 'ownerIdentity': { 'principalId': 'EXAMPLE' } }, 's3SchemaVersion': '1.0' }, 'responseElements': { 'x-amz-id-2': '/mnopqrstuvwxyzABCDEFGH', 'x-amz-request-id': 'EXAMPLE123456789' }, 'awsRegion': 'us-east-1', 'eventName': 'ObjectCreated:Put', 'userIdentity': { 'principalId': 'EXAMPLE' }, 'eventSource': 'aws:s3' } ] } @mock.patch('index.sns') @mock.patch('util.parse_file_key') @mock.patch('index.validate_file') @mock.patch('index.common_config') def test_handler_success( common_config, validate_file, parse_file_key, sns, event_fixture): """Test handler function if no errors.""" success_topic = 'SNS_SUCCESS' failure_topic = 'SNS_FAILURE' common_config.SNS_ARN_SUCCESS = success_topic common_config.SNS_ARN_FAIL = failure_topic # ensure that ignore all events is not a MagicMock object. common_config.IGNORE_ALL_EVENTS = None expected_event = copy.deepcopy(event_fixture) validate_file.return_value = '' attachment_attrs = {'expected': 'attachment_attrs'} parse_file_key.return_value = attachment_attrs expected_sns_message = { 's3_event': expected_event, 'attachment_attrs': attachment_attrs } index.handler(event_fixture, None) common_config.logger.info.assert_called() validate_file.assert_called_once_with( expected_event['Records'][0]['s3']['object']) sns.send_notification.assert_called_with( success_topic, expected_sns_message ) @mock.patch('index.sns') @mock.patch('util.parse_file_key') @mock.patch('index.validate_file') @mock.patch('index.common_config') def test_handler_ignores_all_events( common_config, validate_file, parse_file_key, sns, event_fixture): """Test handler function if no errors.""" success_topic = 'SNS_SUCCESS' failure_topic = 'SNS_FAILURE' common_config.SNS_ARN_SUCCESS = success_topic common_config.SNS_ARN_FAIL = failure_topic common_config.IGNORE_ALL_EVENTS = 'True' validate_file.return_value = '' attachment_attrs = {'expected': 'attachment_attrs'} parse_file_key.return_value = attachment_attrs index.handler(event_fixture, None) assert validate_file.call_count == 0 assert sns.send_notification.call_count == 0 assert parse_file_key.call_count == 0 @mock.patch('index.sns') @mock.patch('util.parse_file_key') @mock.patch('index.validate_file') @mock.patch('index.common_config') def test_handler_failure( common_config, validate_file, parse_file_key, sns, event_fixture): """Test handler function if no errors.""" success_topic = 'SNS_SUCCESS' failure_topic = 'SNS_FAILURE' common_config.SNS_ARN_SUCCESS = success_topic common_config.SNS_ARN_FAIL = failure_topic # ensure that ignore all events is not a MagicMock object. common_config.IGNORE_ALL_EVENTS = None expected_event = copy.deepcopy(event_fixture) validation_failure_message = 'Validation failure. Something went wrong.' validate_file.return_value = validation_failure_message attachment_attrs = {'expected': 'attachment_attrs'} parse_file_key.return_value = attachment_attrs expected_error_message = ( 'Verification error: {} Validation failure. ' 'Something went wrong.'.format( expected_event['Records'][0]['s3']['object']['key'])) expected_sns_payload = { 'status': 'ERROR', 's3_event': expected_event, 'attachment_attrs': attachment_attrs, 'detailed': expected_error_message} index.handler(event_fixture, None) common_config.logger.info.assert_called() validate_file.assert_called_once_with( expected_event['Records'][0]['s3']['object']) sns.send_notification.assert_called_with( failure_topic, expected_sns_payload) @pytest.mark.parametrize('size, expected_result', [ (1024, True), (1024 * 1024, True), (10 * 1024 * 1024, True), (15 * 1024 * 1024, False), ]) @mock.patch('index.general') def test_validate_size(general_const, size, expected_result): """Test validate_size function.""" general_const.MAX_ALLOWED_FILE_SIZE = 10 * 1024 * 1024 assert index.validate_size(size) == expected_result @pytest.mark.parametrize('extension, expected_result', [ ('jpg', True), ('JPG', True), ('pdf', True), ('txt', False) ]) @mock.patch('index.general') def test_validate_extension(general_const, extension, expected_result): """Test validate_extension function.""" general_const.ALLOWED_FILE_EXTENSIONS = ('jpg, pdf') assert index.validate_extension(extension) == expected_result @pytest.mark.parametrize('key, expected_result', [ ('HappyFace.jpg', False), ('another-dir/HappyFace.jpg', False), ('attachment-dir/HappyFace.jpg', True) ]) @mock.patch('index.common_config') def test_validate_path(common_config, key, expected_result): """Test validate_path function.""" common_config.S3_BUCKET_FOLDER = 'attachment-dir' assert index.validate_path(key) == expected_result @pytest.mark.parametrize('validation_to_fail', [ 'validate_path', 'validate_size', 'parse_file_key', 'validate_extension', 'no_failure', ]) @mock.patch('index.validate_extension') @mock.patch('util.parse_file_key') @mock.patch('index.validate_size') @mock.patch('index.validate_path') @mock.patch('index.error') def test_validate_file( error_const, validate_path, validate_size, parse_file_key, validate_extension, validation_to_fail, event_fixture): """Test validate_file function.""" msg_map = { 'validate_path': 'INVALID_PATH_MSG', 'validate_size': 'INVALID_SIZE_MSG', 'parse_file_key': 'INVALID_NAME_MSG', 'validate_extension': 'INVALID_EXTENSION_MSG', } error_msg = msg_map.get(validation_to_fail, '') if error_msg: setattr(error_const, error_msg, error_msg) validation_function_mock = locals().get(validation_to_fail) if validation_function_mock: validation_function_mock.return_value = False expected_result = error_msg s3_object = event_fixture['Records'][0]['s3']['object'] assert index.validate_file(s3_object) == expected_result