"""Art relation handlers unit tests.""" from copy import deepcopy from datetime import datetime from unittest import mock from unittest.mock import ANY, MagicMock, NonCallableMagicMock, call import pytest from pythonfeatures import pythonfeatures from pythonfeatures.constants.split import FEATURE_ENABLED from src.logic.art_relations import handlers from src.logic.art_relations.queries import move_project, queries from src.logic.retry_exception import ProjectMoveError from src.model import ows_account, ows_product, ows_track class TestVendorHandlers: """Test vendor handlers.""" def test_vendor_insert_with_empty_company(self, mocker, neo4j_session_mock): """Test vendor handler with empty company.""" record = {'data': {'company': '', 'name': 'nickname'}} expected_record = deepcopy(record['data']) expected_record['company'] = '' handlers.vendor_insert_update(record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.vendor_insert_update_cypher, expected_record ) class TestCompanyBrandHandlers: """Test company_brand handlers.""" def test_company_brand_insert_update_orchard(self, neo4j_session_mock): """Test company_brand_insert_update handler for Orchard parent company.""" record = { 'data': { 'id': 11, 'name': 'newbrand', 'uuid': 'abc123-def456', 'display_name': 'New Brand', 'parent_company_id': 2, # Orchard } } expected_params = { **record['data'], 'parent_company_uuid': '955a1bbd-b623-4ea1-ab5f-8d6620c442fb', # Orchard UUID } handlers.company_brand_insert_update(record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.company_brand_insert_update_cypher, expected_params ) def test_company_brand_insert_update_sme(self, neo4j_session_mock): """Test company_brand_insert_update handler for SME parent company.""" record = { 'data': { 'id': 12, 'name': 'smebrand', 'uuid': 'xyz789', 'display_name': 'SME Brand', 'parent_company_id': 1, # SME } } expected_params = { **record['data'], 'parent_company_uuid': 'f1594122-7f99-4916-b103-08b0444c7b46', # SME UUID } handlers.company_brand_insert_update(record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.company_brand_insert_update_cypher, expected_params ) def test_company_brand_insert_update_unknown_parent(self, neo4j_session_mock, caplog): """Test company_brand_insert_update handler with unknown parent company.""" record = { 'data': { 'id': 13, 'name': 'unknownbrand', 'uuid': 'unknown-uuid', 'display_name': 'Unknown Brand', 'parent_company_id': 999, # Unknown parent } } handlers.company_brand_insert_update(record) # Should not call Neo4j since parent company is unknown neo4j_session_mock.write_transaction.assert_not_called() class TestSubaccountHandlers: """Test subaccount handlers.""" def test_subaccount_insert_update(self, neo4j_session_mock): """Test subaccount_insert_update handler.""" record = { 'data': {'vendor_id': 15698, 'subaccount_id': 1122334455, 'genre_id': 4, 'subaccount_name': 'dummy sub'} } handlers.subaccount_insert_update(record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.subaccount_insert_update_with_uuid_cypher, record['data'] ) class TestArtistInfoHandlers: """Test artist_info handlers.""" @pytest.fixture def is_distributor_mock(self, mocker): """Mock is_distributor function.""" mock = mocker.patch.object(ows_account, 'is_distributor') mock.return_value = True return mock @pytest.fixture def artist_info_insert(self, mocker): """Mock artist_info_insert function.""" mock = mocker.patch.object(handlers, 'artist_info_insert') mock.return_value = True return mock def test_artist_info_insert(self, neo4j_session_mock, is_distributor_mock): """Test artist_info_insert handler.""" vendor_id = 26640 artist_name = 'artist_name' record = {'data': {'vendor_id': vendor_id, 'name': artist_name}} artist_info_insert_params = { 'is_distributor': is_distributor_mock.return_value, 'vendor_id': vendor_id, 'name': artist_name, } label_participant_uuid_params = { 'name': artist_name, 'vendor_id': vendor_id, 'subaccount_id': 0, } neo4j_session_mock.write_transaction.return_value = None handlers.artist_info_insert(record) is_distributor_mock.assert_called_once_with(vendor_id) neo4j_session_mock.write_transaction.assert_has_calls( [ call(ANY, queries.artist_info_insert_cypher, artist_info_insert_params), call(ANY, queries.set_label_uuids_for_label_participant_by_params, label_participant_uuid_params), ] ) def test_artist_info_insert_new_lp(self, neo4j_session_mock, is_distributor_mock): """Test artist_info_insert handler if a new LabelParticipant node was created.""" vendor_id = 26640 artist_name = 'artist_name' record = {'data': {'vendor_id': vendor_id, 'name': artist_name}} artist_info_insert_params = { 'is_distributor': is_distributor_mock.return_value, 'vendor_id': vendor_id, 'name': artist_name, } label_participant_uuid_params = { 'name': artist_name, 'vendor_id': vendor_id, 'subaccount_id': 0, } uuid = 'my-uuid' neo4j_session_mock.write_transaction.return_value = [{'lp': {'uuid': uuid}}] handlers.artist_info_insert(record) is_distributor_mock.assert_called_once_with(vendor_id) neo4j_session_mock.write_transaction.assert_has_calls( [ call(ANY, queries.artist_info_insert_cypher, artist_info_insert_params), call(ANY, queries.set_label_uuids_for_label_participant_by_params, label_participant_uuid_params), call(ANY, queries.set_label_uuids_for_label_participant_by_uuid, {'uuid': uuid}), ] ) def test_artist_info_update(self, neo4j_session_mock, artist_info_insert): """Test artist_info_update handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.artist_info_update(record) artist_info_insert.assert_called_once_with(record) def test_artist_info_delete(self, neo4j_session_mock, is_distributor_mock): """Test artist_info_delete handler.""" record = {'data': {}} handlers.artist_info_delete(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.artist_info_delete_cypher, {}) class TestProjectHandlers: """Test project handlers.""" @pytest.fixture def project_id(self): """Project id.""" return 123 def test_project_insert(self, neo4j_session_mock, _set_label_uuids_to_label_participant_mock): """Test project_insert handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.project_insert(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.project_insert_cypher, record['data']) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.write_transaction.return_value ) def test_project_update(self, neo4j_session_mock, _set_label_uuids_to_label_participant_mock): """Test project_update handler.""" record = {'data': {'project_id': 1, 'param2': 'value2'}} handlers.project_update(record) neo4j_session_mock.run.assert_called_once_with(queries.project_update_cypher, record['data']) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.run.return_value.data.return_value ) @pytest.fixture def move_projects_enabled(self, mocker): """Mock move_projects_enabled function.""" mocker.patch.object( pythonfeatures, 'get_single_feature_by_attributes', return_value=(NonCallableMagicMock(message=FEATURE_ENABLED)), ) @pytest.fixture def _move_project_dependencies_mock(self, mocker): """Mock _move_project_dependencies function.""" return mocker.patch.object(handlers, '_move_project_dependencies') def test_project_update_moved( self, neo4j_session_mock, _set_label_uuids_to_label_participant_mock, _move_project_dependencies_mock, move_projects_enabled, project_id, ): """Test project_update with a moved project.""" record = {'data': {'project_id': project_id}, 'old': {'subaccount_id': 0}} handlers.project_update(record) neo4j_session_mock.run.assert_called_once_with(queries.project_update_cypher, record['data']) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.run.return_value.data.return_value ) _move_project_dependencies_mock.assert_called_once_with(neo4j_session_mock, record) @pytest.mark.parametrize('exception_class', [ProjectMoveError('my error'), Exception('general error')]) def test_project_update_moved_error( self, neo4j_session_mock, _set_label_uuids_to_label_participant_mock, _move_project_dependencies_mock, move_projects_enabled, project_id, exception_class, ): """Test project_update with a moved project.""" _move_project_dependencies_mock.side_effect = exception_class record = {'data': {'project_id': project_id}, 'old': {'subaccount_id': 0}} handlers.project_update(record) def test_move_project_dependencies(self, neo4j_session_mock, project_id): """Test _move_project_dependencies helper function.""" record = {'data': {'project_id': project_id}} handlers._move_project_dependencies(neo4j_session_mock, record) for query in move_project.MOVE_PROJECT_QUERIES: neo4j_session_mock.run.assert_any_call(query, record['data']) def test_move_project_dependencies_error(self, project_id): """Test _move_project_dependencies raises error.""" result_mock = MagicMock() result_mock.data.return_value = [{'errorMessages': {'my': 'error'}}] tx_mock = MagicMock() tx_mock.run.return_value = result_mock record = {'data': {'project_id': project_id}} with pytest.raises(ProjectMoveError): handlers._move_project_dependencies(tx_mock, record) def test_project_delete(self, neo4j_session_mock): """Test project_delete handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.project_delete(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.project_delete_cypher, record['data']) class TestProductHandlers: """Test product handlers.""" @pytest.fixture def product_information(self): """Product information from ows-product.""" return {'release_id': 123, 'context_type': 'physical'} @pytest.fixture def get_product_mock(self, mocker, product_information): """Mock get_product function.""" mock = mocker.patch.object(ows_product, 'get_product') mock.return_value = product_information return mock def test_product_insert(self, product_information, get_product_mock, neo4j_session_mock): """Test product_insert handler.""" record = {'data': {'release_id': 123, 'param2': 'value2'}} handlers.product_insert(record) get_product_mock.assert_called_once_with(123) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.product_insert_cypher, {**product_information, **record['data']} ) def test_product_update(self, product_information, get_product_mock, neo4j_session_mock): """Test product_update handler.""" record = {'data': {'release_id': 123, 'param2': 'value2'}} handlers.product_update(record) get_product_mock.assert_called_once_with(123) neo4j_session_mock.run.assert_called_once_with( queries.product_insert_cypher, **{**product_information, **record['data']} ) @pytest.mark.parametrize( 'record, expected_result', [ ({'old': {'project_id': 1}}, True), ({'old': {'project_id': 0}}, False), ({'old': {}}, False), ({}, False), ], ) def test__product_moved(self, record, expected_result): """Test _product_moved helper function.""" assert handlers._product_moved(record) == expected_result @pytest.fixture def _set_label_uuids_to_moved_label_participants_mock(self, mocker): """Mock _set_label_uuids_to_moved_label_participants function.""" return mocker.patch.object(handlers, '_set_label_uuids_to_moved_label_participants') def test_product_update_moved( self, product_information, _set_label_uuids_to_moved_label_participants_mock, get_product_mock, neo4j_session_mock, ): """Test product_update handler with a moved product.""" record = {'old': {'project_id': 1}, 'data': {'release_id': 123}} label_participants = [{'uuid': 'uuid1'}] moved_dependencies = {'label_participants': label_participants} neo4j_session_mock.write_transaction.return_value = moved_dependencies handlers.product_update(record) get_product_mock.assert_called_once_with(123) _set_label_uuids_to_moved_label_participants_mock.assert_called_once_with(label_participants) neo4j_session_mock.write_transaction.assert_called_once_with( handlers._move_product_dependencies, record, params={**product_information, **record['data']} ) def test__move_product_to_project_dependencies(self, mocker): """Test _product_moved for new project only.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} tx = mocker.Mock() handlers._move_product_dependencies(tx, record) tx.run.assert_has_calls( [ mocker.call(queries.product_insert_cypher, record['data']), mocker.call(queries.product_move_remove_old_project_cypher, record['data']), ] ) def test__move_product_dependencies(self, mocker): """Test _product_moved helper function.""" record = {'old': {'subaccount_id': 1}, 'data': {'param1': 'value1'}} tx = mocker.MagicMock() handlers._move_product_dependencies(tx, record) tx.run.assert_has_calls( [ mocker.call(queries.product_insert_cypher, record['data']), mocker.call(queries.product_move_remove_old_project_cypher, record['data']), mocker.call(queries.product_move_copy_label_participants_cypher, record['data']), mocker.call(queries.product_move_remove_old_label_participants, record['data']), mocker.call(queries.product_move_copy_track_label_participants_cypher, record['data']), mocker.call(queries.product_move_remove_old_track_label_participants, record['data']), mocker.call(queries.product_move_copy_label_sound_recordings_cypher, record['data']), mocker.call(queries.moved_label_participants_cypher, record['data']), ] ) def test__move_product_dependencies_with_params(self, mocker): """Test _product_moved helper function.""" record = {'old': {'subaccount_id': 1}, 'data': {'release_id': 123}} params = {'release_id': 123, 'context_type': 'physical'} tx = mocker.MagicMock() handlers._move_product_dependencies(tx, record, params=params) tx.run.assert_has_calls( [ mocker.call(queries.product_insert_cypher, params), mocker.call(queries.product_move_remove_old_project_cypher, record['data']), mocker.call(queries.product_move_copy_label_participants_cypher, record['data']), mocker.call(queries.product_move_remove_old_label_participants, record['data']), mocker.call(queries.product_move_copy_track_label_participants_cypher, record['data']), mocker.call(queries.product_move_remove_old_track_label_participants, record['data']), mocker.call(queries.product_move_copy_label_sound_recordings_cypher, record['data']), mocker.call(queries.moved_label_participants_cypher, record['data']), ] ) def test_product_delete(self, neo4j_session_mock): """Test product_delete handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.product_delete(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.product_delete_cypher, record['data']) @pytest.mark.parametrize( 'record, expected_result', [ ({'old': {'subaccount_id': 1}}, True), ({'old': {'subaccount_id': 0}}, True), ({'old': {'subaccount_id': None}}, True), ({'old': {}}, False), ({}, False), ], ) def test_product_moved_to_subaccount(self, record, expected_result): """Test _product_moved_to_subaccount helper function.""" result = handlers._product_moved_to_subaccount(record) assert result is expected_result class TestReleaseArtistHandlers: """Test release_artist handlers.""" @pytest.fixture def release_artist_record(self): """Product record.""" return {'data': {'release_id': 2810803}} @pytest.fixture def product_information(self): """Product information from ows-product.""" return {'release_id': 2810803, 'release_name': 'asb'} @pytest.fixture def _get_release_artist_insert_params_mock(self, mocker): """Mock _get_release_artist_insert_params.""" return mocker.patch.object(handlers, '_get_release_artist_insert_params') @pytest.fixture def release_artist_insert_mock(self, mocker): """Mock release_artist_insert.""" mock = mocker.patch.object(handlers, 'release_artist_insert') return mock @pytest.fixture def release_artist_delete_mock(self, mocker): """Mock release_artist_delete.""" mock = mocker.patch.object(handlers, 'release_artist_delete') return mock def test_release_artist_insert( self, release_artist_record, product_information, neo4j_session_mock, _get_release_artist_insert_params_mock, _set_label_uuids_to_label_participant_mock, ): """Test release_artist_insert handler.""" # test call handlers.release_artist_insert(release_artist_record) # checks params = _get_release_artist_insert_params_mock.return_value neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.release_artist_insert_cypher, params) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.write_transaction.return_value ) def test_release_artist_update( self, neo4j_session_transaction_mock, release_artist_delete_mock, _get_release_artist_insert_params_mock, _set_label_uuids_to_label_participant_mock, ): """Test release_artist_update handler.""" record = { 'data': {'param1': 'value1', 'param2': 'value2'}, 'old': {'param1': 'value1', 'param2': 'value2'}, } insert_params = _get_release_artist_insert_params_mock.return_value handlers.release_artist_update(record) release_artist_delete_mock.assert_called_once_with({'data': {'param1': 'value1', 'param2': 'value2'}}, ANY) neo4j_session_transaction_mock.run.assert_called_once_with(queries.release_artist_insert_cypher, insert_params) result = neo4j_session_transaction_mock.run.return_value _set_label_uuids_to_label_participant_mock.assert_called_once_with(result.single.return_value) def test_release_artist_delete(self, neo4j_session_mock): """Test release_artist_delete handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.release_artist_delete(record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.release_artist_delete_cypher, record['data'] ) class TestTrackHandlers: """Test track handlers.""" @pytest.fixture def track_record(self): """Track record.""" return {'data': {'release_id': 2810803}} @pytest.fixture def product_information(self): """Product information from ows-product.""" return {'release_id': 2810803, 'release_name': 'asb'} @pytest.fixture def get_product_mock(self, mocker, product_information): """Mock get_product function.""" mock = mocker.patch.object(ows_product, 'get_product') mock.return_value = product_information return mock def test_track_insert(self, neo4j_session_mock): """Test track_insert handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.track_insert(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.track_insert_cypher, record['data']) def test_track_with_product_insert(self, track_record, neo4j_session_mock, get_product_mock, product_information): """Test track_insert handler with product.""" product_id = track_record['data']['release_id'] params = {**product_information, **track_record['data']} handlers.track_insert(track_record) get_product_mock.assert_called_once_with(product_id) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.track_insert_with_product_cypher, params ) def test_track_update(self, neo4j_session_mock): """Test track_update handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.track_update(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.track_insert_cypher, record['data']) def test_track_with_product_update(self, neo4j_session_mock, track_record, product_information, get_product_mock): """Test track_update handler with product.""" product_id = track_record['data']['release_id'] params = {**product_information, **track_record['data']} handlers.track_update(track_record) get_product_mock.assert_called_once_with(product_id) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.track_update_cypher, params) def test_track_delete(self, neo4j_session_mock): """Test track_delete handler.""" record = {'data': {'param1': 'value1', 'param2': 'value2'}} handlers.track_delete(record) neo4j_session_mock.write_transaction.assert_called_once_with(ANY, queries.track_delete_cypher, record['data']) class TestTrackArtistHandlers: """Test track_artist handlers.""" @pytest.fixture def track_artist_record(self): """Track record.""" return {'data': {'track_id': 31425464}} @pytest.fixture def track_information(self): """Track information from ows-product.""" return {'track_id': 31425464, 'track_name': 'asb', 'product_id': 2810803} @pytest.fixture def get_track_mock(self, mocker, track_information): """Mock get_product function.""" mock = mocker.patch.object(ows_track, 'get_track') mock.return_value = track_information return mock @pytest.fixture def product_information(self): """Product information from ows-product.""" return {'release_id': 2810803, 'release_name': 'asb'} @pytest.fixture def get_product_mock(self, mocker, product_information): """Mock get_product function.""" mock = mocker.patch.object(ows_product, 'get_product') mock.return_value = product_information return mock @pytest.fixture def track_artist_insert_mock(self, mocker): """Mock track_artist_insert.""" mock = mocker.patch.object(handlers, 'track_artist_insert') return mock @pytest.fixture def track_artist_delete_mock(self, mocker): """Mock track_artist_delete.""" mock = mocker.patch.object(handlers, 'track_artist_delete') return mock @pytest.fixture def _get_track_participant_insert_params_mock(self, mocker): """Mock _get_track_participant_insert_params.""" return mocker.patch.object(handlers, '_get_track_participant_insert_params') def test_track_artist_insert( self, track_artist_record, track_information, _get_track_participant_insert_params_mock, neo4j_session_mock, _set_label_uuids_to_label_participant_mock, ): """Test track_artist_insert handler.""" # test call handlers.track_artist_insert(track_artist_record) # checks track_id = track_artist_record['data']['track_id'] track_participant_insert_params = _get_track_participant_insert_params_mock.return_value neo4j_session_mock.write_transaction.assert_any_call( ANY, queries.track_artist_insert_cypher, track_participant_insert_params ) neo4j_session_mock.write_transaction.assert_called_with( ANY, queries.track_set_sequence_numbers, {'track_id': track_id} ) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.write_transaction.return_value ) def test_track_artist_update_with_name_change( self, neo4j_session_transaction_mock, track_artist_insert_mock, _get_track_participant_insert_params_mock, _set_label_uuids_to_label_participant_mock, track_artist_record, track_artist_delete_mock, ): """Test track_artist_update handler.""" track_id = 1 record = { 'data': {'track_id': track_id, 'name': 'Not Jim Bob'}, 'old': {'name': 'Jim Bob'}, } handlers.track_artist_update(record) track_artist_delete_mock.assert_called_once_with({'data': {'track_id': track_id, 'name': 'Jim Bob'}}, ANY) neo4j_session_transaction_mock.run.assert_has_calls( [ call(queries.track_artist_insert_cypher, _get_track_participant_insert_params_mock.return_value), call(queries.track_set_sequence_numbers, {'track_id': track_id}), ], any_order=True, ) result = neo4j_session_transaction_mock.run.return_value _set_label_uuids_to_label_participant_mock.assert_called_once_with(result.single.return_value) def test_track_artist_delete(self, neo4j_session_mock): """Test track_artist_delete handler.""" record = {'data': {'track_id': 12345, 'param1': 'value1', 'param2': 'value2'}} handlers.track_artist_delete(record) neo4j_session_mock.write_transaction.assert_any_call(ANY, queries.track_artist_delete_cypher, record['data']) neo4j_session_mock.write_transaction.assert_called_with( ANY, queries.track_set_sequence_numbers, {'track_id': record['data']['track_id']} ) class TestTrackWriterHandlers: """Test track_writer handlers.""" @pytest.fixture def track_artist_record(self): """Track record.""" return {'data': {'unique_track_id': 31425464}} @pytest.fixture def track_information(self): """Track information from ows-product.""" return {'track_id': 31425464, 'track_name': 'asb', 'product_id': 2810803} @pytest.fixture def get_track_mock(self, mocker, track_information): """Mock get_product function.""" mock = mocker.patch.object(ows_track, 'get_track') mock.return_value = track_information return mock @pytest.fixture def product_information(self): """Product information from ows-product.""" return {'release_id': 2810803, 'release_name': 'asb'} @pytest.fixture def get_product_mock(self, mocker, product_information): """Mock get_product function.""" mock = mocker.patch.object(ows_product, 'get_product') mock.return_value = product_information return mock @pytest.fixture def track_writer_insert_mock(self, mocker): """Mock track_writer_insert.""" mock = mocker.patch.object(handlers, 'track_writer_insert') return mock @pytest.fixture def track_writer_delete_mock(self, mocker): """Mock track_writer_delete.""" mock = mocker.patch.object(handlers, 'track_writer_delete') return mock @pytest.fixture def _get_track_participant_insert_params_mock(self, mocker): """Mock _get_track_participant_insert_params.""" return mocker.patch.object(handlers, '_get_track_participant_insert_params') def test_track_writer_insert( self, track_artist_record, track_information, _get_track_participant_insert_params_mock, # product_information, get_track_mock, get_product_mock, neo4j_session_mock, _set_label_uuids_to_label_participant_mock, ): """Test track_writer_insert handler.""" # test call handlers.track_writer_insert(track_artist_record) # checks track_id = track_artist_record['data']['unique_track_id'] params = _get_track_participant_insert_params_mock.return_value neo4j_session_mock.write_transaction.assert_any_call(ANY, queries.track_writer_insert_cypher, params) neo4j_session_mock.write_transaction.assert_called_with( ANY, queries.track_set_sequence_numbers, {'track_id': track_id} ) _set_label_uuids_to_label_participant_mock.assert_called_once_with( neo4j_session_mock.write_transaction.return_value ) def test_track_writer_update( self, neo4j_session_transaction_mock, track_writer_insert_mock, _get_track_participant_insert_params_mock, track_writer_delete_mock, _set_label_uuids_to_label_participant_mock, ): """Test track_writer_update handler.""" track_id = 1 record = { 'data': {'unique_track_id': track_id, 'name': 'Not Jim Bob'}, 'old': {'name': 'Jim Bob'}, } handlers.track_writer_update(record) track_writer_delete_mock.assert_called_once_with( {'data': {'unique_track_id': track_id, 'name': 'Jim Bob'}}, ANY ) neo4j_session_transaction_mock.run.assert_has_calls( [ call(queries.track_writer_insert_cypher, _get_track_participant_insert_params_mock.return_value), call(queries.track_set_sequence_numbers, {'track_id': track_id}), ], any_order=True, ) result = neo4j_session_transaction_mock.run.return_value _set_label_uuids_to_label_participant_mock.assert_called_once_with(result.single.return_value) def test_track_writer_delete(self, neo4j_session_mock): """Test track_writer_delete handler.""" record = {'data': {'unique_track_id': 12345, 'param1': 'value1', 'param2': 'value2'}} handlers.track_writer_delete(record) neo4j_session_mock.write_transaction.assert_any_call(ANY, queries.track_writer_delete_cypher, record['data']) neo4j_session_mock.write_transaction.assert_called_with( ANY, queries.track_set_sequence_numbers, {'track_id': record['data']['unique_track_id']} ) class TestParticipantExternalLinkHandlers: """Test participant_external_link handlers.""" @pytest.fixture def twitter_record(self): """Participant external record with twitter store id.""" return {'data': {'store_id': 1437}} @pytest.fixture def youtube_record(self): """Participant external record with youtube store id.""" return {'data': {'store_id': 569}} @pytest.fixture def facebook_record(self): """Participant external record with facebook store id.""" return {'data': {'store_id': 1173, 'store_artist_id': 'bob.smith'}} @pytest.fixture def facebook_record_to_clean(self): """Participant external record with facebook and artist id to clean.""" return {'data': {'store_id': 1173, 'store_artist_id': 'profile.php?id=bob.smith'}} @pytest.fixture def instagram_record(self): """Participant external record with instagram store id.""" return {'data': {'store_id': 1436}} @pytest.fixture def unknown_store_record(self): """Participant external record with apple store id.""" return {'data': {'store_id': 0}} def test_participant_external_link_upsert_with_twitter_record(self, neo4j_session_mock, twitter_record): """Test participant_external_link_upsert handler.""" handlers.participant_external_link_upsert(twitter_record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.participant_external_link_upsert_twitter_cypher, twitter_record['data'] ) def test_participant_external_link_upsert_with_youtube_record(self, neo4j_session_mock, youtube_record): """Test participant_external_link_upsert handler.""" handlers.participant_external_link_upsert(youtube_record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.participant_external_link_upsert_youtube_cypher, youtube_record['data'] ) def test_participant_external_link_upsert_with_instagram_record(self, neo4j_session_mock, instagram_record): """Test participant_external_link_upsert handler.""" handlers.participant_external_link_upsert(instagram_record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.participant_external_link_upsert_instagram_cypher, instagram_record['data'] ) def test_participant_external_link_upsert_with_facebook_record(self, neo4j_session_mock, facebook_record): """Test participant_external_link_upsert handler.""" handlers.participant_external_link_upsert(facebook_record) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.participant_external_link_upsert_facebook_cypher, facebook_record['data'] ) def test_participant_external_link_upsert_with_facebook_record_to_clean( self, neo4j_session_mock, facebook_record_to_clean ): """Test participant_external_link_upsert handler.""" expected_cleaned_record = {'store_id': 1173, 'store_artist_id': 'bob.smith'} handlers.participant_external_link_upsert(facebook_record_to_clean) neo4j_session_mock.write_transaction.assert_called_once_with( ANY, queries.participant_external_link_upsert_facebook_cypher, expected_cleaned_record ) def test_participant_identifier_with_unknown_store_record(self, neo4j_session_mock, unknown_store_record): """Test participant_external_link_upsert handler.""" with pytest.raises(NotImplementedError): handlers.participant_external_link_upsert(unknown_store_record) class TestLabelUUIDs: """Tests for _set_label_uuids_to_label_participant.""" @pytest.fixture def uuid(self): """UUID example.""" return 'my-uuid' @pytest.fixture def query_result(self, uuid): """Non-empty query result.""" return [{'lp': {'uuid': uuid}}] @pytest.fixture def neo4j_query_result(self, uuid): """Non-empty neo4j query result.""" return {'lp': {'uuid': uuid}} def test_with_query_result(self, neo4j_session_mock, query_result, uuid): """Test with non-empty query result.""" handlers._set_label_uuids_to_label_participant(query_result) neo4j_session_mock.write_transaction.assert_any_call( ANY, queries.set_label_uuids_for_label_participant_by_uuid, {'uuid': uuid} ) neo4j_session_mock.write_transaction.assert_any_call( ANY, queries.link_global_participant_to_label_participant, {'uuid': uuid} ) def test_with_neo4j_query_result(self, mocker, neo4j_session_mock, neo4j_query_result, uuid): """Test with non-empty Neo4j query result.""" mocker.patch.object(handlers, 'isinstance', return_value=True) handlers._set_label_uuids_to_label_participant(neo4j_query_result) neo4j_session_mock.write_transaction.assert_any_call( ANY, queries.set_label_uuids_for_label_participant_by_uuid, {'uuid': uuid} ) neo4j_session_mock.write_transaction.assert_any_call( ANY, queries.link_global_participant_to_label_participant, {'uuid': uuid} ) def test_with_non_implemented(self, neo4j_session_mock): """Test with not implemented query result.""" with pytest.raises(NotImplementedError): handlers._set_label_uuids_to_label_participant('test') def test_with_empty_query_result(self, neo4j_session_mock): """Test with empty query result.""" handlers._set_label_uuids_to_label_participant([]) neo4j_session_mock.write_transaction.assert_not_called() def test_set_label_uuids_to_moved_label_participants(self, neo4j_session_mock): """Test _set_label_uuids_to_moved_label_participants.""" records = [ {'uuid': 'uuid1'}, {'uuid': 'uuid2'}, ] handlers._set_label_uuids_to_moved_label_participants(records) neo4j_session_mock.write_transaction.assert_has_calls( [ call(ANY, queries.set_label_uuids_for_label_participant_by_uuid, {'uuid': 'uuid1'}), call(ANY, queries.link_global_participant_to_label_participant, {'uuid': 'uuid1'}), call(ANY, queries.set_label_uuids_for_label_participant_by_uuid, {'uuid': 'uuid2'}), call(ANY, queries.link_global_participant_to_label_participant, {'uuid': 'uuid2'}), ] ) @pytest.fixture def _set_label_uuids_to_label_participant_mock(mocker): """Mock _set_label_uuids_to_label_participant.""" return mocker.patch.object(handlers, '_set_label_uuids_to_label_participant') @mock.patch('src.logic.art_relations.handlers.datetime') class TestVendorServiceTierHandlers: """Test vendor service tier handler(s).""" def test_vendor_service_tier_insert(self, datetime_mock, mocker, neo4j_session_mock): """Test adding a relationship between vendor and service tier.""" now = datetime.now() datetime_mock.datetime.now.return_value = now record = { 'data': { 'vendor_id': 111, 'service_tier_uuid': '1ed7aac0-ceb6-4c09-9166-afda8f349316', } } handlers.vendor_service_tier_insert(record) neo4j_session_mock.write_transaction.assert_called_with( ANY, """ MATCH (v:Vendor {vendorId: $vendor_id}) WITH v MATCH (st:ServiceTier {uuid: $service_tier_uuid}) WITH v, st MERGE (v)-[ist:IN_SERVICE_TIER]->(st) ON CREATE SET ist.createdAt = $current_time, ist.createdBy = 'lambda-kinesis-to-neo4j/vendor-service-tier-insert-cypher' """, { 'vendor_id': record['data']['vendor_id'], 'service_tier_uuid': record['data']['service_tier_uuid'], 'current_time': now, }, ) def test_vendor_service_tier_delete(self, datetime_mock, mocker, neo4j_session_mock): """Test deleting a relationship between vendor and service tier.""" now = datetime.now() datetime_mock.datetime.now.return_value = now record = { 'data': { 'vendor_id': 111, 'service_tier_uuid': '1ed7aac0-ceb6-4c09-9166-afda8f349316', } } handlers.vendor_service_tier_delete(record) neo4j_session_mock.write_transaction.assert_called_with( ANY, """ MATCH (v:Vendor {vendorId: $vendor_id})-[r:IN_SERVICE_TIER]-> (st:ServiceTier {uuid: $service_tier_uuid}) SET r.lastModifiedAt = $current_time, r.lastModifiedBy = 'lambda-kinesis-to-neo4j/vendor-service-tier-delete-cypher' WITH r CALL apoc.refactor.setType(r, 'DELETED_IN_SERVICE_TIER') YIELD input, output RETURN input, output """, { 'vendor_id': record['data']['vendor_id'], 'service_tier_uuid': record['data']['service_tier_uuid'], 'current_time': now, }, )