"""Test logic layer for orchard sound recordings.""" from unittest.mock import call from unittest.mock import patch from neo4j.exceptions import ConstraintError import pytest from sound_recordings.api import app from sound_recordings.logic import orchard_sound_recordings as orchard_sr_logic @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') def test_upsert_success_update(mock_track_info, mock_neo4j_driver): """Test get data with existing result.""" with app.app_context(): track_data = { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['456'], 'osr_ids': ['789'] } mock_track_info.return_value = track_data results = orchard_sr_logic.upsert(123) assert mock_track_info.called assert mock_track_info.call_args_list == [ call(123) ] assert results == ('789', False) @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') @patch('sound_recordings.models.orchard_sound_recordings._isrc_available') @patch('sound_recordings.models.orchard_sound_recordings._create_osr') def test_create_copy_isrc_success( mock_create_osr, mock_isrc_available, mock_track_info, mock_neo4j_driver): """Test create osr.""" with app.app_context(): track_data = { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['456'], 'osr_ids': [] } mock_track_info.return_value = track_data mock_create_osr.return_value = ('789', 1, 1) mock_isrc_available.return_value = True results = orchard_sr_logic.upsert(123) assert results == ('789', True) assert mock_isrc_available.call_args_list == [ call('ISRC') ] assert mock_create_osr.call_args_list == [ call('ISRC', 123, track_data) ] @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') def test_upsert_not_found(mock_track_info, mock_neo4j_driver): """Test get data with no existing result.""" with app.app_context(): with pytest.raises(orchard_sr_logic.AssetNotFound): track_data = {} mock_track_info.return_value = track_data orchard_sr_logic.upsert(123) assert mock_track_info.called assert mock_track_info.call_args_list == [ call(123) ] @pytest.mark.parametrize( 'data, exception', [ ( { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': [], 'acr_ids': [], 'osr_ids': [] }, orchard_sr_logic.AssetNotFound ), ( { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['123', '456'], 'osr_ids': ['123', '789'] }, orchard_sr_logic.MultipleAcrOsrRelationships ), ( { 'isrc': None, 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['456'], 'osr_ids': ['789'] }, orchard_sr_logic.TrackISRCNotSet ) ] ) @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') def test_upsert_validate_failure(mock_track_info, mock_neo4j_driver, data, exception): """Test validate track data.""" with app.app_context(): with pytest.raises(exception): mock_track_info.return_value = data orchard_sr_logic.upsert(123) assert mock_track_info.called @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') @patch('sound_recordings.models.orchard_sound_recordings._isrc_available') @patch('sound_recordings.models.orchard_sound_recordings._create_osr') @patch('sound_recordings.models.ows_track.claim_isrc') def test_create_new_isrc_success( mock_claim_isrc, mock_create_osr, mock_isrc_available, mock_track_info, mock_neo4j_driver): """Test get data with existing osr.""" with app.app_context(): track_data = { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['456'], 'osr_ids': [] } mock_track_info.return_value = track_data mock_create_osr.return_value = ('789', 1, 1) mock_isrc_available.return_value = False mock_claim_isrc.return_value = 'NEW ISRC' orchard_sr_logic.upsert(123) assert mock_claim_isrc.called assert mock_isrc_available.call_args_list == [ call('ISRC') ] assert mock_create_osr.call_args_list == [ call('NEW ISRC', 123, track_data) ] @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings._get_track_info') @patch('sound_recordings.models.orchard_sound_recordings._create_osr') @patch('sound_recordings.models.ows_track.claim_isrc') def test_upsert_with_exception_not_handled( mock_claim_isrc, mock_create_osr, mock_track_info, mock_neo4j_driver): """Test get data with existing osr.""" with app.app_context(): with pytest.raises(ConstraintError): track_data = { 'isrc': 'ISRC', 'product_status': 'in_content', 'assets': ['123'], 'acr_ids': ['456'], 'osr_ids': [] } mock_track_info.return_value = track_data error = ConstraintError() error.message = 'something not handled' mock_create_osr.side_effect = [error, None] orchard_sr_logic.upsert(123) assert not mock_claim_isrc.called assert mock_create_osr.called assert mock_create_osr.call_args_list == [ call(None, 123, track_data) ] @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_fetch_success(mock_fetch): """Test fetch with data.""" mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 123: { 'tuids': [123], 'tracks': { 123: { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } } } }, 'data': 1 } } results = orchard_sr_logic.fetch( track_ids=['tuid123'], orchard_sound_recording_ids=['id123'] ) assert mock_fetch.called assert mock_fetch.call_args_list == [ call( orchard_sound_recording_ids=['id123'], track_ids=['tuid123'], upcs=[], product_ids=[], project_ids=[], asset_ids=[], track_isrcs=[], include_deleted=False, include_transfer_to_content=False, include_inactive=False, ) ] assert results == [ { 'assets': [ { 'tuids': [123], 'tracks': [ { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } ] } ], 'data': 1, 'id': 'id123', 'primary_track_id': 123 } # noqa:E501 ] @patch('sound_recordings.models.orchard_sound_recordings.search_by_isrc') @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_fetch_with_term_success(mock_fetch, mock_search): """Test fetch with data.""" mock_search.return_value = ['id123'] mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 123: { 'tuids': [123], 'tracks': { 123: { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } } } }, 'data': 1 } } results = orchard_sr_logic.fetch(term='QM6MZ2211088*') assert mock_search.called assert mock_search.call_args_list == [call('QM6MZ2211088\\*')] assert mock_fetch.called assert mock_fetch.call_args_list == [ call( orchard_sound_recording_ids=['id123'], track_ids=[], upcs=[], product_ids=[], project_ids=[], asset_ids=[], track_isrcs=[], include_deleted=False, include_transfer_to_content=False, include_inactive=False, ) ] assert results == [ { 'assets': [ { 'tuids': [123], 'tracks': [ { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } ] } ], 'data': 1, 'id': 'id123', 'primary_track_id': 123 } # noqa:E501 ] @patch('sound_recordings.models.orchard_sound_recordings.search_by_isrc') @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_fetch_with_term_without_result(mock_fetch, mock_search): """Test search_by_isrc without data.""" mock_search.return_value = [] results = orchard_sr_logic.fetch(term='QM6MZ2211088*') assert mock_search.called assert mock_search.call_args_list == [call('QM6MZ2211088\\*')] assert not mock_fetch.called assert results == [] @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_default_tuid_missing(mock_fetch): """Test primary tuid empty.""" mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': None, 'assets': { 456: { 'tuids': [456], 'tracks': { 456: { 'tuid': 456, 'isrc': 'BCD1', 'product_id': 222, 'subaccount_id': 0, 'vendor_id': 111 } } }, 123: { 'tuids': [123], 'tracks': { 123: { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } } } }, 'data': 1 } } results = orchard_sr_logic.fetch( track_ids=['tuid123'], orchard_sound_recording_ids=['id123'] ) assert results == [ { 'assets': [ { 'tuids': [456], 'tracks': [ { 'tuid': 456, 'isrc': 'BCD1', 'product_id': 222, 'subaccount_id': 0, 'vendor_id': 111 } ] }, { 'tuids': [123], 'tracks': [ { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } ] } ], 'data': 1, 'id': 'id123', 'primary_track_id': 123 } # noqa:E501 ] @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_default_tuid_mismatch(mock_fetch): """Test primary tuid not match connected track.""" mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 789, 'assets': { 456: { 'tuids': [456], 'tracks': { 456: { 'tuid': 456, 'isrc': 'BCD1', 'product_id': 222, 'subaccount_id': 0, 'vendor_id': 111 } }, }, 123: { 'tuids': [123], 'tracks': { 123: { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } } } }, 'data': 1 } } results = orchard_sr_logic.fetch( track_ids=['tuid123'], orchard_sound_recording_ids=['id123'] ) assert results == [ { 'assets': [ { 'tuids': [456], 'tracks': [ { 'tuid': 456, 'isrc': 'BCD1', 'product_id': 222, 'subaccount_id': 0, 'vendor_id': 111 } ], }, { 'tuids': [123], 'tracks': [ { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } ] } ], 'data': 1, 'id': 'id123', 'primary_track_id': 123 } # noqa:E501 ] @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_fetch_assets_empty(mock_fetch): """Test fetch without assets.""" mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 789, 'assets': {}, 'data': 1 } } results = orchard_sr_logic.fetch( track_ids=['tuid123'], orchard_sound_recording_ids=['id123'] ) assert results == [ {'assets': [], 'data': 1, 'id': 'id123', 'primary_track_id': None} ] @patch('sound_recordings.models.orchard_sound_recordings.fetch') def test_fetch_empty(mock_fetch): """Test fetch without data.""" mock_fetch.return_value = {} results = orchard_sr_logic.fetch( track_ids=['tuid123'], orchard_sound_recording_ids=['id123'] ) assert results == [] @patch('sound_recordings.utils.s3.get_sound_recording_version') def test_fetch_version(mock_s3_util): """Test fetch osr data.""" data = { 'version_id': 'VERSION_ID', 'creation_date': 'DATE', 'data': {'json': 'data'} } mock_s3_util.return_value = data result = orchard_sr_logic.fetch_version( osr_id='OSR_ID', version_id='VERSION_ID' ) assert result == data @patch('sound_recordings.models.orchard_sound_recordings.fetch_product') def test_fetch_product_success(mock_fetch_product): """Test fetch product with data.""" mock_fetch_product.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 123: { 'tuids': [123], 'tracks': { 123: { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } } } }, 'data': 1 } } results = orchard_sr_logic.fetch_product( product_id=111, track_isrcs=[] ) assert mock_fetch_product.called assert mock_fetch_product.call_args_list == [ call( product_id=111, track_isrcs=[], include_transfer_to_content=False ) ] assert results == [ { 'assets': [ { 'tuids': [123], 'tracks': [ { 'tuid': 123, 'isrc': 'ABC1', 'product_id': 111, 'subaccount_id': 0, 'vendor_id': 222 } ] } ], 'data': 1, 'id': 'id123', 'primary_track_id': 123 } # noqa:E501 ] @patch('sound_recordings.utils.s3.get_sound_recording_version') def test_fetch_version_not_found(mock_s3_util): """Test fetch osr not found.""" data = None mock_s3_util.return_value = data result = orchard_sr_logic.fetch_version( osr_id='OSR_ID', version_id='VERSION_ID' ) assert result == data @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') @patch('sound_recordings.logic.orchard_sound_recordings._get_delivery_file_signed_url') def test_fetch_full_delivery_history(mock_signed_url, mock_fetch_full_delivery_history): """Test fetch full osr delivery history.""" data = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'Meta (Audio Fingerprinting)', 'execution_type': 'FULL DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}}, 'event_type': 'success', 'ack': None, 'ack_message': None } ] mock_fetch_full_delivery_history.return_value = data mock_signed_url.return_value = 'signed' expected_result = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'Meta (Audio Fingerprinting)', 'execution_type': 'FULL DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}}, 'delivery_xml_signed_url': 'signed', 'event_type': 'success', 'ack': 'missing', 'ack_message': None } ] update = 'METADATA_UPDATE' tik_tok = 'TikTok (Audio Fingerprinting)' result = orchard_sr_logic.fetch_full_delivery_history( ['OSR_ID'], [update], [tik_tok], ['success'] ) assert mock_fetch_full_delivery_history.called assert mock_fetch_full_delivery_history.call_args_list == [ call( ['OSR_ID'], [update], [tik_tok], ['success'], [], None, None, None, ) ] assert result == expected_result @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') def test_fetch_full_delivery_history_including_ineligible_deliveries( mock_fetch_full_delivery_history): """Test fetch full osr delivery history, including ineligible deliveries.""" data = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'TikTok (Audio Fingerprinting)', 'execution_type': None, 'sfn_execution_id': None, 'datetime': '2022-01-01', 'message': {'details': 'not_in_registry', 'status': 'ok'}, 'event_type': 'not_eligible', 'ack': None, 'ack_message': None } ] mock_fetch_full_delivery_history.return_value = data expected_result = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'TikTok (Audio Fingerprinting)', 'execution_type': None, 'sfn_execution_id': None, 'datetime': '2022-01-01', 'message': {'details': 'not_in_registry', 'status': 'ok'}, 'delivery_xml_signed_url': None, 'event_type': 'not_eligible', 'ack': None, 'ack_message': None } ] result = orchard_sr_logic.fetch_full_delivery_history(['OSR_ID']) assert mock_fetch_full_delivery_history.call_args_list == [ call( ['OSR_ID'], [], [], [], [], None, None, None, ) ] assert result == expected_result @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') @patch('sound_recordings.logic.orchard_sound_recordings._get_delivery_file_signed_url') def test_fetch_full_delivery_history_tiktok_ack_passthrough( mock_signed_url, mock_fetch_full_delivery_history): """Test ack/ack_message from model are passed through for TikTok deliveries.""" data = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'TikTok (Audio Fingerprinting)', 'execution_type': 'FULL_DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}, 'status': 'ok'}, 'event_type': 'success', 'ack': 'success', 'ack_message': '{"ack": "success"}' } ] mock_fetch_full_delivery_history.return_value = data mock_signed_url.return_value = 'signed' expected_result = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'TikTok (Audio Fingerprinting)', 'execution_type': 'FULL_DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}, 'status': 'ok'}, 'delivery_xml_signed_url': 'signed', 'event_type': 'success', 'ack': 'success', 'ack_message': '{"ack": "success"}' } ] result = orchard_sr_logic.fetch_full_delivery_history(['OSR_ID']) assert result == expected_result @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') @patch('sound_recordings.logic.orchard_sound_recordings._get_delivery_file_signed_url') def test_fetch_full_delivery_history_tiktok_no_ack_match( mock_signed_url, mock_fetch_full_delivery_history): """Test TikTok success delivery with no matching ack row keeps None values.""" data = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'TikTok (Audio Fingerprinting)', 'execution_type': 'FULL_DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}, 'status': 'ok'}, 'event_type': 'success', 'ack': None, 'ack_message': None } ] mock_fetch_full_delivery_history.return_value = data mock_signed_url.return_value = 'signed' result = orchard_sr_logic.fetch_full_delivery_history(['OSR_ID']) assert result[0]['ack'] is None assert result[0]['ack_message'] is None @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') @patch('sound_recordings.logic.orchard_sound_recordings._get_delivery_file_signed_url') def test_fetch_full_delivery_history_non_tiktok_ack_overridden( mock_signed_url, mock_fetch_full_delivery_history): """Test ack is forced to 'missing' for non-TikTok deliveries even if model returns a value.""" # noqa:E501 data = [ { 'sound_recording_id': 'OSR_ID', 'version_id': '123', 'service': 'Meta (Audio Fingerprinting)', 'execution_type': 'FULL_DELIVERY', 'sfn_execution_id': '456', 'datetime': '2022-01-01', 'message': {'details': {'batch_id': '123', 'filenames': ['/123/123.xml']}, 'status': 'ok'}, 'event_type': 'success', 'ack': 'success', 'ack_message': '{"ack": "success"}' } ] mock_fetch_full_delivery_history.return_value = data mock_signed_url.return_value = 'signed' result = orchard_sr_logic.fetch_full_delivery_history(['OSR_ID']) assert result[0]['ack'] == 'missing' assert result[0]['ack_message'] is None @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') def test_fetch_full_delivery_history_ack_status_passthrough( mock_fetch_full_delivery_history): """Test ack_status filter is forwarded to the model layer.""" mock_fetch_full_delivery_history.return_value = [] orchard_sr_logic.fetch_full_delivery_history( ['OSR_ID'], ack_status=['success', 'missing']) assert mock_fetch_full_delivery_history.call_args_list == [ call( ['OSR_ID'], [], [], [], ['success', 'missing'], None, None, None, ) ] @patch('sound_recordings.models.orchard_sound_recordings.fetch_full_delivery_history') def test_fetch_full_delivery_history_not_found(mock_fetch_full_delivery_history): """Test fetch full delivery events not found.""" mock_fetch_full_delivery_history.return_value = None expected_result = None result = orchard_sr_logic.fetch_full_delivery_history(['OSR_ID']) assert result == expected_result @patch('sound_recordings.utils.s3.get_signed_url') def test_get_delivery_file_signed_url(mock_get_signed_url): """Test fetch signed url.""" mock_get_signed_url.return_value = 'signed url' message = { 'details': { 'batch_id': '123', 'filenames': [ '/123/123.xml', '/BatchComplete_202203281721371720.xml', '/456.flac', ] }, 'status': 'success' } expected_result = 'signed url' result = orchard_sr_logic._get_delivery_file_signed_url( delivery_sfn_id='SFN_ID', message=message) assert mock_get_signed_url.called assert mock_get_signed_url.call_args_list == [ call('SFN_ID/123/123.xml') ] assert result == expected_result @patch('sound_recordings.utils.s3.get_signed_url') def test_get_delivery_file_signed_url_no_valid_path(mock_get_signed_url): """Test fetch signed url with invalid path.""" message = { 'details': { 'batch_id': '123', 'filenames': [ '/BatchComplete_202203281721371720.xml', '/456.flac', ] }, 'status': 'success' } expected_result = None result = orchard_sr_logic._get_delivery_file_signed_url( delivery_sfn_id='SFN_ID', message=message) assert not mock_get_signed_url.called assert result == expected_result @patch('sound_recordings.utils.s3.get_signed_url') def test_get_delivery_file_no_file(mock_get_signed_url): """Test fetch signed url with no path.""" message = { 'details': { 'Cause': {'errorMessage': 'An error occurred'} }, 'status': 'error' } expected_result = None result = orchard_sr_logic._get_delivery_file_signed_url( delivery_sfn_id='SFN_ID', message=message) assert not mock_get_signed_url.called assert result == expected_result @patch('sound_recordings.utils.s3.get_signed_url') def test_get_delivery_file_signed_url_youtube(mock_get_signed_url): """Test fetch signed url.""" mock_get_signed_url.return_value = 'signed url' message = { 'details': { 'batch_id': '123', 'filenames': [ '/123/123.xml', '/delivery.complete', '/456.flac', ] }, 'status': 'success' } expected_result = 'signed url' result = orchard_sr_logic._get_delivery_file_signed_url( delivery_sfn_id='SFN_ID', message=message, service='YouTube (Audio Fingerprinting)') # noqa:E501 assert mock_get_signed_url.called assert mock_get_signed_url.call_args_list == [ call('youtube/SFN_ID/123/123.xml') ] assert result == expected_result @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings.fetch') @patch('sound_recordings.models.orchard_sound_recordings.update') def test_update(mock_update, mock_fetch, mock_neo4j): """Test update success.""" with app.app_context(): mock_update_result = mock_update.return_value mock_update_result.data.return_value = { 'soundRecording': { 'id': 'id', 'primaryTrackId': 456 } } mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 456: {'tuids': [456]} }, 'data': 1 } } expected_result = { 'id': 'id', 'primary_track_id': 456 } result = orchard_sr_logic.update('id123', 456) assert mock_fetch.called assert mock_update.called assert result == expected_result @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings.fetch') @patch('sound_recordings.models.orchard_sound_recordings.update') def test_update_no_result(mock_update, mock_fetch, mock_neo4j): """Test update no result from update.""" with app.app_context(): mock_update.return_value = None mock_fetch.return_value = { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 456: {'tuids': [456]} }, 'data': 1 } } expected_result = None result = orchard_sr_logic.update('id123', 456) assert mock_fetch.called assert mock_update.called assert result == expected_result @pytest.mark.parametrize( 'primary_track_id, data, exception', [ ( 123, None, orchard_sr_logic.OrchardSoundRecordingNotFound ), ( 123, { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': {}, 'data': 1 } }, orchard_sr_logic.OrchardSoundRecordingWithoutAssets ), ( 456, { 'id123': { 'id': 'id123', 'primary_track_id': 123, 'assets': { 123: {'tuids': [123]} }, 'data': 1 } }, orchard_sr_logic.PrimaryTrackNotRelated ) ] ) @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings.fetch') @patch('sound_recordings.models.orchard_sound_recordings.update') def test_update_exception(mock_update, mock_fetch, mock_neo4j, primary_track_id, data, exception): # noqa:E501 """Test update exceptions.""" with app.app_context(): with pytest.raises(exception): mock_fetch.return_value = data orchard_sr_logic.update('id123', primary_track_id) assert mock_fetch.called assert not mock_update.called @patch('connector_neo4j.neo4j_driver') @patch('sound_recordings.models.orchard_sound_recordings.touch_osr') def test_touch(mock_touch, mock_neo4j): """Test touch success.""" with app.app_context(): mock_touch.return_value = 2 expected_result = { 'nodes_updated': 2 } result = orchard_sr_logic.touch( 123, 'vendor', '2021-10-14T15:22:35.390000000Z', 15) assert mock_touch.called assert result == expected_result