import { describe, expect, test } from "bun:test"; import { collapseHookSummaries } from "../collapseHookSummaries"; function makeHookSummary(overrides: Partial<{ hookLabel: string; hookCount: number; hookInfos: any[]; hookErrors: any[]; preventedContinuation: boolean; hasOutput: boolean; totalDurationMs: number; }> = {}): any { return { type: "system", subtype: "stop_hook_summary", hookLabel: overrides.hookLabel ?? "PostToolUse", hookCount: overrides.hookCount ?? 1, hookInfos: overrides.hookInfos ?? [], hookErrors: overrides.hookErrors ?? [], preventedContinuation: overrides.preventedContinuation ?? false, hasOutput: overrides.hasOutput ?? false, totalDurationMs: overrides.totalDurationMs ?? 10, }; } function makeNonHookMessage(): any { return { type: "user", message: { content: "hello" } }; } describe("collapseHookSummaries", () => { test("returns same messages when no hook summaries", () => { const messages = [makeNonHookMessage(), makeNonHookMessage()]; expect(collapseHookSummaries(messages)).toEqual(messages); }); test("collapses consecutive messages with same hookLabel", () => { const messages = [ makeHookSummary({ hookLabel: "PostToolUse", hookCount: 1 }), makeHookSummary({ hookLabel: "PostToolUse", hookCount: 2 }), ]; const result = collapseHookSummaries(messages); expect(result).toHaveLength(1); expect(result[0].hookCount).toBe(3); }); test("does not collapse messages with different hookLabels", () => { const messages = [ makeHookSummary({ hookLabel: "PostToolUse" }), makeHookSummary({ hookLabel: "PreToolUse" }), ]; const result = collapseHookSummaries(messages); expect(result).toHaveLength(2); }); test("aggregates hookCount across collapsed messages", () => { const messages = [ makeHookSummary({ hookLabel: "A", hookCount: 3 }), makeHookSummary({ hookLabel: "A", hookCount: 5 }), ]; const result = collapseHookSummaries(messages); expect(result[0].hookCount).toBe(8); }); test("merges hookInfos arrays", () => { const info1 = { tool: "Read" }; const info2 = { tool: "Write" }; const messages = [ makeHookSummary({ hookLabel: "A", hookInfos: [info1] }), makeHookSummary({ hookLabel: "A", hookInfos: [info2] }), ]; const result = collapseHookSummaries(messages); expect(result[0].hookInfos).toEqual([info1, info2]); }); test("merges hookErrors arrays", () => { const err1 = new Error("e1"); const err2 = new Error("e2"); const messages = [ makeHookSummary({ hookLabel: "A", hookErrors: [err1] }), makeHookSummary({ hookLabel: "A", hookErrors: [err2] }), ]; const result = collapseHookSummaries(messages); expect(result[0].hookErrors).toHaveLength(2); }); test("takes max totalDurationMs", () => { const messages = [ makeHookSummary({ hookLabel: "A", totalDurationMs: 50 }), makeHookSummary({ hookLabel: "A", totalDurationMs: 100 }), makeHookSummary({ hookLabel: "A", totalDurationMs: 75 }), ]; const result = collapseHookSummaries(messages); expect(result[0].totalDurationMs).toBe(100); }); test("takes any truthy preventContinuation", () => { const messages = [ makeHookSummary({ hookLabel: "A", preventedContinuation: false }), makeHookSummary({ hookLabel: "A", preventedContinuation: true }), ]; const result = collapseHookSummaries(messages); expect(result[0].preventedContinuation).toBe(true); }); test("leaves single hook summary unchanged", () => { const msg = makeHookSummary({ hookLabel: "PostToolUse", hookCount: 5 }); const result = collapseHookSummaries([msg]); expect(result).toHaveLength(1); expect(result[0].hookCount).toBe(5); }); test("handles three consecutive same-label summaries", () => { const messages = [ makeHookSummary({ hookLabel: "X", hookCount: 1 }), makeHookSummary({ hookLabel: "X", hookCount: 1 }), makeHookSummary({ hookLabel: "X", hookCount: 1 }), ]; const result = collapseHookSummaries(messages); expect(result).toHaveLength(1); expect(result[0].hookCount).toBe(3); }); test("preserves non-hook messages in between", () => { const messages = [ makeHookSummary({ hookLabel: "A" }), makeNonHookMessage(), makeHookSummary({ hookLabel: "A" }), ]; const result = collapseHookSummaries(messages); expect(result).toHaveLength(3); }); test("returns empty array for empty input", () => { expect(collapseHookSummaries([])).toEqual([]); }); });