import urllib.parse from datetime import datetime import freezegun from fansifter_common.adapters.sendgrid.models import MergeTags from fansifter_common.identifiers.types import DoubleOptInToken from fansifter_common.identifiers.utils import decrypt_double_opt_in_identifier from app.adapters.preferences import encrypter from app.fandata import get_fan_data from app.types import CsvRow from tests.unit.helpers import build_model CAMPAIGN_ID = "campaign_001" FROZEN_TIME = "2026-03-04T10:30:00Z" def test_get_fan_data_doi_send_sets_double_opt_in_url() -> None: row = build_model( CsvRow, is_doi_send=True, confirmation_id="confirmation_123", fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.double_opt_in_url is not None assert "/double-opt-in?token=" in fan_data.double_opt_in_url def test_get_fan_data_non_doi_send_has_no_double_opt_in_url() -> None: row = build_model( CsvRow, is_doi_send=False, confirmation_id=None, fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.double_opt_in_url is None @freezegun.freeze_time(FROZEN_TIME) def test_get_fan_data_doi_url_round_trip() -> None: row = build_model( CsvRow, is_doi_send=True, confirmation_id="conf_456", automated_email_trigger_id="trigger_001", fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.double_opt_in_url is not None token_encoded = fan_data.double_opt_in_url.split("/double-opt-in?token=")[1] token = urllib.parse.unquote(token_encoded) identifier = decrypt_double_opt_in_identifier( encrypter, token=DoubleOptInToken(token) ) assert identifier.event_row_id == "conf_456" assert identifier.email_id == CAMPAIGN_ID assert identifier.automated_email_trigger_id == "trigger_001" assert identifier.iat == datetime.fromisoformat(FROZEN_TIME).timestamp() def test_get_fan_data_missing_profile_id_returns_none_profile_token() -> None: row = build_model( CsvRow, fan_profile_id=None, fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.profile_token is None def test_get_fan_data_empty_profile_id_returns_none_profile_token() -> None: row = build_model( CsvRow, fan_profile_id="", fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.profile_token is None def test_get_fan_data_valid_profile_id_returns_encrypted_token() -> None: row = build_model( CsvRow, fan_profile_id="profile_123", fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.profile_token is not None assert len(fan_data.profile_token) > 0 def test_get_fan_data_doi_send_without_confirmation_id_has_no_url() -> None: row = build_model( CsvRow, is_doi_send=True, confirmation_id=None, fan_email="fan@sonymusic-pde.com", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.double_opt_in_url is None def test_get_fan_data_de_fan_has_profile_text() -> None: row = build_model( CsvRow, fan_email="fan@sonymusic-pde.com", fan_country_code="DE", is_doi_send=False, confirmation_id=None, ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.profile_text == "Deine Daten verwalten" def test_get_fan_data_non_de_fan_has_no_profile_text() -> None: row = build_model( CsvRow, fan_email="fan@sonymusic-pde.com", fan_country_code="US", is_doi_send=False, confirmation_id=None, ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.profile_text is None def test_get_fan_data_propagates_first_name_merge_tag() -> None: row = build_model( CsvRow, fan_email="fan@sonymusic-pde.com", fan_first_name="Anna", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.merge_tags == MergeTags(first_name="Anna") def test_get_fan_data_with_empty_first_name_falls_back_to_default() -> None: row = build_model( CsvRow, fan_email="fan@sonymusic-pde.com", fan_first_name="", ) fan_data = get_fan_data(row, CAMPAIGN_ID) assert fan_data is not None assert fan_data.merge_tags == MergeTags(first_name="") assert fan_data.merge_tags.to_substitutions() == {"-firstName-": "Fan"}