"""ExtractedIntake → (PatientProfile, StructuredIntake) bridge and its safety guards.""" from __future__ import annotations import pytest from pydantic import ValidationError from app import CHECK_MAP from interview_schema import ( INTAKE_BOOL_FIELDS, ExtractedIntake, extracted_to_intake, intake_json_schema, next_question_json_schema, ) from schema import StructuredIntake def test_bool_fields_match_structured_intake_and_check_map(): """The bridge must cover exactly the StructuredIntake booleans the UI exposes.""" assert set(INTAKE_BOOL_FIELDS) == set(CHECK_MAP.values()) for field in INTAKE_BOOL_FIELDS: assert field in StructuredIntake.model_fields, field assert field in ExtractedIntake.model_fields, field def test_full_round_trip_into_intake(): extracted = ExtractedIntake( chief_concern="Crown feels high", tooth_or_area="Upper left molar", recent_dental_work="Root canal and crown", symptom_duration="Three weeks", pain_score=6, biting_pain=True, hot_cold_sensitivity=True, age=38, meds="Ibuprofen", allergies="Penicillin rash", goals="Understand the crown fit", language="Bilingual", ) profile, intake = extracted_to_intake(extracted) assert intake.chief_concern == "Crown feels high" assert intake.biting_pain is True assert intake.hot_cold_sensitivity is True assert intake.pain_score == 6 assert profile.age == 38 assert profile.meds == "Ibuprofen" assert profile.language == "Bilingual" def test_pain_score_and_age_are_bounded(): with pytest.raises(ValidationError): ExtractedIntake(pain_score=55) with pytest.raises(ValidationError): ExtractedIntake(age=999) def test_diagnosis_language_in_extracted_text_is_blanked(): """The extractor is model output: a diagnosis-flavored string must not flow into the intake as if the patient had said it.""" extracted = ExtractedIntake( chief_concern="Patient has an abscess and needs a root canal", tooth_or_area="Upper left molar", ) _profile, intake = extracted_to_intake(extracted) assert intake.chief_concern == "" assert intake.tooth_or_area == "Upper left molar" def test_unknown_extractor_keys_are_ignored(): extracted = ExtractedIntake.model_validate( {"chief_concern": "Crown feels high", "made_up_field": "x"} ) assert extracted.chief_concern == "Crown feels high" def test_intake_schema_is_strict_for_xgrammar(): schema = intake_json_schema() assert schema["additionalProperties"] is False assert "pain_score" in schema["properties"] for field in INTAKE_BOOL_FIELDS: assert field in schema["properties"] def test_next_question_schema_is_strict(): schema = next_question_json_schema() assert schema["additionalProperties"] is False assert "question" in schema["properties"] assert "covered_axes" in schema["properties"] assert "covered_details" in schema["properties"]