"""Functional tests for `remove_ownership` endpoint.""" from decimal import Decimal import json from unittest.mock import Mock from unittest.mock import patch from boto3.dynamodb.conditions import Key from freezegun import freeze_time from moto import mock_aws import boto3 import pytest from masters_registry import utils from masters_registry.models import ownership from masters_registry.models import yt_ownership from tests import test_utils from tests.helpers import patches from masters_registry.connectors.dynamodb import client as outside_dynamodb_client dynamodb_resource = boto3.resource("dynamodb") @pytest.fixture def dynamodb_get_item_response_old_format(): """Response containing item from DynamoDB for given ISRC. Note that for brevity most of territories were removed. In real item there will be 246 territories. """ item = { 'Item': { 'territories': { 'US': {'tuid': Decimal('11684704')}, 'AF': {'tuid': Decimal('11684704')} }, 'locked_territories': {}, 'isrc': 'GBQXA0910579', 'updated_timestamp': Decimal('1514457624.334598'), 'timestamp': Decimal('1489014691557.2031') }, 'ResponseMetadata': {} } return item @pytest.fixture def dynamodb_get_item_response_new_format(): """Response containing item from DynamoDB for given ISRC. Note that for brevity most of territories were removed. In real item there will be 246 territories. """ item = { 'Item': { 'territories': { 'AF': [{'tuid': 11432391}], 'US': [{'tuid': 11432391}] }, 'locked_territories': {}, 'isrc': 'QA123', 'updated_timestamp': Decimal('1514457624.334598'), 'timestamp': Decimal('1489014691557.2031') }, 'ResponseMetadata': {} } return item @pytest.fixture def dynamodb_get_item_response_new_format_with_conflict(): """Response containing item from DynamoDB for given ISRC. This item contains an internal conflict for AF territory Note that for brevity most of territories were removed. In real item there will be 246 territories. """ item = { 'Item': { 'territories': { 'AF': [{'tuid': 11432391}, {'tuid': 11684704}], 'US': [{'tuid': 11432391}] }, 'locked_territories': {}, 'isrc': 'QA123', 'updated_timestamp': Decimal('1514457624.334598'), 'timestamp': Decimal('1489014691557.2031') }, 'ResponseMetadata': {} } return item @pytest.fixture def test_headers(): """Headers required to test this endpoint.""" headers = { 'Orchard-User-Id': '123', 'Correlation-Id': 'test', 'Content-Type': 'application/json' } return headers @patch( 'masters_registry.models.ows_carveouts.get_dms_carveout_for_upc', new=patches.ows_carveouts_get_dms_carveout_for_upc_not_carved_out_patch) @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_afghanistan_patch) @patch( 'masters_registry.logic.masters_registry._is_valid_isrc_tuid', new=lambda *args: True) @patch( 'masters_registry.models.yt_ownership.send_message', new=Mock()) @patch( 'masters_registry.models.ownership.get_upc_by_tuid', new=patches.ownership_get_upc_by_tuid_functional_tests_patch) @freeze_time(patches.TEST_TIME_AS_STR) @mock_aws def test_remove_ownership_new_format( client, test_headers, feature_engine, test_isrc, setup_masters_active_table, seed_masters_active_table, setup_new_audit_table, dynamodb_get_item_response_new_format): """Expect tuid to be removed from existing list of tuid dicts. New format means that in DynamoDB masters-active table in single item we store mapping between territories and tuids like this: { 'isrc': 'GBQXA0910579', 'territories': { 'US': [{'tuid': 11684704}], 'UA': [{'tuid': 11684704}, {'tuid': 1235432}], # and 244 more of same dicts, one for each territory } # rest of field skipped } So when we remove ownership we remove territory dict from list. """ from moto.core import patch_client, patch_resource patch_client(outside_dynamodb_client) patch_resource(dynamodb_resource) setup_masters_active_table(dynamodb_resource.meta.client) seed_masters_active_table( dynamodb_resource.meta.client, item=dynamodb_get_item_response_new_format['Item']) setup_new_audit_table(dynamodb_resource.meta.client) data = json.dumps({ 'tuid': 11432391, 'territories': ['AF'] }) update_url = '/ownership/isrc/{}/territories'.format(test_isrc) update_response = client.delete( update_url, headers=test_headers, data=data) update_payload = json.loads(update_response.data.decode()) expected_update_payload = {'tuid': 11432391, 'territories': ['AF']} assert update_payload == expected_update_payload get_response = ownership.get_ownership(test_isrc) expected_saved_item = { 'isrc': test_isrc, 'locked_territories': {}, 'territories': {'US': [{'tuid': Decimal('11432391')}]}, 'timestamp': Decimal('1489014691557.2031'), 'updated_timestamp': utils.get_timestamp_for_dynamo()} assert get_response.message == expected_saved_item created_audit_entry = ownership.new_audit_table.query( KeyConditionExpression=Key('isrc').eq(test_isrc) )['Items'][0] expected_audit_entry = { 'territories': {'AF': 11432391}, 'isrc': test_isrc, 'opcode': 'REMOVE', 'correlation_id': 'test', 'timestamp': Decimal('1512140400000.0'), 'user': '123'} assert created_audit_entry == expected_audit_entry yt_ownership.send_message.assert_called_with( test_isrc, {'US'}, 'test') @patch( 'masters_registry.models.ows_carveouts.get_dms_carveout_for_upc', new=patches.ows_carveouts_get_dms_carveout_for_upc_not_carved_out_patch) @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_afghanistan_patch) @patch( 'masters_registry.logic.masters_registry._is_valid_isrc_tuid', new=lambda *args: True) @patch( 'masters_registry.models.yt_ownership.send_message', new=Mock()) @patch( 'masters_registry.models.ownership.get_upc_by_tuid', new=patches.ownership_get_upc_by_tuid_functional_tests_patch) @patch( 'masters_registry.models.ownership.get_tracks', new=patches.tracks_response_with_conflict_patch) @patch( 'masters_registry.utils.get_timestamp_for_dynamo', new=lambda: patches.TEST_TIME_AS_TIMESTAMP) @mock_aws def test_remove_ownership_new_format_conflict_resolved( client, test_headers, feature_engine, setup_masters_active_table, seed_masters_active_table, setup_new_audit_table, dynamodb_get_item_response_new_format_with_conflict, test_isrc): """Expect tuid to be removed from existing list of tuid dicts. Expect resolved internal conflict information to be written to audit log. """ from moto.core import patch_client, patch_resource patch_client(outside_dynamodb_client) patch_resource(dynamodb_resource) setup_masters_active_table(dynamodb_resource.meta.client) seed_masters_active_table( dynamodb_resource.meta.client, item=dynamodb_get_item_response_new_format_with_conflict['Item']) setup_new_audit_table(dynamodb_resource.meta.client) conflict_tuid = 11684704 ignore_fields = ['timestamp', 'updated_timestamp'] data = json.dumps({ 'tuid': conflict_tuid, 'territories': ['AF'] }) update_url = '/ownership/isrc/{}/territories'.format(test_isrc) update_response = client.delete( update_url, headers=test_headers, data=data) expected_response_payload = {'territories': ['AF'], 'tuid': conflict_tuid} received_payload = json.loads(update_response.data.decode()) assert received_payload == expected_response_payload get_response = ownership.get_ownership(test_isrc) expected_saved_item = { 'isrc': test_isrc, 'locked_territories': {}, 'territories': { 'US': [{'tuid': Decimal('11432391')}], 'AF': [{'tuid': Decimal('11432391')}] }} assert test_utils.equal_dicts( get_response.message, expected_saved_item, ignore_fields) created_audit_entries = ownership.new_audit_table.query( KeyConditionExpression=Key('isrc').eq(test_isrc) )['Items'] assert len(created_audit_entries) == 2 remove_audit_item = created_audit_entries[0] conflict_audit_item = created_audit_entries[1] expected_audit_entry = { 'territories': {'AF': Decimal(conflict_tuid)}, 'isrc': test_isrc, 'opcode': 'REMOVE', 'correlation_id': 'test', 'user': '123' } expected_conflict_entry = { 'territories': {'AF': Decimal(conflict_tuid)}, 'isrc': test_isrc, 'opcode': 'CONFLICT_RESOLVED', 'source': 'MANUAL_EDIT_ISRC', 'correlation_id': 'test', 'user': '123', 'conflict': { 'conflicting_tuid': [], 'status': 'CONFLICT_RESOLVED', 'resolved': 1 } } assert test_utils.equal_dicts( expected_audit_entry, remove_audit_item, ignore_fields) assert test_utils.equal_dicts( expected_conflict_entry, conflict_audit_item, ignore_fields ) yt_ownership.send_message.assert_called_with( test_isrc, {'US', 'AF'}, 'test')