| """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"] |
|
|