224 lines · 7.1 KB · TypeScript
RawDownload
import { describe, expect, it } from "vite-plus/test"; import { filterUnifiedDiffFiles, parseTurnDiffFilesFromUnifiedDiff } from "./Diffs.ts"; describe("parseTurnDiffFilesFromUnifiedDiff", () => {  it("returns empty list for empty diff", () => {    expect(parseTurnDiffFilesFromUnifiedDiff("")).toEqual([]);  });   it("parses per-file additions and deletions", () => {    const diff = [      "diff --git a/a.txt b/a.txt",      "index 1111111..2222222 100644",      "--- a/a.txt",      "+++ b/a.txt",      "@@ -1,2 +1,3 @@",      " one",      "-two",      "+two updated",      "+three",      "diff --git a/src/b.ts b/src/b.ts",      "index 3333333..4444444 100644",      "--- a/src/b.ts",      "+++ b/src/b.ts",      "@@ -3,2 +3,0 @@",      "-old",      "-stale",      "",    ].join("\n");     expect(parseTurnDiffFilesFromUnifiedDiff(diff)).toEqual([      { path: "a.txt", additions: 2, deletions: 1 },      { path: "src/b.ts", additions: 0, deletions: 2 },    ]);  });   it("parses rename-only diffs with zero line changes", () => {    const diff = [      "diff --git a/src/old.ts b/src/new.ts",      "similarity index 100%",      "rename from src/old.ts",      "rename to src/new.ts",      "",    ].join("\n");     expect(parseTurnDiffFilesFromUnifiedDiff(diff)).toEqual([      { path: "src/new.ts", additions: 0, deletions: 0 },    ]);  });   it("normalizes CRLF input before parsing", () => {    const diff = [      "diff --git a/a.txt b/a.txt",      "index 1111111..2222222 100644",      "--- a/a.txt",      "+++ b/a.txt",      "@@ -1 +1,2 @@",      "-one",      "+one updated",      "+two",      "",    ].join("\r\n");     expect(parseTurnDiffFilesFromUnifiedDiff(diff)).toEqual([      { path: "a.txt", additions: 2, deletions: 1 },    ]);  });}); describe("filterUnifiedDiffFiles", () => {  const twoFileDiff = [    "diff --git a/kept.ts b/kept.ts",    "index 1111111..2222222 100644",    "--- a/kept.ts",    "+++ b/kept.ts",    "@@ -1,1 +1,1 @@",    "-old",    "+new",    "diff --git a/dropped.ts b/dropped.ts",    "index 3333333..4444444 100644",    "--- a/dropped.ts",    "+++ b/dropped.ts",    "@@ -1,1 +1,1 @@",    "-before",    "+after",    "",  ].join("\n");   it("removes sections whose path is rejected", () => {    const filtered = filterUnifiedDiffFiles(twoFileDiff, (path) => path !== "dropped.ts");    expect(filtered).toContain("kept.ts");    expect(filtered).not.toContain("dropped.ts");    expect(parseTurnDiffFilesFromUnifiedDiff(filtered)).toEqual([      { path: "kept.ts", additions: 1, deletions: 1 },    ]);  });   it("returns the diff unchanged when everything is kept", () => {    expect(filterUnifiedDiffFiles(twoFileDiff, () => true)).toBe(twoFileDiff);  });   it("filters deleted files via the --- a/ path", () => {    const deletionDiff = [      "diff --git a/removed.ts b/removed.ts",      "deleted file mode 100644",      "index 1111111..0000000",      "--- a/removed.ts",      "+++ /dev/null",      "@@ -1,1 +0,0 @@",      "-gone",      "",    ].join("\n");    expect(filterUnifiedDiffFiles(deletionDiff, (path) => path !== "removed.ts").trim()).toBe("");  });   it("filters simple header-only sections via the diff --git header", () => {    const oddDiff = ["diff --git a/x b/x", "Binary files differ", ""].join("\n");    expect(filterUnifiedDiffFiles(oddDiff, (path) => path !== "x").trim()).toBe("");  });   it("keeps unparseable sections", () => {    const oddDiff = ["diff --git malformed-header", "Binary files differ", ""].join("\n");    expect(filterUnifiedDiffFiles(oddDiff, () => false)).toBe(oddDiff);  });   it("returns empty diff unchanged", () => {    expect(filterUnifiedDiffFiles("", () => false)).toBe("");  });}); describe("resolveDiffSectionPath via renames", () => {  it("filters rename sections by the post-image path", () => {    const renameDiff = [      "diff --git a/old/name.ts b/new/name.ts",      "similarity index 90%",      "rename from old/name.ts",      "rename to new/name.ts",      "index 1111111..2222222 100644",      "--- a/old/name.ts",      "+++ b/new/name.ts",      "@@ -1,1 +1,1 @@",      "-before",      "+after",      "",    ].join("\n");    // The attribution map is keyed by post-image paths; a filter that only    // knows the new path must still match this section.    expect(filterUnifiedDiffFiles(renameDiff, (path) => path !== "new/name.ts").trim()).toBe("");    expect(filterUnifiedDiffFiles(renameDiff, (path) => path !== "old/name.ts")).toBe(renameDiff);  });}); describe("filterUnifiedDiffFiles fallback paths", () => {  it("filters binary sections via the diff --git header", () => {    const binaryDiff = [      "diff --git a/assets/logo.png b/assets/logo.png",      "index 1111111..2222222 100644",      "Binary files a/assets/logo.png and b/assets/logo.png differ",      "",    ].join("\n");    expect(filterUnifiedDiffFiles(binaryDiff, (path) => path !== "assets/logo.png").trim()).toBe(      "",    );    expect(filterUnifiedDiffFiles(binaryDiff, () => true)).toBe(binaryDiff);  });   it("filters mode-only sections via the diff --git header", () => {    const modeDiff = [      "diff --git a/scripts/run.sh b/scripts/run.sh",      "old mode 100644",      "new mode 100755",      "",    ].join("\n");    expect(filterUnifiedDiffFiles(modeDiff, (path) => path !== "scripts/run.sh").trim()).toBe("");  });   it("filters binary sections with spaces in the path", () => {    const binaryDiff = [      "diff --git a/my assets/logo file.png b/my assets/logo file.png",      "Binary files differ",      "",    ].join("\n");    expect(      filterUnifiedDiffFiles(binaryDiff, (path) => path !== "my assets/logo file.png").trim(),    ).toBe("");  });   it("decodes git C-style quoted paths", () => {    const quotedDiff = [      'diff --git "a/sp\\303\\244ce.ts" "b/sp\\303\\244ce.ts"',      "index 1111111..2222222 100644",      '--- "a/sp\\303\\244ce.ts"',      '+++ "b/sp\\303\\244ce.ts"',      "@@ -1,1 +1,1 @@",      "-old",      "+new",      "",    ].join("\n");    // git octal-escapes the raw UTF-8 bytes (`ä` = \303\244); the decoder    // must reassemble them into the real character, not per-byte mojibake.    expect(filterUnifiedDiffFiles(quotedDiff, (path) => path !== "späce.ts").trim()).toBe("");  });   it("filters rename-only sections without hunks via rename metadata", () => {    const renameOnlyDiff = [      "diff --git a/old.ts b/new.ts",      "similarity index 100%",      "rename from old.ts",      "rename to new.ts",      "",    ].join("\n");    expect(filterUnifiedDiffFiles(renameOnlyDiff, (path) => path !== "new.ts").trim()).toBe("");    expect(filterUnifiedDiffFiles(renameOnlyDiff, (path) => path !== "old.ts")).toBe(      renameOnlyDiff,    );  });   it("keeps ambiguous unquoted rename headers", () => {    // Rename of a space-containing path is ambiguous in the header when git    // does not quote; with no other metadata the section must be kept.    const ambiguous = ["diff --git a/x y b/y z", "Binary files differ", ""].join("\n");    expect(filterUnifiedDiffFiles(ambiguous, () => false)).toBe(ambiguous);  });});