OmniRoute/tests/unit/model-sync-route.test.ts
diegosouzapw fceb9d4145 fix(core): resolve runtime edge cases and TypeScript regressions
Tighten request and provider typing across the SSE pipeline to fix
nullability and inference issues in Claude compatibility, wildcard
routing, usage tracking, proxy fetch, and response sanitization.

Address runtime edge cases by normalizing Bailian hosts, guarding TLS
session creation, preserving custom provider base URLs, and using safer
OAuth form param construction during token refresh flows.

Update dashboard data path exports and usage stats typing, and align
E2E/unit tests with paginated API responses, internal model sync auth,
and current response payload shapes.
2026-04-17 20:02:45 -03:00

590 lines
19 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";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-model-sync-route-"));
process.env.DATA_DIR = TEST_DATA_DIR;
// FASE-01: API_KEY_SECRET is required for CRC operations (no hardcoded fallback)
if (!process.env.API_KEY_SECRET) {
process.env.API_KEY_SECRET = "test-model-sync-secret-" + Date.now();
}
const core = await import("../../src/lib/db/core.ts");
const localDb = await import("../../src/lib/localDb.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const modelsDb = await import("../../src/lib/db/models.ts");
const callLogs = await import("../../src/lib/usage/callLogs.ts");
const modelSyncRoute = await import("../../src/app/api/providers/[id]/sync-models/route.ts");
const scheduler = await import("../../src/shared/services/modelSyncScheduler.ts");
const originalFetch = globalThis.fetch;
async function resetStorage() {
delete process.env.INITIAL_PASSWORD;
globalThis.fetch = originalFetch;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(() => {
globalThis.fetch = originalFetch;
core.resetDbInstance();
apiKeysDb.resetApiKeyState();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true });
});
async function enableAuth() {
process.env.INITIAL_PASSWORD = "bootstrap-password";
await localDb.updateSettings({ requireLogin: true, password: "" });
}
test("model sync route skips success log when fetched models do not change stored models", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "MAIN",
displayName: "OpenRouter Main",
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
{
id: "custom-model-1",
name: "Custom Model 1",
source: "auto-sync",
},
]);
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({
models: [{ id: "custom-model-1", name: "Custom Model 1" }],
});
};
try {
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.logged, false);
assert.deepEqual(body.modelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(logs.length, 0);
} finally {
globalThis.fetch = originalFetch;
}
});
test("model sync route stores the real provider while keeping the account label", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "MAIN",
displayName: "OpenRouter Main",
apiKey: "test-key",
});
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({
models: [{ id: "custom-model-2", name: "Custom Model 2" }],
});
};
try {
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 1, removed: 0, updated: 0, total: 1 });
assert.equal(body.provider, "openrouter");
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(logs.length, 1);
assert.equal(logs[0].provider, "openrouter");
assert.equal(logs[0].account, "MAIN");
assert.equal(logs[0].model, "model-sync");
} finally {
globalThis.fetch = originalFetch;
}
});
test("model sync route requires authentication for external requests when auth is enabled", async () => {
await resetStorage();
await enableAuth();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "Protected Connection",
apiKey: "test-key",
});
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
}),
{ params: { id: connection.id } }
);
const body = await response.json();
assert.equal(response.status, 401);
assert.equal(body.error.message, "Authentication required");
assert.equal(body.error.type, "invalid_api_key");
});
test("model sync route returns 404 for unknown connections after internal auth passes", async () => {
await resetStorage();
const response = await modelSyncRoute.POST(
new Request("http://localhost/api/providers/missing/sync-models", {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: "missing" } }
);
assert.equal(response.status, 404);
assert.deepEqual(await response.json(), { error: "Connection not found" });
});
test("model sync route propagates upstream failures and records an error log entry", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "Error Branch",
apiKey: "test-key",
});
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({ error: "Provider upstream unavailable" }, { status: 502 });
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 502);
assert.equal(body.error, "Provider upstream unavailable");
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 502);
assert.equal(logs[0].provider, "openrouter");
assert.equal(logs[0].path, `/api/providers/${connection.id}/models`);
});
test("model sync route falls back to the upstream HTTP status when the models payload has no error field", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "Rate Limited Sync",
apiKey: "test-key",
});
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({}, { status: 429 });
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 429);
assert.equal(body.error, "Failed to fetch models");
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 429);
assert.equal(logs[0].error, "HTTP 429");
});
test("model sync route preserves previously synced models when the upstream omits the models list", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "No Models Returned",
apiKey: "test-key",
});
await modelsDb.replaceCustomModels("openrouter", [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
},
]);
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 200);
assert.equal(body.syncedModels, 1);
assert.equal(body.logged, false);
assert.deepEqual(body.modelChanges, { added: 0, removed: 0, updated: 0, total: 0 });
assert.deepEqual(body.models, [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
apiFormat: "chat-completions",
supportedEndpoints: ["chat"],
},
]);
assert.equal(logs.length, 0);
});
test("model sync route writes synced available models for Gemini connections", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "gemini",
authType: "apikey",
name: "Gemini Sync",
apiKey: "gm-key",
});
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({
models: [
{
id: "gemini-custom-preview",
name: "Gemini Custom Preview",
supportedEndpoints: ["chat", "embeddings"],
inputTokenLimit: 32768,
outputTokenLimit: 8192,
description: "Custom Gemini preview model",
supportsThinking: true,
},
],
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const synced = await modelsDb.getSyncedAvailableModels("gemini");
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 200);
assert.equal(body.provider, "gemini");
assert.equal(body.syncedModels, 1);
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 1, removed: 0, updated: 0, total: 1 });
assert.deepEqual(body.models, [
{
id: "gemini-custom-preview",
name: "Gemini Custom Preview",
source: "auto-sync",
apiFormat: "chat-completions",
supportedEndpoints: ["chat", "embeddings"],
inputTokenLimit: 32768,
outputTokenLimit: 8192,
description: "Custom Gemini preview model",
supportsThinking: true,
},
]);
assert.deepEqual(synced, [
{
id: "gemini-custom-preview",
name: "Gemini Custom Preview",
source: "api-sync",
supportedEndpoints: ["chat", "embeddings"],
inputTokenLimit: 32768,
outputTokenLimit: 8192,
description: "Custom Gemini preview model",
supportsThinking: true,
},
]);
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 200);
});
test("model sync route records added, removed, and updated model diffs with fallback identifiers", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "oauth",
email: "sync@example.com",
accessToken: "sync-token",
});
await modelsDb.replaceCustomModels("openrouter", [
{
id: "persisted-model",
name: "Persisted Model",
source: "auto-sync",
},
{
id: "removed-model",
name: "Removed Model",
source: "auto-sync",
},
]);
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({
models: [
{
id: "persisted-model",
name: "Persisted Model v2",
supportedEndpoints: ["chat", "embeddings"],
},
{
model: "fallback-model",
displayName: "Fallback Model",
description: "Fallback from model field",
},
],
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 200);
assert.equal(body.syncedModels, 2);
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 1, removed: 1, updated: 1, total: 3 });
assert.deepEqual(
body.models.map((model) => ({
id: model.id,
name: model.name,
supportedEndpoints: model.supportedEndpoints,
description: model.description,
})),
[
{
id: "persisted-model",
name: "Persisted Model v2",
supportedEndpoints: ["chat", "embeddings"],
description: undefined,
},
{
id: "fallback-model",
name: "Fallback Model",
supportedEndpoints: ["chat"],
description: "Fallback from model field",
},
]
);
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 200);
assert.equal(logs[0].provider, "openrouter");
assert.ok(logs[0].account.includes("*"), `Expected masked email, got: ${logs[0].account}`);
});
test("model sync route forwards cookies, filters built-ins, and syncs aliases for internal requests", async () => {
await resetStorage();
await enableAuth();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "External Sync",
displayName: "OpenRouter External",
apiKey: "test-key",
});
await localDb.setModelAlias("stale-model", "openrouter/stale-model");
await localDb.setModelAlias("router-v2", "other-provider/router-v2");
globalThis.fetch = async (url, init = {}) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
assert.equal(init.headers.cookie, "session=test-cookie");
assert.equal(
init.headers[scheduler.getModelSyncInternalAuthHeaderName()],
scheduler.buildModelSyncInternalHeaders()[scheduler.getModelSyncInternalAuthHeaderName()]
);
return Response.json({
models: [
{ id: "auto", name: "Auto (Best Available)" },
{ id: "router-v2", name: "Router V2" },
{ id: "router-v3", name: "Router V3" },
],
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: {
cookie: "session=test-cookie",
...scheduler.buildModelSyncInternalHeaders(),
},
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const aliases = await localDb.getModelAliases();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 200);
assert.equal(body.provider, "openrouter");
assert.equal(body.syncedModels, 2);
assert.equal(body.syncedAliases, 2);
assert.equal(body.logged, true);
assert.deepEqual(body.modelChanges, { added: 2, removed: 0, updated: 0, total: 2 });
assert.deepEqual(
body.models.map((model) => ({ id: model.id, name: model.name })),
[
{ id: "router-v2", name: "Router V2" },
{ id: "router-v3", name: "Router V3" },
]
);
assert.equal(aliases["stale-model"], undefined);
assert.equal(aliases["openrouter-router-v2"], "openrouter/router-v2");
assert.equal(aliases["router-v3"], "openrouter/router-v3");
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 200);
assert.equal(logs[0].account, "External Sync");
});
test("model sync route uses provider-node prefixes when syncing compatible-provider aliases", async () => {
await resetStorage();
await providersDb.createProviderNode({
id: "anthropic-compatible-demo",
type: "anthropic-compatible",
name: "Anthropic Demo",
prefix: "cm",
baseUrl: "https://proxy.example.com",
chatPath: "/v1/messages",
modelsPath: "/v1/models",
});
const connection = await providersDb.createProviderConnection({
provider: "anthropic-compatible-demo",
authType: "apikey",
name: "Compatible Sync",
apiKey: "compat-key",
providerSpecificData: {
baseUrl: "https://proxy.example.com",
chatPath: "/v1/messages",
modelsPath: "/v1/models",
},
});
await localDb.setModelAlias("sonnet-4-6", "some-other-provider/sonnet-4-6");
globalThis.fetch = async (url) => {
assert.equal(String(url), `http://localhost/api/providers/${connection.id}/models`);
return Response.json({
models: [{ id: "sonnet-4-6", name: "Sonnet 4.6" }],
});
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const aliases = await localDb.getModelAliases();
assert.equal(response.status, 200);
assert.equal(body.provider, "anthropic-compatible-demo");
assert.equal(body.syncedAliases, 1);
assert.equal(aliases["cm-sonnet-4-6"], "anthropic-compatible-demo/sonnet-4-6");
});
test("model sync route returns 500 and records a failure when the internal models fetch throws", async () => {
await resetStorage();
const connection = await providersDb.createProviderConnection({
provider: "openrouter",
authType: "apikey",
name: "Exploding Sync",
apiKey: "test-key",
});
globalThis.fetch = async () => {
throw new Error("network exploded");
};
const response = await modelSyncRoute.POST(
new Request(`http://localhost/api/providers/${connection.id}/sync-models`, {
method: "POST",
headers: scheduler.buildModelSyncInternalHeaders(),
}),
{ params: { id: connection.id } }
);
const body = await response.json();
const logs = await callLogs.getCallLogs({ model: "model-sync", limit: 10 });
assert.equal(response.status, 500);
assert.equal(body.error, "network exploded");
assert.equal(logs.length, 1);
assert.equal(logs[0].status, 500);
assert.equal(logs[0].provider, "openrouter");
});