from unittest import mock import pytest from freezegun import freeze_time from dmp.adapters.google.exceptions import GoogleClientError from dmp.adapters.google.models import Audience as GoogleAudience, User from dmp.audiences.enums import ( AudienceExportJustification, AudienceExportStatus, AudienceSharePlatform, ) from dmp.audiences.exceptions import ( AudienceShareActiveConnectionNotFoundError, AudienceTooSmallError, EmptyAudienceError, ) from dmp.audiences.models import Audience, AudienceSnapshot from dmp.google.exceptions import InvalidGoogleAdAccountId from dmp.google.handlers import ShareGoogleAudienceHandler, ShareGoogleAudienceRequest from dmp.google.models import ( GoogleAdAccount, GoogleUserAdAccount, GoogleUserConnection, GoogleUserConnectionAdAccount, ) from dmp.google.repositories import ( GoogleUserConnectionRepository, ) from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel, CreateModel @pytest.fixture(autouse=True) def mock_google_client( google_client_mock: mock.MagicMock, build_model: BuildModel ) -> None: google_client_mock.get_user.return_value = build_model(User) class TestShareGoogleAudienceHandler: @pytest.mark.db @freeze_time("2022-07-20") def test_share_audience( self, handler: ShareGoogleAudienceHandler, create_model: CreateModel, identity_id: str, build_model: BuildModel, google_client_mock: mock.MagicMock, fake: FakerTyped, ) -> None: justification = AudienceExportJustification.OTHER reason_notes = "test" ad_account = create_model(GoogleAdAccount) audience = create_model(Audience) snapshot = create_model(AudienceSnapshot, audience=audience, fan_count=1001) user_connection = create_model( GoogleUserConnection, is_valid=True, identity_id=identity_id, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=ad_account), ], ) create_model( GoogleUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) api_audience = build_model( GoogleAudience, resource_name=f"{fake.pystr()}/{fake.pystr()}" ) google_client_mock.create_user_list.return_value = api_audience google_audience = handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=ad_account.id, justification=justification, reason_notes=reason_notes, ) ) assert google_audience.audience_id == snapshot.audience_id assert google_audience.external_id == api_audience.id assert google_audience.snapshot_id == snapshot.id assert ( google_audience.name == f"[FS] {audience.name} - 2022-Jul-20 00:00:00 UTC" ) assert google_audience.ad_account_id == ad_account.id assert google_audience.share.status == AudienceExportStatus.PENDING assert google_audience.share.platform == AudienceSharePlatform.GOOGLE assert google_audience.share.justification == justification assert google_audience.share.reason_notes == reason_notes assert google_audience.share.created_by == identity_id @pytest.mark.db def test_share_audience_no_active_user_connection( self, handler: ShareGoogleAudienceHandler, create_model: CreateModel, identity_id: str, ) -> None: ad_account = create_model(GoogleAdAccount) audience = create_model(Audience) snapshot = create_model(AudienceSnapshot, audience=audience, fan_count=1001) user_connection = create_model( GoogleUserConnection, is_valid=False, identity_id=identity_id, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=ad_account), ], ) create_model( GoogleUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) with pytest.raises(AudienceShareActiveConnectionNotFoundError) as exc_info: handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=ad_account.id, justification=AudienceExportJustification.OTHER, reason_notes="test", ) ) assert exc_info.value.code == "audience_share_active_connection_not_found" assert ( exc_info.value.message == "Active connection for Audience share not found" ) assert exc_info.value.status_code == 404 @pytest.mark.db def test_share_audience_no_user_ad_account( self, handler: ShareGoogleAudienceHandler, create_model: CreateModel, identity_id: str, fake: FakerTyped, ) -> None: snapshot = create_model(AudienceSnapshot, fan_count=1001) with pytest.raises(InvalidGoogleAdAccountId): handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=fake.uuid4_string(), justification=AudienceExportJustification.OTHER, reason_notes="test", ) ) @pytest.mark.db def test_share_audience_without_snapshot( self, handler: ShareGoogleAudienceHandler, create_model: CreateModel, identity_id: str, fake: FakerTyped, ) -> None: ad_account_id = fake.pystr() audience = create_model(Audience) with pytest.raises(EmptyAudienceError): handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=audience.id, ad_account_id=ad_account_id, justification=AudienceExportJustification.OTHER, reason_notes="test", ) ) @pytest.mark.db def test_share_audience_with_zero_fans( self, handler: ShareGoogleAudienceHandler, create_model: CreateModel, identity_id: str, fake: FakerTyped, ) -> None: ad_account_id = fake.pystr() snapshot = create_model(AudienceSnapshot, fan_count=0) with pytest.raises(EmptyAudienceError): handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=ad_account_id, justification=AudienceExportJustification.OTHER, reason_notes="test", ) ) @pytest.mark.db def test_share_audience_get_user_error( self, handler: ShareGoogleAudienceHandler, google_client_mock: mock.MagicMock, create_model: CreateModel, google_user_connection_repository: GoogleUserConnectionRepository, identity_id: str, ) -> None: justification = AudienceExportJustification.OTHER reason_notes = "test" ad_account = create_model(GoogleAdAccount) audience = create_model(Audience) snapshot = create_model(AudienceSnapshot, audience=audience, fan_count=1001) user_connection = create_model( GoogleUserConnection, is_valid=True, identity_id=identity_id, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=ad_account), ], ) create_model( GoogleUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) google_client_mock.get_user.side_effect = GoogleClientError with pytest.raises(AudienceShareActiveConnectionNotFoundError) as exc_info: handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=ad_account.id, justification=justification, reason_notes=reason_notes, ) ) assert exc_info.value.status_code == 404 user_connections = google_user_connection_repository.all() user_connection = user_connections[0] assert not user_connection.is_valid @pytest.mark.db def test_share_audience_with_small_audience( self, handler: ShareGoogleAudienceHandler, google_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, identity_id: str, ) -> None: justification = AudienceExportJustification.OTHER reason_notes = "test" ad_account = create_model(GoogleAdAccount) audience = create_model(Audience) snapshot = create_model(AudienceSnapshot, audience=audience, fan_count=999) user_connection = create_model( GoogleUserConnection, is_valid=True, identity_id=identity_id, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=ad_account), ], ) create_model( GoogleUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) google_client_mock.get_user.return_value = build_model(User) with pytest.raises(AudienceTooSmallError): handler.handle( ShareGoogleAudienceRequest( identity_id=identity_id, audience_id=snapshot.audience_id, ad_account_id=ad_account.id, justification=justification, reason_notes=reason_notes, ) )