from __future__ import annotations import random from datetime import UTC, datetime, timedelta import pytest from app.domain import ( ContactCandidate, WorkflowState, assert_transition, choose_newest, full_jitter_delay, select_email, validate_phone, ) from app.mapping import CitizenshipDictionary, UnknownCitizenship def test_contact_choice_has_numeric_id_tie_breaker() -> None: created = datetime(2026, 8, 6, tzinfo=UTC) selected = choose_newest( [ ContactCandidate("9", created, None), ContactCandidate("10", created, None), ] ) assert selected and selected.b24_id == "10" def test_phone_email_and_citizenship_mapping() -> None: assert validate_phone("+79001234567") == "+79001234567" with pytest.raises(ValueError): validate_phone("8 900 123-45-67") assert ( select_email( [ {"VALUE": "home@example.test", "VALUE_TYPE": "HOME"}, {"VALUE": "work@example.test", "VALUE_TYPE": "WORK"}, ] ) == "work@example.test" ) dictionary = CitizenshipDictionary(ttl_seconds=60) now = datetime(2026, 8, 6, tzinfo=UTC) dictionary.load([{"ID": "7", "VALUE": "Казахстан"}], now) assert dictionary.resolve("7", now + timedelta(seconds=30)) == "Казахстан" with pytest.raises(UnknownCitizenship): dictionary.resolve("8", now) def test_state_machine_and_retry_bounds() -> None: assert_transition(WorkflowState.CREATED, WorkflowState.RUNNING) with pytest.raises(ValueError): assert_transition(WorkflowState.SUCCEEDED, WorkflowState.RUNNING) delay = full_jitter_delay(4, 1, 5, rng=random.Random(1)) assert 0 <= delay <= 5