from decimal import Decimal import json from unittest.mock import Mock from unittest.mock import patch from boto3.dynamodb import conditions from moto import mock_aws import boto3 import pytest from masters_registry import config from masters_registry.constant import error from masters_registry.constant import field_const from masters_registry.models import 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") def make_post(client, valid_headers, data): url = '/ownership/lock/isrc' valid_headers['Content-Type'] = 'application/json' return client.post(url, data=json.dumps(data), headers=valid_headers) @pytest.fixture def valid_data(): return { field_const.LOCK_REASON: 'lost ownership dispute', field_const.ISRC: 'ZW1010600008', field_const.TERRITORIES: ['AF', 'AS', 'DZ'] } @pytest.fixture def valid_lock_data(): return { field_const.LOCK_REASON: 'lost ownership dispute', field_const.ISRC: 'QA123', field_const.TERRITORIES: ['AF'] } @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. """ 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 @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_afghanistan_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch_for_locks) @patch( 'masters_registry.models.ownership.get_upc_by_tuid', new=patches.ownership_get_upc_by_tuid_functional_tests_patch) @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.yt_ownership.send_message', new=Mock()) @mock_aws def test_post_lock_territories_no_conflict_ff_enabled( client, valid_headers, valid_lock_data, setup_masters_active_table, seed_masters_active_table, setup_new_audit_table, feature_engine, test_isrc, dynamodb_get_item_response_new_format, valid_correlation_id): """Test post valid data.""" from moto.core import patch_client, patch_resource patch_client(outside_dynamodb_client) patch_resource(dynamodb_resource) test_isrc = 'QA123' 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) res = make_post(client, valid_headers, valid_lock_data) assert res.status_code == 200 expected_update_payload = { 'territories': ['AF'], 'created': {'user': '', 'datetime': '2018-02-06T13:19:13.788588'}, 'lock_reason': 'lost ownership dispute', 'isrc': 'QA123', 'updated': {}, } update_payload = json.loads(res.data.decode()) assert test_utils.equal_dicts( update_payload, expected_update_payload, ['created']) assert update_payload['created'] expected_updated_ownership = { 'updated_timestamp': Decimal('1517912353.813532'), 'territories': {}, 'timestamp': Decimal('1489014691557.2031'), 'locked_territories': { 'AF': {'reason': 'lost ownership dispute'} }, 'isrc': 'QA123', } get_response = ownership.get_ownership(test_isrc) assert test_utils.equal_dicts( get_response.message, expected_updated_ownership, ['timestamp', 'updated_timestamp']) expected_remove_audit_entry = { 'territories': ['AF'], 'isrc': 'QA123', 'opcode': 'REMOVE', 'correlation_id': valid_correlation_id, 'user': '1234', 'timestamp': Decimal('1517923153788.647'), } expected_lock_audit_entry = { 'territories': ['AF'], 'timestamp': Decimal('1517923153788.661'), 'isrc': 'QA123', 'opcode': 'LOCK', 'correlation_id': valid_correlation_id, 'user': '1234', 'lock_reason': 'lost ownership dispute' } audit_entries = ownership.new_audit_table.query( KeyConditionExpression=conditions.Key('isrc').eq(test_isrc) )['Items'] assert len(audit_entries) == 2 remove_audit = next(_ for _ in audit_entries if _['opcode'] == 'REMOVE') lock_audit = next(_ for _ in audit_entries if _['opcode'] == 'LOCK') assert test_utils.equal_dicts( remove_audit, expected_remove_audit_entry, ['timestamp']) assert test_utils.equal_dicts( lock_audit, expected_lock_audit_entry, ['timestamp']) @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_afghanistan_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch_for_locks) @patch( 'masters_registry.models.ownership.get_upc_by_tuid', new=patches.ownership_get_upc_by_tuid_functional_tests_patch) @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.yt_ownership.send_message', new=Mock()) @mock_aws def test_post_lock_territories_conflict_resolved_ff_enabled( client, valid_headers, valid_lock_data, setup_masters_active_table, seed_masters_active_table, setup_new_audit_table, feature_engine, test_isrc, dynamodb_get_item_response_new_format_with_conflict, valid_correlation_id): """Test post valid data.""" from moto.core import patch_client, patch_resource patch_client(outside_dynamodb_client) patch_resource(dynamodb_resource) test_isrc = 'QA123' 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) res = make_post(client, valid_headers, valid_lock_data) assert res.status_code == 200 expected_update_payload = { 'territories': ['AF'], 'created': {'user': '', 'datetime': '2018-02-06T13:19:13.788588'}, 'lock_reason': 'lost ownership dispute', 'isrc': 'QA123', 'updated': {}, } update_payload = json.loads(res.data.decode()) assert test_utils.equal_dicts( update_payload, expected_update_payload, ['created']) assert update_payload['created'] expected_updated_ownership = { 'updated_timestamp': Decimal('1517912353.813532'), 'territories': { 'US': [{'tuid': 11432391}] }, 'timestamp': Decimal('1489014691557.2031'), 'locked_territories': { 'AF': {'reason': 'lost ownership dispute'} }, 'isrc': 'QA123', } get_response = ownership.get_ownership(test_isrc) assert test_utils.equal_dicts( get_response.message, expected_updated_ownership, ['timestamp', 'updated_timestamp']) expected_remove_audit_entry = { 'territories': ['AF'], 'isrc': 'QA123', 'opcode': 'REMOVE', 'correlation_id': valid_correlation_id, 'user': '1234', 'timestamp': Decimal('1517923153788.647'), } expected_lock_audit_entry = { 'territories': ['AF'], 'timestamp': Decimal('1517923153788.661'), 'isrc': 'QA123', 'opcode': 'LOCK', 'correlation_id': valid_correlation_id, 'user': '1234', 'lock_reason': 'lost ownership dispute' } expected_conflict_audit_entry = { 'territories': ['AF'], 'timestamp': Decimal('1517923153788.661'), 'isrc': 'QA123', 'opcode': 'CONFLICT_RESOLVED', 'correlation_id': valid_correlation_id, 'user': '1234', 'source': 'MANUAL_EDIT_ISRC', 'conflict': { 'resolved': 1, 'conflicting_tuid': [], 'status': 'CONFLICT_RESOLVED' } } audit_entries = ownership.new_audit_table.query( KeyConditionExpression=conditions.Key('isrc').eq(test_isrc) )['Items'] assert len(audit_entries) == 3 remove_audit = next(_ for _ in audit_entries if _['opcode'] == 'REMOVE') lock_audit = next(_ for _ in audit_entries if _['opcode'] == 'LOCK') conflict_audit = next( _ for _ in audit_entries if _['opcode'] == 'CONFLICT_RESOLVED') assert test_utils.equal_dicts( remove_audit, expected_remove_audit_entry, ['timestamp']) assert test_utils.equal_dicts( lock_audit, expected_lock_audit_entry, ['timestamp']) assert test_utils.equal_dicts( conflict_audit, expected_conflict_audit_entry, ['timestamp']) @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_active_table', new=patches.ownership_get_existing_isrcs_in_active_table_patch) def test_post_invalid_isrc(client, valid_headers, valid_data, feature_engine): """Test post invalid isrc """ request_data = valid_data request_data[field_const.ISRC] = 'XX123456' res = make_post(client, valid_headers, valid_data) assert res.status_code == 400 @patch( 'masters_registry.models.ows_territories.get_territories', new=patches.ows_territories_get_territories_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_active_table', new=patches.ownership_get_existing_isrcs_in_active_table_patch) def test_post_invalid_territories( client, valid_headers, valid_data, feature_engine): """Test post invalid territories """ request_data = valid_data request_data[field_const.TERRITORIES].append('XX') res = make_post(client, valid_headers, valid_data) assert json.loads(res.data.decode())['code'] == error.INVALID_TERRITORIES assert res.status_code == 400 def test_post_no_reason(client, valid_headers, valid_data): """Test post no lock reason """ request_data = valid_data del request_data[field_const.LOCK_REASON] res = make_post(client, valid_headers, valid_data) assert json.loads(res.data.decode())['code'] == error.BODY_VALIDATION_ERROR assert res.status_code == 400 def test_post_no_isrcs(client, valid_headers, valid_data): """Test post no isrc """ request_data = valid_data del request_data[field_const.ISRC] res = make_post(client, valid_headers, request_data) assert json.loads(res.data.decode())['code'] == error.BODY_VALIDATION_ERROR assert res.status_code == 400 def test_post_no_territories(client, valid_headers, valid_data): """Test post no territories """ request_data = valid_data del request_data[field_const.TERRITORIES] res = make_post(client, valid_headers, request_data) assert json.loads(res.data.decode())['code'] == error.BODY_VALIDATION_ERROR assert res.status_code == 400 def test_post_too_long_reason(client, valid_headers, valid_data): """Test post too long lock reason """ request_data = valid_data request_data[field_const.LOCK_REASON] = 'X' * ( config.MAX_REASON_LENGTH + 1) res = make_post(client, valid_headers, request_data) assert json.loads(res.data.decode())['code'] == error.BODY_VALIDATION_ERROR assert res.status_code == 400