Files
OmniRoute/tests/unit/combo-context-relay.test.mjs
diegosouzapw 25bd04e400 feat(settings): unify routing rules and model aliases controls
Move model routing management into Settings and add a unified
model alias editor for exact and wildcard remaps.

Sync combo defaults with global routing strategy settings, add
localized combo onboarding copy, and expand routing i18n across
supported locales.

Also fix supporting routing behavior by accepting all strategy
values in settings schemas, preserving connection ids in combo
tests, honoring non-stream JSON requests for CC-compatible
providers, and handling hashed external package subpaths.
2026-04-12 18:43:19 -03:00

380 lines
11 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-context-relay-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const { handleComboChat } = await import("../../open-sse/services/combo.ts");
const core = await import("../../src/lib/db/core.ts");
const handoffDb = await import("../../src/lib/db/contextHandoffs.ts");
const { registerCodexConnection } = await import("../../open-sse/services/codexQuotaFetcher.ts");
const { clearSessions, touchSession } = await import("../../open-sse/services/sessionManager.ts");
const { resetAllComboMetrics } = await import("../../open-sse/services/comboMetrics.ts");
const { resetAllCircuitBreakers, getCircuitBreaker } =
await import("../../src/shared/utils/circuitBreaker.ts");
const { resetAll: resetAllSemaphores } =
await import("../../open-sse/services/rateLimitSemaphore.ts");
const { _resetAllDecks } = await import("../../src/shared/utils/shuffleDeck.ts");
const { normalizeComboStep } = await import("../../src/lib/combos/steps.ts");
const originalFetch = globalThis.fetch;
function createLog() {
const entries = [];
return {
info: (tag, msg) => entries.push({ level: "info", tag, msg }),
warn: (tag, msg) => entries.push({ level: "warn", tag, msg }),
error: (tag, msg) => entries.push({ level: "error", tag, msg }),
entries,
};
}
function okResponse(body = { choices: [{ message: { content: "ok" } }] }) {
return new Response(JSON.stringify(body), {
status: 200,
headers: { "content-type": "application/json" },
});
}
function buildQuotaResponse(usedPercent, resetAfterSeconds = 3600) {
return new Response(
JSON.stringify({
rate_limit: {
primary_window: {
used_percent: usedPercent,
reset_after_seconds: resetAfterSeconds,
},
secondary_window: {
used_percent: 0,
reset_after_seconds: 86400,
},
},
}),
{
status: 200,
headers: { "content-type": "application/json" },
}
);
}
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
async function waitFor(fn, timeoutMs = 1500) {
const startedAt = Date.now();
while (Date.now() - startedAt < timeoutMs) {
const value = await fn();
if (value) return value;
await new Promise((resolve) => setTimeout(resolve, 25));
}
return null;
}
test.beforeEach(async () => {
resetAllComboMetrics();
resetAllCircuitBreakers();
resetAllSemaphores();
_resetAllDecks();
clearSessions();
globalThis.fetch = originalFetch;
await resetStorage();
});
test.afterEach(async () => {
globalThis.fetch = originalFetch;
clearSessions();
await new Promise((resolve) => setTimeout(resolve, 50));
});
test.after(async () => {
resetAllComboMetrics();
resetAllCircuitBreakers();
resetAllSemaphores();
_resetAllDecks();
clearSessions();
globalThis.fetch = originalFetch;
await resetStorage();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
test("handleComboChat context-relay routes to the first available model", async () => {
const calls = [];
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Hello" }],
},
combo: {
name: "relay-first",
strategy: "context-relay",
models: ["openai/gpt-4o-mini", "claude/sonnet"],
config: { maxRetries: 0 },
},
handleSingleModel: async (_body, modelStr) => {
calls.push(modelStr);
return okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: null,
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/gpt-4o-mini"]);
});
test("handleComboChat context-relay skips unavailable models and falls through to the next one", async () => {
const calls = [];
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Fallback" }],
},
combo: {
name: "relay-skip-unavailable",
strategy: "context-relay",
models: ["codex/gpt-5.4", "openai/gpt-4o-mini"],
config: { maxRetries: 0 },
},
handleSingleModel: async (_body, modelStr) => {
calls.push(modelStr);
return okResponse();
},
isModelAvailable: async (modelStr) => modelStr !== "codex/gpt-5.4",
log: createLog(),
settings: null,
allCombos: null,
relayOptions: {
sessionId: "sess-skip",
config: { handoffProviders: ["codex"] },
},
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/gpt-4o-mini"]);
});
test("handleComboChat context-relay skips models with an open circuit breaker", async () => {
const combo = {
name: "relay-breaker",
strategy: "context-relay",
models: ["codex/gpt-5.4", "openai/gpt-4o-mini"],
config: { maxRetries: 0 },
};
const firstStep = normalizeComboStep(combo.models[0], {
comboName: combo.name,
index: 0,
});
const breaker = getCircuitBreaker(`combo:${combo.name}:${firstStep.id}`, {
failureThreshold: 1,
resetTimeout: 60000,
});
breaker._onFailure();
const log = createLog();
const calls = [];
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Breaker" }],
},
combo,
handleSingleModel: async (_body, modelStr) => {
calls.push(modelStr);
return okResponse();
},
isModelAvailable: async () => true,
log,
settings: null,
allCombos: null,
});
assert.equal(result.ok, true);
assert.deepEqual(calls, ["openai/gpt-4o-mini"]);
assert.ok(log.entries.some((entry) => entry.msg.includes("circuit breaker OPEN")));
});
test("handleComboChat context-relay persists a handoff when codex quota reaches the warning threshold", async () => {
const sessionId = "sess-generate";
const connectionId = "conn-generate";
touchSession(sessionId, connectionId);
registerCodexConnection(connectionId, {
accessToken: "token-generate",
workspaceId: "ws-generate",
});
let usageCalls = 0;
let summaryCalls = 0;
globalThis.fetch = async (url) => {
if (String(url).includes("/backend-api/wham/usage")) {
usageCalls += 1;
return buildQuotaResponse(87);
}
throw new Error(`Unexpected fetch: ${String(url)}`);
};
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Keep context alive" }],
},
combo: {
name: "relay-generate",
strategy: "context-relay",
models: ["codex/gpt-5.4"],
config: { maxRetries: 0, handoffThreshold: 0.85, handoffProviders: ["codex"] },
},
handleSingleModel: async (body) => {
if (body._omnirouteInternalRequest === "context-handoff") {
summaryCalls += 1;
return okResponse({
choices: [
{
message: {
content: JSON.stringify({
summary: "Generated from combo-level test",
keyDecisions: ["generate at 85%"],
taskProgress: "ready",
activeEntities: ["combo.ts"],
}),
},
},
],
});
}
return okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: null,
relayOptions: {
sessionId,
config: {
handoffThreshold: 0.85,
handoffProviders: ["codex"],
},
},
});
const saved = await waitFor(() => handoffDb.getHandoff(sessionId, "relay-generate"));
assert.equal(result.ok, true);
assert.equal(usageCalls, 1);
assert.equal(summaryCalls, 1);
assert.ok(saved);
assert.equal(saved.summary, "Generated from combo-level test");
assert.equal(saved.fromAccount, connectionId);
});
test("handleComboChat context-relay respects handoffProviders and skips generation when codex is disabled", async () => {
const sessionId = "sess-disabled-provider";
const connectionId = "conn-disabled-provider";
touchSession(sessionId, connectionId);
registerCodexConnection(connectionId, {
accessToken: "token-disabled-provider",
workspaceId: "ws-disabled-provider",
});
let usageCalls = 0;
let summaryCalls = 0;
globalThis.fetch = async (url) => {
if (String(url).includes("/backend-api/wham/usage")) {
usageCalls += 1;
return buildQuotaResponse(90);
}
throw new Error(`Unexpected fetch: ${String(url)}`);
};
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Do not generate" }],
},
combo: {
name: "relay-disabled-provider",
strategy: "context-relay",
models: ["codex/gpt-5.4"],
config: { maxRetries: 0, handoffProviders: ["openai"] },
},
handleSingleModel: async (body) => {
if (body._omnirouteInternalRequest === "context-handoff") {
summaryCalls += 1;
}
return okResponse();
},
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: null,
relayOptions: {
sessionId,
config: {
handoffProviders: ["openai"],
},
},
});
await new Promise((resolve) => setTimeout(resolve, 50));
assert.equal(result.ok, true);
assert.equal(usageCalls, 0);
assert.equal(summaryCalls, 0);
assert.equal(handoffDb.getHandoff(sessionId, "relay-disabled-provider"), null);
});
test("handleComboChat context-relay treats explicit empty handoffProviders as disabled", async () => {
const sessionId = "sess-empty-providers";
const connectionId = "conn-empty-providers";
touchSession(sessionId, connectionId);
registerCodexConnection(connectionId, {
accessToken: "token-empty-providers",
workspaceId: "ws-empty-providers",
});
let usageCalls = 0;
globalThis.fetch = async (url) => {
if (String(url).includes("/backend-api/wham/usage")) {
usageCalls += 1;
return buildQuotaResponse(91);
}
throw new Error(`Unexpected fetch: ${String(url)}`);
};
const result = await handleComboChat({
body: {
messages: [{ role: "user", content: "Disabled by empty list" }],
},
combo: {
name: "relay-empty-providers",
strategy: "context-relay",
models: ["codex/gpt-5.4"],
config: { maxRetries: 0, handoffProviders: [] },
},
handleSingleModel: async () => okResponse(),
isModelAvailable: async () => true,
log: createLog(),
settings: null,
allCombos: null,
relayOptions: {
sessionId,
config: {
handoffProviders: [],
},
},
});
await new Promise((resolve) => setTimeout(resolve, 50));
assert.equal(result.ok, true);
assert.equal(usageCalls, 0);
assert.equal(handoffDb.getHandoff(sessionId, "relay-empty-providers"), null);
});