import decimal import time from unittest.mock import ANY from unittest.mock import MagicMock from unittest.mock import Mock from unittest.mock import patch from oto import response import pytest from masters_registry.constant import error from masters_registry.constant import field_const from masters_registry.logic import locks from masters_registry.models import ownership from masters_registry.tasks import bulk from tests.helpers import patches @pytest.fixture def ownership_info(): return { field_const.ISRC: 'ABC123456', field_const.LOCKED_TERRITORIES: { 'BE': { field_const.REASON: 'Owned by Warner' }, 'NL': { field_const.REASON: 'Owned by Universal' }, }, field_const.TERRITORIES: { 'AF': {field_const.TUID: 111111}, 'AS': {field_const.TUID: 111111}, } } def test_create_records(monkeypatch): """Test _create_records function """ isrc = 'AAAAAA' territory = 'CA' correlation_id = '3333333333' user = '1234' opcode = 'lock' reason = 'reason' items = {isrc: [territory]} actual_time = time.time() monkeypatch.setattr( time, 'time', MagicMock(spec=time.time, return_value=actual_time)) expected_record = { field_const.ISRC: isrc, 'correlation_id': correlation_id, field_const.USER: user, field_const.OPCODE: opcode, field_const.TERRITORIES: [territory], field_const.LOCK_REASON: reason, field_const.TIMESTAMP: decimal.Decimal(str(actual_time * 1000)) } records = locks._create_records( items, correlation_id, user, opcode, reason) assert records == [expected_record] def test_check_territories(ownership_info): """Test _check_territories function """ isrc = ownership_info[field_const.ISRC] empty_isrc = 'BB' empty_ownership = { field_const.ISRC: empty_isrc, field_const.LOCKED_TERRITORIES: {}, field_const.TERRITORIES: {} } active_isrcs = [ownership_info, empty_ownership] reason = 'Owned by Warner' territories = {'BE', 'AF', 'PL', 'DE', 'NL'} checked = locks._check_territories(active_isrcs, territories, reason) assert checked[0] == {isrc: {'AF'}, empty_isrc: set()} assert checked[1] == {isrc: {'NL'}, empty_isrc: set()} assert checked[2] == { isrc: {'AF', 'PL', 'DE', 'NL'}, empty_isrc: territories } @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) def test_is_valid_isrcs_valid(ownership_info): """Test _is_valid_isrcs for valid ISRCs """ isrcs = ['ABC123456'] response = locks._is_valid_isrcs(isrcs, [ownership_info]) assert response.status == 200 @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) def test_is_valid_isrcs_not_existing_in_tracks(ownership_info, feature_engine): """Test _is_valid_isrcs for isrc that doesn't exist in art_relations.track table """ isrcs = ['ABC123456', '1111111'] response = locks._is_valid_isrcs(isrcs, [ownership_info]) assert response.status == 400 assert response.errors['code'] == error.NOT_EXISTING_ISRCS_IN_TRACK @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) def test_is_valid_isrcs_not_existing_in_active(ownership_info, feature_engine): """Test _is_valid_isrcs for isrc that doesn't exist in active table """ isrcs = ['ABC123456', 'DEF890123'] response = locks._is_valid_isrcs(isrcs, [ownership_info]) assert response.status == 400 assert response.errors['code'] == error.NOT_EXISTING_ISRCS_IN_ACTIVE @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=patches.ownership_get_existing_isrcs_in_tracks_patch) def test_is_valid_isrcs_missing_in_track_new_format(ownership_info): """Expect error message to be a list of missing isrcs in art relations.""" isrcs = ['QA123', 'QA234'] response = locks._is_valid_isrcs(isrcs, [ownership_info]) assert response.status == 400 assert response.errors['code'] == error.NOT_EXISTING_ISRCS_IN_TRACK assert sorted(response.errors['message']) == isrcs @patch( 'masters_registry.models.ownership.get_existing_isrcs_in_tracks', new=lambda *args: {'QA123'}) def test_is_valid_isrcs_missing_in_dynamo_new_format(): """Expect error message to be a list of missing isrcs in masters active.""" response = locks._is_valid_isrcs(['QA123'], [{'isrc': 'QA456'}]) assert response.status == 400 assert response.errors['code'] == error.NOT_EXISTING_ISRCS_IN_ACTIVE assert response.errors['message'] == ['QA123'] def test_get_updated_claimed_territories_remove_some(ownership_info): """Test _get_updated_claimed_territories for the case: removing some claimed territories """ isrc = ownership_info[field_const.ISRC] territories = { 'AAAAAA': {'PL', 'NL'}, isrc: {'PL', 'NL', 'AF'} } result = locks._get_updated_claimed_territories( [ownership_info], territories) assert result == {isrc: {'AS'}} def test_get_updated_claimed_territories_remove_all(ownership_info): """Test _get_updated_claimed_territories for the case: removing all claimed territories """ isrc = ownership_info[field_const.ISRC] territories = { 'AAAAAA': {'PL', 'NL'}, isrc: {'PL', 'NL', 'AF', 'AS'} } result = locks._get_updated_claimed_territories( [ownership_info], territories) assert result == {isrc: set()} def test_get_updated_claimed_territories_remove_none(ownership_info): """Test _get_updated_claimed_territories for the case: not removing any claimed territories """ territories = { 'AAAAAA': {'PL', 'NL'}, } result = locks._get_updated_claimed_territories( [ownership_info], territories) assert result == {} @patch( 'masters_registry.models.ownership.update_active_record', autospec=True) def test_lock_update_active_table(update_active_mock): """Test _lock_update_active_table function """ to_remove = {'US1': ['QA', 'CA'], 'US2': ['AD']} to_unlock = {'US1': ['US'], 'US2': ['DE', 'NR']} to_lock = {'US1': ['QA', 'CA', 'AD'], 'US2': ['QA', 'CA', 'US']} reason = 'Lock reason' expected_records = [ { 'isrc': 'US1', 'territories': { 'to_remove': ['QA', 'CA'], 'to_unlock': ['US'], 'to_lock': ['QA', 'CA', 'AD'], }, 'lock_reason': reason }, { 'isrc': 'US2', 'territories': { 'to_remove': ['AD'], 'to_unlock': ['DE', 'NR'], 'to_lock': ['QA', 'CA', 'US'], }, 'lock_reason': reason } ] records = locks._lock_create_active_table_records( to_remove, to_unlock, to_lock, reason) assert expected_records[0] in records assert expected_records[1] in records def test_unlock_isrc(monkeypatch): """Test unlock_isrc function """ update_active_record = Mock() monkeypatch.setattr( ownership, 'update_active_record', update_active_record) update_multiple_ownerships = Mock() monkeypatch.setattr( ownership, 'update_multiple_ownerships', update_multiple_ownerships) isrc = 'ZW1010600008' territories = ['US', 'PL', 'QA'] correlation_id = '123' user = '1' expected_audit_records = [ { 'correlation_id': correlation_id, 'territories': territories, 'isrc': isrc, 'opcode': 'UNLOCK', 'user': user, 'timestamp': ANY }] locks.unlock_isrc(isrc, territories, correlation_id, user) update_active_record.assert_called_once_with( isrc, {'to_unlock': territories}) update_multiple_ownerships.assert_called_once_with( expected_audit_records) @patch.object( locks, '_validate_isrcs_and_territories', new=Mock(return_value=(response.Response(['AF']), True))) @patch.object( locks, '_check_territories', new=Mock(return_value=(True, True, True))) @patch.object( locks, '_lock_create_active_table_records', new=Mock(return_value=True)) @patch.object( locks.logic_bulk_tasks, 'create_task', new=Mock(return_value=1)) @patch.object( locks.conflicts, 'filter_lock_territories_without_conflict', new=Mock(return_value={'conflicting_isrcs': ['AF']})) @patch.object(bulk.bulk_lock_isrcs, 'delay', new=Mock()) def test_bulk_lock_territories_no_accepted_isrcs(valid_correlation_id): """Should handle when all ISRCs have conflicts on given territories.""" result = locks.bulk_lock_territories( lock_reason='test lock', isrcs=['QA123'], territories_to_lock=['AF'], correlation_id=valid_correlation_id, user='oa:123') expected_message = { 'lock_reason': 'test lock', 'conflicting_isrcs': ['AF'], 'accepted_isrcs': [] } assert result.message == expected_message bulk.bulk_lock_isrcs.delay.assert_called_with( 'test lock', ['QA123'], ['AF'], valid_correlation_id, 'oa:123', 1)