FN-7000: test unified CLI merge entrypoints
Strengthen CLI merge tests around the unified runAiMerge path.\n\n- Mock runAiMerge separately from deprecated aiMergeTask in CLI task and dashboard tests.\n- Assert task merge uses runAiMerge without falling back to workspace landing or aiMergeTask.\n- Verify dashboard manual merge logging streams through runAiMerge and restores spies safely.\n\nFiles changed:\n .../cli/src/commands/__tests__/dashboard.test.ts | 51 +++++++++++++---------\n packages/cli/src/commands/__tests__/task.test.ts | 30 +++++++++----\n 2 files changed, 52 insertions(+), 29 deletions(-) Fusion-Task-Id: FN-7000 Fusion-Task-Lineage: 96080841-c813-4a9e-97bd-a41aba60089f
This commit is contained in:
@@ -412,6 +412,8 @@ vi.mock("@fusion/engine", async (importOriginal) => {
|
||||
createFollowUpTask: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
const aiMergeTask = vi.fn().mockImplementation(() => Promise.resolve({ merged: true }));
|
||||
const runAiMerge = vi.fn().mockImplementation(() => Promise.resolve({ merged: true }));
|
||||
const landWorkspaceTask = vi.fn().mockImplementation(() => Promise.resolve({ allLanded: true, repos: [] }));
|
||||
const CronRunner = makeConstructibleMock(() => ({
|
||||
start: vi.fn(),
|
||||
stop: vi.fn(),
|
||||
@@ -756,7 +758,8 @@ vi.mock("@fusion/engine", async (importOriginal) => {
|
||||
PrMonitor,
|
||||
PrCommentHandler,
|
||||
aiMergeTask,
|
||||
runAiMerge: aiMergeTask,
|
||||
runAiMerge,
|
||||
landWorkspaceTask,
|
||||
CronRunner,
|
||||
createAiPromptExecutor,
|
||||
SelfHealingManager,
|
||||
@@ -3159,7 +3162,7 @@ describe("runDashboard — merge stream sink routing", () => {
|
||||
resetGitHubMocks();
|
||||
process.env.FUSION_DASHBOARD_TOKEN = "fn_test_dashboard_token";
|
||||
const { TaskStore, AutomationStore, AgentStore, PluginStore, PluginLoader, CentralCore } = await import("@fusion/core");
|
||||
const { aiMergeTask, createFusionAuthStorage } = await import("@fusion/engine");
|
||||
const { runAiMerge, createFusionAuthStorage } = await import("@fusion/engine");
|
||||
const { createServer } = await import("@fusion/dashboard");
|
||||
const { DefaultPackageManager, ModelRegistry, discoverAndLoadExtensions, createExtensionRuntime } = await import("@earendil-works/pi-coding-agent");
|
||||
|
||||
@@ -3211,7 +3214,8 @@ describe("runDashboard — merge stream sink routing", () => {
|
||||
const stdoutWriteSpy = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
||||
const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
(aiMergeTask as ReturnType<typeof vi.fn>).mockImplementationOnce(
|
||||
// FNXC:MergerUnification 2026-06-24-23:04: UI-only dashboard manual merges stream through runAiMerge's onAgentText callback; the test must assert line-buffered merge logs instead of deprecated aiMergeTask or raw stdout writes.
|
||||
(runAiMerge as ReturnType<typeof vi.fn>).mockImplementationOnce(
|
||||
async (_store: unknown, _cwd: string, _taskId: string, opts: { onAgentText?: (delta: string) => void }) => {
|
||||
opts.onAgentText?.("Hel");
|
||||
opts.onAgentText?.("lo");
|
||||
@@ -3221,24 +3225,29 @@ describe("runDashboard — merge stream sink routing", () => {
|
||||
},
|
||||
);
|
||||
|
||||
await runDashboard(0, { open: false, noEngine: true });
|
||||
consoleLogSpy.mockClear();
|
||||
stdoutWriteSpy.mockClear();
|
||||
try {
|
||||
await runDashboard(0, { open: false, noEngine: true });
|
||||
consoleLogSpy.mockClear();
|
||||
stdoutWriteSpy.mockClear();
|
||||
|
||||
const createServerCall = (createServer as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const serverOpts = createServerCall[1] as { onMerge: (taskId: string) => Promise<unknown> };
|
||||
const createServerCall = (createServer as ReturnType<typeof vi.fn>).mock.calls[0];
|
||||
const serverOpts = createServerCall[1] as { onMerge: (taskId: string) => Promise<unknown> };
|
||||
|
||||
await serverOpts.onMerge("FN-TEST");
|
||||
await serverOpts.onMerge("FN-TEST");
|
||||
|
||||
expect(stdoutWriteSpy).not.toHaveBeenCalled();
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] Hello");
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] World!");
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] Tail");
|
||||
expect(consoleLogSpy).not.toHaveBeenCalledWith("[merge] H");
|
||||
|
||||
stdoutWriteSpy.mockRestore();
|
||||
consoleLogSpy.mockRestore();
|
||||
delete process.env.FUSION_DASHBOARD_TOKEN;
|
||||
expect(runAiMerge).toHaveBeenCalledWith(expect.anything(), expect.any(String), "FN-TEST", expect.objectContaining({
|
||||
onAgentText: expect.any(Function),
|
||||
}));
|
||||
expect(stdoutWriteSpy).not.toHaveBeenCalled();
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] Hello");
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] World!");
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith("[merge] Tail");
|
||||
expect(consoleLogSpy).not.toHaveBeenCalledWith("[merge] H");
|
||||
} finally {
|
||||
stdoutWriteSpy.mockRestore();
|
||||
consoleLogSpy.mockRestore();
|
||||
delete process.env.FUSION_DASHBOARD_TOKEN;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -96,8 +96,11 @@ vi.mock("@fusion/core", async (importActual) => {
|
||||
});
|
||||
|
||||
// Mock @fusion/engine
|
||||
const { runAiMergeMock } = vi.hoisted(() => ({ runAiMergeMock: vi.fn() }));
|
||||
vi.mock("@fusion/engine", () => ({ runAiMerge: runAiMergeMock, aiMergeTask: runAiMergeMock }));
|
||||
vi.mock("@fusion/engine", () => ({
|
||||
aiMergeTask: vi.fn(),
|
||||
runAiMerge: vi.fn(),
|
||||
landWorkspaceTask: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock @fusion/dashboard
|
||||
vi.mock("@fusion/dashboard", () => ({
|
||||
@@ -156,7 +159,7 @@ import {
|
||||
import { GitHubClient, generatePrMetadata } from "@fusion/dashboard";
|
||||
import { createSession, submitResponse } from "@fusion/dashboard/planning";
|
||||
import { resolveProject } from "../../project-context.js";
|
||||
import { runAiMerge } from "@fusion/engine";
|
||||
import { aiMergeTask, runAiMerge, landWorkspaceTask } from "@fusion/engine";
|
||||
|
||||
const mockedExec = vi.mocked(exec);
|
||||
|
||||
@@ -1229,6 +1232,7 @@ describe("project-aware task command behavior", () => {
|
||||
isRegistered: true,
|
||||
store: resolvedStore,
|
||||
});
|
||||
// FNXC:MergerUnification 2026-06-24-22:58: CLI `fn task merge` normal-task tests must exercise the unified runAiMerge entrypoint; aiMergeTask remains deprecated and should not mask stale mocks.
|
||||
vi.mocked(runAiMerge).mockResolvedValue({
|
||||
merged: true,
|
||||
task: makeTask({ id: "FN-123" }),
|
||||
@@ -1236,16 +1240,26 @@ describe("project-aware task command behavior", () => {
|
||||
worktreeRemoved: true,
|
||||
branchDeleted: true,
|
||||
} as never);
|
||||
const exitSpy = vi.spyOn(process, "exit").mockImplementation((((code?: number) => {
|
||||
throw new Error(`process.exit:${code}`);
|
||||
}) as unknown) as (code?: string | number | null | undefined) => never);
|
||||
|
||||
await runTaskUpdate("FN-123", "0", "done", "demo-project");
|
||||
await runTaskLog("FN-123", "hello", undefined, "demo-project");
|
||||
await runTaskMerge("FN-123", "demo-project");
|
||||
await runTaskDuplicate("FN-123", "demo-project");
|
||||
await runTaskRefine("FN-123", "more tests", "demo-project");
|
||||
try {
|
||||
await runTaskUpdate("FN-123", "0", "done", "demo-project");
|
||||
await runTaskLog("FN-123", "hello", undefined, "demo-project");
|
||||
await runTaskMerge("FN-123", "demo-project");
|
||||
await runTaskDuplicate("FN-123", "demo-project");
|
||||
await runTaskRefine("FN-123", "more tests", "demo-project");
|
||||
} finally {
|
||||
exitSpy.mockRestore();
|
||||
}
|
||||
|
||||
expect(updateStep).toHaveBeenCalled();
|
||||
expect(logEntry).toHaveBeenCalled();
|
||||
expect(runAiMerge).toHaveBeenCalledWith(resolvedStore, "/test", "FN-123", expect.any(Object));
|
||||
expect(landWorkspaceTask).not.toHaveBeenCalled();
|
||||
expect(aiMergeTask).not.toHaveBeenCalled();
|
||||
expect(exitSpy).not.toHaveBeenCalled();
|
||||
expect(duplicateTask).toHaveBeenCalledWith("FN-123");
|
||||
expect(refineTask).toHaveBeenCalledWith("FN-123", "more tests");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user