mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 13:14:56 +03:00
Freezes the 137-entry provider-id → executor mapping (class, provider identity, backing PROVIDERS config), the no-shared-instances invariant, and the getExecutor() dispatch rules (memoized DefaultExecutor fallback, cloud-agent guard #6699, search-provider guard #10274) as stable JSON snapshots. The upcoming ExecutorRegistry must keep both snapshots byte-identical.
135 lines
5.5 KiB
TypeScript
135 lines
5.5 KiB
TypeScript
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";
|
|
|
|
// R0.3 GOLDEN LOCK (characterization BEFORE the ExecutorRegistry refactor):
|
|
// freeze the full provider-id → executor mapping of open-sse/executors/index.ts —
|
|
// every specialized key with its executor class, effective provider identity and
|
|
// which PROVIDERS config entry backs it — plus the getExecutor() dispatch rules
|
|
// (specialized hit, DefaultExecutor fallback + cache, cloud-agent guard #6699,
|
|
// search-provider guard #10274). The registry refactor must keep this snapshot
|
|
// byte-identical: any drift in keys, classes, provider identity or guard behavior
|
|
// is a golden diff, not a silent routing change.
|
|
|
|
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-executor-golden-"));
|
|
process.env.DATA_DIR = TEST_DATA_DIR;
|
|
|
|
// Dynamic imports AFTER DATA_DIR is set so db/core.ts picks up the temp path.
|
|
const { getExecutor, hasSpecializedExecutor, DefaultExecutor } = await import(
|
|
"../../open-sse/executors/index.ts"
|
|
);
|
|
const { PROVIDERS } = await import("../../open-sse/config/constants.ts");
|
|
const { SEARCH_PROVIDERS } = await import("../../open-sse/config/searchRegistry.ts");
|
|
const { goldenSnapshot } = await import("../helpers/goldenSnapshot.ts");
|
|
|
|
test.after(() => {
|
|
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
|
|
});
|
|
|
|
// The specialized keys are not exported; enumerate them through the public
|
|
// surface by probing every plausible id source AND the literal keys read from
|
|
// the module source. Reading the source keeps the golden honest: a key added
|
|
// to (or removed from) the hard-coded map cannot hide from the snapshot.
|
|
function readSpecializedKeys(): string[] {
|
|
const src = fs.readFileSync(
|
|
path.resolve(path.dirname(new URL(import.meta.url).pathname), "../../open-sse/executors/index.ts"),
|
|
"utf8"
|
|
);
|
|
const mapMatch = src.match(/const executors = \{([\s\S]*?)\n\};/);
|
|
assert.ok(mapMatch, "executors map literal not found in open-sse/executors/index.ts");
|
|
const keys: string[] = [];
|
|
for (const line of mapMatch[1].split("\n")) {
|
|
const m = line.match(/^\s*(?:"([^"]+)"|([A-Za-z0-9_$-]+)):\s*new /);
|
|
if (m) keys.push(m[1] ?? m[2]);
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
// Map a ProviderConfig object back to its PROVIDERS key by identity, so the
|
|
// snapshot records WHICH config backs each executor without freezing the whole
|
|
// (huge, frequently-edited) config content.
|
|
const providerConfigKeyByRef = new Map<object, string>();
|
|
for (const [key, cfg] of Object.entries(PROVIDERS)) {
|
|
if (cfg && typeof cfg === "object" && !providerConfigKeyByRef.has(cfg)) {
|
|
providerConfigKeyByRef.set(cfg, key);
|
|
}
|
|
}
|
|
|
|
function describeExecutor(instance: unknown): {
|
|
className: string;
|
|
provider: string | null;
|
|
configSource: string | null;
|
|
} {
|
|
const inst = instance as { constructor: { name: string }; provider?: string; config?: object };
|
|
const cfg = inst.config;
|
|
return {
|
|
className: inst.constructor.name,
|
|
provider: typeof inst.provider === "string" ? inst.provider : null,
|
|
configSource:
|
|
cfg == null ? null : (providerConfigKeyByRef.get(cfg) ?? "<custom-config>"),
|
|
};
|
|
}
|
|
|
|
const specializedKeys = readSpecializedKeys();
|
|
|
|
test("golden: specialized executor map — key → class + provider identity + config source", () => {
|
|
assert.ok(specializedKeys.length >= 100, `suspiciously few keys: ${specializedKeys.length}`);
|
|
|
|
const entries: Record<
|
|
string,
|
|
{ className: string; provider: string | null; configSource: string | null }
|
|
> = {};
|
|
const byInstance = new Map<unknown, string[]>();
|
|
|
|
for (const key of [...specializedKeys].sort()) {
|
|
assert.equal(hasSpecializedExecutor(key), true, `hasSpecializedExecutor(${key})`);
|
|
const instance = getExecutor(key);
|
|
entries[key] = describeExecutor(instance);
|
|
const group = byInstance.get(instance) ?? [];
|
|
group.push(key);
|
|
byInstance.set(instance, group);
|
|
}
|
|
|
|
// Keys sharing the SAME instance share per-instance state (session pools,
|
|
// rotation cooldowns); today every map entry is its own `new X()`. Freeze that.
|
|
const sharedInstances = [...byInstance.values()]
|
|
.filter((keys) => keys.length > 1)
|
|
.map((keys) => keys.sort())
|
|
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
|
|
goldenSnapshot("executors/executor-map", {
|
|
keyCount: specializedKeys.length,
|
|
entries,
|
|
sharedInstances,
|
|
});
|
|
});
|
|
|
|
test("golden: getExecutor dispatch rules — fallback, cache and 400-guards", () => {
|
|
// 1. Unknown provider → DefaultExecutor for that provider, memoized.
|
|
const unknown = "golden-test-unknown-provider";
|
|
assert.equal(hasSpecializedExecutor(unknown), false);
|
|
const fallback = getExecutor(unknown);
|
|
assert.ok(fallback instanceof DefaultExecutor, "fallback must be DefaultExecutor");
|
|
assert.equal(getExecutor(unknown), fallback, "DefaultExecutor fallback must be cached");
|
|
|
|
// 2. Cloud-agent guard (#6699) and search guard (#10274) → status-400 throw.
|
|
const guardOutcome = (provider: string) => {
|
|
try {
|
|
getExecutor(provider);
|
|
return { throws: false as const };
|
|
} catch (err) {
|
|
const e = err as Error & { status?: number };
|
|
return { throws: true as const, status: e.status ?? null, message: e.message };
|
|
}
|
|
};
|
|
|
|
const searchProviders = Object.keys(SEARCH_PROVIDERS).sort();
|
|
goldenSnapshot("executors/dispatch-rules", {
|
|
fallback: describeExecutor(fallback),
|
|
cloudAgentGuard: { jules: guardOutcome("jules") },
|
|
searchGuard: Object.fromEntries(searchProviders.map((p) => [p, guardOutcome(p)])),
|
|
});
|
|
});
|