import { afterEach, describe, expect, it, vi } from "vitest"; /** * Next.js compiles `instrumentation.js` and every API route into SEPARATE server * bundles, so one source file gets a distinct module instance per bundle. A * reader installed into a plain module-local variable therefore lands in the * startup copy while the chat route reads its own still-empty copy: the setter * appears to succeed and the feature is silently dead. * * Distinct `?bundle=` suffixes give this file two instances of the same module in * one process — it models "two bundles" without a real build. */ const boot = await import("../../open-sse/providers/capabilities.js?bundle=boot"); const route = await import("../../open-sse/providers/capabilities.js?bundle=route"); afterEach(() => { boot.setUserCapsSource(null); boot.setCatalogSource(null); }); describe("capability sources across server bundles", () => { it("keeps distinct module instances per bundle", () => { // If these ever collapse into one instance the tests below prove nothing. expect(route).not.toBe(boot); }); it("honours a user-asserted caps source installed at startup", () => { boot.setUserCapsSource(() => ({ vision: false })); // qwen3-vl-plus is vision by name heuristic; the assertion must win in the // bundle that actually serves the request. expect(route.getCapabilitiesForModel("custom-node", "qwen3-vl-plus").vision).toBe(false); boot.setUserCapsSource(() => ({ reasoning: true })); expect(route.getCapabilitiesForModel("custom-node", "my-private-chatml-9").reasoning).toBe(true); }); it("honours a catalog source installed at startup", () => { boot.setCatalogSource({ getModalities: (model) => (model === "hand-rolled-7b" ? { vision: true } : null), getLimits: (provider, model) => (model === "hand-rolled-7b" ? { contextWindow: 400000 } : null), }); const caps = route.getCapabilitiesForModel("custom-node", "hand-rolled-7b"); expect(caps.vision).toBe(true); expect(caps.contextWindow).toBe(400000); }); it("detaches the catalog everywhere when one bundle detaches it", () => { // modelCatalog/sync.js nulls the reader while computing a delta against the // hand-written tables, then reinstalls it; both bundles must agree. boot.setCatalogSource({ getModalities: () => ({ vision: true }), getLimits: () => null }); expect(route.getCapabilitiesForModel("custom-node", "hand-rolled-7b").vision).toBe(true); boot.setCatalogSource(null); expect(route.getCapabilitiesForModel("custom-node", "hand-rolled-7b").vision).toBe(false); }); }); describe("user-caps snapshot across server bundles", () => { const rows = [{ providerAlias: "node-abc", id: "chat-9", type: "llm", caps: { reasoning: true } }]; it("makes a rebuild started by the write route visible to the request route", async () => { vi.doMock("@/lib/db/index.js", () => ({ getCustomModels: async () => rows })); try { const write = await import("../../src/lib/modelCaps/userCaps.js?bundle=write"); const request = await import("../../src/lib/modelCaps/userCaps.js?bundle=request"); write.invalidateUserCaps(); await vi.waitFor(() => { expect(request.getUserCapsFor("node-abc", "chat-9")).toEqual({ reasoning: true }); }); } finally { vi.doUnmock("@/lib/db/index.js"); vi.resetModules(); } }); });