import { describe, expect, it } from "vitest"; import { ChatLog } from "./chat-log.js"; describe("ChatLog", () => { it("caps component growth to avoid unbounded render trees", () => { const chatLog = new ChatLog(20); for (let i = 1; i <= 40; i++) { chatLog.addSystem(`system-${i}`); } expect(chatLog.children.length).toBe(20); const rendered = chatLog.render(120).join("\n"); expect(rendered).toContain("system-40"); expect(rendered).not.toContain("system-1"); }); it("drops stale streaming references when old components are pruned", () => { const chatLog = new ChatLog(20); chatLog.startAssistant("first", "run-1"); for (let i = 0; i < 25; i++) { chatLog.addSystem(`overflow-${i}`); } // Should not throw if the original streaming component was pruned. chatLog.updateAssistant("recreated", "run-1"); const rendered = chatLog.render(120).join("\n"); expect(chatLog.children.length).toBe(20); expect(rendered).toContain("recreated"); }); it("does not append duplicate assistant components when a run is started twice", () => { const chatLog = new ChatLog(40); chatLog.startAssistant("first", "run-dup"); chatLog.startAssistant("second", "run-dup"); const rendered = chatLog.render(120).join("\n"); expect(rendered).toContain("second"); expect(rendered).not.toContain("first"); expect(chatLog.children.length).toBe(1); }); it("drops stale tool references when old components are pruned", () => { const chatLog = new ChatLog(20); chatLog.startTool("tool-1", "read_file", { path: "a.txt" }); for (let i = 0; i < 25; i++) { chatLog.addSystem(`overflow-${i}`); } // Should no-op safely after the tool component is pruned. chatLog.updateToolResult("tool-1", { content: [{ type: "text", text: "done" }] }); expect(chatLog.children.length).toBe(20); }); });