test(FN-5103): address review gaps in landed-files coverage

Fusion-Task-Id: FN-5103
Fusion-Task-Lineage: e4e4d9ba-4884-4feb-b18b-a30e799c3fb1
This commit is contained in:
Fusion (runfusion.ai)
2026-05-18 23:51:02 -07:00
committed by gsxdsm
parent 03212ce00b
commit 3e7073ab84
2 changed files with 58 additions and 4 deletions

View File

@@ -103,6 +103,42 @@ describe("FN-4646 aiMergeTask landedFiles capture", () => {
expect(detailsUpdate?.[1].modifiedFiles).toBeUndefined();
});
it("sums shortstat across multiple own commits on rebase", async () => {
const store = makeStore({ directMergeCommitStrategy: "always-rebase" });
vi.spyOn(attributionModule, "filterFilesToOwnTaskCommits").mockResolvedValue({
files: ["a.ts", "b.ts", "c.ts"],
foreignCommits: [],
ownCommitCount: 3,
ownCommitShas: ["ownsha1", "ownsha2", "ownsha3"],
rawDiffFileCount: 3,
});
mockedExecSync.mockImplementation((cmd: any) => {
const s = String(cmd);
if (s.includes("rev-parse --verify")) return Buffer.from("abc123");
if (s === "git rev-parse HEAD" || s.startsWith("git rev-parse HEAD ")) return "rebasesha123";
if (s.includes("git log")) return "- feat: summary";
if (s.includes("merge-base")) return Buffer.from("abc123");
if (s.includes("rev-parse \"abc123\"")) return "rebasebase123";
if (s.includes("rev-list --reverse \"rebasebase123..fusion/FN-4646\"")) return "";
if (s.includes("status --porcelain")) return "";
if (s.includes("rev-parse --git-path CHERRY_PICK_HEAD")) return ".git/CHERRY_PICK_HEAD";
if (s.includes("rev-parse --git-path sequencer")) return ".git/sequencer";
if (s.includes("show --shortstat --format= \"ownsha1\"")) return "1 file changed, 3 insertions(+), 1 deletion(-)";
if (s.includes("show --shortstat --format= \"ownsha2\"")) return "2 files changed, 5 insertions(+), 2 deletions(-)";
if (s.includes("show --shortstat --format= \"ownsha3\"")) return "1 file changed, 2 insertions(+)";
if (s.includes("branch -d") || s.includes("branch -D") || s.includes("worktree remove")) return Buffer.from("");
return Buffer.from("");
});
await mergerModule.aiMergeTask(store, "/tmp/root", "FN-4646");
const detailsUpdate = (store.updateTask as any).mock.calls.find((call: any[]) => call[1]?.mergeDetails?.commitSha === "rebasesha123");
expect(detailsUpdate?.[1].mergeDetails.landedFiles).toEqual(["a.ts", "b.ts", "c.ts"]);
expect(detailsUpdate?.[1].mergeDetails.filesChanged).toBe(3);
expect(detailsUpdate?.[1].mergeDetails.insertions).toBe(10);
expect(detailsUpdate?.[1].mergeDetails.deletions).toBe(3);
expect(detailsUpdate?.[1].mergeDetails.landedFilesAttributionRestricted).toBe(true);
});
it("falls back to legacy rebase capture when attribution fails", async () => {
const store = makeStore({ directMergeCommitStrategy: "always-rebase" });
vi.spyOn(attributionModule, "filterFilesToOwnTaskCommits").mockRejectedValue(new BranchAttributionError("boom"));

View File

@@ -3,7 +3,7 @@ import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { execSync, spawnSync } from "node:child_process";
import { BranchAttributionError, filterFilesToOwnTaskCommits } from "../../branch-attribution.js";
import { filterFilesToOwnTaskCommits } from "../../branch-attribution.js";
const hasGit = spawnSync("git", ["--version"], { stdio: "pipe" }).status === 0;
const describeIfGit = hasGit ? describe : describe.skip;
@@ -82,8 +82,26 @@ describeIfGit("FN-5103 reliability interaction: landed-files attribution", () =>
expect(attribution.foreignCommits.length).toBe(1);
});
it("supports attribution failure fallback signal path", async () => {
const error = new BranchAttributionError("synthetic attribution failure");
expect(error.message).toContain("synthetic attribution failure");
it("surfaces attribution failure when git reads fail", async () => {
const { repoDir, baseSha } = await initRepo("fn-5103-ri-");
dirs.push(repoDir);
const taskId = "FN-5103";
git(repoDir, `git checkout -b fusion/${taskId.toLowerCase()}`);
await commitFile(repoDir, "task-owned.ts", "owned\n", "feat(FN-5103): own", taskId);
await expect(
filterFilesToOwnTaskCommits({
worktreePath: repoDir,
baseRef: baseSha,
taskId,
execAsyncImpl: async () => {
throw new Error("forced attribution failure");
},
}),
).rejects.toMatchObject({
name: "BranchAttributionError",
message: expect.stringContaining("forced attribution failure"),
});
});
});