// @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", }); }); });