from unittest.mock import MagicMock import anyio.to_thread import pytest from dmp.artists.handlers import ( GetArtistSegmentsExplainedHandler, GetArtistSegmentsExplainedRequest, ) from dmp.fandata.enums import ( FanSegment, SegmentCategory, SegmentExplainedResponseLanguage, ) from dmp.fandata.models import ( FansByArtistAccountCategoryShareDbt, FansByArtistAccountSegmentCampaignDbt, GlobalFansByArtistCategoryShareDbt, GlobalFansByArtistSegmentCampaignDbt, ) from dmp.rosters.exceptions import SnowflakeLlmError from tests.unit.faker import FakerTyped from tests.unit.types import CreateReportingModel class TestGetArtistSegmentsExplainedHandler: @pytest.mark.db async def test_get_artist_account_segments_missing_empty_response( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, ) -> None: global_participant_id = fake.uuid4_string() vendor_id = fake.integer() subaccount_id = fake.integer() create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=vendor_id, subaccount_id=subaccount_id, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert result == [] @pytest.mark.db async def test_get_artist_account_campaigns_missing_nonempty_response( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() vendor_id = fake.integer() subaccount_id = fake.integer() create_reporting_model( FansByArtistAccountCategoryShareDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.4, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=vendor_id, subaccount_id=subaccount_id, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 1 assert result[0].explanation @pytest.mark.db @pytest.mark.anyio async def test_get_artist_account_segments_1_explained( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() vendor_id = fake.integer() subaccount_id = fake.integer() create_reporting_model( FansByArtistAccountCategoryShareDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.4, ) create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=vendor_id, subaccount_id=subaccount_id, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 1 assert result[0].explanation @pytest.mark.db @pytest.mark.anyio async def test_get_artist_account_segments_3_explained( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() vendor_id = fake.integer() subaccount_id = fake.integer() create_reporting_model( FansByArtistAccountCategoryShareDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.31, ) create_reporting_model( FansByArtistAccountCategoryShareDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.ENGAGED_FANS, category=SegmentCategory.ADDED_ARTISTS_MUSIC, fans_share=0.31, ) create_reporting_model( FansByArtistAccountCategoryShareDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.CASUAL_FANS, category=SegmentCategory.FAN_CLUB, fans_share=0.31, ) create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.CASUAL_FANS, fans_share=0.2, ) create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.ENGAGED_FANS, fans_share=0.2, ) create_reporting_model( FansByArtistAccountSegmentCampaignDbt, global_participant_id=global_participant_id, vendor_id=vendor_id, subaccount_id=subaccount_id, segment_name=FanSegment.FANS_TO_WIN_BACK, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=vendor_id, subaccount_id=subaccount_id, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 3 assert result[0].explanation assert result[1].explanation assert result[2].explanation @pytest.mark.db @pytest.mark.artist_access(is_global=True) async def test_get_global_artist_segments_missing_empty_response( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, ) -> None: global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, global_participant_id=global_participant_id, vendor_id=None, subaccount_id=None, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert result == [] @pytest.mark.db @pytest.mark.anyio @pytest.mark.artist_access(is_global=True) async def test_get_global_artist_campaigns_missing_nonempty_response( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.4, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=None, subaccount_id=None, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 1 assert result[0].explanation @pytest.mark.db @pytest.mark.anyio @pytest.mark.artist_access(is_global=True) async def test_get_global_artist_segments_1_explained( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.4, ) create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=None, subaccount_id=None, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 1 assert result[0].explanation @pytest.mark.db @pytest.mark.anyio @pytest.mark.artist_access(is_global=True) async def test_get_global_artist_segments_3_explained( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.ENGAGED_FANS, category=SegmentCategory.ADDED_ARTISTS_MUSIC, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.CASUAL_FANS, category=SegmentCategory.FAN_CLUB, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, fans_share=0.2, ) create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.CASUAL_FANS, fans_share=0.2, ) create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.ENGAGED_FANS, fans_share=0.2, ) create_reporting_model( GlobalFansByArtistSegmentCampaignDbt, global_participant_id=global_participant_id, segment_name=FanSegment.FANS_TO_WIN_BACK, fans_share=0.2, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=None, subaccount_id=None, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert mock_cortex_complete assert len(result) == 3 assert result[0].explanation assert result[1].explanation assert result[2].explanation @pytest.mark.db @pytest.mark.anyio @pytest.mark.artist_access(is_global=True) async def test_get_artist_segments_fails_gracefully( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: mock_cortex_complete.side_effect = SnowflakeLlmError() global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.ENGAGED_FANS, category=SegmentCategory.ADDED_ARTISTS_MUSIC, fans_share=0.31, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=None, subaccount_id=None, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert result == [] @pytest.mark.db @pytest.mark.anyio @pytest.mark.artist_access(is_global=True) async def test_get_artist_segments_fails_partially( self, handler: GetArtistSegmentsExplainedHandler, create_reporting_model: CreateReportingModel, identity_id: str, fake: FakerTyped, mock_cortex_complete: MagicMock, ) -> None: mock_cortex_complete.side_effect = [ Exception("Random Failure 1"), "Valid LLM Response A", Exception("Random Failure 2"), "Valid LLM Response B", ] global_participant_id = fake.uuid4_string() create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.SUPER_FANS, category=SegmentCategory.FOLLOW_ARTIST, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.ENGAGED_FANS, category=SegmentCategory.ADDED_ARTISTS_MUSIC, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.CASUAL_FANS, category=SegmentCategory.LIVE_STREAMING, fans_share=0.31, ) create_reporting_model( GlobalFansByArtistCategoryShareDbt, global_participant_id=global_participant_id, segment_name=FanSegment.FANS_TO_WIN_BACK, category=SegmentCategory.HEAVY_LISTENING, fans_share=0.31, ) request = GetArtistSegmentsExplainedRequest( identity_id=identity_id, vendor_id=None, subaccount_id=None, global_participant_id=global_participant_id, language=SegmentExplainedResponseLanguage.english, ) result = await anyio.to_thread.run_sync(handler.handle, request) assert len(result) == 2 returned_segment_names = {r.segment.name for r in result} all_possible_segments = {s.name for s in FanSegment} assert returned_segment_names.issubset(all_possible_segments)