import { afterEach, describe, expect, it } from "vitest"; import { createTestApp } from "./test-app.js"; const apps: Array["app"]> = []; afterEach(async () => Promise.all(apps.splice(0).map((app) => app.close()))); describe("model API", () => { it("configures and returns only redacted model metadata", async () => { const { app, modelStore, eventStore } = createTestApp(); apps.push(app); const configured = await app.inject({ method: "PUT", url: "/api/sessions/ses_1/model-config", payload: { providerId: "training", api: "openai-responses", apiKey: "sk-super-secret", baseUrl: "https://api.example.com/v1", modelId: "training-model", supportsImages: true, } }); const response = await app.inject({ method: "GET", url: "/api/sessions/ses_1/model-config" }); expect(configured.statusCode).toBe(200); expect(modelStore.configure).toHaveBeenCalled(); expect(response.body).not.toContain("sk-super-secret"); expect(response.body).not.toContain("server-only-secret"); const body = response.json<{ modelConfig: { keyHint: string }; requestId: string }>(); expect(body.modelConfig.keyHint).toBe("••••cret"); expect(body.requestId).toMatch(/^req_/); expect(eventStore.listAfter("ses_1").events.at(-1)).toMatchObject({ type: "model.configured", payload: { providerId: "training", modelId: "training-model", supportsImages: true, source: "session" }, }); }); it("clears credentials idempotently and returns an honest deferred connection test", async () => { const { app, modelStore } = createTestApp(); apps.push(app); const cleared = await app.inject({ method: "DELETE", url: "/api/sessions/ses_1/model-config/credentials" }); const tested = await app.inject({ method: "POST", url: "/api/sessions/ses_1/model-config/test" }); expect(cleared.statusCode).toBe(200); expect(modelStore.clearCredentials).toHaveBeenCalledWith("ses_1"); const body = tested.json<{ status: string; requestId: string }>(); expect(body.status).toBe("deferred"); expect(body.requestId).toMatch(/^req_/); expect(tested.body).not.toContain("server-only-secret"); }); it("emits a safe validation event when model configuration fails", async () => { const { app, modelStore, eventStore } = createTestApp(); apps.push(app); modelStore.configure.mockRejectedValueOnce(new Error("provider leaked sk-super-secret")); const response = await app.inject({ method: "PUT", url: "/api/sessions/ses_1/model-config", payload: { providerId: "training", api: "openai-responses", apiKey: "sk-super-secret", modelId: "training-model", supportsImages: true, } }); expect(response.statusCode).toBe(500); const serialized = JSON.stringify(eventStore.listAfter("ses_1").events); expect(serialized).toContain("model.validation_failed"); expect(serialized).not.toContain("sk-super-secret"); expect(serialized).not.toContain("provider leaked"); }); });