import { expect, test } from "bun:test"; import { CORE_DESIGN_SYSTEMS } from "@wrnexus/design-system"; import { designRevision, DEFAULT_DESIGN_BRIEF } from "@wrnexus/design-studio"; import { AI_TIMEOUT_MS, AI_OUTPUT_TOKENS, apimartProvider, studioAI, } from "../app/lib/studio-ai.ts"; import { addStudioGuide } from "../app/lib/studio-guide.ts"; const proposals = ["2px", "4px", "8px"].map((value, index) => ({ name: `Direction ${index + 1}`, rationale: "A different control shape.", patch: [{ op: "set", path: "/tokens/shape.control/value", value }], })); const completion = () => Response.json({ choices: [ { finish_reason: "stop", message: { content: JSON.stringify({ alternatives: proposals }) }, }, ], }); const mockFetch = ( fn: (input: string | URL | Request, init?: RequestInit) => Promise, ) => fn as typeof fetch; const system = CORE_DESIGN_SYSTEMS.square; const input = async (locks: string[] = []) => ({ system, locks, brief: { ...DEFAULT_DESIGN_BRIEF, product: "A website", audience: "Customers", }, kind: "directions", baseRevision: await designRevision(system, locks), }); const request = async (body?: unknown, origin = "https://components.example") => new Request("https://components.example/design-studio/api/generate", { method: "POST", headers: { "content-type": "application/json", origin }, body: JSON.stringify(body ?? (await input())), }); test("APIMart uses the specified model, bearer authentication, non-streaming JSON and cancellation", async () => { const controller = new AbortController(); const provider = apimartProvider( "test-secret", mockFetch(async (url, init) => { expect(String(url)).toBe("https://api.apimart.ai/v1/chat/completions"); expect(new Headers(init?.headers).get("authorization")).toBe( "Bearer test-secret", ); expect(init?.signal).toBe(controller.signal); const body = JSON.parse(init?.body as string); expect(body.model).toBe("gpt-5-mini"); expect(body.stream).toBe(false); expect(body.max_tokens).toBe(AI_OUTPUT_TOKENS); expect(body.messages).toEqual([ { role: "system", content: "rules" }, { role: "user", content: "brief" }, ]); return completion(); }), ); const result = await provider.generate("brief", { system: "rules", signal: controller.signal, }); expect(JSON.parse(result.value).alternatives).toHaveLength(3); }); test("AI proposals use engine validation, preserve draft source, locks and revision checks", async () => { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; return completion(); }), ); const original = JSON.stringify(system); const response = await handler(await request()); const batch = await response!.json(); expect(batch.alternatives).toHaveLength(3); expect(batch.provenance.provider).toBe("APIMart"); expect(batch.provenance.model).toBe("gpt-5-mini"); expect(JSON.stringify(system)).toBe(original); const locked = await handler( await request(await input(["/tokens/shape.control"])), ); expect((await locked!.json()).alternatives).toHaveLength(0); const before = calls; expect( (await handler( await request({ ...(await input()), baseRevision: "a".repeat(64) }), ))!.status, ).toBe(409); expect(calls).toBe(before); }); test("metadata and errors never contain the key or raw upstream response", async () => { const handler = studioAI( "test-secret", mockFetch( async () => new Response("test-secret upstream credentials", { status: 401 }), ), ); const meta = await handler( new Request("https://components.example/design-studio/api/generation"), ); const text = await meta!.text(); expect(text).toContain("gpt-5-mini"); expect(JSON.parse(text).timeoutMs).toBe(AI_TIMEOUT_MS); expect(AI_TIMEOUT_MS).toBe(60_000); expect(text).not.toContain("test-secret"); const failure = await handler(await request()); const body = await failure!.text(); expect(body).not.toContain("test-secret"); expect(body).not.toContain("upstream credentials"); expect(body).toContain("WRN-STUDIO-PROVIDER"); }); test("a completion beyond the old 20-second deadline produces three validated alternatives", async () => { const handler = studioAI( "test-secret", mockFetch(async (_url, init) => { await Bun.sleep(21_000); expect(init?.signal?.aborted).toBe(false); return completion(); }), ); const original = JSON.stringify(system); const response = await handler(await request()); expect(response!.status).toBe(200); const batch = await response!.json(); expect(batch.alternatives).toHaveLength(3); expect(batch.provenance.provider).toBe("APIMart"); expect(JSON.stringify(system)).toBe(original); }, 30_000); test("cancellation still aborts the upstream request and keeps the draft unchanged", async () => { const controller = new AbortController(); const handler = studioAI( "test-secret", mockFetch(async (_url, init) => { controller.abort(); expect(init?.signal?.aborted).toBe(true); throw new DOMException("Cancelled", "AbortError"); }), ); const original = JSON.stringify(system); const base = await request(); const response = await handler( new Request(base, { signal: controller.signal }), ); expect(response!.status).toBe(408); expect(await response!.text()).toContain("draft is unchanged"); expect(JSON.stringify(system)).toBe(original); }); test("same-origin checks, body bounds and local fallback remain enforced", async () => { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; return completion(); }), ); expect( (await handler(await request(await input(), "https://other.example")))! .status, ).toBe(403); expect( (await handler(await request({ huge: "x".repeat(2_000_001) })))!.status, ).toBe(413); expect(calls).toBe(0); expect(await studioAI()(await request())).toBeUndefined(); expect( addStudioGuide( '
', false, ), ).toContain("APIMART_API_KEY"); expect(addStudioGuide('
', true)).toContain( "APIMart AI is enabled", ); }); test("a public AI endpoint enforces a bounded generation rate", async () => { const handler = studioAI( "test-secret", mockFetch(async () => completion()), ); for (let index = 0; index < 10; index++) expect((await handler(await request()))!.status).toBe(200); expect((await handler(await request()))!.status).toBe(429); }); test("temporary APIMart failures retry once and return three validated designs", async () => { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async (_url, init) => { calls++; expect(JSON.parse(init!.body as string).max_tokens).toBe(8192); return calls === 1 ? new Response("sensitive upstream context test-secret", { status: 503, }) : completion(); }), ); const batch = await (await handler(await request()))!.json(); expect(calls).toBe(2); expect(batch.alternatives).toHaveLength(3); expect(batch.provenance.attempts).toBe(2); expect(batch.provenance.maxOutputTokens).toBe(8192); }); test("authentication and quota failures provide actionable redacted diagnostics without retrying", async () => { for (const [status, code, hint] of [ [401, "invalid_api_key", "server API key"], [402, "insufficient_quota", "credit or quota"], [404, "model_not_found", "configured model"], ] as const) { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; return Response.json( { error: { code, message: "test-secret private brief" } }, { status }, ); }), ); const body = await (await handler(await request()))!.text(); expect(calls).toBe(1); expect(body).toContain(hint); expect(body).not.toContain("test-secret"); expect(body).not.toContain("private brief"); } }); test("truncated and empty completions retry but never exceed two total upstream calls", async () => { for (const kind of ["length", "empty", "invalid-then-outage"]) { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; if (kind === "invalid-then-outage") return calls === 1 ? Response.json({ choices: [{ message: { content: "invalid JSON" } }], }) : new Response("private upstream body", { status: 502 }); return calls === 1 ? Response.json({ choices: [ { finish_reason: kind === "length" ? "length" : "stop", message: { content: "" }, }, ], }) : completion(); }), ); const batch = await (await handler(await request()))!.json(); expect(calls).toBe(2); expect(batch.provenance.attempts).toBe(2); expect(batch.alternatives).toHaveLength( kind === "invalid-then-outage" ? 0 : 3, ); } }); test("network and repeated rate failures remain bounded and describe the actual failure category", async () => { for (const network of [true, false]) { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; if (network) throw Error("private test-secret TLS details"); return new Response("private test-secret", { status: 429 }); }), ); const body = await (await handler(await request()))!.text(); expect(calls).toBe(2); expect(body).toContain( network ? "could not reach APIMart" : "rate limiting", ); expect(body).not.toContain("test-secret"); } }); test("a retry followed by an invalid design does not spend a third request or mislabel a schema error", async () => { let calls = 0; const handler = studioAI( "test-secret", mockFetch(async () => { calls++; return calls === 1 ? new Response("unavailable", { status: 503 }) : Response.json({ choices: [{ message: { content: "not JSON" } }] }); }), ); const batch = await (await handler(await request()))!.json(); expect(calls).toBe(2); expect(batch.alternatives).toHaveLength(0); expect(batch.diagnostics[0].code).toBe("WRN-STUDIO-PROPOSAL"); expect(batch.diagnostics[0].message).toContain("schema"); }); test("async generation acknowledges immediately and polling retrieves the validated batch", async () => { let finish!: () => void; let entered!: () => void; const started = new Promise((resolve) => { entered = resolve; }); const blocked = new Promise((resolve) => { finish = resolve; }); const handler = studioAI( "test-secret", mockFetch(async () => { entered(); await blocked; return completion(); }), ); const original = await request(); const headers = new Headers(original.headers); headers.set("prefer", "respond-async"); const accepted = await handler(new Request(original, { headers })); expect(accepted!.status).toBe(202); const { job } = await accepted!.json(); await started; const url = `https://components.example/design-studio/api/generate/jobs/${job.id}`; expect((await (await handler(new Request(url)))!.json()).status).toBe( "pending", ); expect( (await handler( new Request(url, { headers: { origin: "https://other.example" } }), ))!.status, ).toBe(403); finish(); let result: any; for (let i = 0; i < 50; i++) { result = await (await handler(new Request(url)))!.json(); if (result.status !== "pending") break; await Bun.sleep(10); } expect(result.status).toBe("complete"); expect(result.result.alternatives).toHaveLength(3); expect( (await handler( new Request(url.replace(job.id, "00000000-0000-4000-8000-000000000000")), ))!.status, ).toBe(410); }); test("background generation cancellation aborts the provider and leaves no proposals", async () => { let entered!: () => void; const started = new Promise((resolve) => { entered = resolve; }); let aborted = false; const handler = studioAI( "test-secret", mockFetch(async (_url, init) => { entered(); return await new Promise((_, reject) => { init!.signal!.addEventListener( "abort", () => { aborted = true; reject(new DOMException("cancelled", "AbortError")); }, { once: true }, ); }); }), ); const original = await request(); const headers = new Headers(original.headers); headers.set("prefer", "respond-async"); const { job } = await (await handler( new Request(original, { headers }), ))!.json(); await started; const response = await handler( new Request( `https://components.example/design-studio/api/generate/jobs/${job.id}`, { method: "DELETE" }, ), ); expect((await response!.json()).status).toBe("cancelled"); expect(aborted).toBe(true); }); test("the configured model is used consistently for metadata and upstream requests", async () => { let selected = ""; const handler = studioAI( "test-secret", mockFetch(async (_url, init) => { selected = JSON.parse(init!.body as string).model; return completion(); }), "custom-model", ); const metadata = await (await handler( new Request("https://components.example/design-studio/api/generation"), ))!.json(); expect(metadata.model).toBe("custom-model"); const batch = await (await handler(await request()))!.json(); expect(selected).toBe("custom-model"); expect(batch.provenance.model).toBe("custom-model"); expect( addStudioGuide('
', true, "custom-model"), ).toContain("custom-model"); expect( addStudioGuide('
', true, "