93 lines
3.6 KiB
Python
93 lines
3.6 KiB
Python
import copy
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from jsonschema import Draft202012Validator
|
|
except ModuleNotFoundError: # pragma: no cover - optional test helper dependency
|
|
Draft202012Validator = None
|
|
|
|
from app.services.case_worksheet_rubric import load_rubric, validate_rubric
|
|
from app.session_read_model import case_worksheet_template_item_keys
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[3]
|
|
RUBRIC_PATH = REPO_ROOT / "data" / "rubrics" / "case-worksheet-rubric.json"
|
|
SCHEMA_PATH = REPO_ROOT / "data" / "rubrics" / "case-worksheet-rubric.schema.json"
|
|
SCRIPT_PATH = REPO_ROOT / "scripts" / "check-case-worksheet-rubric.py"
|
|
|
|
|
|
class CaseWorksheetRubricTests(unittest.TestCase):
|
|
def test_scaffold_matches_generated_worksheet_keys_without_enabling_scoring(self) -> None:
|
|
rubric = load_rubric(RUBRIC_PATH)
|
|
report = validate_rubric(rubric, expected_item_keys=case_worksheet_template_item_keys())
|
|
|
|
self.assertTrue(report["passed"], report)
|
|
self.assertEqual(report["schema_version"], "vignette.case_worksheet_rubric.v1")
|
|
self.assertEqual(report["status"], "scaffold_only")
|
|
self.assertFalse(report["scoring_enabled"])
|
|
self.assertEqual(report["sections_total"], 5)
|
|
self.assertEqual(report["items_total"], 28)
|
|
self.assertGreaterEqual(len(report["warnings"]), 20)
|
|
self._validate_with_schema(rubric, SCHEMA_PATH)
|
|
|
|
def test_scoring_requires_clinical_approval(self) -> None:
|
|
rubric = load_rubric(RUBRIC_PATH)
|
|
draft = copy.deepcopy(rubric)
|
|
draft["status"] = "draft"
|
|
draft["scoring_enabled"] = True
|
|
|
|
report = validate_rubric(draft, expected_item_keys=case_worksheet_template_item_keys())
|
|
|
|
self.assertFalse(report["passed"])
|
|
self.assertIn("scoring_enabled requires status=approved", report["errors"])
|
|
|
|
def test_missing_worksheet_item_fails_validation(self) -> None:
|
|
rubric = load_rubric(RUBRIC_PATH)
|
|
broken = copy.deepcopy(rubric)
|
|
broken["sections"][0]["items"] = broken["sections"][0]["items"][1:]
|
|
|
|
report = validate_rubric(broken, expected_item_keys=case_worksheet_template_item_keys())
|
|
|
|
self.assertFalse(report["passed"])
|
|
self.assertIn("exploration_11: missing worksheet item: presenting_complaint", report["errors"])
|
|
|
|
def test_cli_reports_json(self) -> None:
|
|
completed = subprocess.run(
|
|
[
|
|
sys.executable,
|
|
"-X",
|
|
"utf8",
|
|
str(SCRIPT_PATH),
|
|
"--rubric",
|
|
str(RUBRIC_PATH),
|
|
"--json",
|
|
],
|
|
cwd=str(REPO_ROOT),
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
)
|
|
|
|
report = json.loads(completed.stdout)
|
|
self.assertTrue(report["passed"], report)
|
|
self.assertEqual(report["items_total"], 28)
|
|
self.assertFalse(report["scoring_enabled"])
|
|
self.assertEqual(report["rubric_path"], "data/rubrics/case-worksheet-rubric.json")
|
|
self.assertRegex(report["content_sha256"], r"^[a-f0-9]{64}$")
|
|
|
|
def _validate_with_schema(self, instance: object, schema_path: Path) -> None:
|
|
schema = json.loads(schema_path.read_text(encoding="utf-8"))
|
|
self.assertEqual(schema.get("$schema"), "https://json-schema.org/draft/2020-12/schema")
|
|
if Draft202012Validator is None:
|
|
return
|
|
Draft202012Validator.check_schema(schema)
|
|
Draft202012Validator(schema).validate(instance)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|