120 lines
4.7 KiB
Python
120 lines
4.7 KiB
Python
import re
|
|
from pathlib import Path
|
|
|
|
|
|
MIGRATION = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "frontend"
|
|
/ "supabase"
|
|
/ "migrations"
|
|
/ "20260717020000_birth_time_journey.sql"
|
|
)
|
|
FRONTEND = Path(__file__).resolve().parents[1] / "frontend"
|
|
|
|
|
|
def _sql() -> str:
|
|
return re.sub(r"\s+", " ", MIGRATION.read_text(encoding="utf-8").lower()).strip()
|
|
|
|
|
|
def test_birth_time_profile_contract_separates_reported_and_active_times() -> None:
|
|
sql = _sql()
|
|
|
|
for definition in (
|
|
"reported_birth_time time without time zone",
|
|
"active_birth_time time without time zone",
|
|
"birth_time_source text",
|
|
"birth_time_period text",
|
|
"birth_time_clue text",
|
|
"uncertainty_before_minutes integer",
|
|
"uncertainty_after_minutes integer",
|
|
"birth_time_status text",
|
|
"rectification_confidence numeric(5, 2)",
|
|
"rectification_case_id uuid",
|
|
):
|
|
assert f"add column if not exists {definition}" in sql
|
|
|
|
assert "reported_birth_time = coalesce(reported_birth_time, birth_time)" in sql
|
|
assert "active_birth_time = coalesce(active_birth_time, birth_time)" in sql
|
|
assert "birth_time_source = coalesce(birth_time_source, 'legacy_import')" in sql
|
|
assert "birth_time_status = coalesce(birth_time_status, 'confirmed')" in sql
|
|
assert "old.reported_birth_time is not null" in sql
|
|
assert "new.reported_birth_time is distinct from old.reported_birth_time" in sql
|
|
assert "raise exception 'reported_birth_time_is_immutable'" in sql
|
|
assert "new.birth_time := new.active_birth_time" in sql
|
|
assert "revoke update (birth_time) on table public.profiles from authenticated" in sql
|
|
client_grant = sql.split("grant update ( reported_birth_time", 1)[1]
|
|
client_grant = client_grant.split(") on table public.profiles to authenticated", 1)[0]
|
|
assert "active_birth_time" not in client_grant
|
|
assert "birth_time_status" not in client_grant
|
|
assert "rectification_case_id" not in client_grant
|
|
|
|
|
|
def test_birth_time_profile_contract_constrains_deterministic_states() -> None:
|
|
sql = _sql()
|
|
|
|
for value in (
|
|
"hospital_record",
|
|
"family_exact",
|
|
"approximate",
|
|
"period_only",
|
|
"unknown",
|
|
"legacy_import",
|
|
):
|
|
assert f"'{value}'" in sql
|
|
|
|
for value in ("reported", "assessing", "rectifying", "candidate", "confirmed"):
|
|
assert f"'{value}'" in sql
|
|
|
|
assert "uncertainty_before_minutes between 0 and 720" in sql
|
|
assert "uncertainty_after_minutes between 0 and 720" in sql
|
|
assert "rectification_confidence between 0 and 100" in sql
|
|
|
|
|
|
def test_rectification_cases_are_owner_scoped_and_auditable() -> None:
|
|
sql = _sql()
|
|
|
|
assert "create table if not exists public.birth_time_rectification_cases" in sql
|
|
for definition in (
|
|
"id uuid primary key default gen_random_uuid()",
|
|
"user_id uuid not null references auth.users(id) on delete cascade",
|
|
"questionnaire jsonb not null default '{}'::jsonb",
|
|
"journey_snapshot jsonb not null default '{}'::jsonb",
|
|
"answers jsonb not null default '{}'::jsonb",
|
|
"candidate_scan jsonb not null default '{}'::jsonb",
|
|
"scoring_result jsonb not null default '{}'::jsonb",
|
|
"algorithm_version text not null default 'birth-time-journey-v1'",
|
|
"ayanamsa text not null default 'lahiri'",
|
|
"node_mode text not null default 'true'",
|
|
"confirmed_at timestamptz",
|
|
):
|
|
assert definition in sql
|
|
|
|
assert "alter table public.birth_time_rectification_cases enable row level security" in sql
|
|
assert "birth_time_cases_select_own" in sql
|
|
assert "birth_time_cases_insert_own" in sql
|
|
assert "birth_time_cases_update_own" in sql
|
|
assert "using ((select auth.uid()) = user_id)" in sql
|
|
assert "with check ((select auth.uid()) = user_id)" in sql
|
|
assert "grant select on table public.birth_time_rectification_cases to authenticated" in sql
|
|
assert "grant insert" in sql
|
|
assert "grant update" in sql
|
|
assert "grant delete" not in sql
|
|
|
|
|
|
def test_web_onboarding_uses_the_deterministic_free_journey() -> None:
|
|
page = (FRONTEND / "src" / "app" / "page.tsx").read_text(encoding="utf-8")
|
|
route = (
|
|
FRONTEND / "src" / "app" / "api" / "birth-time-journey" / "route.ts"
|
|
).read_text(encoding="utf-8")
|
|
mastra = (FRONTEND / "src" / "mastra" / "index.ts").read_text(encoding="utf-8")
|
|
|
|
assert "<BirthTimeIntakeFields" in page
|
|
assert "requestBirthTimeAssessment" in page
|
|
assert "<BirthTimeRectification" in page
|
|
assert "reported_birth_time" in page
|
|
assert "active_birth_time" in page
|
|
assert "birthTimeStatus" in page
|
|
assert "consultation-billing" not in route
|
|
assert 'entry_mode: entryMode' in mastra
|
|
assert 'entry_mode: "direct_chart"' not in mastra
|