"""Integration tests for handle_product_metadata_update.""" import json from mock import Mock, patch import pytest from switchboard_consumer.constants import entity_type from switchboard_consumer.constants import message_type from switchboard_consumer.constants.exceptions import ( ParticipantHandlerError, ParticipantHandlerGraphQLError ) from switchboard_consumer.constants.product_status import ( COMPLETED, ERROR_CORRECTION, SUBMITTED) from switchboard_consumer.constants.release_correction import ( FAILED_TO_RESUBMIT_PRODUCT) from switchboard_consumer.constants.system import ORCHARD from switchboard_consumer.formatters.product import format_create_product_input from switchboard_consumer.kafka_message import KafkaMessage from switchboard_consumer.logic import product_metadata_update from tests.conftest import correlation_id, SONY create_message = { 'entityType': entity_type.PRODUCT, 'messageType': message_type.METADATA_UPDATE, 'sendingSystem': 'SONY', 'ids': [ { 'localId': '1022869', 'system': SONY, 'businessKey': '09876543210987', 'businessKeyType': 'UPC' }], 'correlationId': correlation_id } update_message = { 'entityType': entity_type.PRODUCT, 'messageType': message_type.METADATA_UPDATE, 'sendingSystem': 'SONY', 'ids': [ { 'localId': '1022869', 'system': SONY, 'businessKey': '09876543210987', 'businessKeyType': 'UPC' }, { 'localId': '754021', 'system': ORCHARD, 'businessKey': '09876543210987', 'businessKeyType': 'UPC' } ], 'correlationId': correlation_id } @pytest.fixture() def get_product_by_upc_create_response(switchboard_track_item_1): """get_product_by_upc_create_response fixture.""" return { 'ids': [ { 'system': SONY, 'localId': '1022869', }, ], 'displayArtist': { 'ids': { 'localId': '12345', 'system': 'SONY' }, 'name': 'Edguy' }, 'formalTitle': {'titleText': 'cool product', 'subText': 'cool version text'}, 'labelAccount': { 'companyCode': '1234', }, 'subAccount': None, 'project': { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, { 'system': 'ORCHARD', 'localId': '1978731', }, ] }, 'components': [{ 'sides': [{ 'productTracks': [ switchboard_track_item_1 ], }] }], 'releaseType': 'DIGITAL_AUDIO' } get_product_by_upc_update_response = { 'ids': [{ 'system': SONY, 'localId': '1022869', }, { 'system': ORCHARD, 'localId': '754021', }, ], 'upc': 123456, 'displayArtist': { 'ids': { 'localId': '12345', 'system': 'SONY' }, 'name': 'Edguy' }, 'formalTitle': {'titleText': 'cool product', 'subText': 'cool version text'}, 'labelAccount': { 'companyCode': '1234', }, 'subAccount': None, 'project': { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, { 'system': 'ORCHARD', 'localId': '1978731', }, ] }, 'releaseType': 'DIGITAL_AUDIO' } get_product_by_upc_update_response_no_rc = { 'ids': [{ 'system': SONY, 'localId': '1022869', }, { 'system': ORCHARD, 'localId': '99999', }, ], 'metaLanguage': 'en', 'upc': 123456, 'displayArtist': { 'ids': { 'localId': '12345', 'system': 'SONY' }, 'name': 'Edguy' }, 'formalTitle': {'titleText': 'Test Orchard Product', 'subText': 'cool version text'}, 'labelAccount': { 'companyCode': '1234', }, 'subAccount': None, 'project': { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, { 'system': 'ORCHARD', 'localId': '1978731', }, ] }, 'releaseType': 'DIGITAL_AUDIO', 'cLine': { 'cLineText': '2020 Something', }, 'genres': [ { 'genreId': 5, 'genreText': 'Pop', 'subGenres': [{'subGenreId': 63}], 'system': 'ORCHARD', 'territoryCode': 'Worldwide' } ], 'imprint': { 'name': 'Something', }, } orchard_get_product_by_upc_update_response = { 'ids': [{ 'system': SONY, 'localId': '1022869', }, { 'system': ORCHARD, 'localId': '754021', }, ], 'upc': 123456, 'displayArtist': { 'ids': { 'localId': '12345', 'system': 'SONY' }, 'name': 'Edguy' }, 'formalTitle': {'titleText': 'another cool product', 'subText': 'cool version text'}, 'labelAccount': { 'companyCode': '1234', }, 'subAccount': None, 'project': { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, { 'system': 'ORCHARD', 'localId': '1978731', }, ] }, 'releaseType': 'DIGITAL_AUDIO' } @pytest.fixture() def mock_participant_handler(primary_artists): """Mock participant handler logic.""" with patch(('switchboard_consumer.logic.' 'product_metadata_update.' 'ProductParticipantHandler')) as participant_handler: participant_handler().process \ .return_value = ([], [primary_artists[0]]) yield participant_handler @pytest.fixture() def mock_track_participant_handler(): """Mock track participant handler logic.""" with patch(('switchboard_consumer.logic.' 'product_metadata_update.' 'TrackParticipantHandler')) as track_participant_handler: track_participant_handler().process \ .return_value = ([], [], []) yield track_participant_handler @pytest.fixture() def mock_rc_product_participant_handler(primary_artists): """Mock product participant handler logic for RC.""" with patch(('switchboard_consumer.logic.' 'product.' 'ProductParticipantHandler')) as participant_handler: participant_handler().process \ .return_value = ([], [primary_artists[0]]) yield participant_handler @pytest.fixture() def mock_rc_track_participant_handler(primary_artists): """Mock track participant handler logic for RC.""" with patch(('switchboard_consumer.logic.' 'product.' 'TrackParticipantHandler')) as track_participant_handler: track_participant_handler().process \ .return_value = ([], [], []) yield track_participant_handler @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_fail_to_create_product_orchard_error( mock_label_participant_handler, get_product_by_upc_create_response, mock_track_participant_handler, mock_participant_handler, mock_logger, swb_client, orchard_client): """Test that an error when inserting a product returns an error message.""" # Setup the SWB Client mock to return a correct response. swb_client.get_product_by_upc.return_value = \ get_product_by_upc_create_response # Setup the Orchard Client mock to return an error. orchard_errors = [ { 'extensions': { 'code': 'some_code', }, 'message': 'An error message', } ] expected_formatted_orchard_error = [ { 'code': 'some_code', 'message': 'An error message' } ] orchard_client.create_product.return_value = { 'errors': orchard_errors } kafka_message = KafkaMessage(json.dumps(create_message)) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 response = responses[0] assert response['ids'] == create_message['ids'] assert response['errors'] == expected_formatted_orchard_error def test_fail_to_create_product_swb_get_product_error( mock_logger, swb_client, orchard_client): """Test that an error when getting a product returns an error message.""" # Setup the SWB Client mock to return an error response. swb_errors = [ { 'extensions': { 'code': 'some_error' }, 'message': 'there was an error' } ] expected_formatted_swb_error = [ { 'code': 'some_error', 'message': 'there was an error' } ] swb_client.get_product_by_upc.return_value = { 'errors': swb_errors } orchard_client.create_product.return_value = {'productId': 12345} kafka_message = KafkaMessage(json.dumps(create_message)) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 response = responses[0] assert response['ids'] == create_message['ids'] assert response['errors'] == expected_formatted_swb_error @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_handle_product_create_product_success( mock_label_participant_handler, mock_participant_handler, mock_track_participant_handler, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that a create product message creates a product.""" local_id = '99999' orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [] kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': create_message['ids'][0]['localId'], 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': local_id, 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'] } ] assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids def test_handle_product_update_product_success(mock_participant_handler, mock_logger, swb_client, orchard_client): """Test that a create product message creates a product.""" local_id = update_message['ids'][1]['localId'] orchard_client.update_product.return_value = {'productId': local_id} orchard_client.get_product_by_id.return_value = { 'productId': local_id, 'displayStatus': 'in_progress', 'tracks': [] } kafka_message = KafkaMessage(json.dumps(update_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_update_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': update_message['ids'][0]['localId'], 'businessKey': update_message['ids'][0]['businessKey'], 'businessKeyType': update_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': local_id, 'businessKey': update_message['ids'][0]['businessKey'], 'businessKeyType': update_message['ids'][0]['businessKeyType'] } ] assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids @patch('switchboard_consumer.logic.email.ses.send_email') def test_handle_product_update_product_submitted( mock_send_email, mock_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test that a create product message unsubmits a Submitted product.""" orchard_client.unsubmit_product.return_value = { 'result': 'product unsubmitted' } graphql_product_get_product_response.update({ 'displayStatus': 'submitted' }) orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } graphql_product_get_product_response['productId'] = ( update_message['ids'][1]['localId']) orchard_client.get_product_by_id.return_value = ( graphql_product_get_product_response ) kafka_message = KafkaMessage(json.dumps(update_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_update_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': update_message['ids'][0]['localId'], 'businessKey': update_message['ids'][0]['businessKey'], 'businessKeyType': update_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': graphql_product_get_product_response['productId'], 'businessKey': update_message['ids'][0]['businessKey'], 'businessKeyType': update_message['ids'][0]['businessKeyType'] } ] assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids mock_send_email.assert_called_once() def test_update_completed_product_with_no_release_corrections( mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test a completed product is not updated if there are no RC details.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = ( graphql_product_get_product_response ) kafka_message = KafkaMessage(json.dumps(update_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_update_response_no_rc) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 assert not responses[0].get('errors') @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_handle_product_update_product_completed( validate_local_ids_mock, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test a completed product updates.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = ( graphql_product_get_product_response ) orchard_client.create_release_correction.return_value = { 'releaseCorrectionId': 67890 } orchard_client.create_release_correction_detail.return_value = {} orchard_client.submit_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) vendor_id = int( get_product_by_upc_update_response['labelAccount']['companyCode']) assert mock_rc_product_participant_handler.mock_calls[1][1] == ( mock_logger, orchard_client, kafka_message, vendor_id, None ) assert mock_rc_track_participant_handler.mock_calls[1][1] == ( get_product_by_upc_update_response, mock_logger, orchard_client, kafka_message, vendor_id, None ) assert len(responses) == 1 assert not responses[0].get('errors') validate_local_ids_mock.assert_called_once() orchard_client.delete_release_correction.assert_not_called() @patch('switchboard_consumer.logic.email.ses.send_email') @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_completed_product_in_release_correction_can_be_submitted( validate_local_ids_mock, mock_send_email, graphql_product_get_product_response, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, mock_logger, swb_client, orchard_client): """Test a product in RC can be submitted.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = ( graphql_product_get_product_response ) orchard_client.create_release_correction.return_value = { 'releaseCorrectionId': 12345 } orchard_client.create_release_correction_detail.return_value = {} orchard_client.submit_product.return_value = { 'productId': 67890 } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 assert not responses[0].get('errors') validate_local_ids_mock.assert_called_once() orchard_client.delete_release_correction.assert_not_called() @patch('switchboard_consumer.logic.email.ses.send_email') @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_completed_product_in_release_correction_fails_to_be_submitted( validate_local_ids_mock, mock_send_email, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, mock_logger, swb_client, orchard_client, graphql_product_get_product_response): """Test a product in RC fails to be submitted.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = { **graphql_product_get_product_response, 'displayStatus': COMPLETED } orchard_client.create_release_correction.return_value = { 'releaseCorrectionId': 12345 } orchard_client.create_release_correction_detail.return_value = {} # This would trigger a FAILED_TO_RESUBMIT_PRODUCT message orchard_client.submit_product.return_value = { 'errors': 'something' } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 message = responses[0].get('errors')[0]['message'] assert message == FAILED_TO_RESUBMIT_PRODUCT.format( graphql_product_get_product_response['productId'] ) validate_local_ids_mock.assert_called_once() @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_release_correction_handles_failure_to_fetch_product( validate_local_ids_mock, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test a that we handle a failure to fetch product information in RC.""" errors = [ { 'extensions': { 'code': 'some_error' }, 'message': 'there was an error', } ] expected_formatted_error = [ {'code': 'error', 'message': 'Failed to fetch product information from The Orchard.' } ] orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.side_effect = [ { **graphql_product_get_product_response, 'displayStatus': COMPLETED }, { 'errors': errors } ] orchard_client.create_release_correction.return_value = { 'releaseCorrectionId': 67890 } orchard_client.create_release_correction_detail.return_value = {} orchard_client.submit_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 assert responses[0].get('errors') == expected_formatted_error validate_local_ids_mock.assert_called_once() orchard_client.delete_release_correction.assert_not_called() def test_fail_to_update_product(mock_participant_handler, mock_logger, swb_client, orchard_client): """Test that an error when updating a product returns an error message.""" # Setup the Orchard Client mock to return an error. orchard_client.get_product_by_id.return_value = { 'productId': 754021, 'displayStatus': 'in_progress', 'tracks': [] } errors = [ { 'extensions': { 'code': 'some_error' }, 'message': 'there was an error', } ] formatted_errors = [ { 'code': 'some_error', 'message': 'there was an error' } ] orchard_client.update_product.return_value = { 'errors': errors } swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_update_response) kafka_message = KafkaMessage(json.dumps(update_message)) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 response = responses[0] assert response['ids'] == update_message['ids'] assert response['errors'] == formatted_errors @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_participant_results_added_to_responses( mock_label_participant_handler, mock_participant_handler, primary_artists, mock_track_participant_handler, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that participant results are properly handled.""" mock_participant_response = { 'entityType': 'PARTICIPANT', 'messageType': 'METADATA_UPDATE' } mock_participant_handler().process \ .return_value = ([mock_participant_response], [primary_artists[0]]) local_id = '99999' orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [] kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': create_message['ids'][0]['localId'], 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': local_id, 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'] } ] assert len(responses) == 2 response = responses[1] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids @patch(('switchboard_consumer.logic.' 'product_metadata_update.TrackParticipantHandler.process'), return_value=([], [], [])) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_process_product_tracks_update_success( mock_label_participant_handler, mock_track_participant_handler, mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement): """Test for successful update of tracks on product update.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.side_effect = [ update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement ] orchard_client.create_tracks.return_value = [ { 'tuid': 10003, 'isrc': 'ISRC_003' } ] orchard_client.rearrange_tracks = Mock() swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) del results[0]['messageCreatedDateTime'] del results[0]['messageId'] assert results == [ { 'correlationId': 'abc123', 'entityType': 'TRACK', 'errors': [], 'ids': [ { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': 'sony-id-10003', 'system': 'SONY' }, { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': '10003', 'system': 'ORCHARD' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } ] assert orchard_client.rearrange_tracks.call_count == 0 @patch(('switchboard_consumer.logic.' 'product_metadata_update.TrackParticipantHandler.process'), return_value=([], [], [])) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_process_product_tracks_update_with_publishers_success( mock_label_participant_handler, mock_track_participant_handler, mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement): """Test for successful create of tracks with publishers.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.side_effect = [ update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement ] orchard_client.create_tracks.return_value = [ { 'tuid': 10003, 'isrc': 'ISRC_003', 'publishing': { 'publisherNames': [ 'EMI Blackwood Music Inc.', 'EMI Music Publishing, Ltd.' ] } } ] orchard_client.rearrange_tracks = Mock() sides = update_product_tracks_swb_product['components'][0]['sides'] sides[0]['productTracks'][2]['track'].update({ 'publishers': [ { 'ids': [ { 'localId': '1113180', 'system': 'SONY' } ], 'name': 'EMI Blackwood Music Inc.' }, { 'ids': [ { 'localId': '416192', 'system': 'SONY' }, { 'localId': 'UNID:CQS+WelcOZTQj/VYnHKy=', 'system': 'ORCHARD' } ], 'name': 'EMI Music Publishing, Ltd.' }] }) swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) assert len(results) == 2 assert results[0]['entityType'] == 'TRACK' assert results[1]['entityType'] == 'PUBLISHER' assert results[1]['ids'] == [ {'localId': '1113180', 'system': 'SONY'}, {'localId': 'UNID:KFYCV/wy8PpdqmtUYsZ4TFb7R4w=', 'system': 'ORCHARD'} ] @patch(('switchboard_consumer.logic.' 'product_metadata_update.TrackParticipantHandler.process'), return_value=([], [], [])) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_process_product_tracks_update_success_with_rearrange( mock_label_participant_handler, mock_track_participant_handler, mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement): """Test for successful update of tracks on product update.""" kafka_message = KafkaMessage(json.dumps(create_message)) sides = update_product_tracks_swb_product['components'][0]['sides'] swb_product_tracks = sides[0]['productTracks'] swb_product_tracks[0]['sequenceNumber'] = 2 swb_product_tracks[1]['sequenceNumber'] = 1 orchard_client.get_product_by_id.side_effect = [ update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement ] orchard_client.create_tracks.return_value = [ { 'tuid': 10003, 'isrc': 'ISRC_003' } ] arrange_args = [ { 'volumeNumber': 1, 'trackNumber': 2, 'tuid': 10001 }, { 'volumeNumber': 1, 'trackNumber': 1, 'tuid': 10002 }, { 'volumeNumber': 1, 'trackNumber': 3, 'tuid': 10003 } ] orchard_client.rearrange_tracks.return_value = arrange_args swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) del results[0]['messageCreatedDateTime'] del results[0]['messageId'] assert results == [ { 'correlationId': 'abc123', 'entityType': 'TRACK', 'errors': [], 'ids': [ { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': 'sony-id-10003', 'system': 'SONY' }, { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': '10003', 'system': 'ORCHARD' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } ] assert orchard_client.rearrange_tracks.call_count == 1 assert orchard_client.rearrange_tracks.call_args[0][0] == arrange_args product_id = update_product_tracks_swb_product['ids'][0]['localId'] assert orchard_client.get_product_by_id.call_args[0][0] == str(product_id) @patch(('switchboard_consumer.logic.' 'product_metadata_update.TrackParticipantHandler.process'), return_value=([], [], [])) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_process_product_tracks_update_success_with_rearrange_failure( mock_label_participant_handler, mock_track_participant_handler, mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement): """Test for failuer update of tracks on product update on rearrange.""" kafka_message = KafkaMessage(json.dumps(create_message)) sides = update_product_tracks_swb_product['components'][0]['sides'] swb_product_tracks = sides[0]['productTracks'] swb_product_tracks[0]['sequenceNumber'] = 2 swb_product_tracks[1]['sequenceNumber'] = 1 orchard_client.get_product_by_id.side_effect = [ update_product_tracks_insert_get_orchard_product, update_product_tracks_insert_get_orchard_product_before_arrangement ] orchard_client.create_tracks.return_value = [ { 'tuid': '10003', 'isrc': 'ISRC_003' } ] orchard_client.rearrange_tracks.return_value = { 'errors': [ { 'extensions': { 'code': 'rearrange_tracks_error_code', }, 'message': 'ERROR_MESSAGE', } ] } swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) assert orchard_client.rearrange_tracks.call_count == 1 arrange_args = [ { 'volumeNumber': 1, 'trackNumber': 2, 'tuid': 10001 }, { 'volumeNumber': 1, 'trackNumber': 1, 'tuid': 10002 }, { 'volumeNumber': 1, 'trackNumber': 3, 'tuid': 10003 } ] assert orchard_client.rearrange_tracks.call_args[0][0] == arrange_args assert len(results) == 2 del results[0]['messageCreatedDateTime'] del results[0]['messageId'] del results[1]['messageCreatedDateTime'] del results[1]['messageId'] assert results[0] == { 'correlationId': 'abc123', 'entityType': 'TRACK', 'errors': [], 'ids': [ { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': 'sony-id-10003', 'system': 'SONY' }, { 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', 'localId': '10003', 'system': 'ORCHARD' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } assert results[1] == { 'correlationId': 'abc123', 'entityType': 'PRODUCT', 'errors': [ { 'code': 'rearrange_tracks_error_code', 'message': 'ERROR_MESSAGE' } ], 'ids': [ { 'businessKey': '09876543210987', 'businessKeyType': 'UPC', 'localId': '1022869', 'system': 'SONY' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } def test_process_product_tracks_update_failure_get_product( mock_logger, orchard_client, update_product_tracks_swb_product): """Test for failure on get product call.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.return_value = { 'errors': [ { 'extensions': { 'code': 'get_product_error_code', }, 'message': 'An error message', } ] } orchard_client.create_tracks.return_value = [ { 'tuid': 10003, 'isrc': 'ISRC_003' } ] swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) del results[0]['messageCreatedDateTime'] del results[0]['messageId'] assert results[0] == { 'correlationId': 'abc123', 'entityType': 'PRODUCT', 'errors': [ { 'code': 'get_product_error_code', 'message': 'An error message' } ], 'ids': [ { 'businessKey': '09876543210987', 'businessKeyType': 'UPC', 'localId': '1022869', 'system': 'SONY' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } @patch(('switchboard_consumer.logic.' 'product_metadata_update.TrackParticipantHandler.process'), return_value=([], [], [])) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_process_product_tracks_update_failure_create_tracks( mock_label_participant_handler, mock_track_participant_handler, mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_insert_get_orchard_product): """Test for failure on create_tracks call.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.return_value = \ update_product_tracks_insert_get_orchard_product orchard_client.create_tracks.return_value = { 'errors': [ { 'extensions': { 'code': 'create_tracks_error_code', }, 'message': 'An error message', } ] } swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) assert not success del results[0]['messageCreatedDateTime'] del results[0]['messageId'] assert results[0] == { 'correlationId': 'abc123', 'entityType': 'PRODUCT', 'errors': [ { 'code': 'create_tracks_error_code', 'message': 'An error message' } ], 'ids': [ { 'businessKey': '09876543210987', 'businessKeyType': 'UPC', 'localId': '1022869', 'system': 'SONY' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } @patch(('switchboard_consumer.logic.' 'product_metadata_update.format_create_product_input'), wraps=format_create_product_input) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') def test_participant_member_results_added_to_responses( mock_label_participant_handler, mock_product_formatter, mock_track_participant_handler, mock_participant_handler, primary_artists, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that participant results with multiple members are handled.""" primary_artists.extend( [ { 'artistInfoId': 124, 'artistName': 'Test Artist 2', 'artistType': 'featuring' }, { 'artistInfoId': 125, 'artistName': 'Test Artist 3', 'artistType': 'featuring' } ] ) mock_participant_response = [ { 'entityType': 'PARTICIPANT', 'messageType': 'METADATA_UPDATE' }, { 'entityType': 'PARTICIPANT', 'messageType': 'METADATA_UPDATE' } ] mock_participant_handler().process \ .return_value = (mock_participant_response, primary_artists) local_id = '99999' orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [] kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': create_message['ids'][0]['localId'], 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': local_id, 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'] } ] assert mock_product_formatter.call_args_list[0][0][2] == primary_artists assert len(responses) == 3 response = responses[2] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids def test_failed_validation_causes_error_responses( mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that an invalid product is handled.""" kafka_message = KafkaMessage(json.dumps(create_message)) fake_product = get_product_by_upc_create_response sides = fake_product['components'][0]['sides'] sides[0]['productTracks'][0]['track']['trackType'] = 'MEDLEY' swb_client.get_product_by_upc = \ Mock(return_value=fake_product) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_track_ids = [ {'localId': 'D1631147620131205:', 'system': 'SONY', 'businessKeyType': 'ISRC', 'businessKey': 'USSM11307800'} ] expected_product_ids = [ {'localId': create_message['ids'][0]['localId'], 'system': 'SONY', 'businessKeyType': 'UPC', 'businessKey': create_message['ids'][0]['businessKey']} ] assert len(responses) == 2 response = responses[0] assert response['entityType'] == entity_type.TRACK assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_track_ids response = responses[1] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_product_ids def test_participant_error_handling( mock_participant_handler, primary_artists, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that an error result is thrown when participant error occurs.""" graphql_error = [{ 'extensions': { 'code': 'create_participant_error_code', }, 'message': 'An error message', }] expected_error = [{'code': 'create_participant_error_code', 'message': 'An error message'}] # Throw a ParticipantHandlerGraphQLError from the participant handler mock_participant_handler().process \ .side_effect = Mock(side_effect=ParticipantHandlerGraphQLError( 'Test error', graphql_error)) kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': create_message['ids'][0]['localId'], 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], } ] assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids assert response['errors'] == expected_error def test_participant_handler_error_exception( mock_participant_handler, primary_artists, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that an error result is thrown when handler error occurs.""" exception_message = 'An error message for an exception in a handler' exception = ParticipantHandlerError(exception_message) expected_error = [{ 'code': 'daemon_exception', 'message': '{}: {}'.format(type(exception).__name__, exception) }] # Throw a ParticipantHandlerError from the participant handler mock_participant_handler().process \ .side_effect = Mock(side_effect=exception) kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_ids = [ { 'system': SONY, 'localId': create_message['ids'][0]['localId'], 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], } ] assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_ids assert response['errors'] == expected_error def test_process_product_tracks_update_delete_tracks_success( mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_delete_get_orchard_product, update_product_tracks_delete_get_orchard_product_before_arrangement): """Test for successful delete of tracks on product update.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.side_effect = [ update_product_tracks_delete_get_orchard_product, update_product_tracks_delete_get_orchard_product_before_arrangement ] orchard_client.delete_tracks.return_value = [ { 'tuid': '10001' }, { 'tuid': '10002' }, { 'tuid': '10003' } ] orchard_client.rearrange_tracks = Mock() swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) assert success assert results == [] delete_args = orchard_client.delete_tracks.call_args expected_track_to_delete_id = '10004' assert delete_args[0][0] == [expected_track_to_delete_id] def test_process_product_tracks_update_delete_tracks_failure( mock_logger, orchard_client, update_product_tracks_swb_product, update_product_tracks_delete_get_orchard_product): """Test for failed delete of tracks on product update.""" kafka_message = KafkaMessage(json.dumps(create_message)) orchard_client.get_product_by_id.return_value = \ update_product_tracks_delete_get_orchard_product orchard_client.delete_tracks.return_value = { 'errors': [ { 'extensions': { 'code': 'delete_tracks_error_code', }, 'message': 'An error message', } ] } swb_product_data = update_product_tracks_swb_product success, results = product_metadata_update.process_product_tracks_update( mock_logger, kafka_message, orchard_client, swb_product_data) assert not success del results[0]['messageCreatedDateTime'] del results[0]['messageId'] assert results[0] == { 'correlationId': 'abc123', 'entityType': 'PRODUCT', 'errors': [ { 'code': 'delete_tracks_error_code', 'message': 'An error message' } ], 'ids': [ { 'businessKey': '09876543210987', 'businessKeyType': 'UPC', 'localId': '1022869', 'system': 'SONY' } ], 'messageType': 'PROCESSING_RESULT', 'sendingSystem': 'ORCHARD' } @patch(('switchboard_consumer.logic.' 'product_metadata_update.create_track_mapping')) @patch('switchboard_consumer.logic.product_metadata_update.' 'LabelParticipantHandler') @patch('switchboard_consumer.logic.product_metadata_update.' 'get_product_track_by_isrc') def test_handle_product_create_product_with_tracks_success( mock_get_product_track_by_isrc, mock_label_participant_handler, mock_create_track_mapping, mock_participant_handler, mock_track_participant_handler, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test that a create product message creates a product.""" local_id = 99999 orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [ { 'tuid': 10003, 'isrc': 'ISRC_003' } ] mock_create_track_mapping.return_value = { 'ISRC_003': { 'system': SONY, 'localId': '20001', } } mock_get_product_track_by_isrc.return_value = { 'track': { 'isrc': 'ISRC_003', 'contributors': [] } } kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = \ Mock(return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) expected_product_ids = [ { 'system': SONY, 'localId': str(create_message['ids'][0]['localId']), 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'], }, { 'system': ORCHARD, 'localId': str(local_id), 'businessKey': create_message['ids'][0]['businessKey'], 'businessKeyType': create_message['ids'][0]['businessKeyType'] } ] expected_track_ids = [ { 'system': ORCHARD, 'localId': '10003', 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC' }, { 'system': SONY, 'localId': '20001', 'businessKey': 'ISRC_003', 'businessKeyType': 'ISRC', } ] assert len(responses) == 2 # First response should be a product response = responses[0] assert response['entityType'] == entity_type.PRODUCT assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_product_ids # Second response should be a track response = responses[1] assert response['entityType'] == entity_type.TRACK assert response['messageId'] assert response['correlationId'] assert response['ids'] == expected_track_ids @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_handle_product_update_product_error_correction( validate_local_ids_mock, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test that a product with RC displayStatus updates.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = { **graphql_product_get_product_response, 'displayStatus': ERROR_CORRECTION, 'releaseCorrectionId': 12345, 'tracks': [] } orchard_client.unsubmit_product.return_value = {} orchard_client.create_release_correction_detail.return_value = {} orchard_client.submit_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 assert not responses[0].get('errors') orchard_client.unsubmit_product.assert_not_called() orchard_client.create_release_correction.assert_not_called() orchard_client.create_release_correction_detail.assert_called_once() orchard_client.submit_product.assert_called_once() @patch('switchboard_consumer.logic.product_handler.ProductHandler.' 'validate_local_ids') def test_handle_product_update_product_submitted_with_rc_id( validate_local_ids_mock, mock_rc_track_participant_handler, mock_participant_handler, mock_rc_product_participant_handler, graphql_product_get_product_response, mock_logger, swb_client, orchard_client): """Test that a product with SUBMITTED displayStatus and rc id updates.""" orchard_client.update_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } orchard_client.get_product_by_id.return_value = { **graphql_product_get_product_response, 'displayStatus': SUBMITTED, 'releaseCorrectionId': 12345, 'tracks': [] } orchard_client.unsubmit_product.return_value = {} orchard_client.create_release_correction_detail.return_value = {} orchard_client.submit_product.return_value = { 'productId': graphql_product_get_product_response['productId'] } validate_local_ids_mock.return_value = True kafka_message = KafkaMessage(json.dumps(update_message)) get_product_by_upc_mock = Mock(side_effect=[ get_product_by_upc_update_response, orchard_get_product_by_upc_update_response, ]) swb_client.get_product_by_upc = get_product_by_upc_mock responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert len(responses) == 1 assert not responses[0].get('errors') validate_local_ids_mock.assert_called_once() orchard_client.unsubmit_product.assert_called_once() orchard_client.create_release_correction.assert_not_called() orchard_client.create_release_correction_detail.assert_called_once() orchard_client.submit_product.assert_called_once() @patch('switchboard_consumer.logic.product_metadata_update.' 'VideoSingleHandler.__init__') @patch('switchboard_consumer.logic.product_metadata_update.' 'VideoSingleHandler.process') def test_handle_product_create_video_product_success( mock_process, mock_init, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test product_metadata_update do correct choice for audio/video.""" local_id = '99999' orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [] mock_init.return_value = None video_processing_results = [ Mock('video_result_1'), Mock('video_result_2')] mock_process.return_value = video_processing_results kafka_message = KafkaMessage(json.dumps(create_message)) get_product_by_upc_create_response['releaseType'] = 'VIDEO_SINGLE' swb_client.get_product_by_upc = Mock( return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert responses == video_processing_results mock_init.assert_called_once_with( get_product_by_upc_create_response, kafka_message, mock_logger, orchard_client, swb_client) mock_process.assert_called_once() @patch('switchboard_consumer.logic.product_metadata_update.' 'AudioHandler.__init__') @patch('switchboard_consumer.logic.product_metadata_update.' 'AudioHandler.process') def test_handle_product_create_audio_product_success( mock_process, mock_init, mock_logger, swb_client, orchard_client, get_product_by_upc_create_response): """Test product_metadata_update do correct choice for audio/video.""" local_id = '99999' orchard_client.create_product.return_value = {'productId': local_id} orchard_client.create_tracks.return_value = [] mock_init.return_value = None audio_processing_results = [ Mock('audio_result_1'), Mock('audio_result_2')] mock_process.return_value = audio_processing_results kafka_message = KafkaMessage(json.dumps(create_message)) swb_client.get_product_by_upc = Mock( return_value=get_product_by_upc_create_response) responses = product_metadata_update.handle_product_metadata_update( mock_logger, kafka_message, swb_client, orchard_client) assert responses == audio_processing_results mock_init.assert_called_once_with( get_product_by_upc_create_response, kafka_message, mock_logger, orchard_client, swb_client) mock_process.assert_called_once() def test_construct_mappings_for_existing_tracks( mock_logger, update_product_tracks_swb_product_with_missing_mapping, update_product_tracks_get_orchard_product): """Test that unmapped existing tracks create mappings.""" results = product_metadata_update.construct_mappings_for_existing_tracks( update_product_tracks_get_orchard_product, update_product_tracks_swb_product_with_missing_mapping, Mock(), mock_logger ) expected_processing_result = { 'entityType': 'TRACK', 'messageType': 'PROCESSING_RESULT', 'messageId': '8a1e96ba-c8e8-43bc-ab21-eb21d0358fda', 'sendingSystem': 'ORCHARD', 'ids': [ { 'localId': '10001', 'businessKey': 'ISRC_001', 'businessKeyType': 'ISRC', 'system': 'ORCHARD' }, { 'businessKey': 'ISRC_001', 'businessKeyType': 'ISRC', 'system': 'SONY', 'localId': 'sony-id-10001' } ], 'messageCreatedDateTime': '2020-06-23T12:03:51.030Z', 'errors': [] } assert len(results) == 1 assert results[0]['ids'] == expected_processing_result['ids']