// @vitest-environment node
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("server-only", () => ({}));
import {
CatalogError,
listGateModels,
parseOpenAIModelList,
} from "@/lib/chat/catalog";
beforeEach(() => {
vi.stubEnv("AEGIDA_GATE_URL", "https://gate.example.test");
});
afterEach(() => {
vi.unstubAllEnvs();
vi.unstubAllGlobals();
});
describe("Aegida model catalog", () => {
it("parses provider-native models, removes invalid duplicates and guarantees auto", () => {
expect(
parseOpenAIModelList({
object: "list",
data: [
{ id: "qwen", object: "model", created: 1_785_758_400, owned_by: "qwen" },
{ id: "QWEN", object: "model", created: 1_785_758_400, owned_by: "qwen" },
{ id: "qwen", object: "model", created: 1_785_758_401, owned_by: "duplicate" },
{ id: "claude-sonnet.4", object: "model", created: 1_785_758_402, owned_by: "anthropic" },
],
}),
).toEqual([
{
id: "auto",
name: "Aegida Auto",
description: "Автоматический выбор модели через Aegida Gate.",
},
{ id: "qwen", name: "Qwen", description: "Владелец модели: qwen." },
{
id: "claude-sonnet.4",
name: "Claude Sonnet 4",
description: "Владелец модели: anthropic.",
},
]);
});
it.each([
null,
{},
{ object: "collection", data: [] },
{ object: "list", data: "not-an-array" },
])("rejects a malformed top-level OpenAI model envelope %j", (value) => {
expect(() => parseOpenAIModelList(value)).toThrow(CatalogError);
});
it("relays only the exact user bearer token with no-store semantics", async () => {
const fetchMock = vi.fn().mockResolvedValue(
Response.json({
object: "list",
data: [
{ id: "auto", object: "model", created: 1_785_758_400, owned_by: "aegida" },
],
}),
);
vi.stubGlobal("fetch", fetchMock);
const signal = new AbortController().signal;
await expect(listGateModels("Bearer exact-user-jwt", signal)).resolves.toHaveLength(1);
expect(fetchMock).toHaveBeenCalledWith("https://gate.example.test/v1/models", {
method: "GET",
headers: { Accept: "application/json", Authorization: "Bearer exact-user-jwt" },
cache: "no-store",
redirect: "error",
signal,
});
expect(JSON.stringify(fetchMock.mock.calls[0])).not.toContain("API_KEY");
});
it("classifies upstream authentication separately and never exposes dependency bodies", async () => {
vi.stubGlobal(
"fetch",
vi
.fn()
.mockResolvedValueOnce(new Response("private-auth-body", { status: 401 }))
.mockResolvedValueOnce(new Response("private-dependency-body", { status: 500 })),
);
await expect(listGateModels("Bearer user", new AbortController().signal)).rejects.toMatchObject({
reason: "unauthorized",
message: "Model catalog is unavailable",
});
await expect(listGateModels("Bearer user", new AbortController().signal)).rejects.toMatchObject({
reason: "unavailable",
message: "Model catalog is unavailable",
});
});
});