Files
claude-code/src/utils/__tests__/collapseHookSummaries.test.ts
2026-04-02 20:28:08 +08:00

137 lines
4.5 KiB
TypeScript

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([]);
});
});