Files
oh-my-pi/packages/coding-agent/test/tools/edit-diff.test.ts
T
can1357 59d3a025f0 fix(packages/coding-agent): patched diff row ordering after line shifts
- Tracked cumulative plus/minus line changes to translate new-file context rows to old indices.
- Merged old and translated new block-boundary context rows in one pass to prevent duplicate/out-of-order rows.
2026-06-09 20:45:01 +02:00

87 lines
3.5 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { generateDiffString } from "@oh-my-pi/pi-coding-agent/edit/diff";
describe("generateDiffString", () => {
it("collapses unchanged lines between distant edits", () => {
const oldLines = Array.from({ length: 20 }, (_, index) => `line ${index + 1}`);
const newLines = [...oldLines];
newLines[1] = "line 2 changed";
newLines[17] = "line 18 changed";
const result = generateDiffString(oldLines.join("\n"), newLines.join("\n"), 2);
const diffLines = result.diff.split("\n");
// The mid-skip emits no placeholder row; the jump from the leading
// context (line 4) to the trailing context (line 16) conveys the gap.
expect(diffLines.some(line => line.endsWith("|...") || line.endsWith("|…"))).toBe(false);
expect(diffLines[diffLines.indexOf(" 4|line 4") + 1]).toBe(" 16|line 16");
expect(diffLines).toContain("-2|line 2");
expect(diffLines).toContain("+2|line 2 changed");
expect(diffLines).toContain("-18|line 18");
expect(diffLines).toContain("+18|line 18 changed");
expect(diffLines).not.toContain(" 8|line 8");
expect(diffLines).not.toContain(" 12|line 12");
});
it("adds an elided matching bracket line when context stops before the closer", () => {
const oldLines = [
"function outer() {",
" const value = 1;",
" const two = 2;",
" const three = 3;",
" const four = 4;",
" return value + two + three + four;",
"}",
];
const newLines = [...oldLines];
newLines[0] = "function renamed() {";
const result = generateDiffString(oldLines.join("\n"), newLines.join("\n"), 1, { path: "sample.ts" });
const diffLines = result.diff.split("\n");
expect(diffLines).toContain("-1|function outer() {");
expect(diffLines).toContain("+1|function renamed() {");
expect(diffLines).toContain("...");
expect(diffLines).toContain(" 7|}");
expect(diffLines).not.toContain(" 5| const four = 4;");
expect(diffLines).not.toContain(" 6| return value + two + three + four;");
});
it("emits bracket context under pre-edit numbers when edits shift line offsets", () => {
// Two change runs around an unchanged line, net +2 lines before the
// closing brace. The closer is discovered via the NEW file's block
// boundaries, so it must be translated back to its pre-edit number
// (compact-preview renumbering contract). Regression: it used to be
// either dropped (broken new-file visibility window) or re-inserted
// under its post-edit number — duplicated and out of order.
const oldLines = [
"function outer() {",
" const a = 1;",
" const keep = 2;",
" const b = 3;",
" return a + keep + b;",
"}",
];
const newLines = [
"function outer() {",
" const a = 10;",
" const a2 = 11;",
" const keep = 2;",
" const b = 30;",
" const b2 = 31;",
" return a + keep + b;",
"}",
];
const result = generateDiffString(oldLines.join("\n"), newLines.join("\n"), 1, { path: "sample.ts" });
const diffLines = result.diff.split("\n");
expect(diffLines.filter(line => line.endsWith("|}"))).toEqual([" 6|}"]);
// Context rows must stay in pre-edit order — no duplicate of the
// shifted unchanged line under another number.
const contextNumbers = diffLines
.filter(line => line.startsWith(" "))
.map(line => Number.parseInt(line.slice(1), 10));
expect(contextNumbers).toEqual([...contextNumbers].sort((a, b) => a - b));
expect(diffLines.filter(line => line.includes("| const keep = 2;"))).toEqual([" 3| const keep = 2;"]);
});
});