From 7c4b29f642afcb1aa477b289cf67d1ef477e701a Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sun, 9 Aug 2026 02:28:33 -0700 Subject: [PATCH] FN-8895: harden group merge coordinator store fakes Ensure group-merge coordinator tests exercise the production store seams without warning suppression. - Provide scoped settings and branch-group task fake methods used by promotion evaluation - Await promotion evaluation and fail the fixture on missing-store-method warnings - Assert branch-group task lookup is reached during the merge drain Files changed: packages/engine/src/__tests__/group-merge-coordinator.test.ts | 59 ++++++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-8895 Fusion-Task-Lineage: 99748c07-2083-41f9-b776-da644bf1857a Co-authored-by: Fusion (runfusion.ai) --- .../__tests__/group-merge-coordinator.test.ts | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/packages/engine/src/__tests__/group-merge-coordinator.test.ts b/packages/engine/src/__tests__/group-merge-coordinator.test.ts index b0fca6872f..c11ca4f111 100644 --- a/packages/engine/src/__tests__/group-merge-coordinator.test.ts +++ b/packages/engine/src/__tests__/group-merge-coordinator.test.ts @@ -1300,17 +1300,27 @@ function createPostReviewTask(groupId: string): Record { } function createPostReviewStore(task: Record, branchGroup: Record | null) { + const settings = { + autoMerge: false, + merger: { maxReviewPasses: 0 }, + includeTaskIdInCommit: false, + mergeIntegrationWorktree: "cwd-main", + mergeStrategy: "direct", + directMergeCommitStrategy: "auto", + }; + return { getTask: vi.fn(async () => task), listTasks: vi.fn(async () => [task]), - getSettings: vi.fn(async () => ({ - autoMerge: false, - merger: { maxReviewPasses: 0 }, - includeTaskIdInCommit: false, - mergeIntegrationWorktree: "cwd-main", - mergeStrategy: "direct", - directMergeCommitStrategy: "auto", - })), + getSettings: vi.fn(async () => settings), + /* + FNXC:BranchGroupAutoMergeGate 2026-08-09-08:55: + The production drain calls these methods for token-budget enforcement and branch-group promotion + evaluation inside catch-and-warn wrappers. Supply the real, budget-free seams so a missing method + cannot silently remove coverage while leaving this prototype fixture green. + */ + getSettingsByScope: vi.fn(async () => ({ global: {}, project: settings })), + listTasksByBranchGroup: vi.fn(async () => (branchGroup ? [task] : [])), getBranchGroup: vi.fn(() => branchGroup), updateTask: vi.fn(async (_id: string, patch: Record) => Object.assign(task, patch)), moveTask: vi.fn(async (_id: string, column: string) => { task.column = column; return task; }), @@ -1498,10 +1508,39 @@ describe("resolveBranchGroupMergeRouting", () => { */ seedMergeLaneState(engine); - const released = await ProjectEngine.prototype.onMerge.call(engine, "FN-3324"); - createResolvedAgentSessionMock.mockReset(); + let resolvePromotionEvaluation!: () => void; + const promotionEvaluationReached = new Promise((resolve) => { + resolvePromotionEvaluation = resolve; + }); + store.recordRunAuditEvent.mockImplementation(async (event: { target?: string; mutationType?: string }) => { + if (event.target === "BG-user-hold" && event.mutationType?.startsWith("merge:branch-group-promotion")) { + resolvePromotionEvaluation(); + } + }); + /* + FNXC:BranchGroupAutoMergeGate 2026-08-09-09:00: + The drain's catch-and-warn wrappers can turn a missing fake-store seam into silent coverage loss. + This fixture therefore treats an `is not a function` warning as a failure after its asynchronous + promotion continuation completes, rather than allowing stderr noise to hide the skipped path. + */ + const warnSpy = vi.spyOn(console, "warn"); + let released: any; + let offendingWarnings: string[] = []; + try { + released = await ProjectEngine.prototype.onMerge.call(engine, "FN-3324"); + await promotionEvaluationReached; + offendingWarnings = warnSpy.mock.calls + .map((args) => args.map((arg) => String(arg)).join(" ")) + .filter((message) => message.includes("is not a function")); + } finally { + warnSpy.mockRestore(); + createResolvedAgentSessionMock.mockReset(); + } + + expect(offendingWarnings, "merge drain emitted missing-store-seam warnings").toEqual([]); expect(released.merged).toBe(true); + expect(store.listTasksByBranchGroup).toHaveBeenCalledWith("BG-user-hold"); expect(mergeAttempts).toBe(1); expect(git(repo, "git rev-parse main")).toBe(mainBefore); expect(git(repo, "git show mission/M-8811:user-hold-feature.txt")).toBe("release only after operator confirmation");