import { describe, expect, it } from "vitest";
import { parseCompactDraft } from "../server/compact";
import { assess } from "../server/context";
import type { Draft, Review, Source } from "../src/types";
// Synthetic, deterministic fault injection. These tests measure the review gate,
// not the rate at which any live model omits medication information.
const variants = [
{ name: "and-separated medicines", text: "She takes Warfarin 5 mg and Ibuprofen 200 mg.", ibuprofenRef: 2 },
{ name: "comma-separated medicines", text: "She takes Warfarin 5 mg, Ibuprofen 200 mg.", ibuprofenRef: 2 },
{ name: "semicolon-separated medicines", text: "She takes Warfarin 5 mg; she also takes Ibuprofen 200 mg.", ibuprofenRef: 2 },
{ name: "reversed medicine order", text: "She takes Ibuprofen 200 mg and Warfarin 5 mg.", ibuprofenRef: 2 },
{ name: "medicine on a separate line", text: "She takes Warfarin 5 mg.\nShe also takes Ibuprofen 200 mg.", ibuprofenRef: 3 },
];
function extraction(variant: (typeof variants)[number], complete: boolean) {
const sources: Source[] = [{
id: "synthetic-note",
title: "Synthetic omission regression",
kind: "Synthetic",
date: "2026-10-29",
text: "Patient: Amina Stone\n" + variant.text,
}];
const facts: [number, string][] = [[2, "Medication: Warfarin; strength: 5 mg"]];
if (complete) facts.push([variant.ibuprofenRef, "Medication: Ibuprofen; strength: 200 mg"]);
const draft = parseCompactDraft({ facts }, sources, "simulated-response", 0);
const review: Review = Object.fromEntries(draft.facts.map((fact) => [fact.id, "include"]));
return { sources, draft, review };
}
function check(input: { sources: Source[]; draft: Draft; review: Review }, acknowledged: boolean) {
return assess(input.sources, input.review, input.draft.fingerprint, acknowledged, input.draft);
}
describe("medication omission safety contract", () => {
it.each(variants)("complete extraction retains the interaction: $name", (variant) => {
const input = extraction(variant, true);
expect(input.draft.facts.filter((fact) => fact.kind === "medicine")).toHaveLength(2);
expect(input.draft.warnings).toHaveLength(0);
const finding = check(input, true).contextual.findings.find((item) => item.ruleId === "vka-nsaid");
expect(finding).toBeDefined();
expect(finding!.medicines.map((medicine) => medicine.id).sort()).toEqual(
input.draft.facts.filter((fact) => fact.kind === "medicine").map((fact) => fact.id).sort(),
);
});
it.each(variants)("existing acknowledgement gate remains enforced: $name", (variant) => {
const input = extraction(variant, false);
expect(input.draft.facts.filter((fact) => fact.kind === "medicine")).toHaveLength(1);
expect(input.draft.sourceReviewRequired).toBe(true);
expect(() => check(input, false)).toThrow("Review the original notes for omissions");
});
// Generic acknowledgement cannot resolve a detected unmatched medication.
it.each(variants)("unresolved omission blocks assessment after acknowledgement: $name", (variant) => {
const input = extraction(variant, false);
expect(() => check(input, true)).toThrow(/omission|coverage|unresolved medication/i);
});
});