"""Synthetic exhaustive scoring and calendar/diary boundary checks.""" from datetime import date, timedelta from itertools import product import pytest from app.core.intake_runtime import diary_flags, due_scales, load_runtime, month_before from app.core.questionnaire import IntakePolicy, alcohol_expansion, load_policy, score_alcohol from app.core.scales import score def test_release_is_closed(): assert not load_runtime().released assert load_policy().approval is None def test_all_tsq_patterns(): for answers in product((0, 1), repeat=10): result = score("tsq", {f"tsq_{i + 1}": v for i, v in enumerate(answers)}) assert result.complete and result.total == sum(answers) assert (result.severity_code == "positive") == (sum(answers) >= 6) @pytest.mark.parametrize( "day,expected", [ (date(2024, 3, 31), date(2024, 2, 29)), (date(2025, 3, 31), date(2025, 2, 28)), (date(2026, 1, 1), date(2025, 12, 1)), ], ) def test_calendar_month(day, expected): assert month_before(day) == expected def test_medication_schedule_overlapping_and_delayed_completion(): start = date(2026, 1, 1) last = {} for day in (6, 7, 13, 14, 27, 28, 55, 56): due = due_scales( today=start + timedelta(days=day), enrolled=start, last=last, route="self_help", medication_changed=start ) assert ("phq9" in due) == (day in (7, 14, 28, 56)) if "phq9" in due: last.update(phq9=start + timedelta(days=day), pcl5=start + timedelta(days=day)) # A missed tick yields one form, not each historical milestone. assert ( due_scales( today=start + timedelta(days=80), enrolled=start, last={}, route="self_help", medication_changed=start ).count("phq9") == 1 ) assert "phq9" in due_scales( today=start + timedelta(days=7), enrolled=start, last={}, route="specialist", after_crisis=start ) def norm_and_week(norm: dict, week: dict, today=date(2026, 10, 30)): """14 days of personal norm starting 27 days ago, then the last 7 days with other values.""" start = today - timedelta(days=27) history = {start + timedelta(days=i): dict(norm) for i in range(14)} history.update({today - timedelta(days=i): dict(week) for i in range(7)}) return history, today def test_weekly_mood_against_personal_norm(): history, today = norm_and_week({"mood": 8}, {"mood": 5}) assert "phq9_due_mood" in diary_flags(history, today) history, today = norm_and_week({"mood": 8}, {"mood": 6}) assert "phq9_due_mood" not in diary_flags(history, today) # Inside the norm window there is no trend yet. early = {date(2026, 10, 1) + timedelta(days=i): {"mood": 9 if i < 3 else 1} for i in range(10)} assert "phq9_due_mood" not in diary_flags(early, date(2026, 10, 10)) def test_module_trends_bring_full_scales_forward(): ptsd = {"dpcl_1": 1, "dpcl_7": 1, "dpcl_10": 1, "dpcl_18": 1} history, today = norm_and_week(ptsd, {k: 2 for k in ptsd}) assert "pcl5_due_ptsd" in diary_flags(history, today) history, today = norm_and_week({"dphq_4": 0}, {"dphq_4": 1}) assert "phq9_due_depression" in diary_flags(history, today) def test_sleep_alcohol_and_medication_flags(): today = date(2026, 10, 3) nights = {today - timedelta(days=i): {"sleep_hours": 7, "sleep_quality": 0} for i in range(3)} nights[today - timedelta(days=1)] = {"sleep_hours": 3.5, "sleep_quality": 2} assert "sleep_review" in diary_flags(nights, today) nights[today - timedelta(days=1)]["sleep_hours"] = 4 assert "sleep_review" not in diary_flags(nights, today) drinks = {today: {"alcohol_portions": 6}, today - timedelta(days=2): {"alcohol_portions": 7}} assert set(diary_flags(drinks, today, medication_active=True)) >= {"alcohol_on_medication", "rus_audit_due_alcohol"} assert "alcohol_on_medication" not in diary_flags(drinks, today) missed = {today - timedelta(days=i): {"medication": "no"} for i in range(3)} assert "medication_contact" in diary_flags(missed, today) week = {today - timedelta(days=i): {"medication": "yes" if i % 3 else "no"} for i in range(7)} assert "adherence_review" in diary_flags(week, today) assert diary_flags({today: {"medication": "no", "medication_reason": "side_effects"}}, today) == ( "side_effects_review", ) def test_total_threshold_and_zone_are_separate_decisions(): data = load_policy().model_dump() data.update(cssrs_positive_policy="graded", rus_audit_s_expand_at=1) data["approval"] = dict( reviewer="Synthetic fixture", decision_reference="test", approved_at="2026-10-03T00:00:00Z", policy_version=data["version"], ) policy = IntakePolicy.model_validate(data) r = score_alcohol({"rus_audit_3": 1, "rus_audit_9": 0, "rus_audit_10": 0}, short=True, sex="m") assert r.zone == 1 and alcohol_expansion(r, policy) with pytest.raises(ValueError): IntakePolicy.model_validate({**data, "rus_audit_s_expand_from_zone": 2}) def test_diary_parts_and_module_rotation(): from app.core.intake_questions import diary_questions assert [q.id for q in diary_questions("morning", ["veteran", "depression"])] == [ "sleep_hours", "sleep_quality", "alcohol_portions", ] evening = [q.id for q in diary_questions("evening", ["veteran", "depression"], day_index=0)] assert evening == ["mood", "medication", "medication_reason", "dphq_1", "dphq_2", "dphq_3"] + [ "dpcl_1", "dpcl_7", "dpcl_10", "dpcl_18", ] assert [q.id for q in diary_questions("evening", ["depression"], day_index=5)][-3:] == [ "dphq_7", "dphq_8", "dphq_9", ] assert [q.id for q in diary_questions("evening", [], day_index=1)] == ["mood", "medication", "medication_reason"] def test_demo_policy_is_separate_and_marked(monkeypatch): from app.config import get_settings monkeypatch.setenv("INTAKE_DEMO", "1") monkeypatch.setattr(get_settings(), "demo_clock", True) demo = load_policy() assert demo.approval and "DEMO" in demo.approval.reviewer and demo.cssrs_positive_policy == "graded" monkeypatch.delenv("INTAKE_DEMO") assert load_policy().approval is None def test_full_scale_change_against_the_start_of_observation(): from app.core.interpretation import scale_interpretation def phq(total, baseline, previous=None): totals = {"phq9": {"complete": True, "total": total}} return scale_interpretation( "phq9", totals, baseline=baseline, previous=previous, policy=load_policy(), retriage_delta=5 )["codes"] assert phq(4, 16) == ["minimal", "remission"] assert phq(8, 16) == ["mild", "response"] assert phq(11, 16) == ["moderate", "improved"] assert phq(15, 16) == ["moderately_severe", "stable"] assert phq(21, 10, previous=16) == ["severe", "worse"] pcl = scale_interpretation( "pcl5", {"pcl5": {"complete": True, "total": 27}}, baseline=45, previous=40, policy=load_policy(), retriage_delta=10, ) assert pcl["codes"] == ["below", "response"]