"""Test DynamoDB utility functions.""" import copy import datetime from unittest import mock import pytest from accounting.util import dynamodb @pytest.mark.parametrize('max_attempts, expected_max_attempts', [ (None, 10), (21, 21), ]) def test_get_dynamodb(mocker, max_attempts, expected_max_attempts): """Test DynamoDB resource helper uses proper configuration.""" mocked_botocore_config_instance = mocker.Mock() mocked_botocore_config = mocker.patch( 'accounting.util.dynamodb.botocore_config.Config') mocked_botocore_config.return_value = mocked_botocore_config_instance mocked_dynamodb = mocker.Mock() mocked_resource = mocker.patch('boto3.resource') mocked_resource.return_value = mocked_dynamodb assert dynamodb.get_dynamodb(max_attempts) == mocked_dynamodb mocked_botocore_config.assert_called_with( retries={'max_attempts': expected_max_attempts}) mocked_resource.assert_called_with( 'dynamodb', config=mocked_botocore_config_instance) def test_get_dynamodb_table(mocker): """Test that get_dynamodb_table funciton uses configured table name.""" test_table = 'test_table' mocked_dynamo_db = mocker.Mock() mocked_get_dynamodb = mocker.patch( 'accounting.util.dynamodb.get_dynamodb') mocked_get_dynamodb.return_value = mocked_dynamo_db assert dynamodb.get_dynamodb_table(test_table) mocked_dynamo_db.Table.assert_called_with(test_table) @pytest.mark.parametrize('table_name', ['first_table', 'second_table']) def test_get_table_count(mocker, table_name): """Test that get_table_count uses item_count property.""" expected_item_count = 42 mock_table = mocker.Mock() mocked_get_dynamodb_table = mocker.patch( 'accounting.util.dynamodb.get_dynamodb_table') mocked_full_scan = mocker.patch( 'accounting.util.dynamodb.full_scan', return_value={'Count': expected_item_count}) mocked_get_dynamodb_table.return_value = mock_table assert dynamodb.get_table_count(table_name) == expected_item_count mocked_get_dynamodb_table.assert_called_with(table_name) mocked_full_scan.assert_called_with(mock_table, Select='COUNT') @pytest.mark.parametrize('timed_delta, expected_ttl', [ (datetime.timedelta(days=1), 1514851200), (datetime.timedelta(hours=5), 1514782800), ]) @mock.patch('accounting.util.dynamodb.datetime') def test_get_ttl_value_for_item(mock_datetime, timed_delta, expected_ttl): """Test get_ttl_value_for_item utility function.""" new_year = datetime.datetime( 2018, 1, 1, 0, 0, tzinfo=datetime.timezone.utc) mock_datetime.datetime.now.return_value = new_year assert dynamodb.get_ttl_value_for_item(timed_delta) == expected_ttl mock_datetime.datetime.now.assert_called_once() @pytest.mark.parametrize('scan_kwargs', [ {}, {'a': 1}, {'Select': 'COUNT', 'b': 2}, ]) def test_get_scan_results(scan_kwargs): """Test get_scan_results utility function.""" table = mock.MagicMock() response = {'response': 'item'} table.scan.return_value = response result = list(dynamodb.get_scan_results(table, scan_kwargs)) assert result == [response] table.scan.assert_called_once_with(**scan_kwargs) @pytest.mark.parametrize('scan_kwargs', [ {}, {'a': 1}, {'Select': 'COUNT', 'b': 2}, ]) def test_get_scan_results_items(scan_kwargs): """Test get_scan_results utility function.""" table = mock.MagicMock() expected_scan_kwargs = copy.deepcopy(scan_kwargs) key = {'item': 'key'} response = {'LastEvaluatedKey': key} table.scan.side_effect = (response, {}) result = list(dynamodb.get_scan_results(table, scan_kwargs)) assert result == [response, {}] expected_calls = ( mock.call(**expected_scan_kwargs), mock.call(ExclusiveStartKey=key, **expected_scan_kwargs)) table.scan.assert_has_calls(expected_calls) def test_get_scan_results_raises(): """Test that get_scan_results raises exceptions.""" table = mock.MagicMock() table.scan.side_effect = Exception() with pytest.raises(Exception): next(dynamodb.get_scan_results(table, {})) @pytest.mark.parametrize('resp, expected_result', [ ([ {'Count': 10, 'ScannedCount': 100} ], {'Count': 10, 'ScannedCount': 100}), ([ {'Count': 10, 'ScannedCount': 2121}, {'Count': 4232, 'ScannedCount': 2121}], {'Count': 4242, 'ScannedCount': 4242} ), ([ {'Count': 10, 'ScannedCount': 2121, 'Items': [1, 2]}], {'Count': 10, 'ScannedCount': 2121, 'Items': [1, 2]} ), ([ {'Count': 2, 'ScannedCount': 2121, 'Items': [1, 2]}, {'Count': 0, 'ScannedCount': 2121, 'Items': []}], {'Count': 2, 'ScannedCount': 4242, 'Items': [1, 2]} ), ([ {'Count': 2, 'ScannedCount': 2121, 'Items': [1, 2]}, {'Count': 2, 'ScannedCount': 2121, 'Items': [3, 4]}], {'Count': 4, 'ScannedCount': 4242, 'Items': [1, 2, 3, 4]} ) ]) @pytest.mark.parametrize('scan_kwargs', [ {}, {'a': 1}, {'Select': 'COUNT', 'b': 2}, ]) @mock.patch('accounting.util.dynamodb.get_scan_results') def test_full_scan(get_scan_results, scan_kwargs, resp, expected_result): """Test full_scan utility function.""" table = mock.MagicMock() expected_scan_kwargs = copy.deepcopy(scan_kwargs) get_scan_results.return_value = resp result = dynamodb.full_scan(table, **scan_kwargs) assert result == expected_result get_scan_results.assert_called_once_with(table, expected_scan_kwargs) @mock.patch('accounting.util.dynamodb.get_scan_results') def test_full_scan_raises(get_scan_results): """Test that full_scan raises exceptions.""" table = mock.MagicMock() get_scan_results.side_effect = Exception() with pytest.raises(Exception): dynamodb.full_scan(table, a=1)