import time from fansifter_common.adapters.sendgrid.models import FanData from app import dedup from app.adapters.redis import redis_client from app.config import settings from app.utils import hash_email from tests.unit.helpers import build_model def test_filter_unsent_returns_all_when_none_sent() -> None: fans = [ build_model(FanData, email="a@example.com"), build_model(FanData, email="b@example.com"), ] assert dedup.filter_unsent("email_1", fans) == fans def test_filter_unsent_returns_empty_list_when_input_empty() -> None: assert dedup.filter_unsent("email_1", []) == [] def test_filter_unsent_skips_already_sent() -> None: fan_1 = build_model(FanData, email="a@example.com") fan_2 = build_model(FanData, email="b@example.com") dedup.mark_sent("email_1", [fan_1]) result = dedup.filter_unsent("email_1", [fan_1, fan_2]) assert result == [fan_2] def test_filter_unsent_skips_all_when_all_sent() -> None: fans = [ build_model(FanData, email="a@example.com"), build_model(FanData, email="b@example.com"), ] dedup.mark_sent("email_1", fans) assert dedup.filter_unsent("email_1", fans) == [] def test_dedup_is_scoped_per_automated_email_id() -> None: fan = build_model(FanData, email="a@example.com") dedup.mark_sent("email_1", [fan]) result = dedup.filter_unsent("email_2", [fan]) assert result == [fan] def test_mark_sent_sets_ttl() -> None: fan = build_model(FanData, email="a@example.com") dedup.mark_sent("email_1", [fan]) key = dedup.make_key("email_1", hash_email(fan.email)) ttl = int(redis_client.ttl(key)) # ty: ignore[invalid-argument-type] assert 0 < ttl <= settings.redis_dedup_ttl def test_each_fan_has_independent_ttl() -> None: fan_1 = build_model(FanData, email="a@example.com") fan_2 = build_model(FanData, email="b@example.com") dedup.mark_sent("email_ttl", [fan_1]) time.sleep(1) dedup.mark_sent("email_ttl", [fan_2]) key_1 = dedup.make_key("email_ttl", hash_email(fan_1.email)) key_2 = dedup.make_key("email_ttl", hash_email(fan_2.email)) ttl_1 = int(redis_client.ttl(key_1)) # ty: ignore[invalid-argument-type] ttl_2 = int(redis_client.ttl(key_2)) # ty: ignore[invalid-argument-type] # Each fan's TTL is independent; fan_1 was marked 1s earlier so has less time left assert 0 < ttl_1 <= settings.redis_dedup_ttl assert 0 < ttl_2 <= settings.redis_dedup_ttl assert ttl_1 < ttl_2