"""Unit tests for the create_product Lambda function.""" from unittest.mock import ANY, patch from common.constants.product_status import IN_CONTENT from common.schemas.state_machine_schema import StateMachineSchema import pytest from src import index from src.constants.queries import CREATE_AUDIO_PRODUCT, \ CREATE_OR_UPDATE_VIDEO_PRODUCT, GET_PRODUCT_BY_UPC, UPDATE_PRODUCT from src.exceptions import InContentProductIsNotSmeAnalyticsDummyException @patch('src.index.graphql_gateway') def test_handler_product_is_not_sme( mock_gql_gateway, context_event, get_product_response, ): """Test handler raises InContentProductIsNotSmeAnalyticsDummyException.""" get_product_response['data']['productByUpc']['notForDistribution'] = 'N' get_product_response['data']['productByUpc']['status'] = IN_CONTENT mock_gql_gateway.execute.return_value = get_product_response with pytest.raises( InContentProductIsNotSmeAnalyticsDummyException) as exc_info: index.handler(context_event, None) mock_gql_gateway.execute.assert_called_once() call_args = mock_gql_gateway.execute.call_args assert call_args[0][0] == GET_PRODUCT_BY_UPC assert call_args[0][1] == {'upc': '231232563123'} assert 'InContentProductIsNotSmeAnalyticsDummyException' == exc_info.typename # noqa @patch('src.index.graphql_gateway') def test_test_handler_audio_product_updates(mock_gql_gateway, context_event, get_product_response, update_audio_product_response): """Test handler updates product.""" mock_gql_gateway.execute.side_effect = [ get_product_response, update_audio_product_response ] result = index.handler(context_event, None) assert mock_gql_gateway.execute.call_count == 2 first_call = mock_gql_gateway.execute.call_args_list[0] assert first_call[0][0] == GET_PRODUCT_BY_UPC third_call = mock_gql_gateway.execute.call_args_list[1] assert third_call[0][0] == UPDATE_PRODUCT assert result['product']['upc'] == '231232563123' @patch('src.index.graphql_gateway') def test_handler_product_not_found_creates( mock_gql_gateway, context_event, get_product_no_result, create_audio_product_response, update_audio_product_response ): """Test handler creates product when none exists.""" mock_gql_gateway.execute.side_effect = [ get_product_no_result, create_audio_product_response, update_audio_product_response, ] result = index.handler(context_event, None) assert mock_gql_gateway.execute.call_count == 3 first_call = mock_gql_gateway.execute.call_args_list[0] assert first_call[0][0] == GET_PRODUCT_BY_UPC second_call = mock_gql_gateway.execute.call_args_list[1] assert second_call[0][0] == CREATE_AUDIO_PRODUCT third_call = mock_gql_gateway.execute.call_args_list[2] assert third_call[0][0] == UPDATE_PRODUCT assert result['product']['upc'] == '231232563123' @patch('src.index.graphql_gateway') def test_check_for_product_found( mock_gql_gateway, context_event, get_product_response, ): """Test check_for_product returns result when product exists.""" mock_gql_gateway.execute.return_value = get_product_response sm_context = StateMachineSchema().load(context_event) result = index.check_for_product(sm_context) assert result is not None assert result['productId'] == 3141281 @patch('src.index.graphql_gateway') def test_check_for_product_not_found( mock_gql_gateway, context_event, get_product_no_result, ): """Test check_for_product returns None when not found.""" mock_gql_gateway.execute.return_value = get_product_no_result sm_context = StateMachineSchema().load(context_event) result = index.check_for_product(sm_context) assert result is None def test_check_for_product_no_upc(context_event): """Test check_for_product skips when no UPC.""" sm_context = StateMachineSchema().load(context_event) sm_context.product.upc = None result = index.check_for_product(sm_context) assert result == {} @patch('src.index.store_placeholder_upc') @patch('src.index.update_display_upc') @patch('src.index.graphql_gateway') def test_create_product_with_placeholder_upc( mock_gql_gateway, mock_update_display_upc, mock_store_placeholder_upc, context_event, create_audio_product_response, ): """Test create_audio_product sends correct payload to GraphQL.""" context_event['placeholder_upc_ingestion'] = True context_event['product']['upc'] = None context_event['product']['display_upc'] = '55555' mock_gql_gateway.execute.return_value = create_audio_product_response sm_context = StateMachineSchema().load(context_event) index.create_audio_product(sm_context) mock_gql_gateway.execute.assert_called_once() call_args = mock_gql_gateway.execute.call_args assert call_args[0][0] == CREATE_AUDIO_PRODUCT payload_data = call_args[0][1]['data'] assert payload_data['upc'] is None mock_update_display_upc.assert_called_once_with(3263077, '55555') mock_store_placeholder_upc.assert_called_once_with(ANY, '55555', '231232563123') @patch('src.index.graphql_gateway') def test_create_create_audio_payload( mock_gql_gateway, context_event, create_audio_product_response, ): """Test create_audio_product sends correct payload to GraphQL.""" mock_gql_gateway.execute.return_value = create_audio_product_response sm_context = StateMachineSchema().load(context_event) index.create_audio_product(sm_context) mock_gql_gateway.execute.assert_called_once() call_args = mock_gql_gateway.execute.call_args assert call_args[0][0] == CREATE_AUDIO_PRODUCT payload_data = call_args[0][1]['data'] assert payload_data['productName'] == 'Test Product' assert payload_data['productHighlights'] == 'abc' assert payload_data['upc'] == '231232563123' assert payload_data['accountId'] == 123 assert payload_data['subaccountId'] == 999 assert payload_data['projectId'] == 4498666 assert payload_data['notForDistribution'] == 'SMEAnalyticsDummy' assert payload_data['manufacturerUpc'] == '231232563123' assert payload_data['format'] == 'Single' assert payload_data['imprint'] == 'Eurodisc' assert payload_data['productCode'] == 'G010001336586R' assert 'grid' not in payload_data @patch('src.index.graphql_gateway') def test_create_create_video_payload( mock_gql_gateway, context_event, create_video_product_response, ): """Test create_audio_product sends correct payload to GraphQL.""" mock_gql_gateway.execute.return_value = create_video_product_response sm_context = StateMachineSchema().load(context_event) index.create_video_product(sm_context) mock_gql_gateway.execute.assert_called_once() call_args = mock_gql_gateway.execute.call_args assert call_args[0][0] == CREATE_OR_UPDATE_VIDEO_PRODUCT payload_data = call_args[0][1]['data']['create'] assert payload_data['upc'] == '231232563123' assert payload_data['accountId'] == 123 assert payload_data['subaccountId'] == 999 assert payload_data['projectId'] == '4498666' assert payload_data['typeOfVideo'] == 'Other' assert payload_data['notForDistribution'] == 'SMEAnalyticsDummy' assert payload_data['imprint'] == 'Eurodisc' assert payload_data['productCode'] == 'G010001336586R' assert payload_data['videoTitle'] == 'Test Product' assert 'grid' not in payload_data def test_build_participations(context_event): """Test build_participations maps artists to label participants.""" sm_context = StateMachineSchema().load(context_event) participations = index.build_participations(sm_context) assert len(participations) == 2 assert participations[0]['labelParticipantUuid'] == 'UUID-AAA' assert participations[0]['role'] == 'PRIMARY_ARTIST' assert participations[1]['labelParticipantUuid'] == 'UUID-BBB' assert participations[1]['role'] == 'FEATURED_ARTIST' def test_build_participations_no_artists(context_event): """Test build_participations returns empty for no artists.""" context_event['product']['display_artists'] = None sm_context = StateMachineSchema().load(context_event) participations = index.build_participations(sm_context) assert participations == [] def test_build_participations_no_label_match(context_event): """Test build_participations when no label participant matches.""" context_event['label_participants'] = [] sm_context = StateMachineSchema().load(context_event) participations = index.build_participations(sm_context) assert len(participations) == 2 assert participations[0]['labelParticipantUuid'] is None assert participations[0]['role'] == 'PRIMARY_ARTIST' assert participations[1]['labelParticipantUuid'] is None assert participations[1]['role'] == 'FEATURED_ARTIST' def test_build_participations_filters_non_product_roles(context_event): """Test build_participations skips roles not in PRIMARY/FEATURED_ARTIST.""" context_event['product']['display_artists'][0]['roles'] = ['PRODUCER'] sm_context = StateMachineSchema().load(context_event) participations = index.build_participations(sm_context) assert len(participations) == 1 assert participations[0]['labelParticipantUuid'] == 'UUID-BBB' assert participations[0]['role'] == 'FEATURED_ARTIST' @patch('src.index.graphql_gateway') def test_handler_returns_serialized_context( mock_gql_gateway, context_event, get_product_no_result, create_audio_product_response, update_audio_product_response ): """Test handler returns properly serialized state machine context.""" mock_gql_gateway.execute.side_effect = [ get_product_no_result, create_audio_product_response, update_audio_product_response ] result = index.handler(context_event, None) assert 'product' in result assert 'project' in result assert 'tracks' in result assert result['product']['upc'] == '231232563123' assert result['product']['product_id'] == 3263077 @patch('src.index.graphql_gateway') def test_update_product_payload( mock_gql_gateway, context_event, create_audio_product_response, ): """Test update_product_release_date sends correct payload to GraphQL.""" mock_gql_gateway.execute.return_value = create_audio_product_response sm_context = StateMachineSchema().load(context_event) sm_context.product.product_id = 789 index.update_product(sm_context) mock_gql_gateway.execute.assert_called_once() call_args = mock_gql_gateway.execute.call_args assert call_args[0][0] == UPDATE_PRODUCT payload_data = call_args[0][1]['data'] assert payload_data['productId'] == 789 assert payload_data['releaseDate'] == '2025-01-01' assert payload_data['saleStartDate'] == '2008-03-04' assert payload_data['productName'] == 'Test Product' assert payload_data['manufacturerUpc'] == '231232563123' assert payload_data['format'] == 'Single' assert payload_data['imprint'] == 'Eurodisc' assert payload_data['productCode'] == 'G010001336586R'