import json from unittest.mock import Mock from unittest.mock import patch from moto import mock_aws import boto3 import pytest from masters_registry import config from masters_registry.constant import bulk_tasks_const from masters_registry.constant import error from masters_registry.constant import field_const from masters_registry.models import bulk_tasks from masters_registry.models import ownership from masters_registry.tasks import bulk 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/isrcs' 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.ISRCS: ['ABC123456', 'DEF890123', 'ZW1010600008'], field_const.TERRITORIES: ['AF', 'AS', 'DZ'] } @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_one_isrc_patch) def test_post_bulk_lock_territories_already_locked( monkeypatch, client, valid_headers, feature_engine): """Test post already locked territories with the same reason """ valid_data = { field_const.LOCK_REASON: 'Owned by Warner', field_const.ISRCS: ['ZW1010600008'], field_const.TERRITORIES: ['PL'] } update_multiple_ownerships = Mock() monkeypatch.setattr( ownership, 'update_multiple_ownerships', update_multiple_ownerships) update_task = Mock() monkeypatch.setattr( bulk_tasks, 'update_task', update_task) task_id = 1 create_task = Mock(return_value=task_id) monkeypatch.setattr( bulk_tasks, 'create_task', create_task) correlation_id = valid_headers[field_const.CORRELATION_ID] user = valid_headers[field_const.ORCHARD_USER_ID] territories = valid_data[field_const.TERRITORIES] isrcs = valid_data[field_const.ISRCS] isrcs_count = len(valid_data[field_const.ISRCS]) expected_territories = set(territories) expected_isrcs = set(isrcs) res = make_post(client, valid_headers, valid_data) update_task_args = update_task.call_args[0] assert create_task.called create_task_args = create_task.call_args_list[0][0] assert create_task_args[0] == correlation_id assert create_task_args[1] == user assert create_task_args[2] == bulk_tasks_const.BULK_LOCK assert create_task_args[3] == isrcs_count assert sorted(create_task_args[4]) == sorted(isrcs) assert set(update_task_args[2]['territories']) == expected_territories assert set(update_task_args[2]['successful_isrcs']) == expected_isrcs assert res.status_code == 200 @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_isrcs(client, valid_headers, valid_data, feature_engine): """Test post invalid isrcs """ request_data = valid_data request_data[field_const.ISRCS] = ['XX123456', 'YY1234567'] 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, feature_engine): """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, feature_engine): """Test post no isrcs """ request_data = valid_data del request_data[field_const.ISRCS] 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, feature_engine): """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, feature_engine): """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 def test_post_too_many_isrcs( client, valid_headers, valid_data, feature_engine): """Test post too many isrcs """ request_data = valid_data request_data[field_const.ISRCS] = ['PL'] * (config.MAX_ISRCS_COUNT + 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 @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=lambda *args: {'QA123'}) @patch( 'masters_registry.logic.bulk_tasks.create_task', new=Mock(return_value=1)) @patch( 'masters_registry.tasks.bulk.bulk_lock_isrcs.delay', new=Mock()) @mock_aws def test_bulk_lock_territories_with_internal_conflict( client, feature_engine, test_item, setup_masters_active_table, seed_masters_active_table): """Expect error indicating that some of territories have internal conflict. Bulk lock territories should check that territories to lock do not have internal conflicts. If some territory has internal conflict, it should be removed from lock operation. This should not affect other territories. """ 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) test_item['territories'] = { # AF has > 1 tuid which means there is internal conflict 'AF': [{'tuid': 123}, {'tuid': 456}], 'AX': [{'tuid': 123}] } seed_masters_active_table(dynamodb_resource.meta.client, item=test_item) headers = {'Orchard-User-Id': '123', 'Correlation-Id': 'test id'} payload = { 'lock_reason': 'test lock reason', 'isrcs': ['QA123'], 'territories': ['AF', 'AX'] } result = make_post(client, headers, payload) received_payload = json.loads(result.data.decode()) expected_payload = { 'lock_reason': 'test lock reason', 'accepted_isrcs': {'QA123': ['AX']}, 'conflicting_isrcs': {'QA123': ['AF']} } assert received_payload == expected_payload bulk.bulk_lock_isrcs.delay.assert_called_once_with( 'test lock reason', ['QA123'], ['AF', 'AX'], 'test id', '123', 1) @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=lambda *args: {'QA123'}) @patch( 'masters_registry.logic.bulk_tasks.create_task', new=Mock(return_value=1)) @patch( 'masters_registry.tasks.bulk.bulk_lock_isrcs.delay', new=Mock()) @mock_aws def test_bulk_lock_territories_lock_territories_no_conflict( client, feature_engine, test_item, setup_masters_active_table, seed_masters_active_table): """Expect to get response without 'conflicting_isrcs'. If there is internal conflict in ISRC but it isn't related to territories that user wants to lock, then there shouldn't be 'conflicting_isrcs' key in response """ 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) test_item['territories'] = { 'AF': [{'tuid': 123}, {'tuid': 456}], 'AX': [{'tuid': 123}] } seed_masters_active_table(dynamodb_resource.meta.client, item=test_item) headers = {'Orchard-User-Id': '123', 'Correlation-Id': 'test id'} payload = { 'lock_reason': 'test lock reason', 'isrcs': ['QA123'], 'territories': ['AX'] } result = make_post(client, headers, payload) received_payload = json.loads(result.data.decode()) expected_payload = { 'lock_reason': 'test lock reason', 'accepted_isrcs': {'QA123': ['AX']} } assert received_payload == expected_payload bulk.bulk_lock_isrcs.delay.assert_called_once_with( 'test lock reason', ['QA123'], ['AX'], 'test id', '123', 1)