mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-01 03:52:17 +03:00
Compare commits
2 Commits
docs/opena
...
fix/tests-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbcbffc7bc | ||
|
|
37ecab3659 |
101
scripts/ad-hoc/codemod-rm-maxretries.mjs
Normal file
101
scripts/ad-hoc/codemod-rm-maxretries.mjs
Normal file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* One-shot codemod (#11966): give every recursive temp-dir removal in tests the retry
|
||||
* options Node already supports, so a WAL/backup/worker still writing into the directory
|
||||
* turns into a retried delete instead of a red shard:
|
||||
*
|
||||
* rmSync(dir, { recursive: true, force: true })
|
||||
* → rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
|
||||
*
|
||||
* Applies to `rmSync(`, `fs.rmSync(`, `rm(` / `fs.rm(` / `fs.promises.rm(` (async) and
|
||||
* `rmdirSync(` calls whose option object literal contains `recursive: true` and no
|
||||
* `maxRetries`. Only the option object is touched — call sites, assertions and imports are
|
||||
* left as they are. Usage: node scripts/ad-hoc/codemod-rm-maxretries.mjs [dir=tests]
|
||||
*/
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const root = process.argv[2] || "tests";
|
||||
const CALL = /\b(?:fs\.promises\.|fsp\.|fs\.|promises\.)?(?:rmSync|rmdirSync|rm)\(/g;
|
||||
let files = 0;
|
||||
let sites = 0;
|
||||
|
||||
function walk(dir, out = []) {
|
||||
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, e.name);
|
||||
if (e.isDirectory()) {
|
||||
if (e.name === "node_modules" || e.name === "fixtures") continue;
|
||||
walk(p, out);
|
||||
} else if (/\.(ts|tsx|mts|cts|js|mjs|cjs)$/.test(e.name)) out.push(p);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Find the closing brace of the option object literal that starts at `open`.
|
||||
function objectEnd(src, open) {
|
||||
let depth = 0;
|
||||
for (let i = open; i < src.length; i++) {
|
||||
const c = src[i];
|
||||
if (c === "{") depth++;
|
||||
else if (c === "}") {
|
||||
depth--;
|
||||
if (depth === 0) return i;
|
||||
} else if (c === '"' || c === "'" || c === "`") {
|
||||
const q = c;
|
||||
i++;
|
||||
while (i < src.length && src[i] !== q) {
|
||||
if (src[i] === "\\") i++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (const file of walk(root)) {
|
||||
const src = fs.readFileSync(file, "utf8");
|
||||
let out = "";
|
||||
let last = 0;
|
||||
let touched = 0;
|
||||
for (const m of src.matchAll(CALL)) {
|
||||
const callStart = m.index + m[0].length;
|
||||
// Locate the option object: the first `{` before the call's closing paren at depth 0.
|
||||
let depth = 0;
|
||||
let objOpen = -1;
|
||||
for (let i = callStart; i < src.length; i++) {
|
||||
const c = src[i];
|
||||
if (c === "(" || c === "[") depth++;
|
||||
else if (c === ")" || c === "]") {
|
||||
if (depth === 0) break;
|
||||
depth--;
|
||||
} else if (c === "{" && depth === 0) {
|
||||
objOpen = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (objOpen === -1) continue;
|
||||
const objClose = objectEnd(src, objOpen);
|
||||
if (objClose === -1) continue;
|
||||
const obj = src.slice(objOpen, objClose + 1);
|
||||
if (!/\brecursive:\s*true\b/.test(obj) || /\bmaxRetries\b/.test(obj)) continue;
|
||||
// Insert before the closing brace, respecting an existing trailing comma / newline.
|
||||
const inner = obj.slice(1, -1);
|
||||
const trimmed = inner.replace(/\s+$/, "");
|
||||
const trailing = inner.slice(trimmed.length);
|
||||
const sep = trimmed.endsWith(",") ? " " : ", ";
|
||||
const multiline = /\n/.test(trailing);
|
||||
const insert = multiline
|
||||
? `${trimmed}${trimmed.endsWith(",") ? "" : ","}\n${trailing.replace(/\n$/, "")} maxRetries: 5,\n retryDelay: 100,${trailing}`
|
||||
: `${trimmed}${sep}maxRetries: 5, retryDelay: 100${trailing}`;
|
||||
out += src.slice(last, objOpen + 1) + insert;
|
||||
last = objClose;
|
||||
touched++;
|
||||
}
|
||||
if (touched) {
|
||||
out += src.slice(last);
|
||||
fs.writeFileSync(file, out);
|
||||
files++;
|
||||
sites += touched;
|
||||
}
|
||||
}
|
||||
console.log(`[codemod-rm-maxretries] ${sites} call site(s) in ${files} file(s) under ${root}`);
|
||||
@@ -173,7 +173,7 @@ function arg(name, fallback = "") {
|
||||
}
|
||||
|
||||
function git(root, args) {
|
||||
return execFileSync("git", args, { cwd: root, encoding: "utf8" });
|
||||
return execFileSync("git", args, { cwd: root, encoding: "utf8", maxBuffer: 64 * 1024 * 1024 });
|
||||
}
|
||||
|
||||
function changedEntries(root, base) {
|
||||
|
||||
@@ -33,7 +33,7 @@ if (!process.env.DATA_DIR) {
|
||||
// Best-effort cleanup so a long suite run does not leak hundreds of temp DBs.
|
||||
process.on("exit", () => {
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
// ignore — the OS reaps its temp dir eventually.
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ test.after(async () => {
|
||||
await serverA.stop();
|
||||
await serverB.stop();
|
||||
core.closeDbInstance();
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("primary healthy: request routes to Server A only", async () => {
|
||||
|
||||
@@ -286,7 +286,7 @@ export async function createChatPipelineHarness(prefix) {
|
||||
clearSkillState();
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true });
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(testDataDir, { recursive: true });
|
||||
initTranslators();
|
||||
}
|
||||
@@ -300,7 +300,7 @@ export async function createChatPipelineHarness(prefix) {
|
||||
clearSkillState();
|
||||
resetAllCircuitBreakers();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true });
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
|
||||
async function seedConnection(provider: string, overrides: SeedConnectionOverrides = {}) {
|
||||
|
||||
@@ -24,7 +24,7 @@ const DEFAULT_PATTERNS = [".bank.", ".gov.", "okta.com", "auth0.com"];
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -34,7 +34,11 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
});
|
||||
|
||||
// ── POST patterns ──────────────────────────────────────────────────────────
|
||||
@@ -48,7 +52,10 @@ test("POST /bypass: stores user patterns", async () => {
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { ok: boolean; patterns: Array<{ pattern: string; source: string }> };
|
||||
const body = (await res.json()) as {
|
||||
ok: boolean;
|
||||
patterns: Array<{ pattern: string; source: string }>;
|
||||
};
|
||||
assert.equal(body.ok, true);
|
||||
assert.ok(Array.isArray(body.patterns));
|
||||
const userPatterns = body.patterns.filter((p) => p.source === "user");
|
||||
@@ -64,7 +71,7 @@ test("POST /bypass: invalid body returns 400", async () => {
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 400 error");
|
||||
});
|
||||
@@ -83,7 +90,7 @@ test("GET /bypass: shows default + user patterns", async () => {
|
||||
|
||||
const res = await bypassRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { patterns: Array<{ pattern: string; source: string }> };
|
||||
const body = (await res.json()) as { patterns: Array<{ pattern: string; source: string }> };
|
||||
assert.ok(Array.isArray(body.patterns));
|
||||
|
||||
const sources = new Set(body.patterns.map((p) => p.source));
|
||||
@@ -119,7 +126,10 @@ test("DELETE /bypass?pattern=X: removes a user pattern", async () => {
|
||||
})
|
||||
);
|
||||
assert.equal(deleteRes.status, 200);
|
||||
const deleteBody = await deleteRes.json() as { ok: boolean; patterns: Array<{ pattern: string; source: string }> };
|
||||
const deleteBody = (await deleteRes.json()) as {
|
||||
ok: boolean;
|
||||
patterns: Array<{ pattern: string; source: string }>;
|
||||
};
|
||||
assert.equal(deleteBody.ok, true);
|
||||
|
||||
// Verify it's gone
|
||||
@@ -142,19 +152,18 @@ test("DELETE /bypass: missing pattern param returns 400", async () => {
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in DELETE 400");
|
||||
});
|
||||
|
||||
test("DELETE /bypass?pattern=X: no-op when pattern not in user list", async () => {
|
||||
const res = await bypassRoute.DELETE(
|
||||
new Request(
|
||||
"http://localhost/api/tools/agent-bridge/bypass?pattern=not-in-list.com",
|
||||
{ method: "DELETE" }
|
||||
)
|
||||
new Request("http://localhost/api/tools/agent-bridge/bypass?pattern=not-in-list.com", {
|
||||
method: "DELETE",
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { ok: boolean };
|
||||
const body = (await res.json()) as { ok: boolean };
|
||||
assert.equal(body.ok, true);
|
||||
});
|
||||
|
||||
@@ -19,7 +19,8 @@ process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
|
||||
|
||||
const certRoute = await import("../../src/app/api/tools/agent-bridge/cert/route.ts");
|
||||
const downloadRoute = await import("../../src/app/api/tools/agent-bridge/cert/download/route.ts");
|
||||
const regenerateRoute = await import("../../src/app/api/tools/agent-bridge/cert/regenerate/route.ts");
|
||||
const regenerateRoute =
|
||||
await import("../../src/app/api/tools/agent-bridge/cert/regenerate/route.ts");
|
||||
|
||||
function certDir() {
|
||||
return path.join(TEST_DATA_DIR, "mitm");
|
||||
@@ -30,7 +31,7 @@ function certFilePath() {
|
||||
}
|
||||
|
||||
function resetCertDir() {
|
||||
fs.rmSync(certDir(), { recursive: true, force: true });
|
||||
fs.rmSync(certDir(), { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(certDir(), { recursive: true });
|
||||
}
|
||||
|
||||
@@ -39,7 +40,11 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
});
|
||||
|
||||
// ── GET /cert ─────────────────────────────────────────────────────────────
|
||||
@@ -47,7 +52,7 @@ test.after(() => {
|
||||
test("GET /cert: returns exists:false when no cert file", async () => {
|
||||
const res = await certRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
assert.equal(body.exists, false);
|
||||
assert.equal(body.trusted, false);
|
||||
assert.equal(body.path, null);
|
||||
@@ -59,7 +64,7 @@ test("GET /cert: returns exists:true when cert file present", async () => {
|
||||
|
||||
const res = await certRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
assert.equal(body.exists, true);
|
||||
// trusted may be false in test env (no system store)
|
||||
assert.ok(typeof body.trusted === "boolean");
|
||||
@@ -83,7 +88,7 @@ test("POST /cert: returns 404 when no cert file", async () => {
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 404);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 404 error message");
|
||||
});
|
||||
@@ -106,7 +111,7 @@ MIIBpDCCAQ2gAwIBAgIUFakeMITMCertForTestingOnlyXX==
|
||||
|
||||
// In test env: installCert may throw because the PEM is fake; we accept
|
||||
// either 200 (mocked) or 500 (real OS failure) — NOT a 500 with stack trace
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string | undefined;
|
||||
if (errMsg) {
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in POST /cert error");
|
||||
@@ -118,7 +123,7 @@ MIIBpDCCAQ2gAwIBAgIUFakeMITMCertForTestingOnlyXX==
|
||||
test("GET /cert/download: 404 when no cert file", async () => {
|
||||
const res = await downloadRoute.GET();
|
||||
assert.equal(res.status, 404);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in download 404");
|
||||
});
|
||||
|
||||
@@ -18,13 +18,12 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.DISABLE_SQLITE_AUTO_BACKUP = "true";
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const mappingsRoute = await import(
|
||||
"../../src/app/api/tools/agent-bridge/agents/[id]/mappings/route.ts"
|
||||
);
|
||||
const mappingsRoute =
|
||||
await import("../../src/app/api/tools/agent-bridge/agents/[id]/mappings/route.ts");
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -33,18 +32,21 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
try { fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true }); } catch { /* noop */ }
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
});
|
||||
|
||||
// ── GET (empty) ────────────────────────────────────────────────────────────
|
||||
|
||||
test("GET /mappings: returns empty array for new agent", async () => {
|
||||
const res = await mappingsRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: { id: "copilot" } }
|
||||
);
|
||||
const res = await mappingsRoute.GET(new Request("http://localhost/"), {
|
||||
params: { id: "copilot" },
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { mappings: unknown[] };
|
||||
const body = (await res.json()) as { mappings: unknown[] };
|
||||
assert.ok(Array.isArray(body.mappings));
|
||||
assert.equal(body.mappings.length, 0);
|
||||
});
|
||||
@@ -66,17 +68,21 @@ test("PUT → GET round-trip: stores and retrieves mappings", async () => {
|
||||
{ params: { id: "copilot" } }
|
||||
);
|
||||
assert.equal(putRes.status, 200);
|
||||
const putBody = await putRes.json() as { ok: boolean; mappings: Array<{ agent_id: string; source_model: string; target_model: string }> };
|
||||
const putBody = (await putRes.json()) as {
|
||||
ok: boolean;
|
||||
mappings: Array<{ agent_id: string; source_model: string; target_model: string }>;
|
||||
};
|
||||
assert.equal(putBody.ok, true);
|
||||
assert.equal(putBody.mappings.length, 2);
|
||||
|
||||
// GET reads back the same data
|
||||
const getRes = await mappingsRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: { id: "copilot" } }
|
||||
);
|
||||
const getRes = await mappingsRoute.GET(new Request("http://localhost/"), {
|
||||
params: { id: "copilot" },
|
||||
});
|
||||
assert.equal(getRes.status, 200);
|
||||
const getBody = await getRes.json() as { mappings: Array<{ source_model: string; target_model: string }> };
|
||||
const getBody = (await getRes.json()) as {
|
||||
mappings: Array<{ source_model: string; target_model: string }>;
|
||||
};
|
||||
assert.equal(getBody.mappings.length, 2);
|
||||
|
||||
const sources = getBody.mappings.map((m) => m.source_model).sort();
|
||||
@@ -108,11 +114,10 @@ test("PUT: replaces all previous mappings", async () => {
|
||||
);
|
||||
assert.equal(putRes.status, 200);
|
||||
|
||||
const getRes = await mappingsRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: { id: "cursor" } }
|
||||
);
|
||||
const body = await getRes.json() as { mappings: Array<{ source_model: string }> };
|
||||
const getRes = await mappingsRoute.GET(new Request("http://localhost/"), {
|
||||
params: { id: "cursor" },
|
||||
});
|
||||
const body = (await getRes.json()) as { mappings: Array<{ source_model: string }> };
|
||||
assert.equal(body.mappings.length, 1);
|
||||
assert.equal(body.mappings[0].source_model, "new-model");
|
||||
});
|
||||
@@ -136,7 +141,7 @@ test("PUT: empty mappings array clears all mappings", async () => {
|
||||
{ params: { id: "zed" } }
|
||||
);
|
||||
assert.equal(putRes.status, 200);
|
||||
const body = await putRes.json() as { mappings: unknown[] };
|
||||
const body = (await putRes.json()) as { mappings: unknown[] };
|
||||
assert.equal(body.mappings.length, 0);
|
||||
});
|
||||
|
||||
@@ -152,7 +157,7 @@ test("PUT: invalid body (missing mappings) returns 400", async () => {
|
||||
{ params: { id: "antigravity" } }
|
||||
);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as Record<string, unknown>;
|
||||
const body = (await res.json()) as Record<string, unknown>;
|
||||
const errMsg = (body.error as Record<string, unknown>)?.message as string;
|
||||
assert.ok(!errMsg.includes("at /"), "stack trace leaked in 400 error");
|
||||
});
|
||||
@@ -183,10 +188,9 @@ test("PUT: error responses do not leak stack traces", async () => {
|
||||
});
|
||||
|
||||
test("GET: error responses do not leak stack traces", async () => {
|
||||
const res = await mappingsRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: { id: "antigravity" } }
|
||||
);
|
||||
const res = await mappingsRoute.GET(new Request("http://localhost/"), {
|
||||
params: { id: "antigravity" },
|
||||
});
|
||||
const text = await res.text();
|
||||
assert.ok(!text.includes("at /"), "stack trace leaked in GET /mappings response");
|
||||
});
|
||||
|
||||
@@ -39,7 +39,7 @@ const routeGuard = await import("../../src/server/authz/routeGuard.ts");
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ test.beforeEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
/* noop */
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Auth tests ────────────────────────────────────────────────────────────────
|
||||
@@ -288,6 +288,6 @@ test("grok-build status uses GROK_HOME and returns its managed endpoint", async
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.GROK_HOME;
|
||||
else process.env.GROK_HOME = original;
|
||||
fs.rmSync(grokHome, { recursive: true, force: true });
|
||||
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,14 +25,13 @@ process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "test-8491-antigravit
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const providersDb = await import("../../src/lib/db/providers.ts");
|
||||
const { AntigravityExecutor } = await import("../../open-sse/executors/antigravity.ts");
|
||||
const { clearAntigravityProjectCache } = await import(
|
||||
"../../open-sse/services/antigravityProjectBootstrap.ts"
|
||||
);
|
||||
const { clearAntigravityProjectCache } =
|
||||
await import("../../open-sse/services/antigravityProjectBootstrap.ts");
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
if (fs.existsSync(TEST_DATA_DIR)) {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -91,7 +90,11 @@ test("#8491 PART A: runtime-discovered projectId must be persisted to the connec
|
||||
throw new Error(`Expected an envelope but got a ${result.status} Response`);
|
||||
}
|
||||
assert.equal(loadCodeAssistCalls, 1, "loadCodeAssist must be called to recover the project");
|
||||
assert.equal(result.project, DISCOVERED_PROJECT_ID, "the in-flight request uses the discovered id");
|
||||
assert.equal(
|
||||
result.project,
|
||||
DISCOVERED_PROJECT_ID,
|
||||
"the in-flight request uses the discovered id"
|
||||
);
|
||||
|
||||
const persisted = await providersDb.getProviderConnectionById(connection.id);
|
||||
assert.equal(
|
||||
|
||||
@@ -25,7 +25,7 @@ async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("API keys routes require management auth when login protection is enabled", async () => {
|
||||
|
||||
@@ -24,7 +24,7 @@ async function resetStorage() {
|
||||
delete process.env.ENABLE_SOCKS5_PROXY;
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("critical routes: v1 management proxies covers auth, lookup, where-used, patch, and delete branches", async () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ const auditRoute = await import("../../src/app/api/compliance/audit-log/route.ts
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ test.beforeEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -268,7 +268,7 @@ async function stopProcess(child: ReturnType<typeof spawn>) {
|
||||
async function removeDirWithRetry(dir: string) {
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
return;
|
||||
} catch (error) {
|
||||
if (attempt === 4) throw error;
|
||||
|
||||
@@ -373,7 +373,7 @@ async function resetStorage() {
|
||||
invalidateMemorySettingsCache();
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
initTranslators();
|
||||
}
|
||||
@@ -512,7 +512,7 @@ test.after(async () => {
|
||||
clearInflight();
|
||||
resetAllCircuitBreakers();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("chat pipeline handles OpenAI passthrough with valid API key auth", async () => {
|
||||
|
||||
@@ -28,7 +28,7 @@ async function resetStorage() {
|
||||
readCacheDb.invalidateDbCache();
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ test.after(async () => {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.closeDbInstance();
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {}
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ test.after(async () => {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.closeDbInstance();
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {}
|
||||
});
|
||||
|
||||
|
||||
@@ -22,12 +22,13 @@ process.env.JWT_SECRET = "test-jwt-secret-codewhale";
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/codewhale-settings/route.ts");
|
||||
const { GET, POST, DELETE } =
|
||||
await import("../../src/app/api/cli-tools/codewhale-settings/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -126,7 +127,7 @@ test("codewhale-settings POST: writes primary ~/.codewhale/config.toml for a fre
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -173,7 +174,7 @@ test("codewhale-settings POST: syncs an existing legacy ~/.deepseek/config.toml"
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -201,7 +202,7 @@ test("codewhale-settings GET: falls back to legacy ~/.deepseek/config.toml when
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -238,7 +239,7 @@ test("codewhale-settings DELETE: removes primary and legacy config files", async
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -272,7 +273,7 @@ test("codewhale-settings route.ts: does not call exec() or spawn() directly", ()
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -8,9 +8,7 @@ import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "omniroute-deepseek-tui-settings-")
|
||||
);
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-deepseek-tui-settings-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.API_KEY_SECRET = "test-api-key-secret-deepseek-tui";
|
||||
process.env.JWT_SECRET = "test-jwt-secret-deepseek-tui";
|
||||
@@ -18,14 +16,13 @@ process.env.JWT_SECRET = "test-jwt-secret-deepseek-tui";
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const { GET, POST, DELETE } = await import(
|
||||
"../../src/app/api/cli-tools/deepseek-tui-settings/route.ts"
|
||||
);
|
||||
const { GET, POST, DELETE } =
|
||||
await import("../../src/app/api/cli-tools/deepseek-tui-settings/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -103,10 +100,7 @@ test("deepseek-tui-settings POST: writes config.toml with valid body", async ()
|
||||
}),
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Unexpected status ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
@@ -120,7 +114,7 @@ test("deepseek-tui-settings POST: writes config.toml with valid body", async ()
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -136,23 +130,20 @@ test("deepseek-tui-settings DELETE: removes config file", async () => {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(configDir, "config.toml"),
|
||||
"# managed by OmniRoute (plan 14)\n[openai]\nbase_url = \"http://localhost:20128\"\n"
|
||||
'# managed by OmniRoute (plan 14)\n[openai]\nbase_url = "http://localhost:20128"\n'
|
||||
);
|
||||
|
||||
const res = await DELETE(
|
||||
new Request("http://localhost/api/cli-tools/deepseek-tui-settings", { method: "DELETE" })
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Expected 200/403/500, got ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -186,7 +177,7 @@ test("deepseek-tui-settings route.ts: does not call exec() or spawn() directly",
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -19,14 +19,12 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
// Import route handlers
|
||||
const { GET, POST, DELETE } = await import(
|
||||
"../../src/app/api/cli-tools/forge-settings/route.ts"
|
||||
);
|
||||
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/forge-settings/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -107,10 +105,7 @@ test("forge-settings POST: writes config.toml with valid body", async () => {
|
||||
);
|
||||
|
||||
// 200 = success; 403 = write guard active (test env); 500 = backup dir issue
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Unexpected status ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
|
||||
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
@@ -126,7 +121,7 @@ test("forge-settings POST: writes config.toml with valid body", async () => {
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -143,16 +138,13 @@ test("forge-settings DELETE: removes config file when it exists", async () => {
|
||||
fs.mkdirSync(forgeDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(forgeDir, "config.toml"),
|
||||
"# managed by OmniRoute (plan 14)\n[openai]\nbase_url = \"http://localhost:20128\"\n"
|
||||
'# managed by OmniRoute (plan 14)\n[openai]\nbase_url = "http://localhost:20128"\n'
|
||||
);
|
||||
|
||||
const res = await DELETE(
|
||||
new Request("http://localhost/api/cli-tools/forge-settings", { method: "DELETE" })
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Expected 200/403/500, got ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
|
||||
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
@@ -160,7 +152,7 @@ test("forge-settings DELETE: removes config file when it exists", async () => {
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -194,7 +186,7 @@ test("forge-settings route.ts: does not call exec() or spawn() directly", () =>
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -39,7 +39,7 @@ const { GET, POST, DELETE } =
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ test("grok-build-settings POST: writes [model.omniroute] section and preserves e
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -219,7 +219,7 @@ test("grok-build-settings DELETE: removes our section, preserves the rest, resto
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -237,7 +237,7 @@ test("grok-build-settings DELETE: no-op success when no config file exists", asy
|
||||
assert.equal(body.success, true);
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -289,7 +289,7 @@ test("grok-build-settings: honors GROK_HOME and rejects a relative value", async
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.GROK_HOME;
|
||||
else process.env.GROK_HOME = original;
|
||||
fs.rmSync(grokHome, { recursive: true, force: true });
|
||||
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -313,7 +313,7 @@ test("grok-build-settings POST: returns 409 for an unowned omniroute slot", asyn
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.GROK_HOME;
|
||||
else process.env.GROK_HOME = original;
|
||||
fs.rmSync(grokHome, { recursive: true, force: true });
|
||||
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -342,7 +342,7 @@ test("grok-build-settings POST: resolves keyId to an unmasked key", async () =>
|
||||
} finally {
|
||||
if (original === undefined) delete process.env.GROK_HOME;
|
||||
else process.env.GROK_HOME = original;
|
||||
fs.rmSync(grokHome, { recursive: true, force: true });
|
||||
fs.rmSync(grokHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -376,7 +376,7 @@ test("grok-build-settings route.ts: does not call exec() or spawn() directly", (
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -21,7 +21,7 @@ const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/jcode-se
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ test("jcode-settings POST: writes [providers.omniroute] into config.toml", async
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -159,7 +159,7 @@ test("jcode-settings DELETE: removes only the OmniRoute-managed block", async ()
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -193,7 +193,7 @@ test("jcode-settings route.ts: does not call exec() or spawn() directly", () =>
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -22,9 +22,7 @@ process.env.JWT_SECRET = "test-jwt-secret-letta";
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const { GET, POST, DELETE } = await import(
|
||||
"../../src/app/api/cli-tools/letta-settings/route.ts"
|
||||
);
|
||||
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/letta-settings/route.ts");
|
||||
|
||||
let tmpHome: string;
|
||||
let origHome: string | undefined;
|
||||
@@ -40,7 +38,7 @@ function req(init?: RequestInit) {
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -58,7 +56,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.afterEach(() => {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Test 1: GET without auth → 401 ──────────────────────────────────────────
|
||||
@@ -189,7 +187,7 @@ test("letta-settings: error responses do not leak stack traces", async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -59,7 +59,7 @@ function seedOmpDb() {
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.afterEach(() => {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Test 1: GET without auth → 401 ──────────────────────────────────────────
|
||||
@@ -190,7 +190,7 @@ test("omp-settings: error responses do not leak stack traces", async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -16,14 +16,12 @@ process.env.JWT_SECRET = "test-jwt-secret-pi";
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const { GET, POST, DELETE } = await import(
|
||||
"../../src/app/api/cli-tools/pi-settings/route.ts"
|
||||
);
|
||||
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/pi-settings/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -101,10 +99,7 @@ test("pi-settings POST: writes config.json with valid body", async () => {
|
||||
}),
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Unexpected status ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
@@ -118,7 +113,7 @@ test("pi-settings POST: writes config.json with valid body", async () => {
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -145,17 +140,14 @@ test("pi-settings DELETE: removes OmniRoute fields from existing config", async
|
||||
const res = await DELETE(
|
||||
new Request("http://localhost/api/cli-tools/pi-settings", { method: "DELETE" })
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Expected 200/403/500, got ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -189,7 +181,7 @@ test("pi-settings route.ts: does not call exec() or spawn() directly", () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -16,14 +16,12 @@ process.env.JWT_SECRET = "test-jwt-secret-smelt";
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const { GET, POST, DELETE } = await import(
|
||||
"../../src/app/api/cli-tools/smelt-settings/route.ts"
|
||||
);
|
||||
const { GET, POST, DELETE } = await import("../../src/app/api/cli-tools/smelt-settings/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -101,10 +99,7 @@ test("smelt-settings POST: writes config.json with valid body", async () => {
|
||||
}),
|
||||
})
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Unexpected status ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Unexpected status ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
@@ -118,7 +113,7 @@ test("smelt-settings POST: writes config.json with valid body", async () => {
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -145,17 +140,14 @@ test("smelt-settings DELETE: removes OmniRoute fields from existing config", asy
|
||||
const res = await DELETE(
|
||||
new Request("http://localhost/api/cli-tools/smelt-settings", { method: "DELETE" })
|
||||
);
|
||||
assert.ok(
|
||||
[200, 403, 500].includes(res.status),
|
||||
`Expected 200/403/500, got ${res.status}`
|
||||
);
|
||||
assert.ok([200, 403, 500].includes(res.status), `Expected 200/403/500, got ${res.status}`);
|
||||
if (res.status === 200) {
|
||||
const body = await res.json();
|
||||
assert.equal(body.success, true);
|
||||
}
|
||||
} finally {
|
||||
process.env.HOME = origHome;
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true });
|
||||
fs.rmSync(tmpHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -189,7 +181,7 @@ test("smelt-settings route.ts: does not call exec() or spawn() directly", () =>
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
delete process.env.DATA_DIR;
|
||||
delete process.env.API_KEY_SECRET;
|
||||
delete process.env.JWT_SECRET;
|
||||
|
||||
@@ -44,6 +44,6 @@ test("Codex Spark cooldown survives a fresh process without creating child conne
|
||||
assert.equal(after.connectionId, before.connectionId);
|
||||
assert.deepEqual(after.upstreamModels, ["gpt-5.5"]);
|
||||
} finally {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -305,6 +305,6 @@ test("chat completions streams Codex Responses reasoning through real route HTTP
|
||||
globalThis.fetch = originalFetch;
|
||||
if (routeServer) await closeServer(routeServer);
|
||||
core.closeDbInstance({ checkpointMode: null });
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,18 +46,18 @@ const IN_SCOPE_PROVIDERS = new Set([
|
||||
|
||||
// Provider → sensible default model (fallback when default_model is null).
|
||||
const PROVIDER_DEFAULT_MODELS: Record<string, string> = {
|
||||
"claude": "claude-3-5-haiku-20241022",
|
||||
"glm": "glm-4-flash",
|
||||
"minimax": "minimax-text-01",
|
||||
claude: "claude-3-5-haiku-20241022",
|
||||
glm: "glm-4-flash",
|
||||
minimax: "minimax-text-01",
|
||||
"kimi-coding-apikey": "moonshot-v1-8k",
|
||||
"ollama-cloud": "llama3.2:3b",
|
||||
"opencode-go": "gpt-4o-mini",
|
||||
"gemini": "gemini-2.0-flash-lite",
|
||||
"deepseek": "deepseek-chat",
|
||||
"groq": "llama-3.1-8b-instant",
|
||||
"cerebras": "llama-3.1-8b",
|
||||
"openrouter": "openai/gpt-4o-mini",
|
||||
"together": "meta-llama/Llama-3-8b-chat-hf",
|
||||
gemini: "gemini-2.0-flash-lite",
|
||||
deepseek: "deepseek-chat",
|
||||
groq: "llama-3.1-8b-instant",
|
||||
cerebras: "llama-3.1-8b",
|
||||
openrouter: "openai/gpt-4o-mini",
|
||||
together: "meta-llama/Llama-3-8b-chat-hf",
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -79,9 +79,11 @@ export type ComboModelEntry = {
|
||||
connectionId: string;
|
||||
};
|
||||
|
||||
export type LiveHarness = {
|
||||
LIVE_ENABLED: false;
|
||||
} | LiveHarnessEnabled;
|
||||
export type LiveHarness =
|
||||
| {
|
||||
LIVE_ENABLED: false;
|
||||
}
|
||||
| LiveHarnessEnabled;
|
||||
|
||||
export type LiveHarnessEnabled = {
|
||||
LIVE_ENABLED: true;
|
||||
@@ -146,12 +148,12 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true });
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
throw new Error(`[liveHarness] Failed to fetch VPS secrets via ssh: ${err.message}`);
|
||||
}
|
||||
|
||||
if (!storageEncryptionKey || !apiKeySecret) {
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true });
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
throw new Error(
|
||||
"[liveHarness] Could not parse STORAGE_ENCRYPTION_KEY or API_KEY_SECRET from VPS .env"
|
||||
);
|
||||
@@ -176,13 +178,11 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
|
||||
const snapshotDbPath = path.join(snapshotDir, "storage.sqlite");
|
||||
|
||||
try {
|
||||
execFileSync(
|
||||
"scp",
|
||||
["root@192.168.0.15:/root/.omniroute/storage.sqlite", snapshotDbPath],
|
||||
{ timeout: 60_000 }
|
||||
);
|
||||
execFileSync("scp", ["root@192.168.0.15:/root/.omniroute/storage.sqlite", snapshotDbPath], {
|
||||
timeout: 60_000,
|
||||
});
|
||||
} catch (err: any) {
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true });
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
throw new Error(`[liveHarness] Failed to scp production DB: ${err.message}`);
|
||||
}
|
||||
|
||||
@@ -262,7 +262,10 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
|
||||
});
|
||||
}
|
||||
|
||||
function liveBody(model: string, overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
function liveBody(
|
||||
model: string,
|
||||
overrides: Record<string, unknown> = {}
|
||||
): Record<string, unknown> {
|
||||
return {
|
||||
model,
|
||||
stream: false,
|
||||
@@ -400,7 +403,7 @@ export async function createLiveHarness(prefix: string): Promise<LiveHarness> {
|
||||
resetAllCircuitBreakers();
|
||||
core.resetDbInstance();
|
||||
// Destroy the snapshot — targets only the temp dir, NEVER /root/.omniroute.
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true });
|
||||
fs.rmSync(snapshotDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
|
||||
// Populate the map eagerly so servedProvider (sync) works right after
|
||||
|
||||
@@ -280,7 +280,7 @@ test.after(async () => {
|
||||
if (app) await stopProcess(app.child);
|
||||
await upstream.stop();
|
||||
core.closeDbInstance();
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -14,7 +14,7 @@ const { createSSEStream } = await import("../../open-sse/utils/stream.ts");
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
if (fs.existsSync(TEST_DATA_DIR)) {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ test.afterEach(() => {
|
||||
clearInflight();
|
||||
resetAllCircuitBreakers();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
@@ -112,7 +112,10 @@ test("llama-cpp provider: routes request to custom baseUrl with no auth header",
|
||||
headers: toPlainHeaders(init.headers),
|
||||
body: init.body ? JSON.parse(String(init.body)) : null,
|
||||
});
|
||||
return buildLlamaResponse("Why did the programmer go broke? Because he used up all his cache!", "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
|
||||
return buildLlamaResponse(
|
||||
"Why did the programmer go broke? Because he used up all his cache!",
|
||||
"unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M"
|
||||
);
|
||||
};
|
||||
|
||||
const response = await handleChat(
|
||||
@@ -135,7 +138,10 @@ test("llama-cpp provider: routes request to custom baseUrl with no auth header",
|
||||
assert.equal(upstream.headers.Authorization, undefined, "no auth header for local provider");
|
||||
assert.equal(upstream.body.messages[0].content, "Tell me a joke.");
|
||||
assert.equal(upstream.body.model, "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
|
||||
assert.equal(json.choices[0].message.content, "Why did the programmer go broke? Because he used up all his cache!");
|
||||
assert.equal(
|
||||
json.choices[0].message.content,
|
||||
"Why did the programmer go broke? Because he used up all his cache!"
|
||||
);
|
||||
});
|
||||
|
||||
test("llama-cpp provider: alias matching works via model catalog prefix", async () => {
|
||||
@@ -152,7 +158,12 @@ test("llama-cpp provider: alias matching works via model catalog prefix", async
|
||||
const fetchCalls: FetchCall[] = [];
|
||||
|
||||
globalThis.fetch = async (url, init: RequestInit = {}) => {
|
||||
fetchCalls.push({ url: String(url), method: init.method, headers: toPlainHeaders(init.headers), body: init.body ? JSON.parse(String(init.body)) : null });
|
||||
fetchCalls.push({
|
||||
url: String(url),
|
||||
method: init.method,
|
||||
headers: toPlainHeaders(init.headers),
|
||||
body: init.body ? JSON.parse(String(init.body)) : null,
|
||||
});
|
||||
return buildLlamaResponse("42", "unsloth/gemma-4-26B-A4B-it-GGUF:UD-IQ2_M");
|
||||
};
|
||||
|
||||
@@ -167,7 +178,11 @@ test("llama-cpp provider: alias matching works via model catalog prefix", async
|
||||
);
|
||||
|
||||
const json = (await response.json()) as any;
|
||||
assert.equal(response.status, 200, `expected 200, got ${response.status}: ${JSON.stringify(json)}`);
|
||||
assert.equal(
|
||||
response.status,
|
||||
200,
|
||||
`expected 200, got ${response.status}: ${JSON.stringify(json)}`
|
||||
);
|
||||
assert.equal(json.choices[0].message.content, "42");
|
||||
});
|
||||
|
||||
|
||||
@@ -22,16 +22,15 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
// Import route AFTER setting DATA_DIR
|
||||
const embeddingProvidersRoute = await import(
|
||||
"../../src/app/api/memory/embedding-providers/route.ts"
|
||||
);
|
||||
const embeddingProvidersRoute =
|
||||
await import("../../src/app/api/memory/embedding-providers/route.ts");
|
||||
const { GET } = embeddingProvidersRoute;
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
@@ -22,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
// Import route AFTER setting DATA_DIR
|
||||
const engineStatusRoute = await import(
|
||||
"../../src/app/api/memory/engine-status/route.ts"
|
||||
);
|
||||
const engineStatusRoute = await import("../../src/app/api/memory/engine-status/route.ts");
|
||||
const { GET } = engineStatusRoute;
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -44,7 +42,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
@@ -67,7 +65,11 @@ test("GET /api/memory/engine-status — 200 + valid MemoryEngineStatusSchema sha
|
||||
assert.strictEqual(body.keyword.backend, "FTS5", "keyword.backend should be FTS5");
|
||||
|
||||
assert.ok(body.embedding, "should have embedding section");
|
||||
assert.strictEqual(typeof body.embedding.available, "boolean", "embedding.available should be boolean");
|
||||
assert.strictEqual(
|
||||
typeof body.embedding.available,
|
||||
"boolean",
|
||||
"embedding.available should be boolean"
|
||||
);
|
||||
assert.ok(typeof body.embedding.reason === "string", "embedding.reason should be a string");
|
||||
assert.ok(body.embedding.cacheStats, "should have cacheStats in embedding");
|
||||
assert.strictEqual(typeof body.embedding.cacheStats.hits, "number");
|
||||
@@ -77,7 +79,7 @@ test("GET /api/memory/engine-status — 200 + valid MemoryEngineStatusSchema sha
|
||||
assert.ok(body.vectorStore, "should have vectorStore section");
|
||||
assert.ok(
|
||||
["sqlite-vec", "qdrant", "none"].includes(body.vectorStore.backend),
|
||||
`vectorStore.backend should be valid: ${body.vectorStore.backend}`,
|
||||
`vectorStore.backend should be valid: ${body.vectorStore.backend}`
|
||||
);
|
||||
assert.strictEqual(typeof body.vectorStore.available, "boolean");
|
||||
assert.strictEqual(typeof body.vectorStore.rowCount, "number");
|
||||
|
||||
@@ -13,9 +13,7 @@ import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
makeManagementSessionRequest,
|
||||
} from "../helpers/managementSession.ts";
|
||||
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-reindex-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
@@ -25,16 +23,14 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
const memoryStore = await import("../../src/lib/memory/store.ts");
|
||||
|
||||
const reindexRoute = await import(
|
||||
"../../src/app/api/memory/reindex/route.ts"
|
||||
);
|
||||
const reindexRoute = await import("../../src/app/api/memory/reindex/route.ts");
|
||||
const { POST } = reindexRoute;
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -66,7 +62,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
@@ -12,9 +12,7 @@ import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
makeManagementSessionRequest,
|
||||
} from "../helpers/managementSession.ts";
|
||||
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-retrieve-preview-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
@@ -24,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
// Import route AFTER setting DATA_DIR
|
||||
const retrieveRoute = await import(
|
||||
"../../src/app/api/memory/retrieve-preview/route.ts"
|
||||
);
|
||||
const retrieveRoute = await import("../../src/app/api/memory/retrieve-preview/route.ts");
|
||||
const { POST } = retrieveRoute;
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -53,7 +49,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
@@ -106,9 +102,7 @@ test("POST /api/memory/retrieve-preview — 401 without auth when requireLogin=t
|
||||
|
||||
test("POST /api/memory/retrieve-preview — error path: no stack trace (invalid JSON)", async () => {
|
||||
// Test via invalid JSON body — the parse step should return 400 without a stack trace
|
||||
const { createManagementSessionHeaders } = await import(
|
||||
"../helpers/managementSession.ts"
|
||||
);
|
||||
const { createManagementSessionHeaders } = await import("../helpers/managementSession.ts");
|
||||
const headers = await createManagementSessionHeaders();
|
||||
|
||||
const req = new Request("http://localhost/api/memory/retrieve-preview", {
|
||||
|
||||
@@ -33,7 +33,7 @@ const { createMemory, getMemory } = memoryStore;
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
@@ -12,9 +12,7 @@ import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
makeManagementSessionRequest,
|
||||
} from "../helpers/managementSession.ts";
|
||||
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-summarize-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
@@ -24,16 +22,14 @@ const core = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
const memoryStore = await import("../../src/lib/memory/store.ts");
|
||||
|
||||
const summarizeRoute = await import(
|
||||
"../../src/app/api/memory/summarize/route.ts"
|
||||
);
|
||||
const summarizeRoute = await import("../../src/app/api/memory/summarize/route.ts");
|
||||
const { POST } = summarizeRoute;
|
||||
|
||||
// ── Helpers ──
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -61,7 +57,7 @@ async function seedOldMemory(daysAgo: number, apiKeyId = "api-key-1") {
|
||||
db.prepare("UPDATE memories SET created_at = ?, updated_at = ? WHERE id = ?").run(
|
||||
oldTs,
|
||||
oldTs,
|
||||
mem.id,
|
||||
mem.id
|
||||
);
|
||||
return mem;
|
||||
}
|
||||
@@ -75,7 +71,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
@@ -19,7 +19,7 @@ const healthRoute = await import("../../src/app/api/health/ping/route.ts");
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
modelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -30,7 +30,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test(
|
||||
|
||||
@@ -23,7 +23,7 @@ after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
if (originalHome === undefined) delete process.env.HOME;
|
||||
else process.env.HOME = originalHome;
|
||||
fs.rmSync(testHome, { recursive: true, force: true });
|
||||
fs.rmSync(testHome, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
function runOpencode(binary: string, args: string[]) {
|
||||
|
||||
@@ -226,7 +226,7 @@ describe("Performance: memory API route handler (1000 records)", () => {
|
||||
db.prepare("DELETE FROM memories WHERE api_key_id = ?").run(TEST_API_KEY_ID);
|
||||
// Final cleanup: reset DB instance and remove temp dir
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
it(`should handle GET /api/memory?limit=50 in <${THRESHOLD_API_ROUTE_MS}ms`, async () => {
|
||||
|
||||
@@ -19,16 +19,12 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// Set up a temp DATA_DIR so getDbInstance() initialises cleanly
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "omniroute-improve-prompt-")
|
||||
);
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-improve-prompt-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
// Disable mandatory auth for most tests
|
||||
process.env.REQUIRE_API_KEY = "false";
|
||||
|
||||
const { POST, OPTIONS } = await import(
|
||||
"../../src/app/api/playground/improve-prompt/route.ts"
|
||||
);
|
||||
const { POST, OPTIONS } = await import("../../src/app/api/playground/improve-prompt/route.ts");
|
||||
|
||||
const BASE_URL = "http://localhost:20128";
|
||||
|
||||
@@ -61,7 +57,7 @@ function postRequest(body: unknown): Request {
|
||||
// ─── Cleanup ─────────────────────────────────────────────────────────────────
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ─── OPTIONS ─────────────────────────────────────────────────────────────────
|
||||
@@ -84,7 +80,11 @@ test("happy path: system + prompt both provided", async () => {
|
||||
) as typeof fetch;
|
||||
|
||||
const res = await POST(
|
||||
postRequest({ system: "You are a helper.", prompt: "Tell me about AI.", model: "gpt-4o-mini" })
|
||||
postRequest({
|
||||
system: "You are a helper.",
|
||||
prompt: "Tell me about AI.",
|
||||
model: "gpt-4o-mini",
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 200);
|
||||
|
||||
@@ -161,15 +161,13 @@ test("happy path: usage defaults to 0 when not in upstream response", async () =
|
||||
try {
|
||||
// Return response without usage field
|
||||
globalThis.fetch = (async (_url: unknown, _opts: unknown) => {
|
||||
return new Response(
|
||||
JSON.stringify({ choices: [{ message: { content: "improved" } }] }),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
return new Response(JSON.stringify({ choices: [{ message: { content: "improved" } }] }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}) as typeof fetch;
|
||||
|
||||
const res = await POST(
|
||||
postRequest({ prompt: "Hello world", model: "gpt-4o-mini" })
|
||||
);
|
||||
const res = await POST(postRequest({ prompt: "Hello world", model: "gpt-4o-mini" }));
|
||||
assert.equal(res.status, 200);
|
||||
|
||||
const body = (await res.json()) as { tokensIn: number; tokensOut: number };
|
||||
@@ -245,9 +243,7 @@ test("upstream error returns sanitized error message — no stack trace in body"
|
||||
"Internal error\n at /home/user/project/src/handler.ts:42:10\n at process.nextTick"
|
||||
) as typeof fetch;
|
||||
|
||||
const res = await POST(
|
||||
postRequest({ prompt: "Hello", model: "gpt-4o-mini" })
|
||||
);
|
||||
const res = await POST(postRequest({ prompt: "Hello", model: "gpt-4o-mini" }));
|
||||
// Should be an error response (not 200)
|
||||
assert.ok(res.status >= 400);
|
||||
|
||||
@@ -270,9 +266,7 @@ test("upstream network error is sanitized", async () => {
|
||||
throw new Error("ECONNREFUSED connect ECONNREFUSED 127.0.0.1:20128");
|
||||
}) as typeof fetch;
|
||||
|
||||
const res = await POST(
|
||||
postRequest({ prompt: "Hello", model: "gpt-4o-mini" })
|
||||
);
|
||||
const res = await POST(postRequest({ prompt: "Hello", model: "gpt-4o-mini" }));
|
||||
assert.ok(res.status >= 500);
|
||||
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
@@ -289,9 +283,7 @@ test("401 when REQUIRE_API_KEY=true and no key provided", async () => {
|
||||
const originalRequired = process.env.REQUIRE_API_KEY;
|
||||
try {
|
||||
process.env.REQUIRE_API_KEY = "true";
|
||||
const res = await POST(
|
||||
postRequest({ prompt: "Test", model: "gpt-4o-mini" })
|
||||
);
|
||||
const res = await POST(postRequest({ prompt: "Test", model: "gpt-4o-mini" }));
|
||||
assert.equal(res.status, 401);
|
||||
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
|
||||
@@ -22,26 +22,28 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// Isolated DB per test file
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "omniroute-presets-crud-")
|
||||
);
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-presets-crud-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.REQUIRE_API_KEY = "false";
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
|
||||
// Import route handlers
|
||||
const { GET: listGet, POST: createPost, OPTIONS: listOptions } = await import(
|
||||
"../../src/app/api/playground/presets/route.ts"
|
||||
);
|
||||
const { GET: idGet, PUT: idPut, DELETE: idDelete, OPTIONS: idOptions } = await import(
|
||||
"../../src/app/api/playground/presets/[id]/route.ts"
|
||||
);
|
||||
const {
|
||||
GET: listGet,
|
||||
POST: createPost,
|
||||
OPTIONS: listOptions,
|
||||
} = await import("../../src/app/api/playground/presets/route.ts");
|
||||
const {
|
||||
GET: idGet,
|
||||
PUT: idPut,
|
||||
DELETE: idDelete,
|
||||
OPTIONS: idOptions,
|
||||
} = await import("../../src/app/api/playground/presets/[id]/route.ts");
|
||||
|
||||
const BASE_URL = "http://localhost:20128";
|
||||
|
||||
const UUID_V4_REGEX =
|
||||
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -86,7 +88,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ─── OPTIONS ─────────────────────────────────────────────────────────────────
|
||||
@@ -186,7 +188,12 @@ test("PUT /presets/[id] partial patch (name only) updates correctly", async () =
|
||||
);
|
||||
assert.equal(putRes.status, 200);
|
||||
|
||||
const updated = (await putRes.json()) as { id: string; name: string; endpoint: string; model: string };
|
||||
const updated = (await putRes.json()) as {
|
||||
id: string;
|
||||
name: string;
|
||||
endpoint: string;
|
||||
model: string;
|
||||
};
|
||||
assert.equal(updated.id, created.id);
|
||||
assert.equal(updated.name, "Updated Name");
|
||||
// Other fields should be preserved
|
||||
@@ -295,10 +302,7 @@ test("GET /presets/[id] with non-UUID id → 400", async () => {
|
||||
|
||||
test("PUT /presets/[id] with non-UUID id → 400", async () => {
|
||||
const badId = "also-not-a-uuid";
|
||||
const res = await idPut(
|
||||
putReq(badId, { name: "Whatever" }),
|
||||
await resolveParams(badId)
|
||||
);
|
||||
const res = await idPut(putReq(badId, { name: "Whatever" }), await resolveParams(badId));
|
||||
assert.equal(res.status, 400);
|
||||
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
|
||||
@@ -19,20 +19,18 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
// Isolated DB per test file
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "omniroute-presets-zod-")
|
||||
);
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-presets-zod-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.REQUIRE_API_KEY = "false";
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
|
||||
const { POST: createPost } = await import(
|
||||
"../../src/app/api/playground/presets/route.ts"
|
||||
);
|
||||
const { GET: idGet, PUT: idPut, DELETE: idDelete } = await import(
|
||||
"../../src/app/api/playground/presets/[id]/route.ts"
|
||||
);
|
||||
const { POST: createPost } = await import("../../src/app/api/playground/presets/route.ts");
|
||||
const {
|
||||
GET: idGet,
|
||||
PUT: idPut,
|
||||
DELETE: idDelete,
|
||||
} = await import("../../src/app/api/playground/presets/[id]/route.ts");
|
||||
|
||||
const BASE_URL = "http://localhost:20128";
|
||||
|
||||
@@ -83,7 +81,7 @@ test.beforeEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ─── POST validation ─────────────────────────────────────────────────────────
|
||||
@@ -133,9 +131,7 @@ test("POST with missing model → 400", async () => {
|
||||
});
|
||||
|
||||
test("POST with empty model → 400", async () => {
|
||||
const res = await createPost(
|
||||
postReq({ name: "Test", endpoint: "chat.completions", model: "" })
|
||||
);
|
||||
const res = await createPost(postReq({ name: "Test", endpoint: "chat.completions", model: "" }));
|
||||
assert.equal(res.status, 400);
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(body.error);
|
||||
@@ -145,7 +141,12 @@ test("POST with empty model → 400", async () => {
|
||||
test("POST with system > 50000 chars → 400", async () => {
|
||||
const longSystem = "x".repeat(50001);
|
||||
const res = await createPost(
|
||||
postReq({ name: "Big System", endpoint: "chat.completions", model: "gpt-4o", system: longSystem })
|
||||
postReq({
|
||||
name: "Big System",
|
||||
endpoint: "chat.completions",
|
||||
model: "gpt-4o",
|
||||
system: longSystem,
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 400);
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
@@ -156,7 +157,12 @@ test("POST with system > 50000 chars → 400", async () => {
|
||||
test("POST with system exactly 50000 chars → 201 (boundary: valid)", async () => {
|
||||
const maxSystem = "x".repeat(50000);
|
||||
const res = await createPost(
|
||||
postReq({ name: "Max System", endpoint: "chat.completions", model: "gpt-4o", system: maxSystem })
|
||||
postReq({
|
||||
name: "Max System",
|
||||
endpoint: "chat.completions",
|
||||
model: "gpt-4o",
|
||||
system: maxSystem,
|
||||
})
|
||||
);
|
||||
assert.equal(res.status, 201);
|
||||
});
|
||||
@@ -205,10 +211,7 @@ test("PUT with empty name → 400", async () => {
|
||||
test("PUT with system > 50000 chars → 400", async () => {
|
||||
const validId = "00000000-0000-4000-8000-000000000001";
|
||||
const longSystem = "y".repeat(50001);
|
||||
const res = await idPut(
|
||||
putReq(validId, { system: longSystem }),
|
||||
await resolveParams(validId)
|
||||
);
|
||||
const res = await idPut(putReq(validId, { system: longSystem }), await resolveParams(validId));
|
||||
assert.equal(res.status, 400);
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(body.error);
|
||||
|
||||
@@ -20,7 +20,11 @@ const { pluginManager } = await import("../../src/lib/plugins/manager.ts");
|
||||
// Scanner expects: sourceDir/<plugin-name>/plugin.json + index.js
|
||||
// Returns the sourceDir (parent) to pass to pluginManager.install()
|
||||
|
||||
function writeTestPlugin(opts?: { name?: string; onRequest?: boolean; enabledByDefault?: boolean }) {
|
||||
function writeTestPlugin(opts?: {
|
||||
name?: string;
|
||||
onRequest?: boolean;
|
||||
enabledByDefault?: boolean;
|
||||
}) {
|
||||
const name = opts?.name ?? "test-lifecycle-plugin";
|
||||
const onRequest = opts?.onRequest ?? true;
|
||||
const enabledByDefault = opts?.enabledByDefault ?? false;
|
||||
@@ -57,7 +61,7 @@ function writeTestPlugin(opts?: { name?: string; onRequest?: boolean; enabledByD
|
||||
// ── Helpers ──
|
||||
|
||||
function cleanupDir(dir: string) {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
|
||||
// Track temp source dirs for cleanup
|
||||
@@ -65,7 +69,9 @@ const activeSourceDirs: string[] = [];
|
||||
|
||||
function cleanupSourceDirs() {
|
||||
for (const dir of activeSourceDirs) {
|
||||
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
|
||||
try {
|
||||
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {}
|
||||
}
|
||||
activeSourceDirs.length = 0;
|
||||
}
|
||||
@@ -83,7 +89,9 @@ test.beforeEach(() => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
cleanupSourceDirs();
|
||||
try { cleanupDir(TEST_DATA_DIR); } catch {}
|
||||
try {
|
||||
cleanupDir(TEST_DATA_DIR);
|
||||
} catch {}
|
||||
});
|
||||
|
||||
// ── Tests: Install ──
|
||||
@@ -230,7 +238,11 @@ test("deactivate: unregisters all hooks for the plugin", async () => {
|
||||
|
||||
// Hook should be gone
|
||||
const after = hooks.getHooks("onRequest");
|
||||
assert.equal(after.find((r) => r.pluginName === name), undefined, "hook should be unregistered");
|
||||
assert.equal(
|
||||
after.find((r) => r.pluginName === name),
|
||||
undefined,
|
||||
"hook should be unregistered"
|
||||
);
|
||||
|
||||
await pluginManager.uninstall(name);
|
||||
});
|
||||
@@ -300,7 +312,10 @@ test("uninstall: deactivates before removing if active", async () => {
|
||||
|
||||
// Plugin should be fully gone
|
||||
assert.equal(dbPlugins.getPluginByName(name), null);
|
||||
assert.equal(hooks.getHooks("onRequest").find((r) => r.pluginName === name), undefined);
|
||||
assert.equal(
|
||||
hooks.getHooks("onRequest").find((r) => r.pluginName === name),
|
||||
undefined
|
||||
);
|
||||
});
|
||||
|
||||
test("uninstall: throws for nonexistent plugin", async () => {
|
||||
@@ -322,7 +337,10 @@ test("full lifecycle: install -> activate -> hook fires -> deactivate -> uninsta
|
||||
await pluginManager.activate(name);
|
||||
const afterActivate = dbPlugins.getPluginByName(name);
|
||||
assert.equal(afterActivate!.status, "active");
|
||||
assert.ok(hooks.getHooks("onRequest").find((r) => r.pluginName === name), "hook registered");
|
||||
assert.ok(
|
||||
hooks.getHooks("onRequest").find((r) => r.pluginName === name),
|
||||
"hook registered"
|
||||
);
|
||||
|
||||
// 3. Fire hook (use emitHookBlocking — child-process isolation means plugins cannot
|
||||
// mutate the parent's in-memory payload object; check the returned merged result).
|
||||
@@ -370,8 +388,14 @@ test("multiple plugins: hooks are isolated per plugin", async () => {
|
||||
await pluginManager.deactivate("multi-p1");
|
||||
|
||||
const afterDeactivate = hooks.getHooks("onRequest");
|
||||
assert.equal(afterDeactivate.find((r) => r.pluginName === "multi-p1"), undefined);
|
||||
assert.ok(afterDeactivate.find((r) => r.pluginName === "multi-p2"), "p2 hook still registered");
|
||||
assert.equal(
|
||||
afterDeactivate.find((r) => r.pluginName === "multi-p1"),
|
||||
undefined
|
||||
);
|
||||
assert.ok(
|
||||
afterDeactivate.find((r) => r.pluginName === "multi-p2"),
|
||||
"p2 hook still registered"
|
||||
);
|
||||
|
||||
// Cleanup
|
||||
await pluginManager.uninstall("multi-p1");
|
||||
|
||||
@@ -118,7 +118,7 @@ async function fetchCatalog(
|
||||
|
||||
test.before(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
// requireLogin + requireAuthForModels ON so the API-key surface is gated.
|
||||
await localDb.updateSettings({ requireLogin: true, requireAuthForModels: true, password: "" });
|
||||
@@ -127,7 +127,7 @@ test.before(async () => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test.describe("provider journey — in-process contract (#8330)", () => {
|
||||
|
||||
@@ -23,13 +23,13 @@ const proxyLogger = await import("../../src/lib/proxyLogger.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("integration: proxy create with inline assignment is atomic and clears legacy config", async () => {
|
||||
|
||||
@@ -45,7 +45,7 @@ const asNextRequest = (req: Request) => req as unknown as import("next/server").
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
// #5597 follow-up: the memory-settings cache is a module-level singleton that
|
||||
// survives per-test DB resets — bust it so each test starts from a clean read.
|
||||
@@ -88,7 +88,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── Settings GET ──
|
||||
|
||||
@@ -35,7 +35,7 @@ async function enableManagementAuth() {
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -91,7 +91,9 @@ test("GET /api/quota/plans includes DB override plans", async () => {
|
||||
// List should include the override
|
||||
const listReq = await makeManagementSessionRequest("http://localhost/api/quota/plans");
|
||||
const listRes = await plansRoute.GET(listReq);
|
||||
const body = (await listRes.json()) as { plans: Array<{ connectionId: string | null; source: string }> };
|
||||
const body = (await listRes.json()) as {
|
||||
plans: Array<{ connectionId: string | null; source: string }>;
|
||||
};
|
||||
const override = body.plans.find((p) => p.connectionId === "conn-override-1");
|
||||
assert.ok(override, "Override plan should appear in list");
|
||||
assert.equal(override?.source, "manual");
|
||||
@@ -160,13 +162,10 @@ test("PUT /api/quota/plans/[connectionId] without auth → 401", async () => {
|
||||
});
|
||||
|
||||
test("PUT /api/quota/plans/[connectionId] with invalid body → 400", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/quota/plans/conn-bad-body",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { dimensions: [] }, // PlanUpsertSchema requires min(1) dimensions
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/quota/plans/conn-bad-body", {
|
||||
method: "PUT",
|
||||
body: { dimensions: [] }, // PlanUpsertSchema requires min(1) dimensions
|
||||
});
|
||||
const res = await planIdRoute.PUT(req, {
|
||||
params: Promise.resolve({ connectionId: "conn-bad-body" }),
|
||||
});
|
||||
@@ -226,7 +225,9 @@ test("DELETE /api/quota/plans/[connectionId] clears override → 204; GET revert
|
||||
`http://localhost/api/quota/plans/${connectionId}`,
|
||||
{ method: "DELETE" }
|
||||
);
|
||||
const deleteRes = await planIdRoute.DELETE(deleteReq, { params: Promise.resolve({ connectionId }) });
|
||||
const deleteRes = await planIdRoute.DELETE(deleteReq, {
|
||||
params: Promise.resolve({ connectionId }),
|
||||
});
|
||||
assert.equal(deleteRes.status, 204);
|
||||
|
||||
// GET should now return auto/empty plan (no DB override)
|
||||
@@ -250,7 +251,10 @@ test("DELETE /api/quota/plans/[connectionId] clears override → 204; GET revert
|
||||
(e as Record<string, unknown>).target === connectionId &&
|
||||
(e as { metadata?: { reverted?: boolean } }).metadata?.reverted === true
|
||||
);
|
||||
assert.ok(deleteEvt, "quota.plan.updated audit event (reverted=true) must be present after DELETE");
|
||||
assert.ok(
|
||||
deleteEvt,
|
||||
"quota.plan.updated audit event (reverted=true) must be present after DELETE"
|
||||
);
|
||||
});
|
||||
|
||||
test("DELETE /api/quota/plans/[connectionId] is idempotent → 204 even when not found", async () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ type Db = {
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ test.beforeEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("DELETE pool waits for scoped quota-combo cleanup before returning 204", async () => {
|
||||
|
||||
@@ -35,7 +35,7 @@ const usageRoute = await import("../../src/app/api/quota/pools/[id]/usage/route.
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ test.beforeEach(() => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /usage surfaces catalog dimensions for a catalog-only pool (provider resolved from connection)", async () => {
|
||||
|
||||
@@ -38,7 +38,7 @@ async function enableManagementAuth() {
|
||||
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -91,7 +91,7 @@ test("POST /api/quota/pools with auth + valid body → 201 + pool returned", asy
|
||||
});
|
||||
const res = await poolsRoute.POST(req);
|
||||
assert.equal(res.status, 201);
|
||||
const body = await res.json() as { pool: { id: string; name: string; connectionId: string } };
|
||||
const body = (await res.json()) as { pool: { id: string; name: string; connectionId: string } };
|
||||
assert.ok(body.pool.id, "Pool should have an id");
|
||||
assert.equal(body.pool.name, "Test Pool Alpha");
|
||||
assert.equal(body.pool.connectionId, "conn-test-1");
|
||||
@@ -109,7 +109,10 @@ test("POST /api/quota/pools → audit event logged", async () => {
|
||||
const events = Array.isArray(logs) ? logs : [];
|
||||
assert.ok(events.length >= 1, "Should have at least one quota.pool.created audit event");
|
||||
const evt = events.find(
|
||||
(e) => typeof e === "object" && e !== null && (e as Record<string, unknown>).action === "quota.pool.created"
|
||||
(e) =>
|
||||
typeof e === "object" &&
|
||||
e !== null &&
|
||||
(e as Record<string, unknown>).action === "quota.pool.created"
|
||||
);
|
||||
assert.ok(evt, "quota.pool.created audit event must be present");
|
||||
});
|
||||
@@ -267,9 +270,7 @@ test("DELETE /api/quota/pools/[id] → 204 + audit event; subsequent GET → 404
|
||||
assert.ok(evt, "quota.pool.deleted audit event must be present");
|
||||
|
||||
// Subsequent GET → 404
|
||||
const getReq = await makeManagementSessionRequest(
|
||||
`http://localhost/api/quota/pools/${poolId}`
|
||||
);
|
||||
const getReq = await makeManagementSessionRequest(`http://localhost/api/quota/pools/${poolId}`);
|
||||
const getRes = await poolIdRoute.GET(getReq, { params: Promise.resolve({ id: poolId }) });
|
||||
assert.equal(getRes.status, 404);
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ async function enableManagementAuth() {
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ test.beforeEach(async () => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /api/quota/pools/[id]/usage without auth → 401", async () => {
|
||||
@@ -137,11 +137,7 @@ test("GET /api/quota/pools/[id]/usage → PoolUsageSnapshot shape with correct f
|
||||
// Even with no plan dimensions (empty plan for unknown provider), the response
|
||||
// is valid with an empty dimensions array — endpoint falls back to poolUsage()
|
||||
// which returns what's available from the store.
|
||||
assert.doesNotMatch(
|
||||
JSON.stringify(body),
|
||||
/\s+at\s+\//,
|
||||
"No stack trace in usage response"
|
||||
);
|
||||
assert.doesNotMatch(JSON.stringify(body), /\s+at\s+\//, "No stack trace in usage response");
|
||||
});
|
||||
|
||||
test("GET /api/quota/pools/[id]/usage response has required PoolUsageSnapshot fields", async () => {
|
||||
|
||||
@@ -40,7 +40,7 @@ async function enableManagementAuth() {
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -52,14 +52,12 @@ test.beforeEach(async () => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /api/quota/preview without auth → 401", async () => {
|
||||
await enableManagementAuth();
|
||||
const req = new Request(
|
||||
"http://localhost/api/quota/preview?apiKeyId=k1&poolId=p1"
|
||||
);
|
||||
const req = new Request("http://localhost/api/quota/preview?apiKeyId=k1&poolId=p1");
|
||||
const res = await previewRoute.GET(req);
|
||||
assert.equal(res.status, 401);
|
||||
});
|
||||
@@ -89,9 +87,7 @@ test("GET /api/quota/preview with nonexistent poolId → 404", async () => {
|
||||
test("GET /api/quota/preview with valid params → { decision } with kind", async () => {
|
||||
// Create a real pool
|
||||
const pool = createPool({ connectionId: "conn-preview", name: "Preview Pool" });
|
||||
upsertAllocations(pool.id, [
|
||||
{ apiKeyId: "preview-key-1", weight: 100, policy: "soft" },
|
||||
]);
|
||||
upsertAllocations(pool.id, [{ apiKeyId: "preview-key-1", weight: 100, policy: "soft" }]);
|
||||
|
||||
const req = await makeManagementSessionRequest(
|
||||
`http://localhost/api/quota/preview?apiKeyId=preview-key-1&poolId=${pool.id}&estimatedTokens=100`
|
||||
@@ -110,9 +106,7 @@ test("GET /api/quota/preview with valid params → { decision } with kind", asyn
|
||||
test("GET /api/quota/preview is dry-run: store counters unchanged after call", async () => {
|
||||
// Create pool and seed some consumption
|
||||
const pool = createPool({ connectionId: "conn-dryrun", name: "Dry Run Pool" });
|
||||
upsertAllocations(pool.id, [
|
||||
{ apiKeyId: "dryrun-key", weight: 100, policy: "hard" },
|
||||
]);
|
||||
upsertAllocations(pool.id, [{ apiKeyId: "dryrun-key", weight: 100, policy: "hard" }]);
|
||||
|
||||
const store = getSqliteQuotaStore();
|
||||
const dim = { poolId: pool.id, unit: "tokens" as const, window: "daily" as const };
|
||||
|
||||
@@ -17,9 +17,7 @@ import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { makeManagementSessionRequest } from "../helpers/managementSession.ts";
|
||||
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), "omniroute-quota-err-sanitization-")
|
||||
);
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-quota-err-sanitization-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.API_KEY_SECRET = "test-quota-sanitization-secret";
|
||||
process.env.QUOTA_STORE_DRIVER = "sqlite";
|
||||
@@ -42,7 +40,7 @@ const settingsRoute = await import("../../src/app/api/settings/quota-store/route
|
||||
function resetDb() {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -68,16 +66,8 @@ async function assertNoStackTrace(res: Response, label: string) {
|
||||
|
||||
// Helper to assert secret URL not in response body text
|
||||
function assertNoSecretUrlText(text: string, label: string) {
|
||||
assert.doesNotMatch(
|
||||
text,
|
||||
/secret-host/,
|
||||
`${label}: Response must not contain secret Redis host`
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
text,
|
||||
/redis:\/\/secret/,
|
||||
`${label}: Response must not contain Redis URL`
|
||||
);
|
||||
assert.doesNotMatch(text, /secret-host/, `${label}: Response must not contain secret Redis host`);
|
||||
assert.doesNotMatch(text, /redis:\/\/secret/, `${label}: Response must not contain Redis URL`);
|
||||
}
|
||||
|
||||
// Reads the response body once and runs both assertions (body cannot be read twice)
|
||||
@@ -96,7 +86,7 @@ test.after(() => {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
delete process.env.QUOTA_STORE_REDIS_URL;
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -118,9 +108,7 @@ test("POST /api/quota/pools 400 error response has no stack trace", async () =>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("GET /api/quota/pools/[id] 404 response has no stack trace", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/quota/pools/does-not-exist"
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/quota/pools/does-not-exist");
|
||||
const res = await poolIdRoute.GET(req, {
|
||||
params: Promise.resolve({ id: "does-not-exist" }),
|
||||
});
|
||||
@@ -179,13 +167,10 @@ test("GET /api/quota/plans 200 response has no stack trace or path leak", async
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("PUT /api/quota/plans/[connectionId] 400 error response has no stack trace", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/quota/plans/conn-bad",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { dimensions: [] }, // PlanUpsertSchema requires min(1)
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/quota/plans/conn-bad", {
|
||||
method: "PUT",
|
||||
body: { dimensions: [] }, // PlanUpsertSchema requires min(1)
|
||||
});
|
||||
const res = await planIdRoute.PUT(req, {
|
||||
params: Promise.resolve({ connectionId: "conn-bad" }),
|
||||
});
|
||||
@@ -212,9 +197,7 @@ test("GET /api/quota/preview 400 error response has no stack trace", async () =>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("GET /api/settings/quota-store response does not contain Redis URL (Hard Rule #12/#1)", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store"
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
|
||||
const res = await settingsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
await assertNoStackTraceAndNoSecretUrl(res, "GET /api/settings/quota-store 200");
|
||||
@@ -225,13 +208,10 @@ test("GET /api/settings/quota-store response does not contain Redis URL (Hard Ru
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("PUT /api/settings/quota-store 400 error response has no stack trace", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "baddriver" },
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "baddriver" },
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 400);
|
||||
await assertNoStackTrace(res, "PUT /api/settings/quota-store 400");
|
||||
@@ -242,13 +222,10 @@ test("PUT /api/settings/quota-store 400 error response has no stack trace", asyn
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test("PUT /api/settings/quota-store redis+no-URL error response does not leak Redis URL", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "redis" }, // No URL provided
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "redis" }, // No URL provided
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 400);
|
||||
await assertNoStackTraceAndNoSecretUrl(res, "PUT /api/settings/quota-store redis-no-url 400");
|
||||
|
||||
@@ -41,7 +41,7 @@ function resetDb() {
|
||||
resetQuotaStoreSingleton();
|
||||
delete process.env.QUOTA_STORE_REDIS_URL;
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ test.beforeEach(() => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
resetQuotaStoreSingleton();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -68,9 +68,7 @@ test("GET /api/settings/quota-store without auth → 401", async () => {
|
||||
});
|
||||
|
||||
test("GET /api/settings/quota-store returns driver + redisUrlConfigured (not URL)", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store"
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
|
||||
const res = await settingsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as {
|
||||
@@ -93,9 +91,7 @@ test("GET /api/settings/quota-store returns driver + redisUrlConfigured (not URL
|
||||
|
||||
test("GET /api/settings/quota-store redisUrlConfigured=false when no URL configured", async () => {
|
||||
delete process.env.QUOTA_STORE_REDIS_URL;
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store"
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store");
|
||||
const res = await settingsRoute.GET(req);
|
||||
const body = (await res.json()) as { redisUrlConfigured: boolean };
|
||||
assert.equal(body.redisUrlConfigured, false);
|
||||
@@ -117,13 +113,10 @@ test("PUT /api/settings/quota-store without auth → 401", async () => {
|
||||
});
|
||||
|
||||
test("PUT /api/settings/quota-store driver=sqlite → 200", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "sqlite" },
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "sqlite" },
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as { driver: string; redisUrl: null };
|
||||
@@ -132,13 +125,10 @@ test("PUT /api/settings/quota-store driver=sqlite → 200", async () => {
|
||||
});
|
||||
|
||||
test("PUT /api/settings/quota-store driver=redis without URL → 400", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "redis" }, // No redisUrl
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "redis" }, // No redisUrl
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json();
|
||||
@@ -147,13 +137,10 @@ test("PUT /api/settings/quota-store driver=redis without URL → 400", async ()
|
||||
});
|
||||
|
||||
test("PUT /api/settings/quota-store driver=redis with valid URL → 200 + audit event", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "redis", redisUrl: "redis://localhost:6379" },
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "redis", redisUrl: "redis://localhost:6379" },
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = (await res.json()) as {
|
||||
@@ -187,13 +174,10 @@ test("PUT /api/settings/quota-store driver=redis with valid URL → 200 + audit
|
||||
});
|
||||
|
||||
test("PUT /api/settings/quota-store with invalid driver → 400 (Zod)", async () => {
|
||||
const req = await makeManagementSessionRequest(
|
||||
"http://localhost/api/settings/quota-store",
|
||||
{
|
||||
method: "PUT",
|
||||
body: { driver: "memcached" }, // Not in enum
|
||||
}
|
||||
);
|
||||
const req = await makeManagementSessionRequest("http://localhost/api/settings/quota-store", {
|
||||
method: "PUT",
|
||||
body: { driver: "memcached" }, // Not in enum
|
||||
});
|
||||
const res = await settingsRoute.PUT(req);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json();
|
||||
|
||||
@@ -547,7 +547,7 @@ test.after(async () => {
|
||||
}
|
||||
await relay.stop();
|
||||
core.closeDbInstance();
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
await fsp.rm(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("resilience API only exposes configuration, not runtime breaker state", async () => {
|
||||
|
||||
@@ -102,7 +102,7 @@ async function seedRateLimitedConnection(provider: string) {
|
||||
/** Reset DB state between tests. */
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
await resetStorage();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -37,7 +37,7 @@ async function resetStorage() {
|
||||
readCacheDb.invalidateDbCache();
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ test.after(async () => {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.closeDbInstance();
|
||||
try {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
} catch {
|
||||
// best-effort cleanup
|
||||
}
|
||||
|
||||
@@ -19,30 +19,25 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-captur
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.INSPECTOR_HTTP_PROXY_PORT = "0"; // ephemeral port
|
||||
|
||||
const captureModesRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/route.ts"
|
||||
);
|
||||
const httpProxyRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts"
|
||||
);
|
||||
const systemProxyRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts"
|
||||
);
|
||||
const tlsInterceptRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts"
|
||||
);
|
||||
const { setHttpProxyHandle, getHttpProxyHandle, clearSystemProxy } = await import(
|
||||
"../../src/lib/inspector/captureState.ts"
|
||||
);
|
||||
const { __setExec } = await import(
|
||||
"../../src/mitm/inspector/systemProxyConfig.ts"
|
||||
);
|
||||
const captureModesRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/route.ts");
|
||||
const httpProxyRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts");
|
||||
const systemProxyRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts");
|
||||
const tlsInterceptRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts");
|
||||
const { setHttpProxyHandle, getHttpProxyHandle, clearSystemProxy } =
|
||||
await import("../../src/lib/inspector/captureState.ts");
|
||||
const { __setExec } = await import("../../src/mitm/inspector/systemProxyConfig.ts");
|
||||
|
||||
test.beforeEach(() => {
|
||||
// Ensure no running proxy handle leaks between tests
|
||||
const handle = getHttpProxyHandle();
|
||||
if (handle) {
|
||||
handle.stop().catch(() => {/* ignore */});
|
||||
handle.stop().catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
setHttpProxyHandle(null);
|
||||
}
|
||||
clearSystemProxy();
|
||||
@@ -52,10 +47,12 @@ test.after(() => {
|
||||
// Clean up any running proxy
|
||||
const handle = getHttpProxyHandle();
|
||||
if (handle) {
|
||||
handle.stop().catch(() => {/* ignore */});
|
||||
handle.stop().catch(() => {
|
||||
/* ignore */
|
||||
});
|
||||
setHttpProxyHandle(null);
|
||||
}
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── GET /capture-modes ──────────────────────────────────────────────────────
|
||||
@@ -63,7 +60,7 @@ test.after(() => {
|
||||
test("GET /capture-modes: returns status of all modes", async () => {
|
||||
const res = await captureModesRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as {
|
||||
const body = (await res.json()) as {
|
||||
agentBridge: boolean;
|
||||
httpProxy: { running: boolean; port: number | null };
|
||||
systemProxy: { applied: boolean };
|
||||
@@ -78,17 +75,14 @@ test("GET /capture-modes: returns status of all modes", async () => {
|
||||
// ── POST /capture-modes/http-proxy ─────────────────────────────────────────
|
||||
|
||||
test("http-proxy: start binds an ephemeral port", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "start" }),
|
||||
}
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "start" }),
|
||||
});
|
||||
const res = await httpProxyRoute.POST(req);
|
||||
assert.equal(res.status, 201);
|
||||
const body = await res.json() as { ok: boolean; running: boolean; port: number };
|
||||
const body = (await res.json()) as { ok: boolean; running: boolean; port: number };
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.running, true);
|
||||
assert.ok(body.port > 0, "should have a bound port");
|
||||
@@ -102,17 +96,14 @@ test("http-proxy: start binds an ephemeral port", async () => {
|
||||
});
|
||||
|
||||
test("http-proxy: stop when not running returns ok", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "stop" }),
|
||||
}
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "stop" }),
|
||||
});
|
||||
const res = await httpProxyRoute.POST(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { ok: boolean; running: boolean };
|
||||
const body = (await res.json()) as { ok: boolean; running: boolean };
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.running, false);
|
||||
});
|
||||
@@ -139,16 +130,14 @@ test("http-proxy: start then stop lifecycle", async () => {
|
||||
);
|
||||
const stopRes = await httpProxyRoute.POST(stopReq);
|
||||
assert.equal(stopRes.status, 200);
|
||||
const body = await stopRes.json() as { running: boolean };
|
||||
const body = (await stopRes.json()) as { running: boolean };
|
||||
assert.equal(body.running, false);
|
||||
});
|
||||
|
||||
test("http-proxy: EADDRINUSE returns 409 with structured error", async () => {
|
||||
// Import startHttpProxyServer directly so we can test the low-level error path
|
||||
// without depending on the module-cached DEFAULT_PORT.
|
||||
const { startHttpProxyServer } = await import(
|
||||
"../../src/mitm/inspector/httpProxyServer.ts"
|
||||
);
|
||||
const { startHttpProxyServer } = await import("../../src/mitm/inspector/httpProxyServer.ts");
|
||||
|
||||
// Occupy a random port
|
||||
const blocker = net.createServer();
|
||||
@@ -173,7 +162,10 @@ test("http-proxy: EADDRINUSE returns 409 with structured error", async () => {
|
||||
// ── POST /capture-modes/system-proxy ───────────────────────────────────────
|
||||
|
||||
test("system-proxy: apply with mocked OS commands", async () => {
|
||||
const restore = __setExec(async (_file, _args) => ({ stdout: "Enabled: No\nServer: \nPort: 0", stderr: "" }));
|
||||
const restore = __setExec(async (_file, _args) => ({
|
||||
stdout: "Enabled: No\nServer: \nPort: 0",
|
||||
stderr: "",
|
||||
}));
|
||||
try {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/capture-modes/system-proxy",
|
||||
@@ -185,7 +177,7 @@ test("system-proxy: apply with mocked OS commands", async () => {
|
||||
);
|
||||
const res = await systemProxyRoute.POST(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { ok: boolean; applied: boolean };
|
||||
const body = (await res.json()) as { ok: boolean; applied: boolean };
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.applied, true);
|
||||
} finally {
|
||||
@@ -207,7 +199,7 @@ test("system-proxy: revert without prior apply is a no-op", async () => {
|
||||
);
|
||||
const res = await systemProxyRoute.POST(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { applied: boolean };
|
||||
const body = (await res.json()) as { applied: boolean };
|
||||
assert.equal(body.applied, false);
|
||||
} finally {
|
||||
restore();
|
||||
@@ -240,7 +232,7 @@ test("tls-intercept: toggle on/off", async () => {
|
||||
);
|
||||
const enableRes = await tlsInterceptRoute.POST(enableReq);
|
||||
assert.equal(enableRes.status, 200);
|
||||
const enableBody = await enableRes.json() as { tlsIntercept: { enabled: boolean } };
|
||||
const enableBody = (await enableRes.json()) as { tlsIntercept: { enabled: boolean } };
|
||||
assert.equal(enableBody.tlsIntercept.enabled, true);
|
||||
|
||||
const disableReq = new Request(
|
||||
@@ -253,6 +245,6 @@ test("tls-intercept: toggle on/off", async () => {
|
||||
);
|
||||
const disableRes = await tlsInterceptRoute.POST(disableReq);
|
||||
assert.equal(disableRes.status, 200);
|
||||
const disableBody = await disableRes.json() as { tlsIntercept: { enabled: boolean } };
|
||||
const disableBody = (await disableRes.json()) as { tlsIntercept: { enabled: boolean } };
|
||||
assert.equal(disableBody.tlsIntercept.enabled, false);
|
||||
});
|
||||
|
||||
@@ -17,53 +17,36 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
|
||||
|
||||
const requestsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/route.ts"
|
||||
);
|
||||
const requestDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts"
|
||||
);
|
||||
const annotationRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts"
|
||||
);
|
||||
const hostsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/hosts/route.ts"
|
||||
);
|
||||
const hostDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts"
|
||||
);
|
||||
const sessionsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
|
||||
);
|
||||
const sessionDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
|
||||
);
|
||||
const ingestRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts"
|
||||
);
|
||||
const httpProxyRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts"
|
||||
);
|
||||
const systemProxyRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts"
|
||||
);
|
||||
const tlsInterceptRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts"
|
||||
);
|
||||
const requestsRoute = await import("../../src/app/api/tools/traffic-inspector/requests/route.ts");
|
||||
const requestDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts");
|
||||
const annotationRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts");
|
||||
const hostsRoute = await import("../../src/app/api/tools/traffic-inspector/hosts/route.ts");
|
||||
const hostDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts");
|
||||
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
|
||||
const sessionDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
|
||||
const ingestRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts");
|
||||
const httpProxyRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/http-proxy/route.ts");
|
||||
const systemProxyRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/system-proxy/route.ts");
|
||||
const tlsInterceptRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/capture-modes/tls-intercept/route.ts");
|
||||
|
||||
function noStackTrace(msg: string, label: string): void {
|
||||
assert.ok(
|
||||
!msg.includes("at /"),
|
||||
`${label}: error message must not contain stack trace (found "at /")`
|
||||
);
|
||||
assert.ok(
|
||||
!msg.includes(".ts:"),
|
||||
`${label}: error message must not include TS file paths`
|
||||
);
|
||||
assert.ok(!msg.includes(".ts:"), `${label}: error message must not include TS file paths`);
|
||||
}
|
||||
|
||||
async function getErrorMessage(res: Response): Promise<string> {
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
return body.error?.message ?? "";
|
||||
}
|
||||
|
||||
@@ -72,23 +55,20 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("requests: invalid profile param does not leak stack", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/requests?profile=BAD"
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=BAD");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 400);
|
||||
noStackTrace(await getErrorMessage(res), "GET /requests");
|
||||
});
|
||||
|
||||
test("requests/[id]: unknown id does not leak stack", async () => {
|
||||
const res = await requestDetailRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: randomUUID() }) }
|
||||
);
|
||||
const res = await requestDetailRoute.GET(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: randomUUID() }),
|
||||
});
|
||||
assert.equal(res.status, 404);
|
||||
noStackTrace(await getErrorMessage(res), "GET /requests/[id]");
|
||||
});
|
||||
@@ -148,40 +128,33 @@ test("hosts/[host] PATCH: invalid body does not leak stack", async () => {
|
||||
});
|
||||
|
||||
test("sessions: 404 does not leak stack", async () => {
|
||||
const res = await sessionDetailRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: randomUUID() }) }
|
||||
);
|
||||
const res = await sessionDetailRoute.GET(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: randomUUID() }),
|
||||
});
|
||||
assert.equal(res.status, 404);
|
||||
noStackTrace(await getErrorMessage(res), "GET /sessions/[id]");
|
||||
});
|
||||
|
||||
test("ingest: 403 does not leak stack", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/internal/ingest",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: "Bearer wrong-token",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
}
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
authorization: "Bearer wrong-token",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
const res = await ingestRoute.POST(req);
|
||||
assert.equal(res.status, 403);
|
||||
noStackTrace(await getErrorMessage(res), "POST /internal/ingest (403)");
|
||||
});
|
||||
|
||||
test("http-proxy: invalid action does not leak stack", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy",
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "invalid" }),
|
||||
}
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/capture-modes/http-proxy", {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({ action: "invalid" }),
|
||||
});
|
||||
const res = await httpProxyRoute.POST(req);
|
||||
assert.equal(res.status, 400);
|
||||
noStackTrace(await getErrorMessage(res), "POST /capture-modes/http-proxy");
|
||||
|
||||
@@ -18,29 +18,26 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const localDb = await import("../../src/lib/localDb.ts");
|
||||
|
||||
const hostsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/hosts/route.ts"
|
||||
);
|
||||
const hostDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts"
|
||||
);
|
||||
const hostsRoute = await import("../../src/app/api/tools/traffic-inspector/hosts/route.ts");
|
||||
const hostDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/hosts/[host]/route.ts");
|
||||
|
||||
test.beforeEach(async () => {
|
||||
resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
// Re-init DB with fresh migrations
|
||||
await import("../../src/lib/db/core.ts").then((m) => m.getDbInstance());
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /hosts: returns empty list initially", async () => {
|
||||
const res = await hostsRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { hosts: unknown[] };
|
||||
const body = (await res.json()) as { hosts: unknown[] };
|
||||
assert.deepEqual(body.hosts, []);
|
||||
});
|
||||
|
||||
@@ -52,13 +49,13 @@ test("POST /hosts: adds a host", async () => {
|
||||
});
|
||||
const res = await hostsRoute.POST(req);
|
||||
assert.equal(res.status, 201);
|
||||
const body = await res.json() as { ok: boolean; host: string };
|
||||
const body = (await res.json()) as { ok: boolean; host: string };
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.host, "api.openai.com");
|
||||
|
||||
// Verify it appears in list
|
||||
const listRes = await hostsRoute.GET();
|
||||
const list = await listRes.json() as { hosts: Array<{ host: string }> };
|
||||
const list = (await listRes.json()) as { hosts: Array<{ host: string }> };
|
||||
assert.ok(list.hosts.some((h) => h.host === "api.openai.com"));
|
||||
});
|
||||
|
||||
@@ -70,7 +67,7 @@ test("POST /hosts: rejects empty host string", async () => {
|
||||
});
|
||||
const res = await hostsRoute.POST(req);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
@@ -94,15 +91,14 @@ test("DELETE /hosts/[host]: removes existing host", async () => {
|
||||
await hostsRoute.POST(addReq);
|
||||
|
||||
// Now delete it
|
||||
const delRes = await hostDetailRoute.DELETE(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ host: "remove-me.example.com" }) }
|
||||
);
|
||||
const delRes = await hostDetailRoute.DELETE(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ host: "remove-me.example.com" }),
|
||||
});
|
||||
assert.equal(delRes.status, 204);
|
||||
|
||||
// Verify gone
|
||||
const listRes = await hostsRoute.GET();
|
||||
const list = await listRes.json() as { hosts: Array<{ host: string }> };
|
||||
const list = (await listRes.json()) as { hosts: Array<{ host: string }> };
|
||||
assert.ok(!list.hosts.some((h) => h.host === "remove-me.example.com"));
|
||||
});
|
||||
|
||||
@@ -125,7 +121,7 @@ test("PATCH /hosts/[host]: toggles enabled flag", async () => {
|
||||
{ params: Promise.resolve({ host: "toggle-me.example.com" }) }
|
||||
);
|
||||
assert.equal(patchRes.status, 200);
|
||||
const body = await patchRes.json() as { enabled: boolean };
|
||||
const body = (await patchRes.json()) as { enabled: boolean };
|
||||
assert.equal(body.enabled, false);
|
||||
});
|
||||
|
||||
|
||||
@@ -23,9 +23,8 @@ const VALID_TOKEN = "test-ingest-token-abc123xyz789-longer-than-16";
|
||||
process.env.INSPECTOR_INTERNAL_INGEST_TOKEN = VALID_TOKEN;
|
||||
|
||||
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
|
||||
const ingestRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts"
|
||||
);
|
||||
const ingestRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/internal/ingest/route.ts");
|
||||
|
||||
function makeIngestRequest(token: string | null, body: unknown): Request {
|
||||
const headers: Record<string, string> = {
|
||||
@@ -34,14 +33,11 @@ function makeIngestRequest(token: string | null, body: unknown): Request {
|
||||
if (token !== null) {
|
||||
headers["authorization"] = `Bearer ${token}`;
|
||||
}
|
||||
return new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/internal/ingest",
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
}
|
||||
);
|
||||
return new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
}
|
||||
|
||||
function minimalEntry(overrides: Record<string, unknown> = {}): Record<string, unknown> {
|
||||
@@ -66,14 +62,14 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("ingest: POST without Authorization header → 403", async () => {
|
||||
const req = makeIngestRequest(null, minimalEntry());
|
||||
const res = await ingestRoute.POST(req);
|
||||
assert.equal(res.status, 403);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
@@ -94,7 +90,7 @@ test("ingest: POST with valid token + valid body → 200 + buffer push", async (
|
||||
const req = makeIngestRequest(VALID_TOKEN, minimalEntry({ id }));
|
||||
const res = await ingestRoute.POST(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { ok: boolean; id: string };
|
||||
const body = (await res.json()) as { ok: boolean; id: string };
|
||||
assert.equal(body.ok, true);
|
||||
assert.equal(body.id, id);
|
||||
|
||||
@@ -113,23 +109,20 @@ test("ingest: valid token + missing required field → 400", async () => {
|
||||
});
|
||||
const res = await ingestRoute.POST(req);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
test("ingest: valid token + invalid JSON → 400", async () => {
|
||||
const headers: Record<string, string> = {
|
||||
"content-type": "application/json",
|
||||
"authorization": `Bearer ${VALID_TOKEN}`,
|
||||
authorization: `Bearer ${VALID_TOKEN}`,
|
||||
};
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/internal/ingest",
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: "not valid json",
|
||||
}
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/internal/ingest", {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: "not valid json",
|
||||
});
|
||||
const res = await ingestRoute.POST(req);
|
||||
assert.equal(res.status, 400);
|
||||
});
|
||||
|
||||
@@ -14,12 +14,10 @@ import path from "node:path";
|
||||
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-local-"));
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const { isLocalOnlyPath, isLoopbackHost } = await import(
|
||||
"../../src/server/authz/routeGuard.ts"
|
||||
);
|
||||
const { isLocalOnlyPath, isLoopbackHost } = await import("../../src/server/authz/routeGuard.ts");
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// ── isLocalOnlyPath assertions ──────────────────────────────────────────────
|
||||
|
||||
@@ -16,23 +16,21 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-ti-reqs-"
|
||||
process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const { globalTrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
|
||||
const requestsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/route.ts"
|
||||
);
|
||||
const requestDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts"
|
||||
);
|
||||
const annotationRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts"
|
||||
);
|
||||
const requestsRoute = await import("../../src/app/api/tools/traffic-inspector/requests/route.ts");
|
||||
const requestDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/route.ts");
|
||||
const annotationRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/requests/[id]/annotation/route.ts");
|
||||
|
||||
function makeEntry(overrides: Partial<{
|
||||
id: string;
|
||||
host: string;
|
||||
detectedKind: "llm" | "app" | "unknown";
|
||||
status: number | "in-flight" | "error";
|
||||
source: "agent-bridge" | "custom-host" | "http-proxy" | "system-proxy";
|
||||
}> = {}) {
|
||||
function makeEntry(
|
||||
overrides: Partial<{
|
||||
id: string;
|
||||
host: string;
|
||||
detectedKind: "llm" | "app" | "unknown";
|
||||
status: number | "in-flight" | "error";
|
||||
source: "agent-bridge" | "custom-host" | "http-proxy" | "system-proxy";
|
||||
}> = {}
|
||||
) {
|
||||
return {
|
||||
id: randomUUID(),
|
||||
source: "agent-bridge" as const,
|
||||
@@ -57,14 +55,14 @@ test.beforeEach(() => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("GET /requests: returns empty list when buffer is empty", async () => {
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { requests: unknown[]; total: number };
|
||||
const body = (await res.json()) as { requests: unknown[]; total: number };
|
||||
assert.deepEqual(body.requests, []);
|
||||
assert.equal(body.total, 0);
|
||||
});
|
||||
@@ -76,7 +74,7 @@ test("GET /requests: returns all entries without filter", async () => {
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { requests: unknown[]; total: number };
|
||||
const body = (await res.json()) as { requests: unknown[]; total: number };
|
||||
assert.equal(body.total, 2);
|
||||
});
|
||||
|
||||
@@ -84,12 +82,10 @@ test("GET /requests: filters by profile=llm", async () => {
|
||||
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), detectedKind: "llm" }));
|
||||
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), detectedKind: "app" }));
|
||||
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/requests?profile=llm"
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=llm");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { requests: unknown[]; total: number };
|
||||
const body = (await res.json()) as { requests: unknown[]; total: number };
|
||||
assert.equal(body.total, 1);
|
||||
});
|
||||
|
||||
@@ -97,23 +93,19 @@ test("GET /requests: filters by host", async () => {
|
||||
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), host: "target.com" }));
|
||||
globalTrafficBuffer.push(makeEntry({ id: randomUUID(), host: "other.com" }));
|
||||
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/requests?host=target.com"
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?host=target.com");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { requests: Array<{ host: string }>; total: number };
|
||||
const body = (await res.json()) as { requests: Array<{ host: string }>; total: number };
|
||||
assert.equal(body.total, 1);
|
||||
assert.equal(body.requests[0]?.host, "target.com");
|
||||
});
|
||||
|
||||
test("GET /requests: rejects invalid profile param with 400", async () => {
|
||||
const req = new Request(
|
||||
"http://localhost/api/tools/traffic-inspector/requests?profile=invalid"
|
||||
);
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/requests?profile=invalid");
|
||||
const res = await requestsRoute.GET(req);
|
||||
assert.equal(res.status, 400);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
@@ -134,19 +126,17 @@ test("GET /requests/[id]: returns entry by id", async () => {
|
||||
params: Promise.resolve({ id: entry.id }),
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { id: string };
|
||||
const body = (await res.json()) as { id: string };
|
||||
assert.equal(body.id, entry.id);
|
||||
});
|
||||
|
||||
test("GET /requests/[id]: returns 404 for unknown id", async () => {
|
||||
const req = new Request(
|
||||
`http://localhost/api/tools/traffic-inspector/requests/${randomUUID()}`
|
||||
);
|
||||
const req = new Request(`http://localhost/api/tools/traffic-inspector/requests/${randomUUID()}`);
|
||||
const res = await requestDetailRoute.GET(req, {
|
||||
params: Promise.resolve({ id: randomUUID() }),
|
||||
});
|
||||
assert.equal(res.status, 404);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
@@ -166,7 +156,7 @@ test("PUT /requests/[id]/annotation: attaches annotation", async () => {
|
||||
params: Promise.resolve({ id: entry.id }),
|
||||
});
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { annotation: string };
|
||||
const body = (await res.json()) as { annotation: string };
|
||||
assert.equal(body.annotation, "my note");
|
||||
|
||||
// Confirm buffer was updated
|
||||
|
||||
@@ -17,20 +17,16 @@ const { resetDbInstance, getDbInstance } = await import("../../src/lib/db/core.t
|
||||
|
||||
async function resetStorage() {
|
||||
resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
getDbInstance();
|
||||
}
|
||||
|
||||
const sessionsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
|
||||
);
|
||||
const sessionDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
|
||||
);
|
||||
const sessionRequestsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/[id]/requests/route.ts"
|
||||
);
|
||||
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
|
||||
const sessionDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
|
||||
const sessionRequestsRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/requests/route.ts");
|
||||
|
||||
async function createSession(name?: string): Promise<string> {
|
||||
const res = await sessionsRoute.POST(
|
||||
@@ -61,7 +57,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(() => {
|
||||
resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("POST /sessions/[id]/requests: seq increments 1, 2, 3", async () => {
|
||||
@@ -131,7 +127,7 @@ test("POST /sessions/[id]/requests: error response does not leak stack trace", a
|
||||
// POST to non-existent session — exercises the 404 path error body
|
||||
const res = await postRequest("00000000-0000-4000-8000-000000000099", "data");
|
||||
assert.equal(res.status, 404);
|
||||
const body = await res.json() as { error?: { message?: string } };
|
||||
const body = (await res.json()) as { error?: { message?: string } };
|
||||
const msg = body?.error?.message ?? "";
|
||||
assert.ok(!msg.includes("at /"), "should not contain stack trace");
|
||||
});
|
||||
|
||||
@@ -18,21 +18,17 @@ const { resetDbInstance, getDbInstance } = await import("../../src/lib/db/core.t
|
||||
|
||||
async function resetStorage() {
|
||||
resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
// Re-initialize db
|
||||
getDbInstance();
|
||||
}
|
||||
|
||||
const sessionsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/route.ts"
|
||||
);
|
||||
const sessionDetailRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts"
|
||||
);
|
||||
const sessionHarRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/sessions/[id]/export.har/route.ts"
|
||||
);
|
||||
const sessionsRoute = await import("../../src/app/api/tools/traffic-inspector/sessions/route.ts");
|
||||
const sessionDetailRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/route.ts");
|
||||
const sessionHarRoute =
|
||||
await import("../../src/app/api/tools/traffic-inspector/sessions/[id]/export.har/route.ts");
|
||||
const { appendSessionRequest } = await import("../../src/lib/db/inspectorSessions.ts");
|
||||
|
||||
test.beforeEach(async () => {
|
||||
@@ -40,7 +36,7 @@ test.beforeEach(async () => {
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("POST /sessions: creates a session", async () => {
|
||||
@@ -51,7 +47,7 @@ test("POST /sessions: creates a session", async () => {
|
||||
});
|
||||
const res = await sessionsRoute.POST(req);
|
||||
assert.equal(res.status, 201);
|
||||
const body = await res.json() as { id: string; started_at: string };
|
||||
const body = (await res.json()) as { id: string; started_at: string };
|
||||
assert.ok(body.id, "should have an id");
|
||||
assert.ok(body.started_at, "should have started_at");
|
||||
});
|
||||
@@ -85,7 +81,7 @@ test("GET /sessions: lists all sessions", async () => {
|
||||
|
||||
const res = await sessionsRoute.GET();
|
||||
assert.equal(res.status, 200);
|
||||
const body = await res.json() as { sessions: unknown[] };
|
||||
const body = (await res.json()) as { sessions: unknown[] };
|
||||
assert.equal(body.sessions.length, 2);
|
||||
});
|
||||
|
||||
@@ -97,7 +93,7 @@ test("PATCH /sessions/[id]: stop adds ended_at", async () => {
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
);
|
||||
const session = await createRes.json() as { id: string };
|
||||
const session = (await createRes.json()) as { id: string };
|
||||
|
||||
const patchReq = new Request("http://localhost/", {
|
||||
method: "PATCH",
|
||||
@@ -108,7 +104,7 @@ test("PATCH /sessions/[id]: stop adds ended_at", async () => {
|
||||
params: Promise.resolve({ id: session.id }),
|
||||
});
|
||||
assert.equal(patchRes.status, 200);
|
||||
const body = await patchRes.json() as { ended_at: string | null };
|
||||
const body = (await patchRes.json()) as { ended_at: string | null };
|
||||
assert.ok(body.ended_at !== null, "ended_at should be set after stop");
|
||||
});
|
||||
|
||||
@@ -120,7 +116,7 @@ test("PATCH /sessions/[id]: rename updates name", async () => {
|
||||
body: JSON.stringify({ name: "old-name" }),
|
||||
})
|
||||
);
|
||||
const session = await createRes.json() as { id: string };
|
||||
const session = (await createRes.json()) as { id: string };
|
||||
|
||||
const patchRes = await sessionDetailRoute.PATCH(
|
||||
new Request("http://localhost/", {
|
||||
@@ -131,7 +127,7 @@ test("PATCH /sessions/[id]: rename updates name", async () => {
|
||||
{ params: Promise.resolve({ id: session.id }) }
|
||||
);
|
||||
assert.equal(patchRes.status, 200);
|
||||
const body = await patchRes.json() as { name: string };
|
||||
const body = (await patchRes.json()) as { name: string };
|
||||
assert.equal(body.name, "new-name");
|
||||
});
|
||||
|
||||
@@ -143,7 +139,7 @@ test("GET /sessions/[id]: returns session with requests", async () => {
|
||||
body: JSON.stringify({ name: "with-reqs" }),
|
||||
})
|
||||
);
|
||||
const session = await createRes.json() as { id: string };
|
||||
const session = (await createRes.json()) as { id: string };
|
||||
|
||||
// Append a fake request
|
||||
const payload = JSON.stringify({
|
||||
@@ -163,12 +159,11 @@ test("GET /sessions/[id]: returns session with requests", async () => {
|
||||
});
|
||||
appendSessionRequest(session.id, payload);
|
||||
|
||||
const getRes = await sessionDetailRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: session.id }) }
|
||||
);
|
||||
const getRes = await sessionDetailRoute.GET(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: session.id }),
|
||||
});
|
||||
assert.equal(getRes.status, 200);
|
||||
const body = await getRes.json() as { session: { id: string }; requests: unknown[] };
|
||||
const body = (await getRes.json()) as { session: { id: string }; requests: unknown[] };
|
||||
assert.equal(body.session.id, session.id);
|
||||
assert.equal(body.requests.length, 1);
|
||||
});
|
||||
@@ -181,21 +176,19 @@ test("DELETE /sessions/[id]: cascades requests", async () => {
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
);
|
||||
const session = await createRes.json() as { id: string };
|
||||
const session = (await createRes.json()) as { id: string };
|
||||
|
||||
appendSessionRequest(session.id, JSON.stringify({ note: "test" }));
|
||||
|
||||
const delRes = await sessionDetailRoute.DELETE(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: session.id }) }
|
||||
);
|
||||
const delRes = await sessionDetailRoute.DELETE(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: session.id }),
|
||||
});
|
||||
assert.equal(delRes.status, 204);
|
||||
|
||||
// Session should be gone
|
||||
const getRes = await sessionDetailRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: session.id }) }
|
||||
);
|
||||
const getRes = await sessionDetailRoute.GET(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: session.id }),
|
||||
});
|
||||
assert.equal(getRes.status, 404);
|
||||
});
|
||||
|
||||
@@ -207,7 +200,7 @@ test("GET /sessions/[id]/export.har: returns HAR file", async () => {
|
||||
body: JSON.stringify({ name: "har-test" }),
|
||||
})
|
||||
);
|
||||
const session = await createRes.json() as { id: string };
|
||||
const session = (await createRes.json()) as { id: string };
|
||||
|
||||
const reqPayload = {
|
||||
id: randomUUID(),
|
||||
@@ -226,16 +219,15 @@ test("GET /sessions/[id]/export.har: returns HAR file", async () => {
|
||||
};
|
||||
appendSessionRequest(session.id, JSON.stringify(reqPayload));
|
||||
|
||||
const harRes = await sessionHarRoute.GET(
|
||||
new Request("http://localhost/"),
|
||||
{ params: Promise.resolve({ id: session.id }) }
|
||||
);
|
||||
const harRes = await sessionHarRoute.GET(new Request("http://localhost/"), {
|
||||
params: Promise.resolve({ id: session.id }),
|
||||
});
|
||||
assert.equal(harRes.status, 200);
|
||||
assert.ok(
|
||||
harRes.headers.get("content-disposition")?.includes(".har"),
|
||||
"should have .har filename"
|
||||
);
|
||||
const har = await harRes.json() as { log: { entries: unknown[] } };
|
||||
const har = (await harRes.json()) as { log: { entries: unknown[] } };
|
||||
assert.ok(har.log, "should be a HAR object");
|
||||
assert.equal(har.log.entries.length, 1);
|
||||
});
|
||||
|
||||
@@ -18,9 +18,7 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
process.env.INSPECTOR_BUFFER_SIZE = "100";
|
||||
|
||||
const { TrafficBuffer } = await import("../../src/mitm/inspector/buffer.ts");
|
||||
const wsRoute = await import(
|
||||
"../../src/app/api/tools/traffic-inspector/ws/route.ts"
|
||||
);
|
||||
const wsRoute = await import("../../src/app/api/tools/traffic-inspector/ws/route.ts");
|
||||
|
||||
function makeRequest(upgrade = "websocket", clientKey = "dGhlIHNhbXBsZSBub25jZQ=="): Request {
|
||||
return new Request("http://localhost/api/tools/traffic-inspector/ws", {
|
||||
@@ -33,14 +31,14 @@ function makeRequest(upgrade = "websocket", clientKey = "dGhlIHNhbXBsZSBub25jZQ=
|
||||
}
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("ws/route: rejects non-WebSocket GET with 426", async () => {
|
||||
const req = new Request("http://localhost/api/tools/traffic-inspector/ws");
|
||||
const res = await wsRoute.GET(req);
|
||||
assert.equal(res.status, 426);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(body.error.message.includes("Upgrade"), "should mention upgrade");
|
||||
});
|
||||
|
||||
@@ -57,7 +55,7 @@ test("ws/route: rejects when no raw socket available with 500", async () => {
|
||||
// No `.socket` property injected — Next.js standalone would attach it
|
||||
const res = await wsRoute.GET(req);
|
||||
assert.equal(res.status, 500);
|
||||
const body = await res.json() as { error: { message: string } };
|
||||
const body = (await res.json()) as { error: { message: string } };
|
||||
assert.ok(!body.error.message.includes("at /"), "must not leak stack trace");
|
||||
});
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ function productionShapedSynchronousRefresh() {
|
||||
}
|
||||
|
||||
test.after(() => {
|
||||
fs.rmSync(CACHE_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(CACHE_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("the /v1/models route wires Next after() as its response-flush-safe scheduler", () => {
|
||||
@@ -152,7 +152,7 @@ test("an external client receives the stale body before synchronous refresh fini
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "EPERM") {
|
||||
t.skip("sandbox does not permit opening HTTP listener sockets");
|
||||
fs.rmSync(socketDir, { recursive: true, force: true });
|
||||
fs.rmSync(socketDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
@@ -177,7 +177,7 @@ test("an external client receives the stale body before synchronous refresh fini
|
||||
);
|
||||
} finally {
|
||||
await close(server);
|
||||
fs.rmSync(socketDir, { recursive: true, force: true });
|
||||
fs.rmSync(socketDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
catalogCache.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -112,7 +112,9 @@ test(
|
||||
{ skip: REAL_FFMPEG_SKIP },
|
||||
async (context) => {
|
||||
const directory = await mkdtemp(join(tmpdir(), "omniroute-video-sampler-fixtures-"));
|
||||
context.after(async () => rm(directory, { force: true, recursive: true }));
|
||||
context.after(async () =>
|
||||
rm(directory, { force: true, recursive: true, maxRetries: 5, retryDelay: 100 })
|
||||
);
|
||||
|
||||
const rapidCuts = await createRapidEdgeCutFixture(directory);
|
||||
await context.test("rapid cuts near both ends retain coverage within the cap", async () => {
|
||||
|
||||
@@ -49,7 +49,7 @@ async function readTransformed(chunks: string[], options: object): Promise<strin
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
if (fs.existsSync(TEST_DATA_DIR)) {
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -59,7 +59,8 @@ function leakedUpstreamControlLines(output: string): string[] {
|
||||
.trim()
|
||||
.split("\n")
|
||||
.filter(
|
||||
(l) => /^(?:id:|event:|retry:)/i.test(l) || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
|
||||
(l) =>
|
||||
/^(?:id:|event:|retry:)/i.test(l) || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,7 +167,9 @@ test("#10017: OpenAI Responses passthrough KEEPS event framing (regression guard
|
||||
"Responses output_text.delta event framing must be preserved"
|
||||
);
|
||||
assert.ok(
|
||||
!lines.some((l) => l.startsWith("id:") || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))),
|
||||
!lines.some(
|
||||
(l) => l.startsWith("id:") || (l.startsWith(":") && !l.startsWith(": x-omniroute-"))
|
||||
),
|
||||
"Responses passthrough must still strip id:/comment control lines"
|
||||
);
|
||||
});
|
||||
@@ -191,5 +194,8 @@ test("#10017: Claude Messages passthrough KEEPS event framing", async () => {
|
||||
|
||||
const lines = text.trim().split("\n");
|
||||
assert.ok(lines.includes("event: message_start"), "Claude event framing must be preserved");
|
||||
assert.ok(lines.includes("event: content_block_delta"), "Claude delta event framing must be preserved");
|
||||
});
|
||||
assert.ok(
|
||||
lines.includes("event: content_block_delta"),
|
||||
"Claude delta event framing must be preserved"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -33,13 +33,13 @@ const NODE_B_ID = `openai-compatible-chat-558d982b-0000-4000-8000-000000000000`;
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
async function seedNode() {
|
||||
|
||||
@@ -37,7 +37,7 @@ async function resetStorage() {
|
||||
globalThis.fetch = originalFetch;
|
||||
apiKeysDb.resetApiKeyState();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -68,7 +68,7 @@ test.after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
apiKeysDb.resetApiKeyState();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#10197 v1 image edit POST forwards built-in openrouter edits to the unified Image API", async () => {
|
||||
@@ -95,10 +95,14 @@ test("#10197 v1 image edit POST forwards built-in openrouter edits to the unifie
|
||||
else if (raw instanceof Uint8Array) hitBody = Buffer.from(raw).toString("utf8");
|
||||
else if (raw instanceof ArrayBuffer) hitBody = Buffer.from(raw).toString("utf8");
|
||||
else if (raw && typeof (raw as { arrayBuffer?: unknown }).arrayBuffer === "function") {
|
||||
hitBody = Buffer.from(await (raw as { arrayBuffer(): Promise<ArrayBuffer> }).arrayBuffer()).toString("utf8");
|
||||
hitBody = Buffer.from(
|
||||
await (raw as { arrayBuffer(): Promise<ArrayBuffer> }).arrayBuffer()
|
||||
).toString("utf8");
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({ data: [{ b64_json: Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString("base64") }] }),
|
||||
JSON.stringify({
|
||||
data: [{ b64_json: Buffer.from([0x89, 0x50, 0x4e, 0x47]).toString("base64") }],
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } }
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ const SECRET = "sk-live-PROBE-10313-SUPER-SECRET-TOKEN";
|
||||
test.beforeEach(() => {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
});
|
||||
@@ -26,7 +26,7 @@ test.beforeEach(() => {
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
function captureMapKeys(): { keys: string[]; restore: () => void } {
|
||||
@@ -118,14 +118,26 @@ test("cache keys embed the sha256 digest of the secret, never the raw secret (#1
|
||||
// The hashed fingerprint, not the raw secret, rides in the cache keys.
|
||||
const keysWithDigestA = catalogKeys.filter((k) => k.includes(digestA));
|
||||
const keysWithDigestB = catalogKeys.filter((k) => k.includes(digestB));
|
||||
assert.ok(keysWithDigestA.length > 0, `expected a cache key embedding the fingerprint of A: ${catalogKeys.join(",")}`);
|
||||
assert.ok(keysWithDigestB.length > 0, `expected a cache key embedding the fingerprint of B: ${catalogKeys.join(",")}`);
|
||||
assert.ok(
|
||||
keysWithDigestA.length > 0,
|
||||
`expected a cache key embedding the fingerprint of A: ${catalogKeys.join(",")}`
|
||||
);
|
||||
assert.ok(
|
||||
keysWithDigestB.length > 0,
|
||||
`expected a cache key embedding the fingerprint of B: ${catalogKeys.join(",")}`
|
||||
);
|
||||
|
||||
// Raw secrets must never appear (issue #10313 root cause).
|
||||
assert.ok(!catalogKeys.some((k) => k.includes(rawA) || k.includes(rawB)));
|
||||
|
||||
// Identical secrets ⇒ identical key (memoized reuse); different ⇒ distinct.
|
||||
assert.ok(keysWithDigestA.every((k) => k === keysWithDigestA[0]), "all A keys must be identical");
|
||||
assert.ok(keysWithDigestB.every((k) => k === keysWithDigestB[0]), "all B keys must be identical");
|
||||
assert.ok(
|
||||
keysWithDigestA.every((k) => k === keysWithDigestA[0]),
|
||||
"all A keys must be identical"
|
||||
);
|
||||
assert.ok(
|
||||
keysWithDigestB.every((k) => k === keysWithDigestB[0]),
|
||||
"all B keys must be identical"
|
||||
);
|
||||
assert.notEqual(keysWithDigestA[0], keysWithDigestB[0]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,17 +27,19 @@ const { handleEmbedding } = await import("../../open-sse/handlers/embeddings.ts"
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
function readConnectionRow(connId: string) {
|
||||
const db = core.getDbInstance() as unknown as {
|
||||
prepare: (sql: string) => {
|
||||
get: (id: string) => {
|
||||
test_status: unknown;
|
||||
rate_limited_until: unknown;
|
||||
last_error_type: unknown;
|
||||
} | undefined;
|
||||
get: (id: string) =>
|
||||
| {
|
||||
test_status: unknown;
|
||||
rate_limited_until: unknown;
|
||||
last_error_type: unknown;
|
||||
}
|
||||
| undefined;
|
||||
};
|
||||
};
|
||||
return db
|
||||
@@ -100,4 +102,4 @@ test("embed 402 marks the connection terminal credits_exhausted (stops re-select
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -79,7 +79,7 @@ test.before(async () => {
|
||||
test.after(() => {
|
||||
proxyServer?.close();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#7993 getProviderCredentials('opencode-zen') hydrates the proxy saved under the sibling 'opencode' connection", async () => {
|
||||
|
||||
@@ -20,13 +20,13 @@ const auth = await import("../../src/sse/services/auth.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("BUG #8200: single perplexity-web 401 (cookie expiry) does not terminal-expire the only connection", async () => {
|
||||
|
||||
@@ -29,9 +29,8 @@ process.env.DATA_DIR = TEST_DATA_DIR;
|
||||
|
||||
const core = await import("../../src/lib/db/core.ts");
|
||||
const routeModule = await import("../../src/app/api/v1/providers/[provider]/models/route.ts");
|
||||
const { isCompatibleProviderConnectionId } = await import(
|
||||
"../../src/shared/utils/compatibleProviderId.ts"
|
||||
);
|
||||
const { isCompatibleProviderConnectionId } =
|
||||
await import("../../src/shared/utils/compatibleProviderId.ts");
|
||||
const { getProviderDisplayName } = await import("../../src/lib/display/names.ts");
|
||||
|
||||
function makeRequest(provider: string) {
|
||||
@@ -50,7 +49,7 @@ test.beforeEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
const UUID = "02669115-2545-4896-b003-cb4dac09d441";
|
||||
@@ -105,10 +104,7 @@ test("GET /v1/providers/:provider/models still rejects unrelated look-alike pref
|
||||
});
|
||||
|
||||
test("getProviderDisplayName simplifies all 4 generated compatible id shapes", () => {
|
||||
assert.equal(
|
||||
getProviderDisplayName("openai-compatible-chat-" + UUID),
|
||||
"Compatible (openai)"
|
||||
);
|
||||
assert.equal(getProviderDisplayName("openai-compatible-chat-" + UUID), "Compatible (openai)");
|
||||
assert.equal(
|
||||
getProviderDisplayName("openai-compatible-responses-" + UUID),
|
||||
"Compatible (openai)"
|
||||
|
||||
@@ -39,7 +39,7 @@ const CONFIGURED_PREFIX = "pix4k-talk";
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -50,7 +50,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#8327: synced models on a compatible provider node expose the configured prefix as owned_by, not the raw UUID", async () => {
|
||||
@@ -361,10 +361,7 @@ test("#9416: provider with configured prefix still uses the configured prefix (r
|
||||
|
||||
// Must still use the configured prefix, NOT slugified name
|
||||
const entry = body.data.find((m) => m.id === `${CONFIGURED_PREFIX}/glm-5.2`);
|
||||
assert.ok(
|
||||
entry,
|
||||
`expected entry with configured prefix "${CONFIGURED_PREFIX}/glm-5.2"`
|
||||
);
|
||||
assert.ok(entry, `expected entry with configured prefix "${CONFIGURED_PREFIX}/glm-5.2"`);
|
||||
assert.equal(entry!.owned_by, CONFIGURED_PREFIX);
|
||||
assert.notEqual(entry!.owned_by, "pix4k-talk-probe"); // not slugified
|
||||
});
|
||||
|
||||
@@ -96,7 +96,7 @@ test.after(() => {
|
||||
clearModelsDevCapabilities();
|
||||
settingsDb.clearAllLKGP();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
if (ORIGINAL_DATA_DIR === undefined) {
|
||||
delete process.env.DATA_DIR;
|
||||
} else {
|
||||
@@ -153,7 +153,11 @@ test(
|
||||
[],
|
||||
"vision-incapable rr-blind must never receive the image_url body, even as a last-resort fallback"
|
||||
);
|
||||
assert.notEqual(result.status, 200, "must not silently succeed via the vision-incapable target");
|
||||
assert.notEqual(
|
||||
result.status,
|
||||
200,
|
||||
"must not silently succeed via the vision-incapable target"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ const originalGetCookieStore = loginRoute.authRouteInternals.getCookieStore;
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
process.env.INITIAL_PASSWORD = "correct-secret-8336";
|
||||
}
|
||||
@@ -52,7 +52,7 @@ test.afterEach(() => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
if (ORIGINAL_INITIAL_PASSWORD === undefined) {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
} else {
|
||||
|
||||
@@ -44,7 +44,7 @@ before(() => {
|
||||
|
||||
after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
if (originalDataDir === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = originalDataDir;
|
||||
});
|
||||
|
||||
@@ -27,13 +27,13 @@ async function resetStorage() {
|
||||
delete process.env.INITIAL_PASSWORD;
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("issue #8385: global perKeyProxyEnabled=false must override a connection's per_key_proxy_enabled=1", async () => {
|
||||
|
||||
@@ -18,17 +18,15 @@ import path from "node:path";
|
||||
const tmpDataDir = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-8388-"));
|
||||
process.env.DATA_DIR = tmpDataDir;
|
||||
|
||||
const { compressionSettingsUpdateSchema } = await import(
|
||||
"../../src/shared/validation/compressionConfigSchemas.ts"
|
||||
);
|
||||
const { compressionSettingsUpdateSchema } =
|
||||
await import("../../src/shared/validation/compressionConfigSchemas.ts");
|
||||
const { resetDbInstance } = await import("../../src/lib/db/core.ts");
|
||||
const { getCompressionSettings, updateCompressionSettings } = await import(
|
||||
"../../src/lib/db/compression.ts"
|
||||
);
|
||||
const { getCompressionSettings, updateCompressionSettings } =
|
||||
await import("../../src/lib/db/compression.ts");
|
||||
|
||||
test.after(() => {
|
||||
resetDbInstance();
|
||||
fs.rmSync(tmpDataDir, { recursive: true, force: true });
|
||||
fs.rmSync(tmpDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#8388: PUT body carrying ccr detail (minChars/retrievalRampFactor) is ACCEPTED by the schema", () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ test(
|
||||
|
||||
t.after(async () => {
|
||||
loaded?.cleanup();
|
||||
await rm(pluginDir, { recursive: true, force: true });
|
||||
await rm(pluginDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
await writeFile(
|
||||
@@ -98,23 +98,20 @@ export async function onRequest(ctx) {
|
||||
}
|
||||
);
|
||||
|
||||
test(
|
||||
"loadPlugin no longer spawns the plugin host with stdout/stderr fully ignored",
|
||||
async () => {
|
||||
const source = await readFile(
|
||||
join(import.meta.dirname, "../../src/lib/plugins/loader.ts"),
|
||||
"utf-8"
|
||||
);
|
||||
// The original bug: stdio: ["ignore", "ignore", "ignore", "ipc"] discards
|
||||
// stdout (fd 1) and stderr (fd 2) at the OS level unconditionally.
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/stdio:\s*\[\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ipc["']\s*\]/,
|
||||
"loader.ts must not spawn the plugin host with stdout+stderr both set to " +
|
||||
"'ignore' — that silently discards all plugin console.log/console.error output"
|
||||
);
|
||||
}
|
||||
);
|
||||
test("loadPlugin no longer spawns the plugin host with stdout/stderr fully ignored", async () => {
|
||||
const source = await readFile(
|
||||
join(import.meta.dirname, "../../src/lib/plugins/loader.ts"),
|
||||
"utf-8"
|
||||
);
|
||||
// The original bug: stdio: ["ignore", "ignore", "ignore", "ipc"] discards
|
||||
// stdout (fd 1) and stderr (fd 2) at the OS level unconditionally.
|
||||
assert.doesNotMatch(
|
||||
source,
|
||||
/stdio:\s*\[\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ignore["']\s*,\s*["']ipc["']\s*\]/,
|
||||
"loader.ts must not spawn the plugin host with stdout+stderr both set to " +
|
||||
"'ignore' — that silently discards all plugin console.log/console.error output"
|
||||
);
|
||||
});
|
||||
|
||||
// Secondary #8395 finding: runPluginOnResponseHook was only wired into chatCore.ts's
|
||||
// STREAMING success path — the non-streaming (stream:false) JSON-return branch
|
||||
@@ -131,7 +128,9 @@ test("chatCore.ts calls runPluginOnResponseHook from both the non-streaming and
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
const nonStreamingReturnIndex = source.indexOf("buildNonStreamingJsonResponse(translatedResponse");
|
||||
const nonStreamingReturnIndex = source.indexOf(
|
||||
"buildNonStreamingJsonResponse(translatedResponse"
|
||||
);
|
||||
const hookCallNeedle = "await runPluginOnResponseHook({";
|
||||
const hookCallIndex = source.indexOf(hookCallNeedle);
|
||||
const secondHookCallIndex = source.indexOf(hookCallNeedle, hookCallIndex + 1);
|
||||
|
||||
@@ -35,10 +35,17 @@ const quotaCache = await import("../../src/domain/quotaCache.ts");
|
||||
|
||||
test.after(() => {
|
||||
coreDb.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
const COLD_WINDOWS = ["Bonus Pack 1", "Bonus Pack 2", "Bonus Pack 3", "Bonus Pack 4", "Weekly", "Daily"];
|
||||
const COLD_WINDOWS = [
|
||||
"Bonus Pack 1",
|
||||
"Bonus Pack 2",
|
||||
"Bonus Pack 3",
|
||||
"Bonus Pack 4",
|
||||
"Weekly",
|
||||
"Daily",
|
||||
];
|
||||
const HOT_WINDOWS = ["Monthly", "Bonus Pack 5", "Bonus Pack 6"];
|
||||
|
||||
test("#8431 idle healthy windows survive rehydration even when hot windows accumulate >200 rows", () => {
|
||||
|
||||
@@ -67,13 +67,13 @@ const log = {
|
||||
|
||||
test.beforeEach(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#8488 filter: some tool-capable targets kept (unchanged)", () => {
|
||||
|
||||
@@ -19,10 +19,8 @@ const providersDb = await import("../../src/lib/db/providers.ts");
|
||||
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
|
||||
const imageEditRoute = await import("../../src/app/api/v1/images/edits/route.ts");
|
||||
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");
|
||||
const {
|
||||
ADOBE_FIREFLY_IMAGE_UPLOAD_URL,
|
||||
ADOBE_FIREFLY_IMAGE_SUBMIT_URL,
|
||||
} = await import("../../open-sse/services/adobeFireflyClient.ts");
|
||||
const { ADOBE_FIREFLY_IMAGE_UPLOAD_URL, ADOBE_FIREFLY_IMAGE_SUBMIT_URL } =
|
||||
await import("../../open-sse/services/adobeFireflyClient.ts");
|
||||
|
||||
interface ErrorResponseBody {
|
||||
error: { message: string; code?: string };
|
||||
@@ -38,7 +36,7 @@ async function resetStorage() {
|
||||
globalThis.fetch = originalFetch;
|
||||
apiKeysDb.resetApiKeyState();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -86,7 +84,7 @@ test.after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
apiKeysDb.resetApiKeyState();
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#8510 v1 image edit POST uploads Adobe Firefly reference images and dispatches referenceBlobs", async () => {
|
||||
@@ -148,10 +146,7 @@ test("#8510 v1 image edit POST uploads Adobe Firefly reference images and dispat
|
||||
id: string;
|
||||
}>;
|
||||
assert.ok(Array.isArray(referenceBlobs), "generate-async payload must carry referenceBlobs");
|
||||
assert.deepEqual(
|
||||
referenceBlobs.map((r) => r.id).sort(),
|
||||
[...uploadedIds].sort()
|
||||
);
|
||||
assert.deepEqual(referenceBlobs.map((r) => r.id).sort(), [...uploadedIds].sort());
|
||||
});
|
||||
|
||||
test("#8510 v1 image edit POST rejects more than 4 Adobe Firefly reference images", async () => {
|
||||
@@ -206,7 +201,9 @@ test("#8510 v1 image edit POST surfaces missing Adobe Firefly credentials", asyn
|
||||
});
|
||||
|
||||
test("#8510 v1 image edit POST surfaces Adobe Firefly rate-limit sentinel", async () => {
|
||||
await seedAdobeFireflyConnection({ rateLimitedUntil: new Date(Date.now() + 60_000).toISOString() });
|
||||
await seedAdobeFireflyConnection({
|
||||
rateLimitedUntil: new Date(Date.now() + 60_000).toISOString(),
|
||||
});
|
||||
globalThis.fetch = async () => {
|
||||
throw new Error("Rate-limited path must not reach upstream");
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ const model = await import("../../open-sse/services/model.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ async function seedOnly(provider: string) {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("the agy/ prefix still canonicalizes to antigravity (#8013 unchanged)", () => {
|
||||
|
||||
@@ -38,7 +38,7 @@ const MODEL_ID = "opc/big-pickle";
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -85,7 +85,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#8958: alias-backed model on a compatible node is not duplicated under the raw UUID prefix (alias mode)", async () => {
|
||||
|
||||
@@ -29,7 +29,8 @@ type CoreModule = typeof import("../../src/lib/db/core.ts");
|
||||
type ProvidersDbModule = typeof import("../../src/lib/db/providers.ts");
|
||||
type ModelsDbModule = typeof import("../../src/lib/db/models.ts");
|
||||
type CatalogModule = typeof import("../../src/app/api/v1/models/catalog.ts");
|
||||
type ManagedAvailableModelsModule = typeof import("../../src/lib/providerModels/managedAvailableModels.ts");
|
||||
type ManagedAvailableModelsModule =
|
||||
typeof import("../../src/lib/providerModels/managedAvailableModels.ts");
|
||||
|
||||
let core: CoreModule;
|
||||
let providersDb: ProvidersDbModule;
|
||||
@@ -45,7 +46,7 @@ const MODEL_NAME = "kimi-k2";
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -61,7 +62,7 @@ test.before(async () => {
|
||||
|
||||
test.after(async () => {
|
||||
if (core) core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#9034: alias-backed model id must use the configured prefix, not the raw provider-node UUID", async () => {
|
||||
@@ -117,4 +118,4 @@ test("#9034: alias-backed model id must use the configured prefix, not the raw p
|
||||
`entry id "${id}" must not start with the raw provider-node UUID "${NODE_ID}" when a prefix ("${CONFIGURED_PREFIX}") is configured`
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ const originalFetch = globalThis.fetch;
|
||||
test.after(() => {
|
||||
globalThis.fetch = originalFetch;
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
/** Minimal but structurally valid WAV so nothing rejects the upload shape. */
|
||||
@@ -72,8 +72,7 @@ test("#9134 combo name is rejected instead of resolved", async () => {
|
||||
new Response(JSON.stringify({ text: "ok" }), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
) as typeof fetch;
|
||||
})) as typeof fetch;
|
||||
|
||||
const res = await route.POST(transcriptionRequest("transcricao"));
|
||||
const body = await res.text();
|
||||
@@ -92,4 +91,4 @@ test("#9134 combo name is rejected instead of resolved", async () => {
|
||||
!body.includes("Invalid transcription model"),
|
||||
`BUG #9134: combo name was not resolved — got: ${body}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ const MODELS_PER_CONNECTION = 12; // ~720 synced models total
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
|
||||
}
|
||||
@@ -55,7 +55,7 @@ test.beforeEach(async () => {
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
apiKeysDb.resetApiKeyState();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test("#9147 — catalog build at catalog-scale must not pin the event loop for a long stretch", async (t) => {
|
||||
|
||||
@@ -66,7 +66,7 @@ test.after(async () => {
|
||||
searchRegistry.SEARCH_PROVIDERS["serper-search"].baseUrl = originalSerperBaseUrl;
|
||||
await new Promise<void>((resolve) => proxyServer.close(() => resolve()));
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(dataDir, { recursive: true, force: true });
|
||||
fs.rmSync(dataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
function installProxyResponseCounter() {
|
||||
|
||||
@@ -17,7 +17,8 @@ async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
for (let attempt = 0; attempt < 10; attempt++) {
|
||||
try {
|
||||
if (fs.existsSync(TEST_DATA_DIR)) fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
if (fs.existsSync(TEST_DATA_DIR))
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
break;
|
||||
} catch (error: unknown) {
|
||||
const code = (error as { code?: string } | undefined)?.code;
|
||||
@@ -34,7 +35,7 @@ test.beforeEach(async () => {
|
||||
});
|
||||
test.after(async () => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
});
|
||||
|
||||
async function setupConnectionWithAssignment() {
|
||||
|
||||
@@ -46,11 +46,11 @@ export function setupSettingsFixture(slug: string): SettingsFixture {
|
||||
const runtime = await import("../../../src/lib/config/runtimeSettings.ts");
|
||||
core.resetDbInstance();
|
||||
runtime.resetRuntimeSettingsStateForTests();
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true });
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(testDataDir, { recursive: true });
|
||||
},
|
||||
cleanup() {
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true });
|
||||
fs.rmSync(testDataDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
},
|
||||
};
|
||||
activeFixture = fixture;
|
||||
|
||||
@@ -34,14 +34,14 @@ function makeJsonRpcRequest(token?: string): NextRequest {
|
||||
|
||||
test.beforeEach(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
process.env.OMNIROUTE_API_KEY = API_KEY;
|
||||
});
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
|
||||
if (ORIGINAL_DATA_DIR === undefined) delete process.env.DATA_DIR;
|
||||
else process.env.DATA_DIR = ORIGINAL_DATA_DIR;
|
||||
|
||||
@@ -19,7 +19,7 @@ const a2aRoute = await import("../../src/app/a2a/route.ts");
|
||||
|
||||
async function resetStorage() {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ test.beforeEach(async () => {
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
|
||||
if (ORIGINAL_DATA_DIR === undefined) {
|
||||
delete process.env.DATA_DIR;
|
||||
|
||||
@@ -24,7 +24,7 @@ const ORIGINAL_A2A_KEY = process.env.OMNIROUTE_API_KEY;
|
||||
|
||||
test.after(() => {
|
||||
core.resetDbInstance();
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
||||
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
if (ORIGINAL_REQUIRE === undefined) delete process.env.REQUIRE_API_KEY;
|
||||
else process.env.REQUIRE_API_KEY = ORIGINAL_REQUIRE;
|
||||
if (ORIGINAL_A2A_KEY === undefined) delete process.env.OMNIROUTE_API_KEY;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user