"""Integration tests for handle_project_metadata_update.""" import json from mock import Mock, patch import pytest from switchboard_consumer.constants import entity_type, message_type from switchboard_consumer.constants.exceptions import ( ParticipantHandlerError, ) from switchboard_consumer.kafka_message import KafkaMessage from switchboard_consumer.logic.project_metadata_update import \ create_project, handle_project_metadata_update, \ update_project, validate_project from switchboard_consumer.utils.message import create_processing_result from tests.conftest import ORCHARD, SONY @pytest.fixture() def project_artist(): """Mock participant handler logic.""" return { 'artistInfoId': 123, 'artistName': 'Project Artist', 'artistType': None } @pytest.fixture() def mock_participant_handler(project_artist): """Mock participant handler logic.""" with patch(('switchboard_consumer.logic.' 'project_metadata_update.' 'ProjectParticipantHandler')) as participant_handler: participant_handler().process \ .return_value = (None, project_artist) yield participant_handler def test_project_metadata_creates_project(project_artist, mock_participant_handler): """Test creating a project.""" mock_logger = Mock() message = { 'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': '12345', }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': SONY, 'ids': [ {'localId': '12345', 'system': SONY}, ], 'artist': { 'ids': [ {'localId': 'AIID:000111', 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': '222222', }, 'subAccount': { 'companyCode': '333333', }, } orchard_client = Mock() # There is NO existing project that matches the sony local id orchard_client.get_project_by_project_code.return_value = None orchard_client.create_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) expected_data = {'projectCode': '12345', 'name': 'Testing Project A', 'artistId': project_artist['artistInfoId'], 'accountId': 222222, 'description': None, 'subaccountId': 333333 } orchard_client.create_project.assert_called_with(expected_data, message['correlationId']) assert swb_client.lookup_project_by_local_id.call_count == 1 assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, {'system': 'ORCHARD', 'localId': '67890'} ] def test_create_project_with_project_code_exists_in_orchard_no_description( project_artist, mock_participant_handler): """Test creating a project with a project_code.""" mock_logger = Mock() expected_label_account_code = 222222 expected_sub_label_account_code = 333333 sony_local_id = '12345' message = { 'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': sony_local_id, }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': SONY, 'ids': [ {'localId': sony_local_id, 'system': SONY}, ], 'artist': { 'ids': [ {'localId': '000111', 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': f'{expected_label_account_code}', }, 'subAccount': { 'companyCode': f'{expected_sub_label_account_code}', }, } orchard_client = Mock() # Project found in Orchard that matches sony local id orchard_client.get_project_by_project_code.return_value = { 'projectId': 67890 } orchard_client.update_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) expected_update_data = { 'projectId': 67890, 'name': 'Testing Project A', 'artistId': project_artist['artistInfoId'], } orchard_client.update_project.assert_called_with( expected_update_data, message['correlationId']) assert swb_client.lookup_project_by_local_id.call_count == 1 orchard_client.get_project_by_project_code.assert_called_with( sony_local_id, expected_label_account_code, expected_sub_label_account_code, message['correlationId']) assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, {'system': 'ORCHARD', 'localId': '67890'} ] def test_create_project_with_project_code_exists_in_orchard_with_description( project_artist, mock_participant_handler): """Test creating a project with a project_code.""" mock_logger = Mock() expected_label_account_code = 222222 expected_sub_label_account_code = 333333 sony_local_id = '12345' message = { 'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': sony_local_id, }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': SONY, 'ids': [ {'localId': sony_local_id, 'system': SONY}, ], 'artist': { 'ids': [ {'localId': '000111', 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': 'Test Description', 'labelAccount': { 'companyCode': f'{expected_label_account_code}', }, 'subAccount': { 'companyCode': f'{expected_sub_label_account_code}', }, } orchard_client = Mock() # Project found in Orchard that matches sony local id orchard_client.get_project_by_project_code.return_value = { 'projectId': 67890 } orchard_client.update_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) expected_update_data = { 'projectId': 67890, 'name': 'Testing Project A', 'artistId': project_artist['artistInfoId'], 'description': 'Test Description', } orchard_client.update_project.assert_called_with( expected_update_data, message['correlationId']) assert swb_client.lookup_project_by_local_id.call_count == 1 orchard_client.get_project_by_project_code.assert_called_with( sony_local_id, expected_label_account_code, expected_sub_label_account_code, message['correlationId']) assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, {'system': 'ORCHARD', 'localId': '67890'} ] def test_project_code_lookup_fails( mock_participant_handler): """Test create project but a project code lookup fails.""" mock_logger = Mock() expected_label_account_code = 222222 expected_sub_label_account_code = 333333 sony_local_id = '12345' message = { 'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': sony_local_id, }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': SONY, 'ids': [ {'localId': sony_local_id, 'system': SONY}, ], 'artist': { 'ids': [ {'localId': '000111', 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': f'{expected_label_account_code}', }, 'subAccount': { 'companyCode': f'{expected_sub_label_account_code}', }, } orchard_client = Mock() # Project found in Orchard that matches sony local id orchard_client.get_project_by_project_code.return_value = { 'errors': [{'message': 'Something happened'}] } orchard_client.update_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) orchard_client.get_project_by_project_code.assert_called_once() assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'} ] assert response['errors'] == [{'code': 'unknown_code', 'message': 'Something happened'}] def test_project_metadata_updates_existing_project( project_artist, mock_participant_handler): """Test that an existing project can be updated.""" mock_logger = Mock() message = { 'sendingSystem': 'SONY', 'entityType': entity_type.PROJECT, 'ids': [ { 'system': 'SONY', 'localId': '12345', }, ] } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': 'SONY', 'ids': [ {'localId': '12345', 'system': SONY}, {'localId': '67890', 'system': ORCHARD}, ], 'artist': { 'ids': [ {'localId': '222', 'system': SONY}, {'localId': project_artist['artistInfoId'], 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': '222222', }, 'subAccount': { 'companyCode': '333333', }, } orchard_client = Mock() orchard_client.get_project_by_project_code = Mock() orchard_client.update_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) assert swb_client.lookup_project_by_local_id.call_count == 1 # We have an Orchard and Sony mapping, no need to lookup the Project orchard_client.get_project_by_project_code.assert_not_called() assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, {'system': 'ORCHARD', 'localId': '67890'} ] def test_project_metadata_handles_local_id_lookup_errors(): """Test that errors returned in inital lookup trigger a kafka response.""" mock_logger = Mock() message = { 'sendingSystem': 'SONY', 'entityType': entity_type.PROJECT, 'ids': [ { 'system': 'SONY', 'localId': '12345', }, ] } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'errors': [ { 'extensions': { 'code': 'some_code', }, 'message': 'An error message', } ] } formatted_errors = {'errors': [{'code': 'some_code', 'message': 'An error message'}]} orchard_client = Mock() kafka_message = KafkaMessage(json.dumps(message)) with patch(('switchboard_consumer.logic.' 'project_metadata_update.create_processing_result'), wraps=create_processing_result) as mock_result: handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) assert mock_result.call_args[1] == formatted_errors def test_create_project_fails(): """Test a failure during project create.""" mock_logger = Mock() message = { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, ] } data = {} orchard_client = Mock() errors = [ { 'extensions': { 'code': 'some_code', }, 'message': 'An error message', } ] formatted_errors = [ { 'code': 'some_code', 'message': 'An error message' } ] orchard_client.create_project.return_value = { 'errors': errors } kafka_message = KafkaMessage(json.dumps(message)) response = create_project(mock_logger, kafka_message, data, orchard_client) assert response['ids'] == message['ids'] assert response['errors'] == formatted_errors def test_update_project_fails(): """Test a failure during project update.""" mock_logger = Mock() message = { 'ids': [ { 'system': 'SONY', 'localId': '12345', }, ] } data = {} orchard_client = Mock() errors = [ { 'extensions': { 'code': 'some_code', }, 'message': 'An error message', } ] formatted_errors = [ { 'code': 'some_code', 'message': 'An error message' } ] orchard_client.update_project.return_value = { 'errors': errors } kafka_message = KafkaMessage(json.dumps(message)) response = update_project(mock_logger, kafka_message, data, orchard_client) assert response['ids'] == message['ids'] assert response['errors'] == formatted_errors def test_project_metadata_creates_ignore_null_subaccount( project_artist, mock_participant_handler): """Test if we handle subaccounts returned as null from switchboard api.""" mock_logger = Mock() message = { 'sendingSystem': 'SONY', 'entityType': entity_type.PROJECT, 'ids': [ { 'system': 'SONY', 'localId': '12345', }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': 'SONY', 'ids': [ {'localId': '12345', 'system': 'SONY'}, ], 'artist': { 'ids': [ {'localId': project_artist['artistInfoId'], 'system': ORCHARD}, {'localId': '222', 'system': SONY}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': '222222', }, 'subAccount': None } orchard_client = Mock() orchard_client.create_project.return_value = { 'projectId': 67890 } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, orchard_client) expected_data = {'projectCode': '12345', 'name': 'Testing Project A', 'artistId': project_artist['artistInfoId'], 'accountId': 222222, 'description': None} orchard_client.create_project.assert_called_with(expected_data, message['correlationId']) assert swb_client.lookup_project_by_local_id.call_count == 1 assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, {'system': 'ORCHARD', 'localId': '67890'} ] @patch(('switchboard_consumer.logic.' 'project_metadata_update.create_processing_result'), wraps=create_processing_result) @patch('switchboard_consumer.utils.message.format_datetime') @patch('switchboard_consumer.utils.message.uuid.uuid4') def test_create_project_handler_outputs_correct_message(mock_uuid, mock_format_datetime, mock_processing_result ): """Test explicitily whether our format is correct.""" uuid_value = '123456' mock_uuid.return_value = uuid_value datetime_value = '2019' mock_format_datetime.return_value = datetime_value mock_logger = Mock() orchard_client = Mock() orchard_client.create_project.return_value = { 'projectId': 67890 } message = {'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': '12345', }, ], 'correlationId': 'abc123'} kafka_message = KafkaMessage(json.dumps(message)) expected = {'sendingSystem': ORCHARD, 'entityType': entity_type.PROJECT, 'messageType': message_type.PROCESSING_RESULT, 'ids': [ { 'system': SONY, 'localId': '12345', }, { 'system': ORCHARD, 'localId': '67890', } ], 'correlationId': 'abc123', 'messageId': uuid_value, 'messageCreatedDateTime': datetime_value, 'errors': [] } response = create_project( mock_logger, kafka_message, message, orchard_client) assert response == expected @patch(('switchboard_consumer.logic.' 'project_metadata_update.create_processing_result'), wraps=create_processing_result) @patch('switchboard_consumer.utils.message.format_datetime') @patch('switchboard_consumer.utils.message.uuid.uuid4') def test_update_project_handler_outputs_correct_message(mock_uuid, mock_format_datetime, mock_processing_result ): """Test explicitily whether our format is correct.""" uuid_value = '123456' mock_uuid.return_value = uuid_value datetime_value = '2019' mock_format_datetime.return_value = datetime_value mock_logger = Mock() orchard_client = Mock() orchard_client.update_project.return_value = { 'projectId': 67890 } message = {'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': '12345', }, { 'system': ORCHARD, 'localId': '67890', } ], 'correlationId': 'abc123'} kafka_message = KafkaMessage(json.dumps(message)) expected = {'sendingSystem': ORCHARD, 'entityType': entity_type.PROJECT, 'messageType': message_type.PROCESSING_RESULT, 'ids': [ { 'system': SONY, 'localId': '12345', }, { 'system': ORCHARD, 'localId': '67890', } ], 'correlationId': 'abc123', 'messageId': uuid_value, 'messageCreatedDateTime': datetime_value, 'errors': [] } response = update_project( mock_logger, kafka_message, message, orchard_client) assert response == expected @patch('switchboard_consumer.logic.project.validate_project_data') def test_validate_project_with_no_errors(mock_validate_project_data): """Test validate project with no errors.""" message = KafkaMessage(json.dumps({ 'ids': [{}] })) data = {} mock_validate_project_data.return_value = [] messages = validate_project(message, data) assert len(messages) == 0 @patch('switchboard_consumer.logic.project.validate_project_data') def test_validate_project_with_errors(mock_validate_project_data): """Test validate project with errors.""" message = KafkaMessage(json.dumps({ 'ids': [{}] })) data = {} mock_validate_project_data.return_value = [{ 'code': 'validation', 'message': 'MISSING_PARTICIPANT_DATA' }] messages = validate_project(message, data) assert len(messages) == 1 def test_project_metadata_update_handles_participant_errors( project_artist, mock_participant_handler): """Test that project error is returned when an error handling participants occurs""" # noqa mock_participant_handler().process.side_effect = ParticipantHandlerError( 'An Error' ) mock_logger = Mock() message = { 'sendingSystem': SONY, 'entityType': entity_type.PROJECT, 'ids': [ { 'system': SONY, 'localId': '12345', }, ], 'correlationId': 'abc123', } swb_client = Mock() swb_client.lookup_project_by_local_id.return_value = { 'owningSystem': SONY, 'ids': [ {'localId': '12345', 'system': SONY}, ], 'artist': { 'ids': [ {'localId': 'AIID:000111', 'system': ORCHARD}, ] }, 'name': 'Testing Project A', 'description': None, 'labelAccount': { 'companyCode': '222222', }, 'subAccount': { 'companyCode': '333333', }, } kafka_message = KafkaMessage(json.dumps(message)) responses = handle_project_metadata_update(mock_logger, kafka_message, swb_client, Mock()) assert len(responses) == 1 response = responses[0] assert response['entityType'] == entity_type.PROJECT assert response['messageId'] assert response['correlationId'] assert response['ids'] == [ {'system': 'SONY', 'localId': '12345'}, ] assert response['errors'][0]['message'] == ( 'ParticipantHandlerError: An Error')