import datetime from datetime import timedelta import pytest from fansifter_common.utils import timezone from dmp.audiences.enums import ( AudienceExportFormat, AudienceExportJustification, AudienceExportReason, AudienceExportStatus, AudienceSharePlatform, ) from dmp.audiences.exceptions import AudienceExportInvalidStatusError from dmp.audiences.filters import AudienceFilters, FanSegmentInFilter, Operator from dmp.audiences.models import ( Audience, AudienceExport, AudienceShare, AudienceSnapshot, ) from dmp.fandata.enums import FanSegment from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel, CreateModel class TestAudience: @pytest.mark.db def test_filters_spend(self, create_model: CreateModel) -> None: spend_min = 10.0 spend_max = 10000.0 audience = create_model( Audience, filters=AudienceFilters.model_construct( spend_min=spend_min, spend_max=spend_max, ), ) assert audience.filters.spend_min == spend_min assert audience.filters.spend_max == spend_max @pytest.mark.parametrize( ("fan_segments", "expected"), [ (None, []), ( FanSegmentInFilter( operator=Operator.INCLUDE, value=[FanSegment.SUPER_FANS, FanSegment.NEW_FANS], ), [FanSegment.SUPER_FANS, FanSegment.NEW_FANS], ), ( FanSegmentInFilter( operator=Operator.EXCLUDE, value=[FanSegment.FANS_TO_WIN_BACK], ), [ FanSegment.SUPER_FANS, FanSegment.ENGAGED_FANS, FanSegment.CASUAL_FANS, FanSegment.NEW_FANS, FanSegment.SECONDARY_FANS, ], ), ], ) def test_fan_segments( self, build_model: BuildModel, fan_segments: FanSegmentInFilter | None, expected: list[FanSegment], ) -> None: audience = build_model( Audience, filters=AudienceFilters.model_construct(fan_segments=fan_segments), ) assert audience.fan_segments == expected def test_latest_snapshot(self, build_model: BuildModel) -> None: audience = build_model(Audience) dt_now = timezone.now() snapshot_1 = build_model( AudienceSnapshot, audience=audience, created_at=dt_now + timedelta(seconds=10), ) snapshot_2 = build_model( AudienceSnapshot, audience=audience, created_at=dt_now - timedelta(seconds=10), ) snapshot_3 = build_model( AudienceSnapshot, audience=audience, created_at=dt_now, ) audience.snapshots = [snapshot_3, snapshot_1, snapshot_2] assert audience.latest_snapshot == snapshot_1 def test_export_key(self, build_model: BuildModel, fake: FakerTyped) -> None: audience_name = "Test Audience" audience = build_model(Audience, name=audience_name) snapshot_created_at = datetime.datetime( 2021, 1, 1, 0, 0, 0, tzinfo=timezone.UTC ) snapshot = build_model( AudienceSnapshot, audience=audience, created_at=snapshot_created_at ) export_id = "abc123" export_format = AudienceExportFormat.CSV reason = AudienceExportReason.ADS_META export = AudienceExport( id=export_id, audience=audience, snapshot=snapshot, format=export_format, reason=reason, justification=AudienceExportJustification.RETARGETING, created_by=fake.uuid4_string(), ) assert export.key == "raw/abc123/test_audience_META_2021_01_01_00_00_00.csv" class TestAudienceExport: @pytest.mark.parametrize( ("status", "new_status"), [ (AudienceExportStatus.PENDING, AudienceExportStatus.PENDING), (AudienceExportStatus.PENDING, AudienceExportStatus.PROCESSING), (AudienceExportStatus.PROCESSING, AudienceExportStatus.FAILED), (AudienceExportStatus.PROCESSING, AudienceExportStatus.COMPLETED), (AudienceExportStatus.COMPLETED, AudienceExportStatus.DELETED), ], ) def test_update_status( self, status: AudienceExportStatus, new_status: AudienceExportStatus, build_model: BuildModel, ) -> None: export = build_model(AudienceExport, status=status) export.update_status(new_status) assert export.status == new_status def test_update_final_status_error(self, build_model: BuildModel) -> None: export = build_model(AudienceExport, status=AudienceExportStatus.FAILED) with pytest.raises(AudienceExportInvalidStatusError) as exc_info: export.update_status(AudienceExportStatus.COMPLETED) assert exc_info.value.message == "`FAILED` status cannot be changed." assert exc_info.value.status_code == 400 def test_update_status_error(self, build_model: BuildModel) -> None: export = build_model(AudienceExport, status=AudienceExportStatus.PENDING) with pytest.raises(AudienceExportInvalidStatusError) as exc_info: export.update_status(AudienceExportStatus.COMPLETED) assert exc_info.value.message == "`PENDING` cannot be changed to `COMPLETED`." assert exc_info.value.status_code == 400 class TestAudienceSnapshot: def test_key(self, build_model: BuildModel) -> None: audience_name = "Test Audience" audience = build_model(Audience, name=audience_name) snapshot_created_at = datetime.datetime( 2021, 1, 1, 0, 0, 0, tzinfo=timezone.UTC ) snapshot = build_model( AudienceSnapshot, audience=audience, created_at=snapshot_created_at ) share_id = "test123" share = build_model( AudienceShare, id=share_id, audience=audience, snapshot=snapshot, platform=AudienceSharePlatform.META, ) assert share.key == "shares/test123/test_audience_2021_01_01_00_00_00.csv"