77 lines
4.1 KiB
TypeScript
77 lines
4.1 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { assertCursorOnlyCatalog, discoverCursorModels, gatewayCursorRows, gatewayPayload, loopbackBaseUrl } from "../catalog.ts";
|
|
|
|
function catalogs() {
|
|
return {
|
|
gateway: { data: [{ id: "cursor/grok-4.7", owned_by: "cursor", display_name: "Grok 4.7", context_length: 256000,
|
|
max_output_tokens: 64000, input_modalities: ["text", "image"] }] },
|
|
local: { models: [{ provider: "cursor", kind: "chat", id: "grok-4.7", name: "Grok 4.7", input: ["text", "image"],
|
|
reasoning: true, thinking: ["low", "medium", "high", "xhigh"], contextWindow: 500000, maxTokens: 64000,
|
|
cost: { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 } }] },
|
|
};
|
|
}
|
|
|
|
test("joins routable models with OMP pricing, capabilities, and thinking levels", () => {
|
|
const { gateway, local } = catalogs();
|
|
const models = discoverCursorModels(gateway, local, "http://127.0.0.1:4000/v1");
|
|
assert.equal(models.length, 1);
|
|
const [model] = models;
|
|
assert.equal(model.provider, "cursor");
|
|
assert.equal(model.id, "grok-4.7");
|
|
assert.equal(model.contextWindow, 256000, "Use the conservative intersection, not a guessed 500k limit");
|
|
assert.deepEqual(model.cost, { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 });
|
|
assert.equal(model.thinkingLevelMap?.max, null);
|
|
assert.equal(model.thinkingLevelMap?.xhigh, "xhigh");
|
|
assert.deepEqual(model.input, ["text", "image"]);
|
|
});
|
|
|
|
test("excludes other providers, unsupported tools, non-chat models, and unsafe ids", () => {
|
|
const { gateway } = catalogs();
|
|
const valid = gateway.data[0];
|
|
const payload = { data: [valid, { ...valid, owned_by: "xai" }, { ...valid, supports_tools: false },
|
|
{ ...valid, kind: "image" }, { ...valid, id: "cursor/../../key" }, null] };
|
|
assert.equal(gatewayCursorRows(payload).length, 1);
|
|
assert.throws(() => gatewayCursorRows({ error: "not a catalog" }), /invalid model catalog/);
|
|
});
|
|
|
|
test("rejects a shared-provider gateway before model discovery", () => {
|
|
assertCursorOnlyCatalog(catalogs().gateway);
|
|
assertCursorOnlyCatalog({ data: [] });
|
|
for (const row of [{ id: "xai/grok-4.7", owned_by: "xai" },
|
|
{ id: "xai/grok-4.7", owned_by: "cursor" }, null]) {
|
|
assert.throws(() => assertCursorOnlyCatalog({ data: [row] }), /non-Cursor/);
|
|
}
|
|
});
|
|
|
|
test("never substitutes zero prices or guessed capabilities for malformed metadata", () => {
|
|
const { gateway, local } = catalogs();
|
|
local.models[0].cost.input = Number.NaN;
|
|
assert.throws(() => discoverCursorModels(gateway, local, "http://localhost:4000/v1"), /invalid pricing/);
|
|
const fresh = catalogs();
|
|
fresh.local.models[0].thinking = [];
|
|
assert.throws(() => discoverCursorModels(fresh.gateway, fresh.local, "http://localhost:4000/v1"), /thinking-level/);
|
|
assert.throws(() => discoverCursorModels(fresh.gateway, { models: [] }, "http://localhost:4000/v1"), /do not match/);
|
|
});
|
|
|
|
test("accepts an authoritative empty catalog and deduplicates model ids", () => {
|
|
const { gateway, local } = catalogs();
|
|
gateway.data.push(gateway.data[0]);
|
|
assert.equal(discoverCursorModels(gateway, local, "http://localhost:4000/v1").length, 1);
|
|
assert.deepEqual(discoverCursorModels({ data: [] }, local, "http://localhost:4000/v1"), []);
|
|
});
|
|
|
|
test("qualifies wire ids and keeps session identity and request fields", () => {
|
|
assert.deepEqual(gatewayPayload({ model: "grok-4.7", messages: [], temperature: 0.5 }, "grok-4.7", "session-one"),
|
|
{ model: "cursor/grok-4.7", messages: [], temperature: 0.5, prompt_cache_key: "session-one" });
|
|
assert.throws(() => gatewayPayload(null, "grok-4.7"), /Invalid/);
|
|
assert.throws(() => gatewayPayload({}, "../bad"), /Invalid/);
|
|
});
|
|
|
|
test("rejects remote endpoints, embedded credentials, and query strings", () => {
|
|
assert.equal(loopbackBaseUrl("http://127.0.0.1:4000/v1/"), "http://127.0.0.1:4000/v1");
|
|
assert.equal(loopbackBaseUrl("http://[::1]:4000/v1"), "http://[::1]:4000/v1");
|
|
for (const url of ["https://remote.example/v1", "http://token@localhost:4000/v1", "http://localhost/v1?token=x", "http://localhost/"]) {
|
|
assert.throws(() => loopbackBaseUrl(url), /loopback/);
|
|
}
|
|
});
|