from unittest import mock import pytest from campaigns.auth.dtos import User, UserId from campaigns.campaigns.enums import CampaignStatus from campaigns.connectors.db import Database from campaigns.connectors.facebook import models as fb_models from campaigns.connectors.facebook.arguments import UpdateAdArgs from campaigns.connectors.facebook.enums import ObjectStatus from campaigns.connectors.facebook.models import Ad, AdCampaign, AdCreative, AdSet from campaigns.core.types import PlainToken from campaigns.meta import constants from campaigns.meta.enums import CampaignContentType, CampaignObjective from campaigns.meta.handlers import ( SubmitCampaignHandler, SubmitCampaignRequest, SubmitCampaignResponse, ) from campaigns.meta.models import ( Campaign, EngagementAudience, FacebookAssociation, FacebookPage, TargetingAudience, ) from campaigns.meta.repositories import ( CampaignAdCreativeRepository, CampaignAdRepository, CampaignAdSetEngagementAudienceRepository, CampaignAdSetRepository, CampaignAdSetTargetingAudienceRepository, CampaignRepository, ) from campaigns.meta.services import AdFormatService, CampaignService from campaigns.meta.validators import CampaignValidator from tests.unit.faker import FakerTyped from tests.unit.types import CreateModel @pytest.fixture def handler( db: Database, facebook_client_mock: mock.MagicMock, campaign_service: CampaignService, campaign_validator: CampaignValidator, campaign_repository: CampaignRepository, campaign_ad_set_repository: CampaignAdSetRepository, campaign_ad_set_targeting_audience_repository: CampaignAdSetTargetingAudienceRepository, campaign_ad_set_engagement_audience_repository: CampaignAdSetEngagementAudienceRepository, campaign_ad_creative_repository: CampaignAdCreativeRepository, campaign_ad_repository: CampaignAdRepository, ad_format_service: AdFormatService, ) -> SubmitCampaignHandler: return SubmitCampaignHandler( db=db, facebook_client=facebook_client_mock, campaign_validator=campaign_validator, campaign_repository=campaign_repository, campaign_service=campaign_service, campaign_ad_set_repository=campaign_ad_set_repository, campaign_ad_set_targeting_audience_repository=campaign_ad_set_targeting_audience_repository, campaign_ad_set_engagement_audience_repository=campaign_ad_set_engagement_audience_repository, campaign_ad_creative_repository=campaign_ad_creative_repository, campaign_ad_repository=campaign_ad_repository, ad_format_service=ad_format_service, ) async def test_submit_campaign( handler: SubmitCampaignHandler, facebook_client_mock: mock.MagicMock, create_model: CreateModel, user_id: UserId, fake: FakerTyped, ) -> None: facebook_client_mock.debug_tokens.return_value = { PlainToken("token"): fb_models.DebugToken( is_valid=True, scopes=constants.REQUIRED_SCOPES ) } campaign_external_id = fake.pystr() account_access = mock.MagicMock() account_access.has_direct_access_to_vendor.return_value = True user = User(id=user_id, account_access=account_access) facebook_association = await create_model( FacebookAssociation, user_id=fake.pystr(), orchard_identity_id=user.id.identity_id, ) facebook_page = await create_model( FacebookPage, is_valid=True, association=facebook_association ) engagement_audience = await create_model( EngagementAudience, external_id=str(fake.integer()), event_ids=[fake.pystr()], facebook_page_external_id=fake.pystr(), ) targeting_audience = await create_model( TargetingAudience, ) campaign = await create_model( Campaign, status=CampaignStatus.DRAFT, primary_text="test", targeting_audience=targeting_audience, engagement_audience=engagement_audience, content_type=CampaignContentType.POST, objective=CampaignObjective.ENGAGEMENT, external_id=None, facebook_post_external_id=fake.pystr(), facebook_page=facebook_page, lifetime_budget=fake.integer(), ) ad_campaign = AdCampaign(id=campaign_external_id) facebook_client_mock.create_campaign.return_value = ad_campaign ad_set_1 = AdSet(id=fake.pystr()) ad_set_2 = AdSet(id=fake.pystr()) facebook_client_mock.create_ad_set.side_effect = [ad_set_1, ad_set_2] ad_creative = AdCreative(id=str(fake.integer()), name=fake.pystr()) facebook_client_mock.create_ad_creative.return_value = ad_creative ad_1 = Ad( id=fake.pystr(), preview_shareable_link=fake.uri(), status=ObjectStatus.PAUSED ) ad_2 = Ad( id=fake.pystr(), preview_shareable_link=fake.uri(), status=ObjectStatus.PAUSED ) facebook_client_mock.create_ad.side_effect = [ad_1, ad_2] updated_ad_1 = Ad( id=ad_1.id, preview_shareable_link=fake.uri(), status=ObjectStatus.ACTIVE ) updated_ad_2 = Ad( id=ad_2.id, preview_shareable_link=fake.uri(), status=ObjectStatus.ACTIVE ) facebook_client_mock.update_ad.side_effect = [updated_ad_1, updated_ad_2] request = SubmitCampaignRequest(user=user, campaign_id=campaign.id) result = await handler.handle(request) # creating ad_set for each available audience type (targeting, engagement) ad_sets = await handler.campaign_ad_set_repository.all() ad_set_engagement_audiences = ( await handler.campaign_ad_set_engagement_audience_repository.all() ) ad_set_targeting_audiences = ( await handler.campaign_ad_set_targeting_audience_repository.all() ) assert len(ad_sets) == 2 assert ad_sets[0].external_id == ad_set_1.id assert ad_sets[0].name == "engagement" assert ad_sets[0].campaign_id == campaign.id assert len(ad_set_engagement_audiences) == 1 assert ad_set_engagement_audiences[0].ad_set_id == ad_sets[0].id assert ad_set_engagement_audiences[0].audience_id == engagement_audience.id assert ad_sets[1].external_id == ad_set_2.id assert ad_sets[1].name == "targeting" assert ad_sets[1].campaign_id == campaign.id assert len(ad_set_targeting_audiences) == 1 assert ad_set_targeting_audiences[0].ad_set_id == ad_sets[1].id assert ad_set_targeting_audiences[0].audience_id == targeting_audience.id # creating 1 ad creative always ad_creatives = await handler.campaign_ad_creative_repository.all() assert len(ad_creatives) == 1 assert ad_creatives[0].external_id == ad_creative.id assert ad_creatives[0].name == "test_name" assert ad_creatives[0].campaign_id == campaign.id # creating ad for each combination of ad_set and ad_creative ads = await handler.campaign_ad_repository.all() assert len(ads) == 2 assert ads[0].external_id == ad_1.id assert ads[0].campaign_id == campaign.id assert ads[0].ad_set_id == ad_sets[0].id assert ads[0].ad_creative_id == ad_creatives[0].id assert ads[1].external_id == ad_2.id assert ads[1].campaign_id == campaign.id assert ads[1].ad_set_id == ad_sets[1].id assert ads[1].ad_creative_id == ad_creatives[0].id # updating adds to ACTIVE to start FB review process facebook_client_mock.update_ad.assert_has_calls( [ mock.call(ad_id=ad_1.id, args=UpdateAdArgs(status=ObjectStatus.ACTIVE)), mock.call(ad_id=ad_2.id, args=UpdateAdArgs(status=ObjectStatus.ACTIVE)), ], ) # updating existing campaign once it is submitted for FB review campaigns = await handler.campaign_repository.all() assert len(campaigns) == 1 assert campaigns[0].id == campaign.id assert campaigns[0].external_id == campaign_external_id assert campaigns[0].status == CampaignStatus.SUBMITTED assert result == SubmitCampaignResponse( status=CampaignStatus.SUBMITTED, )