import fs from "node:fs/promises"; import { setTimeout as delay } from "node:timers/promises"; const modeFile = process.env.CONTEXT_FAULT_FILE || "/tmp/marmar-context-fault-mode.json"; const base = "http://127.0.0.1:4198/api/"; const request = async (path, body, signal) => { const t = performance.now(); const r = await fetch(base + path, { ...(body ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), } : {}), signal: signal ?? AbortSignal.timeout(28000), }); return { status: r.status, ms: Math.round(performance.now() - t), retryAfter: r.headers.get("retry-after"), data: await r.json(), }; }; const payload = (id = "fault") => ({ sources: [ { id, title: "Synthetic fault test", kind: "Synthetic", date: "2026-10-23", text: "Patient: Fault Test\nShe takes Metformin 500 mg twice daily.", }, ], useModel: true, freshModel: true, syntheticOnly: true, }); const setMode = async (kind, ms) => { await fs.writeFile( modeFile, JSON.stringify({ kind, ms, epoch: crypto.randomUUID() }) ); await request("model", { endpoint: "https://synthetic.services.ai.azure.com/openai/v1/chat/completions", key: "synthetic-not-a-credential", model: "Fault-test-provider", auth: "api-key", }); }; const stats = async () => JSON.parse(await fs.readFile(modeFile + ".stats", "utf8")); const report = { runAt: new Date().toISOString(), scope: "Isolated simulated-provider HTTP failure tests. No Azure calls.", checks: [], }; const check = (name, passed, detail) => { report.checks.push({ name, passed: !!passed, detail }); console.log(JSON.stringify(report.checks.at(-1))); }; for (const [kind, calls] of [ ["network", 2], ["503", 2], ["401", 1], ["malformed", 2], ["truncated", 1], ["null", 1], ]) { await setMode(kind); const r = await request("extract", payload()); const s = await stats(); check( kind + " safely fails without draft or leaked details", r.status === 502 && !r.data.id && !r.data.facts && !JSON.stringify(r.data).includes("PRIVATE") && s.calls === calls && s.active === 0, { ...r, provider: s } ); } for (const kind of ["recover", "rate-short"]) { await setMode(kind); const r = await request("extract", payload()); check(kind + " recovers once", r.status === 200 && r.data.attempts === 2, { status: r.status, ms: r.ms, provider: await stats(), }); } await setMode("rate-date"); const rate = await request("extract", payload()); const again = await request("extract", payload()); check( "HTTP-date cooldown suppresses repeated provider calls", rate.status === 503 && Number(rate.retryAfter) > 20 && again.status === 503 && (await stats()).calls === 1, { rate, again } ); await setMode("slow", 1500); const burst = await Promise.all( Array.from({ length: 12 }, (_, i) => request("extract", payload("burst-" + i)) ) ); const load = await stats(); check( "12-request burst bounded at four model slots", burst.filter((r) => r.status === 200).length === 4 && burst.filter((r) => r.status === 503).length === 8 && load.peak === 4 && load.active === 0, { statuses: burst.map((r) => r.status), maxRejectedMs: Math.max( ...burst.filter((r) => r.status === 503).map((r) => r.ms) ), provider: load, } ); await setMode("hang"); const controller = new AbortController(); const pending = request("extract", payload(), controller.signal).catch((e) => ({ cancelled: e.name === "AbortError", })); await delay(250); controller.abort(); await pending; await delay(200); const cancelled = await stats(); check( "client disconnect cancels provider work", cancelled.aborted === 1 && cancelled.active === 0, cancelled ); for (const kind of ["hang", "body-hang"]) { await setMode(kind); const pending = request("extract", payload()); await delay(500); const health = await request("status"); const local = await request("extract", { ...payload("structured"), useModel: false, }); check( kind + " keeps status and local reader responsive", health.status === 200 && health.ms < 500 && local.status === 200 && local.ms < 500, { healthMs: health.ms, localMs: local.ms } ); const r = await pending; check( kind + " obeys overall deadline", r.status === 502 && r.ms >= 20000 && r.ms < 24000 && !r.data.id, { ...r, provider: await stats() } ); await setMode("good"); const recovered = await request("extract", payload()); check( kind + " releases capacity for recovery", recovered.status === 200 && (await stats()).active === 0, { status: recovered.status, ms: recovered.ms } ); } report.passed = report.checks.filter((c) => c.passed).length; report.total = report.checks.length; await fs.writeFile( process.argv[2] || "validation/fault-load-2026-10-24.json", JSON.stringify(report, null, 2) + "\n" ); await setMode("slow", 8000); // Keep isolated test UI available for manual cancellation checks. process.exitCode = report.passed === report.total ? 0 : 1;