import { describe, expect, it } from "bun:test"; import type { BoundedThread, ThreadShell } from "../src/t3/types.ts"; import { summarize, trimText } from "../src/views.ts"; const thread = (overrides: Partial = {}): ThreadShell => ({ id: "thread-1", projectId: "project-1", title: "Receipt OCR", createdAt: "2026-11-01T09:00:00.000Z", updatedAt: "2026-11-01T09:00:00.000Z", status: "running", activeRunId: "run-1", pendingRuntimeRequest: null, modelSelection: { instanceId: "claudeAgent", model: "claude-opus-5-5" }, lineage: { relationshipToParent: null }, ...overrides, }); const message = (role: string, text: string, id = text) => ({ id, role, text, createdAt: "2026-11-01T09:00:00.000Z", updatedAt: "2026-11-01T09:00:00.000Z", }); const bounded = (turnItems: BoundedThread["projection"]["turnItems"], messages = [message("assistant", "Tests written.")]) => ({ snapshotSequence: 1, projection: { thread: thread(), messages, turnItems } }) as BoundedThread; describe("trimText", () => { it("collapses whitespace and cuts at a word", () => { expect(trimText("one two\n\nthree", 50)).toBe("one two three"); expect(trimText("alpha beta gamma delta", 15)).toBe("alpha beta…"); }); }); describe("summarize", () => { it("takes the last assistant message and skips empty or system ones", () => { const summary = summarize( bounded([], [message("user", "Add OCR"), message("assistant", "Done, all green."), message("system", "noise"), message("assistant", " ")]), thread(), ); expect(summary.lastAssistantMessage).toBe("Done, all green."); expect(summary.messages.map((m) => m.role)).toEqual(["user", "assistant"]); }); it("reads the pending approval's prompt and options", () => { const summary = summarize( bounded([ { type: "approval_request", requestId: "r1", requestKind: "command", prompt: "bun test --filter ocr", options: [ { decision: "accept", label: "Allow once" }, { decision: "acceptAlways", label: "Always allow", warning: "Applies to every thread" }, { decision: "decline", label: "Deny" }, ], } as BoundedThread["projection"]["turnItems"][number], ]), thread({ pendingRuntimeRequest: { id: "r1", kind: "command", createdAt: "2026-11-01T09:00:00.000Z" } }), ); expect(summary.request).toEqual({ id: "r1", kind: "command", prompt: "bun test --filter ocr", reason: null, options: [ { decision: "accept", label: "Allow once" }, { decision: "acceptAlways", label: "Always allow", warning: "Applies to every thread" }, { decision: "decline", label: "Deny" }, ], questions: [], }); }); it("offers Allow and Deny when the approval's turn item is not loaded yet", () => { const summary = summarize( bounded([]), thread({ pendingRuntimeRequest: { id: "r9", kind: "file-change", createdAt: "2026-11-01T09:00:00.000Z" } }), ); expect(summary.request?.options.map((o) => o.decision)).toEqual(["accept", "decline"]); }); it("reads questions, using the label when an option has no value", () => { const summary = summarize( bounded([ { type: "user_input_request", requestId: "q1", questions: [ { id: "db", header: "Database", question: "Which database?", options: [{ label: "SQLite", description: "" }] }, ], } as BoundedThread["projection"]["turnItems"][number], ]), thread({ pendingRuntimeRequest: { id: "q1", kind: "user_input", createdAt: "2026-11-01T09:00:00.000Z" } }), ); expect(summary.request?.questions).toEqual([ { id: "db", header: "Database", question: "Which database?", options: [{ label: "SQLite", value: "SQLite" }], allowCustomAnswer: false }, ]); }); });