import { describe, it, expect, mock, beforeEach, afterEach } from "bun:test" import { existsSync, unlinkSync, readFileSync } from "fs" import { buildTranscriptFromSession, deleteTempTranscript, clearTranscriptCache, } from "./transcript" function createMockClient(messages: unknown[] = []) { return { session: { messages: mock(() => Promise.resolve({ data: messages, }) ), }, } } describe("transcript caching", () => { afterEach(() => { clearTranscriptCache() }) // #given same session called twice // #when buildTranscriptFromSession is invoked // #then session.messages() should be called only once (cached) it("should cache transcript and not re-fetch for same session", async () => { const client = createMockClient([ { info: { role: "assistant" }, parts: [ { type: "tool", tool: "bash", state: { status: "completed", input: { command: "ls" } }, }, ], }, ]) const path1 = await buildTranscriptFromSession( client, "ses_cache1", "/tmp", "bash", { command: "echo hi" } ) const path2 = await buildTranscriptFromSession( client, "ses_cache1", "/tmp", "read", { path: "/tmp/file" } ) // session.messages() called only once expect(client.session.messages).toHaveBeenCalledTimes(1) // Both return valid paths expect(path1).not.toBeNull() expect(path2).not.toBeNull() // Second call should append the new tool entry if (path2) { const content = readFileSync(path2, "utf-8") expect(content).toContain("Read") } deleteTempTranscript(path1) deleteTempTranscript(path2) }) // #given different sessions // #when buildTranscriptFromSession called for each // #then session.messages() should be called for each it("should not share cache between different sessions", async () => { const client = createMockClient([]) await buildTranscriptFromSession(client, "ses_a", "/tmp", "bash", {}) await buildTranscriptFromSession(client, "ses_b", "/tmp", "bash", {}) expect(client.session.messages).toHaveBeenCalledTimes(2) clearTranscriptCache() }) // #given clearTranscriptCache is called // #when buildTranscriptFromSession called again // #then should re-fetch it("should re-fetch after cache is cleared", async () => { const client = createMockClient([]) await buildTranscriptFromSession(client, "ses_clear", "/tmp", "bash", {}) clearTranscriptCache() await buildTranscriptFromSession(client, "ses_clear", "/tmp", "bash", {}) expect(client.session.messages).toHaveBeenCalledTimes(2) }) it("keeps intermediate tool calls across sequential transcript rebuilds", async () => { // given const client = createMockClient([]) // when const firstPath = await buildTranscriptFromSession( client, "ses_sequential", "/tmp", "bash", { command: "echo first" } ) const secondPath = await buildTranscriptFromSession( client, "ses_sequential", "/tmp", "read", { filePath: "/tmp/second.txt" } ) const thirdPath = await buildTranscriptFromSession( client, "ses_sequential", "/tmp", "write", { filePath: "/tmp/third.txt", content: "third" } ) // then expect(firstPath).not.toBeNull() expect(secondPath).not.toBeNull() expect(thirdPath).not.toBeNull() if (thirdPath) { const content = readFileSync(thirdPath, "utf-8") expect(content).toContain("Bash") expect(content).toContain("Read") expect(content).toContain("Write") } deleteTempTranscript(firstPath) deleteTempTranscript(secondPath) deleteTempTranscript(thirdPath) }) it("cleans up previous temp transcript files when rebuilding cached transcripts", async () => { // given const client = createMockClient([]) // when const firstPath = await buildTranscriptFromSession( client, "ses_cleanup", "/tmp", "bash", { command: "echo first" } ) const secondPath = await buildTranscriptFromSession( client, "ses_cleanup", "/tmp", "read", { filePath: "/tmp/second.txt" } ) // then expect(firstPath).not.toBeNull() expect(secondPath).not.toBeNull() if (firstPath && secondPath) { expect(existsSync(firstPath)).toBe(false) expect(existsSync(secondPath)).toBe(true) } deleteTempTranscript(firstPath) deleteTempTranscript(secondPath) }) })