from unittest import mock import faker import pytest from fansifter_common.adapters.ows_account import Vendor from fansifter_common.auth.account import Account from fansifter_common.core.enums import Brand from fansifter_common.email import EmailType from email_campaigns.audiences.enums import AudienceTarget from email_campaigns.audiences.exceptions import InvalidAudienceIdError from email_campaigns.audiences.models import Audience, AudienceFanDataList from email_campaigns.campaigns.enums import EmailCampaignStatus from email_campaigns.campaigns.exceptions import ( EmailCampaignMustBeDraftError, EmailCampaignSenderEmailNotValidError, MalformedEmailCampaignError, ) from email_campaigns.campaigns.handlers import ( UpdateCampaignHandler, UpdateCampaignRequest, ) from email_campaigns.campaigns.models import EmailCampaign from email_campaigns.emails.exceptions import InvalidEmailDomainIdError from email_campaigns.emails.models import EmailDomain from email_campaigns.rosters.enums import FanDataListType from email_campaigns.rosters.models import CustomList from email_campaigns.rosters.types import FanDataListId from tests.unit.types import BuildModel, CreateModel, CreatePgModel class TestUpdateCampaignHandler: @pytest.mark.db def test_update_campaign( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, campaign_public_preview_cache_mock: mock.MagicMock, faker: faker.Faker, ) -> None: # Reset mock to clear any previous calls campaign_public_preview_cache_mock.reset_mock() vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, fan_count=100, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST ) ], ) email_domain = create_model( EmailDomain, brand=vendor.brand, vendor_id=None, subaccount_id=None ) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=global_participant_id, ) ows_account_client_mock.get_vendor.return_value = vendor campaign = handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=audience.id, ) ) assert campaign.name == name assert campaign.vendor_id == account.vendor_id assert campaign.subaccount_id == account.subaccount_id assert campaign.global_participant_id == audience.global_participant_id assert campaign.custom_list_id is None assert campaign.sender_name == sender_name assert campaign.email_domain_id == email_domain.id assert campaign.email_username == email_username assert campaign.subject == subject assert campaign.preview_text == preview_text assert campaign.audience_id == audience.id assert campaign.audience == audience assert campaign.recipients_count == 100 assert campaign.updated_by == identity_id # Verify cache was invalidated campaign_public_preview_cache_mock.delete.assert_called_once_with( campaign.id, EmailType.CAMPAIGN ) @pytest.mark.db def test_update_campaign_custom_data_list_id( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, faker: faker.Faker, ) -> None: vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() custom_list_id = faker.uuid4() global_participant_id = faker.uuid4() create_model( CustomList, id=custom_list_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, fan_count=100, fandata_lists=[ AudienceFanDataList( list_id=custom_list_id, type=FanDataListType.CUSTOM_LIST, ), AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST, ), ], ) email_domain = create_model( EmailDomain, brand=vendor.brand, vendor_id=None, subaccount_id=None ) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=None, ) ows_account_client_mock.get_vendor.return_value = vendor campaign = handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, name=name, fandata_list_id=FanDataListId.from_custom_list_id(custom_list_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=audience.id, ) ) assert campaign.name == name assert campaign.vendor_id == account.vendor_id assert campaign.subaccount_id == account.subaccount_id assert campaign.global_participant_id is None assert campaign.custom_list_id == custom_list_id assert campaign.sender_name == sender_name assert campaign.email_domain_id == email_domain.id assert campaign.email_username == email_username assert campaign.subject == subject assert campaign.preview_text == preview_text assert campaign.audience_id == audience.id assert campaign.audience == audience assert campaign.recipients_count == 100 assert campaign.updated_by == identity_id @pytest.mark.db def test_update_campaign_artist( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, faker: faker.Faker, ) -> None: vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() global_participant_id = faker.uuid4() custom_list = create_model( CustomList, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) email_domain = create_model( EmailDomain, brand=vendor.brand, vendor_id=None, subaccount_id=None ) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=global_participant_id, email_domain=email_domain, audience_id=None, ) ows_account_client_mock.get_vendor.return_value = vendor campaign = handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=campaign.vendor_id, subaccount_id=campaign.subaccount_id, name=name, fandata_list_id=FanDataListId.from_custom_list_id(custom_list.id), sender_name=campaign.sender_name, email_domain_id=campaign.email_domain_id, email_username=campaign.email_username, subject=campaign.subject, preview_text=campaign.preview_text, audience_id=campaign.audience_id, ) ) assert campaign.global_participant_id is None assert campaign.custom_list_id == custom_list.id assert campaign.audience is None @pytest.mark.db def test_update_campaign_custom_data_list_id_not_supported( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, faker: faker.Faker, ) -> None: vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() custom_list_id = faker.uuid4() global_participant_id = faker.uuid4() create_model( CustomList, id=custom_list_id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, ) audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, fan_count=100, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST, ), ], ) email_domain = create_model(EmailDomain, brand=vendor.brand) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=global_participant_id, ) ows_account_client_mock.get_vendor.return_value = vendor with pytest.raises(MalformedEmailCampaignError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, name=name, fandata_list_id=FanDataListId.from_custom_list_id(custom_list_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=audience.id, ) ) @pytest.mark.db def test_update_campaign_remove_audience( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, faker: faker.Faker, ) -> None: vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() global_participant_id = faker.uuid4() create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, fan_count=100, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST ) ], ) email_domain = create_model( EmailDomain, brand=vendor.brand, vendor_id=None, subaccount_id=None ) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=global_participant_id, ) ows_account_client_mock.get_vendor.return_value = vendor campaign = handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=None, ) ) assert campaign.audience_id is None assert campaign.recipients_count == 0 @pytest.mark.db def test_update_campaign_must_be_draft( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, faker: faker.Faker, ) -> None: vendor = build_model(Vendor, company_brand=Brand.SME) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=vendor.vendor_id, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST ) ], ) email_domain = create_model(EmailDomain, brand=vendor.brand) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.SENT, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, global_participant_id=global_participant_id, ) with pytest.raises(EmailCampaignMustBeDraftError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=vendor.vendor_id, subaccount_id=audience.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=campaign.preview_text, audience_id=audience.id, ) ) @pytest.mark.db def test_update_campaign_invalid_audience_id( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, faker: faker.Faker, ) -> None: vendor = build_model(Vendor, company_brand=Brand.SME) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, vendor_id=vendor.vendor_id, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST ) ], ) email_domain = create_model(EmailDomain) campaign = create_model(EmailCampaign, status=EmailCampaignStatus.DRAFT) with pytest.raises(InvalidAudienceIdError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=vendor.vendor_id, subaccount_id=audience.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=faker.uuid4(), ) ) @pytest.mark.db def test_update_campaign_malformed( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, faker: faker.Faker, ) -> None: vendor = build_model(Vendor, company_brand=Brand.SME) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=vendor.vendor_id, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST, ) ], ) email_domain = create_model(EmailDomain, brand=vendor.brand) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, global_participant_id=global_participant_id, ) with pytest.raises(MalformedEmailCampaignError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=vendor.vendor_id + 1, subaccount_id=audience.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username, subject=subject, preview_text=preview_text, audience_id=audience.id, ) ) @pytest.mark.db def test_update_campaign_invalid_domain( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, faker: faker.Faker, ) -> None: vendor = build_model(Vendor, company_brand=Brand.SME) name = faker.name() subject = faker.sentence() sender_name = faker.first_name() email_username = faker.user_name() preview_text = faker.sentence() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=vendor.vendor_id, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST, ), ], fan_count=100, ) email_domain = create_model(EmailDomain, brand=vendor.brand) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, global_participant_id=global_participant_id, email_domain=email_domain, ) with pytest.raises(InvalidEmailDomainIdError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=vendor.vendor_id, subaccount_id=audience.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=faker.uuid4(), email_username=email_username, subject=subject, preview_text=preview_text, audience_id=audience.id, ) ) @pytest.mark.db def test_update_campaign_sender_email_with_diacritics_rejected( self, handler: UpdateCampaignHandler, build_model: BuildModel, create_model: CreateModel, create_pg_model: CreatePgModel, identity_id: str, account: Account, ows_account_client_mock: mock.MagicMock, faker: faker.Faker, ) -> None: vendor = build_model( Vendor, vendor_id=account.vendor_id, company_brand=Brand.SME ) name = faker.name() subject = faker.sentence() sender_name = "Chance Peña" email_username_with_diacritics = "chancepeña" preview_text = faker.sentence() global_participant_id = faker.uuid4() audience = create_pg_model( Audience, target=AudienceTarget.EMAIL, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, fan_count=100, fandata_lists=[ AudienceFanDataList( list_id=global_participant_id, type=FanDataListType.ARTIST ) ], ) email_domain = create_model( EmailDomain, brand=vendor.brand, domain="sonymusic.com", vendor_id=None, subaccount_id=None, ) campaign = create_model( EmailCampaign, status=EmailCampaignStatus.DRAFT, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, global_participant_id=global_participant_id, ) ows_account_client_mock.get_vendor.return_value = vendor with pytest.raises(EmailCampaignSenderEmailNotValidError): handler.handle( UpdateCampaignRequest( identity_id=identity_id, campaign_id=campaign.id, vendor_id=account.vendor_id, subaccount_id=account.subaccount_id, name=name, fandata_list_id=FanDataListId.from_artist_id(global_participant_id), sender_name=sender_name, email_domain_id=email_domain.id, email_username=email_username_with_diacritics, subject=subject, preview_text=preview_text, audience_id=audience.id, ) )