test(FN-4850): add active worktree rename and copy diff coverage

Fusion-Task-Id: FN-4850
Fusion-Task-Lineage: 903d8e4e-eba7-413e-86d8-94eae7318952
This commit is contained in:
Fusion (runfusion.ai)
2026-05-16 23:48:36 -07:00
committed by gsxdsm
parent 19b9ea4464
commit c491dac28f
2 changed files with 112 additions and 3 deletions

View File

@@ -674,9 +674,9 @@ describe("FN-4308 multi-commit done task aggregation", () => {
store.addTask(createTask({ column: "in-progress", worktree: process.cwd() }));
gitResponses({
"diff --name-status origin/main..HEAD": "M\tcommitted.ts",
"diff --cached --name-status": "A\tstaged.ts",
"diff --name-status": "M\tunstaged.ts",
"diff --name-status -M origin/main..HEAD": "M\tcommitted.ts",
"diff --cached --name-status -M": "A\tstaged.ts",
"diff --name-status -M": "M\tunstaged.ts",
"diff origin/main -- committed.ts": "+c\n",
"diff origin/main -- staged.ts": "+s\n",
"diff origin/main -- unstaged.ts": "+u\n",
@@ -693,6 +693,86 @@ describe("FN-4308 multi-commit done task aggregation", () => {
expect(response.body.files.length).toBe(response.body.stats.filesChanged);
});
it("uses destination path for committed rename in active worktree /diff", async () => {
const store = new MockStore();
store.addTask(createTask({ column: "in-progress", worktree: process.cwd() }));
gitResponses({
"diff --name-status -M origin/main..HEAD": "R100\told.ts\tnew.ts",
"diff --cached --name-status -M": "",
"diff --name-status -M": "",
"diff origin/main -- new.ts": "rename from old.ts\nrename to new.ts\n",
});
const app = createServer(store as any);
const response = await requestDiff(app);
expect(response.status).toBe(200);
expect(response.body.files).toHaveLength(1);
expect(response.body.files[0].path).toBe("new.ts");
expect(response.body.stats.filesChanged).toBe(1);
});
it("uses destination path for committed copy in active worktree /diff", async () => {
const store = new MockStore();
store.addTask(createTask({ column: "in-review", worktree: process.cwd() }));
gitResponses({
"diff --name-status -M origin/main..HEAD": "C100\tsrc.ts\tdst.ts",
"diff --cached --name-status -M": "",
"diff --name-status -M": "",
"diff origin/main -- dst.ts": "+copied\n",
});
const app = createServer(store as any);
const response = await requestDiff(app);
expect(response.status).toBe(200);
expect(response.body.files).toHaveLength(1);
expect(response.body.files[0].path).toBe("dst.ts");
expect(response.body.stats.filesChanged).toBe(1);
});
it("uses destination path for staged rename in active worktree /diff", async () => {
const store = new MockStore();
store.addTask(createTask({ column: "in-progress", worktree: process.cwd() }));
gitResponses({
"diff --name-status -M origin/main..HEAD": "",
"diff --cached --name-status -M": "R100\told-staged.ts\tnew-staged.ts",
"diff --name-status -M": "",
"diff origin/main -- new-staged.ts": "rename from old-staged.ts\nrename to new-staged.ts\n",
});
const app = createServer(store as any);
const response = await requestDiff(app);
expect(response.status).toBe(200);
expect(response.body.files).toHaveLength(1);
expect(response.body.files[0].path).toBe("new-staged.ts");
expect(response.body.stats.filesChanged).toBe(1);
});
it("uses destination path for unstaged rename in active worktree /diff", async () => {
const store = new MockStore();
store.addTask(createTask({ column: "in-review", worktree: process.cwd() }));
gitResponses({
"diff --name-status -M origin/main..HEAD": "",
"diff --cached --name-status -M": "",
"diff --name-status -M": "R100\told-unstaged.ts\tnew-unstaged.ts",
"diff origin/main -- new-unstaged.ts": "rename from old-unstaged.ts\nrename to new-unstaged.ts\n",
});
const app = createServer(store as any);
const response = await requestDiff(app);
expect(response.status).toBe(200);
expect(response.body.files).toHaveLength(1);
expect(response.body.files[0].path).toBe("new-unstaged.ts");
expect(response.body.stats.filesChanged).toBe(1);
});
it("includes mergeDetails.commitSha even when missing from associations", async () => {
const store = new MockStore();
store.addTask(createTask({ column: "done", lineageId: "lin-1", mergeDetails: { commitSha: "merge-only" } }));

View File

@@ -237,6 +237,35 @@ describe("GET /api/tasks/:id/file-diffs", () => {
rmSync(repoDir, { recursive: true, force: true });
}
}, 15_000);
it("returns renamed destination and oldPath for active worktree tasks", async () => {
const repoDir = mkdtempSync(join(tmpdir(), "fn-file-diffs-active-rename-"));
try {
execFileSync("git", ["init", "-b", "main", repoDir], { stdio: "pipe" });
execFileSync("git", ["-C", repoDir, "config", "user.email", "active-rename@example.com"], { stdio: "pipe" });
execFileSync("git", ["-C", repoDir, "config", "user.name", "Active Rename Test"], { stdio: "pipe" });
writeFileSync(join(repoDir, "old.ts"), "export const oldValue = 1;\n");
execFileSync("git", ["-C", repoDir, "add", "old.ts"], { stdio: "pipe" });
execFileSync("git", ["-C", repoDir, "commit", "-m", "base"], { stdio: "pipe" });
execFileSync("git", ["-C", repoDir, "mv", "old.ts", "new.ts"], { stdio: "pipe" });
execFileSync("git", ["-C", repoDir, "commit", "-m", "rename in worktree"], { stdio: "pipe" });
const store = new RepoBackedStore(repoDir);
store.addTask(createTask({ id: "KB-651-active-rename", column: "in-progress", worktree: repoDir, baseBranch: "main" }));
const app = createServer(store as any);
const response = await requestFileDiffs(app, "KB-651-active-rename");
expect(response.status).toBe(200);
expect(response.body).toHaveLength(1);
expect(response.body[0]).toMatchObject({ path: "new.ts", status: "renamed", oldPath: "old.ts" });
} finally {
rmSync(repoDir, { recursive: true, force: true });
}
}, 15_000);
});
describe("resolveDiffBase", () => {