"""Tests for DynamoDB connector.""" import boto3 import decimal from boto3.dynamodb import conditions from freezegun import freeze_time from moto import mock_aws from oto import response import pytest from masters_registry import config from masters_registry import utils from masters_registry.connectors import dynamodb from masters_registry.constant import field_const from masters_registry.connectors.dynamodb import client as outside_dynamodb_client dynamodb_resource = boto3.resource("dynamodb") def test_timestamp_is_saved(mocker): """Expect to store timestamp along with other information.""" table = mocker.MagicMock() mock_update = mocker.patch.object( table, 'update_item', return_value=lambda **kwargs: 'test') mock_log_timestamp = mocker.patch.object( dynamodb, 'log_updated_timestamp') call_args = { 'table': table, 'key': 'test', 'verb': 'SET', 'expressions': ['something = :s'], 'attribute_names': {}, 'attribute_values': {':s': {'S': 'test'}}, 'batch_size': 1 } expected_call_args = { 'Key': 'test', 'UpdateExpression': 'SET something = :s', 'ExpressionAttributeNames': {}, 'ExpressionAttributeValues': { ':s': {'S': 'test'}} } dynamodb.batch_update_item_fields(**call_args) mock_update.assert_called_with(**expected_call_args) mock_log_timestamp.assert_called_with(table, call_args['key']) @freeze_time('2017-11-01 15:00:00') def test_log_updated_timestamp(mocker): """Expected to call DynamoDB update with correct args.""" table = mocker.MagicMock() mock_update = mocker.patch.object( table, 'update_item', return_value=lambda **kwargs: 'test') isrc = 'test_isrc' expected_call_args = { 'Key': isrc, 'UpdateExpression': 'SET updated_timestamp = :r', 'ExpressionAttributeValues': { ':r': dynamodb.utils.get_timestamp_for_dynamo()} } dynamodb.log_updated_timestamp(table, isrc) mock_update.assert_called_with(**expected_call_args) @freeze_time('2017-11-01 15:00:00') def test_update_item_field(mocker): """Expect to update field of given item.""" test_isrc = 'GBQXA0910579' update_data = { 'US': [{'tuid': decimal.Decimal('11684704')}], 'UK': [{'tuid': decimal.Decimal('12334457')}] } mock_dynamodb_client = mocker.patch.object( dynamodb.client, 'update_item') # without mock new conditions object is created on every call # so test will fail mock_conditions = mocker.patch.object( dynamodb.conditions.Key, 'eq') result = dynamodb.update_isrc_item_field( isrc=test_isrc, field_name='territories', new_value=update_data) expected_expression = ( 'SET territories = :new_value, updated_timestamp = :new_timestamp') expected_call_kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Key': {'isrc': test_isrc}, 'ConditionExpression': conditions.Key(field_const.ISRC).eq(test_isrc), 'UpdateExpression': expected_expression, 'ExpressionAttributeValues': { ':new_value': update_data, ':new_timestamp': utils.get_timestamp_for_dynamo() }, } mock_dynamodb_client.assert_called_with(**expected_call_kwargs) mock_conditions.assert_called_with(test_isrc) assert isinstance(result, response.Response) assert result expected_payload = { 'isrc': test_isrc, 'field_name': 'territories', 'saved_value': update_data } assert result.message == expected_payload @freeze_time('2017-11-01 15:00:00') def test_add_updated_timestamp_to_kwargs(): """Expect update timestamp expression to be added to update kwargs.""" update_kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Key': {'isrc': 'QA21354'}, 'UpdateExpression': 'SET territories = :new_value', 'ExpressionAttributeValues': {':new_value': 'some data'}, } result = dynamodb._add_updated_timestamp_to_kwargs(update_kwargs) expected_expression = ( 'SET territories = :new_value, updated_timestamp = :new_timestamp') expected_call_kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Key': {'isrc': 'QA21354'}, 'UpdateExpression': expected_expression, 'ExpressionAttributeValues': { ':new_value': 'some data', ':new_timestamp': utils.get_timestamp_for_dynamo() }, } assert result == expected_call_kwargs @pytest.fixture @freeze_time('2017-11-01 15:00:00') def valid_isrc(): return { 'isrc': 'QA123', 'territories': {'US': [{'tuid': 123}]}, 'locked_territories': [], 'timestamp': utils.get_timestamp_for_dynamo(), 'updated_timestamp': utils.get_timestamp_for_dynamo() } @freeze_time('2017-11-01 15:00:00') def test_update_isrc(mocker, valid_isrc): """Test update full ISRC ownership item.""" mock_dynamodb_client = mocker.patch.object( dynamodb.client, 'put_item') update_kwargs = { 'TableName': config.DYNAMODB_MASTERS_ACTIVE, 'Item': valid_isrc } dynamodb.update_isrc(valid_isrc) mock_dynamodb_client.assert_called_with(**update_kwargs) @mock_aws def test_update_isrc_functional(setup_masters_active_table, valid_isrc): """Test update full ISRC ownership item with moto.""" from moto.core import patch_client, patch_resource patch_client(outside_dynamodb_client) patch_resource(dynamodb_resource) client = dynamodb_resource.meta.client setup_masters_active_table(client) dynamodb.update_isrc(valid_isrc) active_table = dynamodb_resource.Table( config.DYNAMODB_MASTERS_ACTIVE) saved_item = active_table.get_item(Key={field_const.ISRC: 'QA123'})['Item'] assert saved_item == valid_isrc