Files
OmniRoute/tests/unit/8958-alias-backed-node-prefix.test.ts
Diego Rodrigues de Sa e Souza 3d4f3e4960 test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966) (#11968)
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966)

Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.

One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.

Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.

* fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff

The gate shells out to `git diff` through execFileSync with Node's default 1 MB
maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to
overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing
anything. 64 MB is far above any real PR and costs nothing when unused.
2026-08-29 01:17:40 -03:00

145 lines
5.3 KiB
TypeScript

/**
* Regression test for #8958 — /v1/models listed alias-backed models for a
* compatible provider node (openai-compatible / anthropic-compatible, whose id is a
* UUID) TWICE: once with the correct `prefix/model` id and once with the raw
* `<node-uuid>/model` id. The duplicate UUID-prefixed entry appeared even under
* MODELS_CATALOG_PREFIX_MODE=alias, which should only ever emit alias ids.
*
* Root cause: the alias-backed loop in `catalog.ts` built its display prefix as
* `providerIdToAlias[canonicalProviderId] || providerKey` and never consulted
* `providerIdToPrefix` (unlike the synced-models / custom-models loops). For a
* compatible node the alias fell through to the raw UUID `providerKey`, and the
* dedupe (which only checks `alias/model` and `providerKey/model`, both
* UUID-prefixed) never matched the correct `prefix/model` row already emitted.
*
* Fix: `const alias = providerIdToPrefix[providerKey] || providerIdToAlias[...] ||
* providerKey;` — the id then collapses to `prefix/model` and the existing dedupe
* skips the duplicate.
*/
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-8958-"));
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const aliasesDb = await import("../../src/lib/db/models/aliases.ts");
const v1ModelsCatalog = await import("../../src/app/api/v1/models/catalog.ts");
const NODE_ID = "openai-compatible-chat-550e8400-e29b-41d4-a716-446655440000";
const UUID_SHAPE_RE = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i;
const CONFIGURED_PREFIX = "fta";
const MODEL_ID = "opc/big-pickle";
async function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
v1ModelsCatalog.__resetCatalogBuilderRunsForTest();
}
async function seedCompatibleNodeWithAlias() {
await providersDb.createProviderNode({
id: NODE_ID,
type: "openai-compatible",
name: "fta (probe)",
prefix: CONFIGURED_PREFIX,
baseUrl: "https://proxy.example.com",
chatPath: "/v1/chat/completions",
modelsPath: "/v1/models",
});
const connection = await providersDb.createProviderConnection({
provider: NODE_ID,
authType: "apikey",
name: "fta-conn",
apiKey: "sk-test",
isActive: true,
testStatus: "active",
providerSpecificData: {
baseUrl: "https://proxy.example.com",
chatPath: "/v1/chat/completions",
modelsPath: "/v1/models",
},
});
// Synced entry — produces the correct `fta/opc/big-pickle` row.
await modelsDb.replaceSyncedAvailableModelsForConnection(
NODE_ID,
(connection as { id: string }).id,
[{ id: MODEL_ID, name: "Big Pickle", source: "imported", supportedEndpoints: ["chat"] }]
);
// Alias row pointing at the node UUID — as combos/imports register unprefixed
// shortcuts. This is what triggered the duplicate `<uuid>/model` entry.
await aliasesDb.setModelAlias("big-pickle", `${NODE_ID}/${MODEL_ID}`);
}
test.beforeEach(async () => {
await resetStorage();
});
test.after(async () => {
core.resetDbInstance();
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 () => {
await seedCompatibleNodeWithAlias();
const response = await v1ModelsCatalog.getUnifiedModelsResponse(
new Request("http://localhost/api/v1/models?prefix=alias")
);
const body = (await response.json()) as { data: Array<Record<string, unknown>> };
assert.equal(response.status, 200);
const ids = body.data.map((m) => m.id) as string[];
// The correct prefixed id is present exactly once.
assert.equal(
ids.filter((id) => id === `${CONFIGURED_PREFIX}/${MODEL_ID}`).length,
1,
`expected exactly one "${CONFIGURED_PREFIX}/${MODEL_ID}" in ${JSON.stringify(ids)}`
);
// No id leaks the raw provider-node UUID.
for (const id of ids) {
assert.equal(
id.startsWith(`${NODE_ID}/`),
false,
`id "${id}" must not be prefixed with the raw provider-node UUID`
);
assert.equal(
UUID_SHAPE_RE.test(id.split("/")[0]),
false,
`id "${id}" must not carry a UUID-shaped provider prefix`
);
}
});
test("#8958: alias-backed duplicate is absent in default and dual modes too", async () => {
await seedCompatibleNodeWithAlias();
for (const query of ["", "?prefix=dual"]) {
const response = await v1ModelsCatalog.getUnifiedModelsResponse(
new Request(`http://localhost/api/v1/models${query}`)
);
const body = (await response.json()) as { data: Array<Record<string, unknown>> };
const ids = body.data.map((m) => m.id) as string[];
assert.ok(
ids.includes(`${CONFIGURED_PREFIX}/${MODEL_ID}`),
`mode "${query || "default"}": expected "${CONFIGURED_PREFIX}/${MODEL_ID}"`
);
assert.equal(
ids.some((id) => id.startsWith(`${NODE_ID}/`)),
false,
`mode "${query || "default"}": no id may carry the raw provider-node UUID prefix`
);
}
});