feat(FN-5552): narrow push outcome union in useMergeAdvanceNotice

Narrowed the push outcome union type in `useMergeAdvanceNotice` and added regression test coverage for the outcome narrowing behavior, including alignment of root script contract expectations in the package config tests.

Fusion-Task-Id: FN-5552

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5552
This commit is contained in:
gsxdsm
2026-05-23 08:51:40 -07:00
parent 385b6e93bb
commit 63ec709811
3 changed files with 22 additions and 3 deletions

View File

@@ -236,7 +236,7 @@ describe("Workspace bootstrap script contract", () => {
it("makes root test changed-only while keeping explicit full-suite and CI-shard commands", () => {
expect(rootPkg.scripts?.test).toBe("node scripts/test-changed.mjs");
expect(rootPkg.scripts?.["test:full"]).toBe("node scripts/test-changed.mjs --full --no-cache");
expect(rootPkg.scripts?.["test:full"]).toBe("node scripts/test-changed.mjs --full --no-cache && pnpm --filter @fusion/engine test:slow");
expect(rootPkg.scripts?.["test:full"]).not.toContain("pnpm build");
expect(rootPkg.scripts?.["test:ci:shard"]).toBe("node scripts/ci-test-shard.mjs");
});

View File

@@ -86,7 +86,11 @@ describe("useMergeAdvanceNotice", () => {
const first = renderHook(() => useMergeAdvanceNotice({ projectId: "p1" }));
await waitFor(() => expect(first.result.current.pushStatus).not.toBeNull());
await act(async () => { await first.result.current.push(); });
expect(first.result.current.pushState).toMatchObject({ outcome: "rejected-non-ff" });
expect(first.result.current.pushState).toMatchObject({
outcome: "rejected-non-ff",
error: "Remote diverged",
stderr: "[rejected]",
});
expect(first.result.current.pullState).toBe("idle");
const second = renderHook(() => useMergeAdvanceNotice({ projectId: "p1" }));
@@ -96,6 +100,21 @@ describe("useMergeAdvanceNotice", () => {
expect(second.result.current.pullState).toBe("idle");
});
it("maps degenerate failed ok outcome to failed", async () => {
mocked.api.mockImplementationOnce(async () => eventPayload)
.mockImplementationOnce(async () => pushStatus)
.mockImplementationOnce(async () => ({ ok: false, outcome: "ok", message: "unexpected" }));
const { result } = renderHook(() => useMergeAdvanceNotice({ projectId: "p1" }));
await waitFor(() => expect(result.current.pushStatus).not.toBeNull());
await act(async () => { await result.current.push(); });
expect(result.current.pushState).toMatchObject({
outcome: "failed",
error: "unexpected",
});
});
it("does not poll on a timer", async () => {
vi.useFakeTimers();
renderHook(() => useMergeAdvanceNotice({ projectId: "p1" }));

View File

@@ -231,7 +231,7 @@ export function useMergeAdvanceNotice({ projectId, apiBase = "/api" }: { project
}
setPushState({
error: response.message ?? response.outcome,
outcome: response.outcome,
outcome: response.outcome === "ok" ? "failed" : response.outcome,
stderr: response.stderrPreview,
});
} catch (error: unknown) {