import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { AgentController, SuggestionBuilder } from "./index.js"; import type { ResolvedProvider } from "./index.js"; const config: ResolvedProvider = { id: "test-openai", kind: "openai-compatible", baseUrl: "https://api.openai.com/v1", apiKey: "sk-test", model: "gpt-4o", contextWindow: 128_000, }; let savedFetch: typeof fetch; /** Build an SSE Response streaming a single text delta. */ function sseResponse(events: string[]): Response { const encoder = new TextEncoder(); const body = new ReadableStream>({ start(controller) { for (const event of events) controller.enqueue(encoder.encode(`data: ${event}\n\n`)); controller.enqueue(encoder.encode("data: [DONE]\n\n")); controller.close(); }, }); return new Response(body, { status: 200, headers: { "content-type": "text/event-stream" } }); } describe("SuggestionBuilder", () => { let builder: SuggestionBuilder; beforeEach(() => { savedFetch = globalThis.fetch; }); afterEach(() => { globalThis.fetch = savedFetch; vi.restoreAllMocks(); }); it("streams a prompt and builds a pending suggestion", async () => { globalThis.fetch = (() => sseResponse([JSON.stringify({ choices: [{ delta: { content: "Try a flashback here. It reveals the secret early." } }] })])) as unknown as typeof fetch; const agent = new AgentController(config); builder = new SuggestionBuilder(agent); const result = await builder.build( { kind: "expand", prompt: "Expand the reveal scene" }, "sess-1", "msg-1", ); expect(result.id).toBeTruthy(); expect(result.sessionId).toBe("sess-1"); expect(result.sourceMessageId).toBe("msg-1"); expect(result.kind).toBe("expand"); expect(result.status).toBe("pending"); expect(result.text).toContain("flashback"); expect(result.title.length).toBeGreaterThan(0); expect(result.summary.length).toBeGreaterThan(0); expect(result.createdAt).toBeTruthy(); }); it("includes the context in the system prompt", async () => { const calls: Array = []; globalThis.fetch = ((_url: string, init: RequestInit) => { calls.push(init); return Promise.resolve( sseResponse([JSON.stringify({ choices: [{ delta: { content: "ok" } }] })]), ); }) as unknown as typeof fetch; const agent = new AgentController(config); builder = new SuggestionBuilder(agent); await builder.build( { kind: "outline", prompt: "Outline chapter 3", context: { chapterTitle: "The Return", setting: "the capital", characters: ["Aria"] }, }, "sess-1", "msg-1", ); const init = calls[0] as RequestInit; const body = JSON.parse(init.body as string) as Record; const messages = body.messages as Array<{ content: string; role?: string }>; const system = messages.find((m) => m.role === "system")?.content ?? ""; expect(system).toContain("The Return"); expect(system).toContain("the capital"); expect(system).toContain("Aria"); }); it("produces distinct titles per kind", async () => { globalThis.fetch = (() => sseResponse([JSON.stringify({ choices: [{ delta: { content: "Chapter 3: The Return — Aria confronts the council about the missing supplies." } }] })])) as unknown as typeof fetch; const agent = new AgentController(config); builder = new SuggestionBuilder(agent); const result = await builder.build({ kind: "outline", prompt: "Outline" }, "sess-1", "msg-1"); expect(result.title).toContain("Chapter 3"); }); });