// The engine's optional clinician fields (feat/engine-clinician-context) and how Context shows them. import { describe, expect, it } from "vitest"; import type { CheckRun, EngineCalculatedEgfr, EngineKidneyLimit } from "../src/api/types"; import { allergyReactionLine, calculatedEgfrLine, findingWording, kidneyLimitLine, notCheckedFor, ruleReviewLabel, testMedicines, } from "../src/logic/engine-extras"; import { draftWordingPill } from "../src/logic/pills"; import { SPECIFIC_REFERENCES } from "./fixture/engine-options"; type Finding = CheckRun["assessment"]["contextual"]["findings"][number]; const finding = (extra: Record = {}): Finding => ({ id: "f1", kind: "allergy", tier: "today", ruleId: "allergy-penicillin", medicines: [{ id: "m1", name: "Amoxicillin" }], title: "Call today: Amoxicillin and their Penicillin allergy", why: "", what_to_do: [], watch_for: [], numbers: null, evidence_label: "", source_ids: [], questions_for_doctor: [], allow_stop: false, clinical: "Amoxicillin with documented Penicillin allergy (same class).", teach_back: null, ...extra, }) as Finding; describe("clinician wording (CHK-5)", () => { it("prefers the engine's clinician wording and marks a draft", () => { const f = finding({ clinician_title: "Amoxicillin and a recorded Penicillin allergy", clinician_why: "Same class.", clinician_wording_review: "draft" }); expect(findingWording(f, undefined)).toEqual({ title: "Amoxicillin and a recorded Penicillin allergy", why: "Same class.", draft: true }); }); it("falls back to the API's wording, then the engine's clinical line", () => { const detail = { clinicianTitle: "From the API", clinicianExplanation: "API why" } as CheckRun["details"][string]; expect(findingWording(finding(), detail)).toMatchObject({ title: "From the API", why: "API why", draft: false }); expect(findingWording(finding(), undefined).why).toBe("Amoxicillin with documented Penicillin allergy (same class)."); }); it("marks wording nobody signed off: the rule's own note (derived) or the engine's", () => { for (const wording of ["derived", "engine"] as const) { const detail = { wording, clinicianTitle: "From the API", clinicianExplanation: "The rule's note" } as CheckRun["details"][string]; expect(findingWording(finding(), detail)).toMatchObject({ why: "The rule's note", draft: true }); } expect(draftWordingPill).toMatchObject({ label: "Not yet signed off for clinicians", tone: "grey" }); }); }); describe("rule review and the reaction (CHK-6a, CHK-7)", () => { it("labels unsigned rules and says nothing for a signed-off one", () => { expect(ruleReviewLabel(finding({ rule_review: "not_in_accepted_sign_off" }), true)).toBe("Rule not yet signed off for real patients"); expect(ruleReviewLabel(finding({ rule_review: "changed_since_sign_off" }), true)).toBe("Rule changed since sign-off"); expect(ruleReviewLabel(finding({ rule_review: "signed_off" }), false)).toBeNull(); expect(ruleReviewLabel(finding(), false)).toBe("Rule not yet reviewed"); }); it("says how the reaction was read and what the tier was without it", () => { const urgent = finding({ tier: "get_help_now", tier_without_reaction: "today", allergy_reaction: { allergy: "Penicillin", reaction: null, reaction_kind: null, onset: null, severity: null, year: null, reaction_class: "unknown" }, }); expect(allergyReactionLine(urgent)).toBe("Recorded reaction to Penicillin: not recorded, not known, so treated as severe. Without the reaction: Review today."); const mild = finding({ tier_without_reaction: "today", allergy_reaction: { allergy: "Penicillin", reaction: "Hives", reaction_kind: null, onset: "Within a day", severity: null, year: 2019, reaction_class: "not_severe" } }); expect(allergyReactionLine(mild)).toBe("Recorded reaction to Penicillin: hives (Within a day, in 2019), read as not severe."); expect(allergyReactionLine(finding())).toBeNull(); }); it("carries the specific references the engine branch records (FND-2)", () => { expect(SPECIFIC_REFERENCES["lab-metformin-egfr-45"][0]).toMatchObject({ sourceId: "smpc-metformin", section: expect.stringContaining("4.2") }); expect(SPECIFIC_REFERENCES["allergy-penicillin"].map((r) => r.sourceId)).toEqual(["smpc-amoxicillin", "nice-cg183"]); }); }); describe("calculated eGFR, kidney limits and monitoring (CHK-10, CHK-11, FND-7)", () => { it("labels a calculated eGFR with its inputs, or says why it was not calculated", () => { const calculated: EngineCalculatedEgfr = { status: "calculated", value: 52, value_unrounded: 51.8, unit: "mL/min/1.73m2", label: "calculated", formula: "CKD-EPI 2021", observed_at: "2026-10-20", inputs: { creatinine: { value: 1.2, unit: "mg/dL", value_mg_dl: 1.2, observed_at: "2026-10-20" }, age: 60, sex: "female" }, }; expect(calculatedEgfrLine(calculated)).toBe("eGFR 52 mL/min/1.73m2, calculated from creatinine 1.2 mg/dL (20 Oct 2026), age 60, female, with CKD-EPI 2021."); expect(calculatedEgfrLine({ status: "not_calculated", reason: "sex_not_recorded", plain: "Sex: needed to calculate eGFR from creatinine (CKD-EPI 2021).", formula: "CKD-EPI 2021" })).toContain("Sex: needed"); expect(calculatedEgfrLine(undefined)).toBeNull(); }); it("names the kidney band and the label's maximum", () => { const k: EngineKidneyLimit = { ingredient: "metformin", egfr: 38, egfr_comparator: null, egfr_observed_at: "2026-10-12", egfr_calculated: false, band: "30-44", max_daily_mg: 1000, daily_mg: 1000, source_id: "smpc-metformin", section: "4.2" }; expect(kidneyLimitLine(k)).toBe("eGFR 38, 12 Oct 2026: band 30-44 mL/min, the label's maximum in this band is 1,000 mg a day; this list adds up to 1,000 mg a day. Section 4.2."); expect(kidneyLimitLine({ ...k, egfr: 25, band: "under 30", max_daily_mg: null, egfr_calculated: true })).toContain("(calculated)"); }); it("names the medicines a test is needed with", () => { const test = { test: "Kidney function (eGFR)", why: "", when: "", source_id: "label-metformin", status: "up_to_date", last_result_at: null, medicines: [{ id: "m1", name: "Metformin" }] } as unknown as CheckRun["assessment"]["contextual"]["tests_to_ask_about"][number]; expect(testMedicines(test, [])).toEqual(["Metformin"]); expect(testMedicines({ ...test, medicines: undefined } as typeof test, ["Lisinopril"])).toEqual(["Lisinopril"]); }); }); describe("what was not checked (CHK-3)", () => { const check = (contextual: Record): CheckRun => ({ assessment: { contextual: { findings: [], ...contextual } }, notChecked: { productsWithoutRules: ["agbo"], unconfirmedFacts: 1, rulesAwaitingReview: 8, checkInAnswers: true, other: [] }, versions: { engine: "2.2.2", rules: "2026-10-15", rulesReviewed: false }, }) as unknown as CheckRun; it("uses the engine's coverage lines when it gives them", () => { const lines = notCheckedFor(check({ coverage: { lines: ["Agbo: Recorded, not checked: no rules for this product yet.", "Answers from check-ins are recorded as notes; no rule reads them."] } })); expect(lines).toEqual(["Agbo: Recorded, not checked: no rules for this product yet.", "Answers from check-ins are recorded as notes; no rule reads them."]); }); it("falls back to the API's statement and lists rules skipped as unsigned", () => { const lines = notCheckedFor(check({ signed_off_only: { accepted_sign_offs: [], skipped: [{ rule_id: "lab-potassium-raised", kind: "table", review: "not_in_accepted_sign_off", medicines: [{ id: "m", name: "Lisinopril" }] }], plain: "" } })); expect(lines[0]).toBe("agbo: recorded, not checked. No rules for this product yet."); expect(lines).toContain("Rules waiting for review, not run: lab-potassium-raised (Lisinopril)."); expect(lines.some((l) => /check-ins/.test(l))).toBe(true); }); });