import test from "node:test"; import assert from "node:assert/strict"; import { buildView, collectViewFromText } from "../src/view.ts"; import { readOverflowSettings, type OverflowSettings } from "../settings.ts"; function settings(overrides: Partial = {}): OverflowSettings { return { ...readOverflowSettings("/nonexistent/pi-overflow.json"), ...overrides, }; } const meta = { id: "abc123def456", tool: "bash" }; function view(text: string, s = settings()) { return buildView(collectViewFromText(text, s), s, meta); } test("below threshold remains unchanged", () => { const out = view("line one\nline two\nline three"); assert.ok(out.includes("line one")); assert.ok(out.includes("line three")); }); test("above threshold includes header, line ranges, signal lines and tail", () => { const lines: string[] = []; for (let i = 1; i <= 1000; i++) lines.push(`row ${i} ${i === 500 ? "ERROR boom" : "ok"}`); const out = view(lines.join("\n")); assert.match(out, /\[pi-overflow id=abc123def456 tool=bash 1000 lines/); assert.match(out, /--- lines 1-40 ---/); assert.match(out, /L500: row 500 ERROR boom/); assert.match(out, /--- lines 881-1000 ---/); assert.ok(out.includes("row 1000")); }); test("output never exceeds thresholdChars", () => { const lines = Array.from({ length: 5000 }, (_, i) => `line ${i} ${"x".repeat(60)}`); const s = settings({ thresholdChars: 2000 }); const out = view(lines.join("\n"), s); assert.ok(out.length <= 2000, `view ${out.length} exceeds budget`); assert.ok(out.includes("line 4999"), "tail must survive"); }); test("few long lines still expose the ending", () => { const lines = ["start", ...Array.from({ length: 5 }, (_, i) => `middle${i} ${"y".repeat(4000)}`), "THE_END_MARKER"]; const s = settings({ thresholdChars: 3000, headLines: 40, tailLines: 120 }); const out = view(lines.join("\n"), s); assert.ok(out.includes("THE_END_MARKER")); assert.ok(out.length <= 3000 + 100, `view ${out.length} far exceeds budget`); }); test("same input produces identical output", () => { const lines = Array.from({ length: 800 }, (_, i) => `entry ${i} ${i % 77 === 0 ? "warning" : "fine"}`); const text = lines.join("\n"); assert.equal(view(text), view(text)); }); test("a single line over maxLineChars is truncated", () => { const long = "z".repeat(2000); const out = view(`a\n${long}\nb`, settings({ maxLineChars: 50 })); assert.ok(out.includes("z".repeat(50) + "…")); assert.ok(!out.includes("z".repeat(51))); }); test("signals outrank head and survive when head+tail exceed the budget", () => { // ~150-char lines: head 40 + tail 120 = 160 lines * 150 = ~24K plus // overhead, over budget. Shrink order must keep some signals while head // collapses to 10 lines before the 35/65 phase kicks in. const lines = Array.from({ length: 400 }, (_, i) => { const marker = i === 200 ? "ERROR detected here" : "ok"; return `line ${i + 1} ${marker} ${"x".repeat(140)}`; }); const out = view(lines.join("\n")); assert.match(out, /--- signals/); assert.match(out, /L201: line 201 ERROR detected here/); // head collapsed below the configured 40 lines assert.match(out, /--- lines 1-(?:[0-9]|10) ---/); assert.ok(out.includes("line 400")); }); test("signal hits inside the tail region are not duplicated", () => { const lines = Array.from({ length: 100 }, (_, i) => `line ${i + 1} ${"y".repeat(40)}`); lines[99] = "line 100 tail-side ERROR"; const out = view(lines.join("\n")); assert.ok(!out.includes("L100:"), "tail-region hit must not appear in signals"); });