"""Lambda test module.""" import pytest from typing import Optional from unittest.mock import MagicMock from unittest.mock import patch from src import app as index REFRESH_TABLE_NAME_TEST = 'unit-test-table' @pytest.mark.parametrize( 'refresh_table_stream_enabled, refresh_table_global_replicas_enabled', [ pytest.param(True, True, id='stream and global replicas enabled'), pytest.param(False, True, id='global replicas enabled'), pytest.param(True, False, id='stream enabled'), pytest.param(False, False, id='neither stream nor global replicas enabled'), ] ) @patch('src.app.update_table') def test_handler( mock_update_table: MagicMock, refresh_table_stream_enabled: bool, refresh_table_global_replicas_enabled: bool, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ): """Test handler function.""" monkeypatch.setattr(index, 'REFRESH_TABLE_NAME', REFRESH_TABLE_NAME_TEST) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', refresh_table_stream_enabled) monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', refresh_table_global_replicas_enabled) result = index.handler({}, None) assert result == {'status': 'OK'} if refresh_table_stream_enabled or refresh_table_global_replicas_enabled: mock_update_table.assert_called_with(REFRESH_TABLE_NAME_TEST) with caplog.at_level('INFO'): assert f'Starting table update for {REFRESH_TABLE_NAME_TEST}' in caplog.text else: mock_update_table.assert_not_called() @patch('src.app.update_table', side_effect=Exception('Oh no!')) def test_handler_error( mock_update_table: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture ): """Test handler function. Test that an exception is raised when an error occurs during the table update. """ monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', True) with pytest.raises(Exception): index.handler({}, None) with caplog.at_level('ERROR'): assert 'Oh no!' in caplog.text @patch('src.app.replica_update_spec') @patch('src.app.dynamodb.update_table') def test_update_table_refresh_table_global_replicas_enabled( dynamodb_update_table: MagicMock, replica_update_spec: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that the global table update is called when refresh table global replicas is enabled. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', True) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', False) monkeypatch.setattr(index, 'TTL_ENABLED', False) replica_update_spec.return_value = 'replica_update_spec' index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_update_table.assert_called_once() dynamodb_update_table.assert_called_with( TableName=REFRESH_TABLE_NAME_TEST, ReplicaUpdates='replica_update_spec', ) with caplog.at_level('INFO'): assert 'Global table update complete' in caplog.text @patch('src.app.dynamodb.update_table', side_effect=Exception('Bonk!')) def test_update_table_refresh_table_global_replicas_enabled_error( dynamodb_update_table: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that an exception is raised when an error occurs during the global table update. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', True) with pytest.raises(Exception): index.update_table(REFRESH_TABLE_NAME_TEST) with caplog.at_level('ERROR'): assert 'Bonk!' in caplog.text @patch('src.app.stream_update_spec') @patch('src.app.dynamodb.update_table') def test_update_table_refresh_table_stream_enabled( dynamodb_update_table: MagicMock, stream_update_spec: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that the stream update is called when refresh table stream is enabled. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', True) monkeypatch.setattr(index, 'TTL_ENABLED', False) stream_update_spec.return_value = 'stream_update_spec' index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_update_table.assert_called_with( TableName=REFRESH_TABLE_NAME_TEST, StreamSpecification='stream_update_spec', ) with caplog.at_level('INFO'): assert 'Stream update complete' in caplog.text @patch('src.app.set_lambda_event_source_mapping') @patch('src.app.stream_update_spec') @patch('src.app.dynamodb') @patch('src.app.lambda_client') def test_update_table_source_mapping_called( mock_lambda_client: MagicMock, mock_dynamodb_client: MagicMock, stream_update_spec: MagicMock, mock_set_lambda_event_source_mapping: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that the event source mapping is set when env variables are present. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', True) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_CONSUMER_LAMBDA_ARN', 'some_lambda_fn_arn') monkeypatch.setattr(index, 'TTL_ENABLED', False) stream_update_spec.return_value = 'stream_update_spec' index.update_table(REFRESH_TABLE_NAME_TEST) mock_dynamodb_client.update_table.assert_called_with( TableName=REFRESH_TABLE_NAME_TEST, StreamSpecification='stream_update_spec', ) with caplog.at_level('INFO'): assert 'Stream update complete' in caplog.text mock_set_lambda_event_source_mapping.assert_called_once_with( dynamodb_client=mock_dynamodb_client, dynamodb_table=REFRESH_TABLE_NAME_TEST, lambda_client=mock_lambda_client, target_lambda_arn='some_lambda_fn_arn', **{ 'BatchSize': 100, 'ParallelizationFactor': 1, 'StartingPosition': 'LATEST', } ) @patch('src.app.dynamodb.update_table', side_effect=Exception('Kablamo!')) def test_update_table_refresh_table_stream_enabled_error( dynamodb_update_table: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that an exception is raised when an error occurs during the stream update. """ monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', True) with pytest.raises(Exception): index.update_table(REFRESH_TABLE_NAME_TEST) with caplog.at_level('ERROR'): assert 'Kablamo!' in caplog.text @patch('src.app.dynamodb.update_time_to_live') def test_update_table_ttl_enabled( dynamodb_update_time_to_live: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that the TTL update is called when TTL is enabled. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', False) monkeypatch.setattr(index, 'TTL_ENABLED', True) monkeypatch.setattr(index, 'TTL_ATTRIBUTE', 'expires_at') index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_update_time_to_live.assert_called_with( TableName=REFRESH_TABLE_NAME_TEST, TimeToLiveSpecification={ 'AttributeName': 'expires_at', 'Enabled': True } ) with caplog.at_level('INFO'): assert 'TTL update complete' in caplog.text @patch('src.app.dynamodb.update_time_to_live', side_effect=Exception('Pow!')) def test_update_table_ttl_enabled_error( dynamodb_update_time_to_live: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that an exception is raised when an error occurs during the TTL update. """ monkeypatch.setattr(index, 'TTL_ENABLED', True) monkeypatch.setattr(index, 'TTL_ATTRIBUTE', 'expires_at') with pytest.raises(Exception): index.update_table(REFRESH_TABLE_NAME_TEST) with caplog.at_level('ERROR'): assert 'Pow!' in caplog.text @pytest.mark.parametrize('apply_tags_value', [ pytest.param(None, id='APPLY_TAGS environment variable was not set'), pytest.param('', id='APPLY_TAGS environment variable is empty string'), ]) @patch('src.app.dynamodb.tag_resource') def test_update_table_tag_resource_not_called( dynamodb_tag_resource: MagicMock, apply_tags_value: Optional[str], monkeypatch: pytest.MonkeyPatch, ): """Test update_table function. Test that the TTL update is called when TTL is enabled. """ monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', False) monkeypatch.setattr(index, 'TTL_ENABLED', False) monkeypatch.setattr(index, 'APPLY_TAGS', apply_tags_value) index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_tag_resource.assert_not_called() @pytest.mark.parametrize('apply_tags_value, expected_call_arg', [ pytest.param( '[{"Key": "application_family", "Value": "permissions-platform"}, {"Key": "environment", "Value": "qa"}]', [ {'Key': 'application_family', 'Value': 'permissions-platform'}, {'Key': 'environment', 'Value': 'qa'}, ], id='APPLY_TAGS environment variable was set', ), pytest.param('[]', [], id='APPLY_TAGS environment implies to set empty list of tags'), ]) @patch('src.app.dynamodb.tag_resource') def test_update_table_tag_resource( dynamodb_tag_resource: MagicMock, apply_tags_value: str, expected_call_arg: list, monkeypatch: pytest.MonkeyPatch, ): """Test update_table function. Test that the table is tagged. """ arn = 'some:arn:of:dynamodb:table' monkeypatch.setattr(index, 'REFRESH_TABLE_ARN', arn) monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', False) monkeypatch.setattr(index, 'TTL_ENABLED', False) monkeypatch.setattr(index, 'APPLY_TAGS', apply_tags_value) index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_tag_resource.assert_called_once_with(ResourceArn=arn, Tags=expected_call_arg) @patch('src.app.dynamodb.tag_resource') def test_update_table_tag_resource_error( dynamodb_tag_resource: MagicMock, monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture, ): """Test update_table function. Test that the table is tagged. """ arn = 'some:arn:of:dynamodb:table' monkeypatch.setattr(index, 'REFRESH_TABLE_ARN', arn) monkeypatch.setattr(index, 'REFRESH_TABLE_GLOBAL_REPLICAS_ENABLED', False) monkeypatch.setattr(index, 'REFRESH_TABLE_STREAM_ENABLED', False) monkeypatch.setattr(index, 'TTL_ENABLED', False) monkeypatch.setattr(index, 'APPLY_TAGS', '[{"Key": "application_family", "Value": "permissions-platform"}]') dynamodb_tag_resource.side_effect = Exception('Ooof') with pytest.raises(Exception): index.update_table(REFRESH_TABLE_NAME_TEST) dynamodb_tag_resource.assert_called_once() with caplog.at_level('ERROR'): assert 'Ooof' in caplog.text