mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) Two shards on release/v3.8.51 went red in one day with the same signature — "ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only .github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass alone and on re-run: the cleanup races something still writing into the directory (SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner the window opens. 1154 test files do their own cleanup with fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries. One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record): every rm / rmSync / rmdirSync option object with `recursive: true` and no `maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292 files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included. Only the option object changes: no call site, assertion or import is touched. Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292 files; a random 20-file sample runs green (quota-redis-store hangs identically on the untouched tree — it needs a Redis on localhost, an environment matter). The four unit shards on this PR are the full run. * fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff The gate shells out to `git diff` through execFileSync with Node's default 1 MB maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing anything. 64 MB is far above any real PR and costs nothing when unused.
154 lines
5.2 KiB
TypeScript
154 lines
5.2 KiB
TypeScript
import test, { after } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
|
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-responses-parse-once-"));
|
|
process.env.DATA_DIR = dataDir;
|
|
after(() => fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }));
|
|
|
|
// #4041: AI routes must parse each JSON body at most once and thread the parsed value
|
|
// through model resolution and handleChat. /v1/responses now parses after raw-body admission;
|
|
// withInjectionGuard retains the same preParsedBody contract for routes that still wrap it.
|
|
|
|
// ─── Part A: withInjectionGuard threads the parsed body ──────────────────────
|
|
|
|
const { withInjectionGuard } = await import("../../src/middleware/promptInjectionGuard.ts");
|
|
|
|
test("#4041 withInjectionGuard passes the parsed body as 3rd arg to the inner handler", async () => {
|
|
let receivedPreParsed: unknown = undefined;
|
|
|
|
const innerHandler = async (_request: Request, _context: unknown, preParsedBody: unknown) => {
|
|
receivedPreParsed = preParsedBody;
|
|
return new Response("ok");
|
|
};
|
|
|
|
const wrapped = withInjectionGuard(innerHandler, { mode: "warn" });
|
|
const payload = { messages: [{ role: "user", content: "Hello world" }] };
|
|
|
|
const request = new Request("http://localhost/v1/responses", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload),
|
|
});
|
|
|
|
await wrapped(request, {});
|
|
|
|
assert.deepEqual(
|
|
receivedPreParsed,
|
|
payload,
|
|
"withInjectionGuard must thread the body it already parsed into the inner handler as 3rd arg"
|
|
);
|
|
});
|
|
|
|
test("#4041 withInjectionGuard passes null as 3rd arg when body cannot be parsed", async () => {
|
|
let receivedPreParsed: unknown = "sentinel";
|
|
|
|
const innerHandler = async (_request: Request, _context: unknown, preParsedBody: unknown) => {
|
|
receivedPreParsed = preParsedBody;
|
|
return new Response("ok");
|
|
};
|
|
|
|
const wrapped = withInjectionGuard(innerHandler, { mode: "warn" });
|
|
|
|
// A GET request skips the guard entirely — 3rd arg is NOT forwarded (handler gets 2 args)
|
|
// A POST with non-JSON body: body is null, still calls handler with null as 3rd arg
|
|
const request = new Request("http://localhost/v1/responses", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "text/plain" },
|
|
body: "not json",
|
|
});
|
|
|
|
await wrapped(request, {});
|
|
|
|
assert.equal(
|
|
receivedPreParsed,
|
|
null,
|
|
"withInjectionGuard must pass null (not undefined) when body could not be parsed"
|
|
);
|
|
});
|
|
|
|
// ─── Part B: withCodexPreferredModel reuses pre-parsed body ──────────────────
|
|
|
|
test("#4041 withCodexPreferredModel accepts a pre-parsed body and avoids re-cloning the request", async () => {
|
|
const { withCodexPreferredModel } = await import("../../src/app/api/v1/responses/route.ts");
|
|
const body = { model: "openai/gpt-4o", input: "hello" };
|
|
const request = new Request("http://localhost/v1/responses", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(body),
|
|
});
|
|
let cloneCount = 0;
|
|
const originalClone = request.clone.bind(request);
|
|
Object.defineProperty(request, "clone", {
|
|
value: () => {
|
|
cloneCount += 1;
|
|
return originalClone();
|
|
},
|
|
});
|
|
|
|
const result = await withCodexPreferredModel(request, body);
|
|
|
|
assert.equal(cloneCount, 0);
|
|
assert.equal(result.body, body);
|
|
});
|
|
|
|
// ─── Part C: wrapped routes parse once before invoking their handler ─────────
|
|
|
|
test("#4041 the body is parsed AT MOST ONCE through withInjectionGuard + inner handler", async () => {
|
|
let jsonParseCount = 0;
|
|
|
|
// Build a request where we count every .json() call (including on clones)
|
|
const payload = { model: "gpt-4o", messages: [{ role: "user", content: "hi" }] };
|
|
const bodyStr = JSON.stringify(payload);
|
|
|
|
// We create a real Request but intercept .clone() to return a spy-wrapped clone
|
|
const origRequest = new Request("http://localhost/v1/responses", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: bodyStr,
|
|
});
|
|
|
|
function wrapWithJsonSpy(req: Request): Request {
|
|
const origJson = req.json.bind(req);
|
|
const origClone = req.clone.bind(req);
|
|
Object.defineProperty(req, "json", {
|
|
value: async () => {
|
|
jsonParseCount++;
|
|
return origJson();
|
|
},
|
|
writable: true,
|
|
});
|
|
Object.defineProperty(req, "clone", {
|
|
value: () => {
|
|
const cloned = origClone();
|
|
return wrapWithJsonSpy(cloned);
|
|
},
|
|
writable: true,
|
|
});
|
|
return req;
|
|
}
|
|
|
|
const spyRequest = wrapWithJsonSpy(origRequest);
|
|
|
|
let preParsedBodyReceived: unknown = undefined;
|
|
const innerHandler = async (_req: Request, _ctx: unknown, preParsedBody: unknown) => {
|
|
preParsedBodyReceived = preParsedBody;
|
|
return new Response("ok");
|
|
};
|
|
|
|
const wrapped = withInjectionGuard(innerHandler, { mode: "warn" });
|
|
await wrapped(spyRequest, {});
|
|
|
|
assert.ok(
|
|
jsonParseCount <= 1,
|
|
`Expected at most 1 JSON parse through withInjectionGuard, got ${jsonParseCount}`
|
|
);
|
|
assert.deepEqual(
|
|
preParsedBodyReceived,
|
|
payload,
|
|
"inner handler must receive the pre-parsed body as 3rd arg"
|
|
);
|
|
});
|