chore(test-isolation): detect live engine lock + prune stale tests

Three coupled fixes to make `pnpm test:full` exit cleanly when the local
`fn` dashboard is running:

1. scripts/check-test-isolation.mjs — replace timing-based "is the
   engine writing?" heuristic with a deterministic check: if
   `.fusion/engine.lock.lock/` exists (proper-lockfile's held-lock
   marker), the dir is engine-active and auto-skipped from violation
   reporting. The 2-second mutability probe is retained as a backstop
   for dirs with another external writer but no live lock. Also adds
   `engine.lock` / `engine.lock.lock/` to RUNTIME_IGNORE_PATTERNS so
   a mid-test engine start/stop doesn't trip the signature compare.

2. packages/dashboard/.../__tests__/GitManagerModal.test.tsx — prune
   the Status-panel Sync button + Recent-advances-events describe
   blocks. Their UI was removed in 5d35b64bd ("remove duplicate
   integration-advances UI") but the tests stayed and were timing
   out at 1s each. The Remotes-panel Sync describe is kept because
   the `remotes-sync-integration-tip-btn` still exists.

3. packages/engine/.../merge-reuse-task-worktree.slow.test.ts —
   update the happy-path assertion to reflect 4c31e885b
   ("merger auto-syncs project-root checkout after ref advance").
   Before that change, the merger's `update-ref` advance left the
   project root's working tree stale, so `git status --porcelain`
   would differ after the merge. With auto-sync, the new file is
   tracked + clean at HEAD, so status doesn't change. Verify the
   file actually landed via `git ls-files` instead.

After this, `pnpm test:full` exits 0 with the local dashboard running.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-23 20:18:08 -07:00
parent 0c0839eeb6
commit b8919b7bb4
4 changed files with 76 additions and 361 deletions

View File

@@ -2991,180 +2991,6 @@ describe("GitManagerModal", () => {
});
});
// ── Sync Integration Tip button ────────────────────────────────
describe("Sync local tip button (Status panel)", () => {
it("renders the Sync local tip button when integrationBranch is set", async () => {
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: true,
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("sync-integration-tip-btn")).toBeInTheDocument();
});
});
it("the Sync local tip button is disabled when not on integration branch", async () => {
(fetchGitStatus as any).mockResolvedValue({
branch: "feature/foo",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: false,
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
const btn = screen.getByTestId("sync-integration-tip-btn");
expect(btn).toBeDisabled();
});
});
it("the Sync local tip button is disabled when no integrationBranch", async () => {
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
// no integrationBranch
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByText("Repository Status")).toBeInTheDocument();
});
// Button should not render when there's no integrationBranch
expect(screen.queryByTestId("sync-integration-tip-btn")).not.toBeInTheDocument();
});
it("calls POST /api/git/pull with correct body when Sync local tip is clicked", async () => {
const user = userEvent.setup();
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: true,
});
(fetchConfig as any).mockResolvedValue({ maxConcurrent: 4, rootDir: "/my/project" });
(api as any).mockResolvedValue({ kind: "pull-clean", toSha: "deadbeef" });
render(
<GitManagerModal
isOpen={true}
onClose={vi.fn()}
tasks={mockTasks}
addToast={mockAddToast}
projectId="proj-123"
/>
);
await waitFor(() => {
expect(screen.getByTestId("sync-integration-tip-btn")).toBeInTheDocument();
});
await user.click(screen.getByTestId("sync-integration-tip-btn"));
await waitFor(() => {
expect(api).toHaveBeenCalledWith(
expect.stringContaining("/git/pull"),
expect.objectContaining({
method: "POST",
body: JSON.stringify({
worktreePath: "/my/project",
integrationBranch: "main",
taskId: undefined,
}),
})
);
});
});
it("shows a success toast and refreshes status after sync succeeds", async () => {
const user = userEvent.setup();
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: true,
});
(fetchConfig as any).mockResolvedValue({ maxConcurrent: 4, rootDir: "/my/project" });
(api as any).mockResolvedValue({ kind: "pull-clean", toSha: "deadbeef" });
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("sync-integration-tip-btn")).toBeInTheDocument();
});
await user.click(screen.getByTestId("sync-integration-tip-btn"));
await waitFor(() => {
expect(mockAddToast).toHaveBeenCalledWith("Synced worktree to integration tip", "success");
expect(fetchGitStatus).toHaveBeenCalledTimes(2); // initial load + post-sync refresh
});
});
it("shows error toast when sync fails", async () => {
const user = userEvent.setup();
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: true,
});
(fetchConfig as any).mockResolvedValue({ maxConcurrent: 4, rootDir: "/my/project" });
(api as any).mockRejectedValue(new Error("merge conflict"));
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("sync-integration-tip-btn")).toBeInTheDocument();
});
await user.click(screen.getByTestId("sync-integration-tip-btn"));
await waitFor(() => {
expect(mockAddToast).toHaveBeenCalledWith("merge conflict", "error");
});
});
});
describe("Sync local tip button (Remotes panel)", () => {
it("renders the Sync local tip button in the remotes panel when integrationBranch is set", async () => {
(fetchGitStatus as any).mockResolvedValue({
@@ -3212,170 +3038,6 @@ describe("GitManagerModal", () => {
});
});
// ── Recent integration advance events panel ─────────────────────
describe("Recent integration advance events panel", () => {
it("renders the events panel when there are succeeded events", async () => {
(api as any).mockResolvedValue({
events: [
{
taskId: "FN-101",
integrationBranch: "main",
toSha: "aabbccdd1234",
fromSha: "00112233",
advanceMode: "fast-forward",
succeeded: true,
advancedAt: new Date(Date.now() - 2 * 60 * 1000).toISOString(), // 2 minutes ago
},
{
taskId: "FN-102",
integrationBranch: "main",
toSha: "eeff00112233",
fromSha: "aabbccdd1234",
advanceMode: "update-ref",
succeeded: true,
advancedAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(), // 10 minutes ago
},
],
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("recent-advance-events")).toBeInTheDocument();
});
expect(screen.getByText("FN-101")).toBeInTheDocument();
expect(screen.getByText("FN-102")).toBeInTheDocument();
// Short SHAs (first 8 chars)
expect(screen.getByText("aabbccdd")).toBeInTheDocument();
expect(screen.getByText("eeff0011")).toBeInTheDocument();
});
it("shows events in the order returned by the API (newest first)", async () => {
(api as any).mockResolvedValue({
events: [
{
taskId: "FN-200",
integrationBranch: "main",
toSha: "111111112222",
fromSha: null,
advanceMode: "fast-forward",
succeeded: true,
advancedAt: new Date(Date.now() - 1 * 60 * 1000).toISOString(),
},
{
taskId: "FN-199",
integrationBranch: "main",
toSha: "222222223333",
fromSha: null,
advanceMode: "fast-forward",
succeeded: true,
advancedAt: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
},
],
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("recent-advance-events")).toBeInTheDocument();
});
const items = screen.getAllByRole("listitem");
// FN-200 should appear before FN-199
const firstText = items[0].textContent ?? "";
const secondText = items[1].textContent ?? "";
expect(firstText).toContain("FN-200");
expect(secondText).toContain("FN-199");
});
it("hides the events panel when there are no events", async () => {
(api as any).mockResolvedValue({ events: [] });
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByText("Repository Status")).toBeInTheDocument();
});
expect(screen.queryByTestId("recent-advance-events")).not.toBeInTheDocument();
});
it("hides the events panel when only failed events are returned", async () => {
(api as any).mockResolvedValue({
events: [
{
taskId: "FN-300",
integrationBranch: "main",
toSha: "deadbeef1234",
fromSha: null,
advanceMode: "fast-forward",
succeeded: false, // failed — should be filtered out
advancedAt: new Date().toISOString(),
},
],
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByText("Repository Status")).toBeInTheDocument();
});
expect(screen.queryByTestId("recent-advance-events")).not.toBeInTheDocument();
});
it("refreshes events after a successful sync", async () => {
const user = userEvent.setup();
(fetchGitStatus as any).mockResolvedValue({
branch: "main",
commit: "abc1234",
isDirty: false,
ahead: 0,
behind: 0,
integrationBranch: "main",
isOnIntegrationBranch: true,
});
(fetchConfig as any).mockResolvedValue({ maxConcurrent: 4, rootDir: "/project" });
let apiCallCount = 0;
(api as any).mockImplementation((path: string) => {
if (path.includes("/tasks/merge-advance-events")) {
apiCallCount++;
return Promise.resolve({ events: [] });
}
return Promise.resolve({ kind: "pull-clean", toSha: "abc" });
});
render(
<GitManagerModal isOpen={true} onClose={vi.fn()} tasks={mockTasks} addToast={mockAddToast} />
);
await waitFor(() => {
expect(screen.getByTestId("sync-integration-tip-btn")).toBeInTheDocument();
});
const callsBefore = apiCallCount;
await user.click(screen.getByTestId("sync-integration-tip-btn"));
await waitFor(() => {
expect(mockAddToast).toHaveBeenCalledWith("Synced worktree to integration tip", "success");
});
// fetchMergeAdvanceEvents should have been called again after sync
expect(apiCallCount).toBeGreaterThan(callsBefore);
});
});
describe("CSS regression coverage", () => {
it("includes remotes layout selectors and mobile rules", () => {