import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { AgentController, NoteBuilder } 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("NoteBuilder", () => { let builder: NoteBuilder; beforeEach(() => { savedFetch = globalThis.fetch; }); afterEach(() => { globalThis.fetch = savedFetch; vi.restoreAllMocks(); }); it("streams a prompt and builds a draft note", async () => { globalThis.fetch = (() => sseResponse([JSON.stringify({ choices: [{ delta: { content: "Aria is the protagonist. She is a young mage with a mysterious past. #protagonist #mage" } }] })])) as unknown as typeof fetch; const agent = new AgentController(config); builder = new NoteBuilder(agent); const result = await builder.build( { kind: "character", prompt: "Summarize Aria", context: { title: "Aria" } }, "sess-1", ); expect(result.id).toBeTruthy(); expect(result.sessionId).toBe("sess-1"); expect(result.kind).toBe("character"); expect(result.status).toBe("draft"); expect(result.body).toContain("Aria"); expect(result.tags).toContain("protagonist"); expect(result.tags).toContain("mage"); expect(result.createdAt).toBeTruthy(); expect(result.updatedAt).toBeTruthy(); }); it("extracts tags from a #tag list in the response", async () => { globalThis.fetch = (() => sseResponse([JSON.stringify({ choices: [{ delta: { content: "The Sunspire Mountains. #location #magic #north" } }] })])) as unknown as typeof fetch; const agent = new AgentController(config); builder = new NoteBuilder(agent); const result = await builder.build( { kind: "setting", prompt: "Describe the setting" }, "sess-1", ); expect(result.kind).toBe("setting"); expect(result.tags.sort()).toEqual(["location", "magic", "north"]); }); it("includes the note title 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 NoteBuilder(agent); await builder.build( { kind: "plot", prompt: "Summarize the plot thread", context: { title: "The Stolen Relic" } }, "sess-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 Stolen Relic"); }); });