mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-23 15:42:12 +03:00
Obrigado — feature substancial e bem estruturada: separa qualidade operacional (comportamento de wire: 4xx/5xx, 429, respostas malformadas, stream interrompido) de qualidade semântica (só setada por avaliadores externos, nunca inferida do sucesso HTTP), com confidence/sample-awareness para não deixar poucos sucessos de sorte dominarem o ranking. Instrumentação de streaming (TTFT/ITL) threaded até RoutingEvent, endpoint de explicabilidade, e teste E2E determinístico cobrindo degradação→recuperação→blip. Validação (worktree própria a partir de origin/release/v3.8.50, merge limpo, 0 conflitos): - typecheck:core limpo, complexity/cognitive-complexity dentro do baseline - 59/59 testes passando (mlx-provider, routing-adaptive-e2e, routing-events(-concurrency), routing-otel, routing-quality, routing-scoring-quality, stream-timing, auto-combo-scoring-clamp)
142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
/**
|
|
* tests/unit/routing-events.test.ts
|
|
*
|
|
* Routing feedback foundation (open-sse/services/routing/events.ts):
|
|
* - createRoutingEvent normalizes defaults
|
|
* - outcomeFromStatus classifies HTTP statuses
|
|
* - MemoryRoutingEventStore is bounded and returns newest-first
|
|
* - dispatchRoutingEvent fans out to sinks and isolates a throwing sink
|
|
*/
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
MemoryRoutingEventStore,
|
|
createRoutingEvent,
|
|
outcomeFromStatus,
|
|
dispatchRoutingEvent,
|
|
registerRoutingEventSink,
|
|
listRoutingEventSinks,
|
|
clearRoutingEventSinks,
|
|
type RoutingEvent,
|
|
type RoutingEventSink,
|
|
} from "../../open-sse/services/routing/events.ts";
|
|
|
|
function event(partial: Partial<RoutingEvent> = {}): RoutingEvent {
|
|
return createRoutingEvent({
|
|
requestId: "req-1",
|
|
provider: "openai",
|
|
model: "gpt-4o",
|
|
strategy: "auto",
|
|
latencyMs: 120,
|
|
outcome: "success",
|
|
status: 200,
|
|
...partial,
|
|
});
|
|
}
|
|
|
|
test("createRoutingEvent fills observability defaults", () => {
|
|
const e = createRoutingEvent({
|
|
requestId: "req-x",
|
|
provider: "anthropic",
|
|
model: "claude-4",
|
|
latencyMs: 50,
|
|
outcome: "error",
|
|
status: 500,
|
|
});
|
|
assert.equal(e.strategy, "direct");
|
|
assert.equal(e.ttftMs, null);
|
|
assert.equal(e.inputTokens, null);
|
|
assert.equal(e.outputTokens, null);
|
|
assert.equal(e.cost, null);
|
|
assert.equal(e.retries, 0);
|
|
assert.equal(e.fallbackUsed, false);
|
|
assert.equal(e.finishReason, null);
|
|
assert.equal(e.connectionId, null);
|
|
assert.ok(e.ts > 0);
|
|
assert.equal(e.status, 500);
|
|
});
|
|
|
|
test("outcomeFromStatus classifies statuses", () => {
|
|
assert.equal(outcomeFromStatus(200), "success");
|
|
assert.equal(outcomeFromStatus(201), "success");
|
|
assert.equal(outcomeFromStatus(429), "rate_limited");
|
|
assert.equal(outcomeFromStatus(408), "timeout");
|
|
assert.equal(outcomeFromStatus(504), "timeout");
|
|
assert.equal(outcomeFromStatus(500), "error");
|
|
assert.equal(outcomeFromStatus(400), "error");
|
|
assert.equal(outcomeFromStatus(null), "error");
|
|
assert.equal(outcomeFromStatus(undefined), "error");
|
|
});
|
|
|
|
test("MemoryRoutingEventStore returns newest-first within capacity", () => {
|
|
const store = new MemoryRoutingEventStore(5);
|
|
for (let i = 0; i < 5; i++) store.record(event({ requestId: `r-${i}` }));
|
|
const recent = store.recent(5);
|
|
assert.equal(recent.length, 5);
|
|
assert.equal(recent[0].requestId, "r-4");
|
|
assert.equal(recent[4].requestId, "r-0");
|
|
});
|
|
|
|
test("MemoryRoutingEventStore is bounded and still newest-first after overflow", () => {
|
|
const store = new MemoryRoutingEventStore(3);
|
|
for (let i = 0; i < 10; i++) store.record(event({ requestId: `r-${i}` }));
|
|
assert.equal(store.size, 3);
|
|
const recent = store.recent(3);
|
|
assert.deepEqual(
|
|
recent.map((e) => e.requestId),
|
|
["r-9", "r-8", "r-7"]
|
|
);
|
|
store.clear();
|
|
assert.equal(store.size, 0);
|
|
assert.deepEqual(store.recent(), []);
|
|
});
|
|
|
|
test("dispatchRoutingEvent fans out to every registered sink", () => {
|
|
const seen: string[] = [];
|
|
const sink: RoutingEventSink = {
|
|
name: "test-a",
|
|
record: (e) => void seen.push(e.requestId),
|
|
};
|
|
const unsub = registerRoutingEventSink(sink);
|
|
try {
|
|
dispatchRoutingEvent(event({ requestId: "fan-1" }));
|
|
dispatchRoutingEvent(event({ requestId: "fan-2" }));
|
|
assert.deepEqual(seen, ["fan-1", "fan-2"]);
|
|
} finally {
|
|
unsub();
|
|
}
|
|
});
|
|
|
|
test("dispatchRoutingEvent isolates a throwing sink", () => {
|
|
const badSink: RoutingEventSink = {
|
|
name: "test-throw",
|
|
record: () => {
|
|
throw new Error("boom");
|
|
},
|
|
};
|
|
const goodSeen: string[] = [];
|
|
const goodSink: RoutingEventSink = {
|
|
name: "test-good",
|
|
record: (e) => void goodSeen.push(e.requestId),
|
|
};
|
|
registerRoutingEventSink(badSink);
|
|
registerRoutingEventSink(goodSink);
|
|
try {
|
|
dispatchRoutingEvent(event({ requestId: "isolated" }));
|
|
assert.deepEqual(goodSeen, ["isolated"]);
|
|
} finally {
|
|
clearRoutingEventSinks();
|
|
}
|
|
});
|
|
|
|
test("listRoutingEventSinks reports registered names", () => {
|
|
clearRoutingEventSinks();
|
|
assert.deepEqual(listRoutingEventSinks(), []);
|
|
const unsub = registerRoutingEventSink({ name: "probe", record: () => {} });
|
|
try {
|
|
assert.deepEqual(listRoutingEventSinks(), ["probe"]);
|
|
} finally {
|
|
unsub();
|
|
}
|
|
});
|