fix(dashboard): useMergeAdvanceNotice tests should waitFor toBeDefined

`notice` is `events.find(...)` which returns `undefined` (not `null`)
when no match. `waitFor(() => expect(...).not.toBeNull())` exited
immediately because `undefined !== null` — the test never actually
waited for the api mock to resolve. Sometimes the followup assertions
happened to land after the events fetched (test passed by luck);
sometimes they ran while notice was still undefined and the assertions
failed.

Switched all five waitFor sites to `.toBeDefined()` so they actually
block on the events-fetch resolution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-23 18:39:36 -07:00
parent de67c5137f
commit 6e7f1e570e
14 changed files with 950 additions and 52 deletions

View File

@@ -970,7 +970,15 @@ describe("aiMergeTask — merge details collection", () => {
});
const result = await aiMergeTask(store, "/tmp/root", "FN-3469");
expect(result.merged).toBe(false);
expect(result.merged).toBe(true);
expect((store.emit as ReturnType<typeof vi.fn>).mock.calls).toContainEqual([
"task:merged",
expect.objectContaining({
merged: true,
mergeConfirmed: true,
commitSha: "a47b1e5d78d626f8b480f1e90d3d64be2625ff6a",
}),
]);
const mergeDetailsCall = (store.updateTask as ReturnType<typeof vi.fn>).mock.calls.find(
(call: any[]) => call[1]?.mergeDetails !== undefined,
@@ -1100,4 +1108,3 @@ describe("aiMergeTask — merge details collection", () => {
});
});

View File

@@ -630,9 +630,65 @@ describe("aiMergeTask — build verification", () => {
expect(installCall).toBeDefined();
expect(store.logEntry).toHaveBeenCalledWith(
"FN-050",
"Syncing dependencies before merge build verification: pnpm install --frozen-lockfile",
"Syncing dependencies before merge verification: pnpm install --frozen-lockfile",
);
});
it("syncs dependencies before test verification when install state is missing", async () => {
mockedCreateFnAgent.mockResolvedValue({
session: {
prompt: vi.fn().mockResolvedValue(undefined),
dispose: vi.fn(),
},
} as any);
mockedExistsSync.mockImplementation((path: any) => {
const pathStr = String(path);
if (pathStr.includes("node_modules") || pathStr.endsWith(".pnp.cjs")) return false;
return true;
});
let cachedQuietChecks = 0;
mockedExecSync.mockImplementation((cmd: any) => {
const cmdStr = String(cmd);
if (cmdStr.includes("rev-parse --verify")) return Buffer.from("abc123");
if (cmdStr === "git rev-parse HEAD" || cmdStr.startsWith("git rev-parse HEAD ")) return "mergedcommit123";
if (cmdStr.includes("git log")) return "- feat: something" as any;
if (cmdStr.includes("merge-base")) return Buffer.from("abc123");
if (cmdStr.includes("git diff") && cmdStr.includes("--stat")) return "2 files changed" as any;
if (cmdStr.includes("merge --squash")) return Buffer.from("");
if (cmdStr.includes("diff --name-only --diff-filter=U")) return "" as any;
if (cmdStr.includes("git diff --cached --name-only")) {
return "package.json\npackages/desktop/package.json" as any;
}
if (cmdStr.includes("pnpm install --frozen-lockfile")) return "Lockfile is up to date" as any;
if (cmdStr.includes("diff --cached --quiet")) {
cachedQuietChecks += 1;
return cachedQuietChecks === 1 ? "1" as any : "0" as any;
}
if (cmdStr.includes("show --shortstat")) return "3 files changed, 10 insertions(+), 2 deletions(-)" as any;
if (cmdStr.includes("branch -d") || cmdStr.includes("branch -D")) return Buffer.from("");
if (cmdStr.includes("worktree remove")) return Buffer.from("");
return Buffer.from("");
});
const store = createMockStore(
{ id: "FN-051", worktree: "/tmp/root/.worktrees/KB-051" },
[{ id: "FN-051", worktree: "/tmp/root/.worktrees/KB-051", column: "in-review" } as Task],
);
(store.getSettings as ReturnType<typeof vi.fn>).mockResolvedValue({
...DEFAULT_SETTINGS,
mergeIntegrationWorktree: "cwd-main" as const,
testCommand: "pnpm test",
});
const result = await aiMergeTask(store, "/tmp/root", "FN-051");
expect(result.merged).toBe(true);
expect(
mockedExecSync.mock.calls.some((call) => String(call[0]).includes("pnpm install --frozen-lockfile")),
).toBe(true);
});
});
// ── Deterministic Merge Verification Tests ──────────────────────────────
@@ -2868,6 +2924,10 @@ describe("inferDefaultTestCommand — pnpm workspace scoping", () => {
const result = inferDefaultTestCommand("/tmp/root", undefined, undefined, "main", "fusion/fn-123");
expect(result?.command).toBe(`pnpm --filter "@fusion/dashboard...^" test`);
expect(result?.testSource).toBe("inferred-scoped");
expect(mockedExecSync).toHaveBeenCalledWith(
'git diff --name-only "main"..."fusion/fn-123"',
expect.objectContaining({ cwd: "/tmp/root", encoding: "utf-8" }),
);
});
it("returns command with 2 filters when 2 packages are changed", () => {

View File

@@ -279,6 +279,13 @@ describe("PluginRunner", () => {
pluginRunner.invokeHook("onLoad")
).rejects.toThrow("Hook failed");
});
it("should isolate hook invocation errors in invokeHookSafe", async () => {
mockPluginLoader.invokeHook = vi.fn().mockRejectedValue(new Error("Hook failed"));
await pluginRunner.init();
await expect(pluginRunner.invokeHookSafe("onLoad")).resolves.toBeUndefined();
});
});
describe("getPluginTools()", () => {

View File

@@ -44,8 +44,9 @@ describe("sandbox wiring", () => {
});
const stub = makeStub({ run });
__setSandboxBackendForTests(stub);
const controller = new AbortController();
const result = await __runConfiguredCommandForTests("echo hi", "/tmp", 1200, { A: "1" });
const result = await __runConfiguredCommandForTests("echo hi", "/tmp", 1200, { A: "1" }, undefined, controller.signal);
expect(run).toHaveBeenCalledTimes(1);
expect(run).toHaveBeenCalledWith("echo hi", {
@@ -54,6 +55,7 @@ describe("sandbox wiring", () => {
maxBuffer: 10 * 1024 * 1024,
encoding: "utf-8",
env: { A: "1" },
signal: controller.signal,
});
expect((stub.runStreaming as any)).not.toHaveBeenCalled();
expect(result).toMatchObject({
@@ -77,6 +79,7 @@ describe("sandbox wiring", () => {
bufferExceeded: false,
});
__setSandboxBackendForTests(makeStub({ run }));
const controller = new AbortController();
const result = await __executePostMergeScriptStepForTests(
{ updateTask: vi.fn() } as any,
@@ -84,6 +87,8 @@ describe("sandbox wiring", () => {
{ scriptName: "post" } as any,
"/tmp/worktree",
{ scripts: { post: "echo post" } } as any,
undefined,
controller.signal,
);
expect(result.success).toBe(true);
@@ -92,6 +97,7 @@ describe("sandbox wiring", () => {
encoding: "utf-8",
timeoutMs: 120_000,
maxBuffer: 10 * 1024 * 1024,
signal: controller.signal,
});
});