import fs from "node:fs/promises";
const origin = "http://127.0.0.1:4191";
async function req(path, body, headers = {}) {
const r = await fetch(origin + "/api/" + path, {
method: body === undefined ? "GET" : "POST",
headers: {
...(body === undefined ? {} : { "Content-Type": "application/json" }),
...headers,
},
body:
body === undefined
? undefined
: typeof body === "string"
? body
: JSON.stringify(body),
});
return { status: r.status, body: await r.json() };
}
const cases = (await req("cases")).body;
const sources = cases[0].sources;
const results = [];
async function check(name, fn) {
try {
results.push({ name, passed: Boolean(await fn()) });
} catch (e) {
results.push({ name, passed: false, error: e.message });
}
}
await check("Status exposes no credential fields", async () => {
const r = await req("status");
return (
r.status === 200 &&
!/"(?:key|apiKey|api_key|endpoint|authorization)"/i.test(
JSON.stringify(r.body),
)
);
});
await check(
"Unrelated web origins cannot call local APIs",
async () =>
(
await req(
"extract",
{ sources, syntheticOnly: true },
{ Origin: "https://unrelated.example" },
)
).status === 403,
);
await check(
"Real-data acknowledgement rejected",
async () =>
(await req("extract", { sources, syntheticOnly: false })).status === 400,
);
await check(
"Non-JSON bodies rejected",
async () =>
(await req("extract", "plain text", { "Content-Type": "text/plain" }))
.status === 415,
);
await check(
"Malformed JSON rejected",
async () => (await req("extract", "{")).status === 400,
);
await check(
"Duplicate source IDs rejected",
async () =>
(
await req("extract", {
sources: [sources[0], sources[0]],
syntheticOnly: true,
})
).status === 400,
);
await check(
"Too many source records rejected",
async () =>
(
await req("extract", {
sources: Array.from({ length: 9 }, (_, i) => ({
...sources[0],
id: "record-" + i,
})),
syntheticOnly: true,
})
).status === 400,
);
const draft = (
await req("extract", { sources, syntheticOnly: true, useModel: false })
).body;
const review = Object.fromEntries(draft.facts.map((f) => [f.id, "include"]));
const payload = {
sources,
syntheticOnly: true,
draftId: draft.id,
review,
fingerprint: draft.fingerprint,
acknowledgeUnparsed: false,
};
await check(
"Unknown draft IDs rejected",
async () =>
(
await req("assess", {
...payload,
draftId: "00000000-0000-4000-8000-000000000000",
})
).status === 400,
);
await check(
"Unreviewed facts rejected",
async () => (await req("assess", { ...payload, review: {} })).status === 400,
);
await check(
"Client-injected facts rejected",
async () =>
(
await req("assess", {
...payload,
review: { ...review, invented: "include" },
})
).status === 400,
);
await check(
"Source edits invalidate assessment",
async () =>
(
await req("assess", {
...payload,
sources: sources.map((s) => ({ ...s, text: s.text + "\nAge: 99" })),
})
).status === 400,
);
const assessment = await req("assess", payload);
await check(
"Local assessment remains explicitly local and unsigned",
async () =>
assessment.status === 200 &&
assessment.body.extraction.mode === "local" &&
assessment.body.contextual.rulesReviewed === false,
);
await check("Export is generated from the stored reviewed draft", async () => {
const r = await req("reports/" + assessment.body.reportId);
return (
r.status === 200 &&
r.body.extraction.id === draft.id &&
r.body.assessment.reportId === assessment.body.reportId &&
JSON.stringify(r.body.review) === JSON.stringify(review)
);
});
const report = {
runAt: new Date().toISOString(),
scope: "Local API boundary and evidence-integrity tests",
passed: results.filter((r) => r.passed).length,
total: results.length,
tests: results,
};
await fs.writeFile(
process.argv[2] || "validation/api-2026-10-23.json",
JSON.stringify(report, null, 2) + "\n",
);
console.log(JSON.stringify(report, null, 2));
process.exitCode = report.passed === report.total ? 0 : 1;