Files
OmniRoute/tests/unit/responses-parse-once-4041.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

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 }));
// #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"
);
});