feat(FN-3044): add canonical mock helpers and migrate representative test s

This merge introduces canonical mock helper modules across the dashboard and engine packages, and migrates representative test suites to use them for consistency. The changes add four new mock helpers (`mockApi.ts`, `mockLucide.ts`, `mockCore.ts`, `mockCoreEngine.ts`) and harden the API mock proxy's

Fusion-Task-Id: FN-3044
This commit is contained in:
Fusion
2026-05-01 00:31:31 -07:00
committed by gsxdsm
parent 3563881db5
commit 07c552f823
15 changed files with 304 additions and 124 deletions

View File

@@ -28,9 +28,8 @@ vi.mock("../../sse-bus", () => ({
}));
vi.mock("../../api", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../api")>();
return {
...actual,
const { createDashboardApiMock } = await import("../../test/mockApi");
return createDashboardApiMock(() => importOriginal<typeof import("../../api")>(), {
fetchTasks: vi.fn(() => Promise.resolve([])),
fetchConfig: vi.fn(() => Promise.resolve({ maxConcurrent: 2, rootDir: "/workspace/project" })),
fetchSettings: vi.fn(() => Promise.resolve({ ...defaultSettings })),
@@ -60,7 +59,7 @@ vi.mock("../../api", async (importOriginal) => {
fetchScripts: vi.fn(() => Promise.resolve({ build: "npm run build", test: "pnpm test" })),
runScript: vi.fn(() => Promise.resolve({ sessionId: "sess-script-1", command: "echo hello" })),
killPtyTerminalSession: vi.fn(() => Promise.resolve({ killed: true })),
};
});
});
const mockUseTasks = vi.fn(() => ({

View File

@@ -50,51 +50,54 @@ const mockFetchRemoteUrl = vi.fn();
const mockTriggerMemoryDreams = vi.fn();
const mockUseWorkspaceFileBrowser = vi.fn();
vi.mock("../../api", () => ({
fetchSettings: (...args: unknown[]) => mockFetchSettings(...args),
fetchSettingsByScope: (...args: unknown[]) => mockFetchSettingsByScope(...args),
updateSettings: (...args: unknown[]) => mockUpdateSettings(...args),
updateGlobalSettings: (...args: unknown[]) => mockUpdateGlobalSettings(...args),
exportSettings: (...args: unknown[]) => mockExportSettings(...args),
importSettings: (...args: unknown[]) => mockImportSettings(...args),
fetchAuthStatus: (...args: unknown[]) => mockFetchAuthStatus(...args),
loginProvider: (...args: unknown[]) => mockLoginProvider(...args),
logoutProvider: (...args: unknown[]) => mockLogoutProvider(...args),
saveApiKey: (...args: unknown[]) => mockSaveApiKey(...args),
fetchModels: (...args: unknown[]) => mockFetchModels(...args),
fetchCustomProviders: (...args: unknown[]) => mockFetchCustomProviders(...args),
createCustomProvider: (...args: unknown[]) => mockCreateCustomProvider(...args),
updateCustomProvider: (...args: unknown[]) => mockUpdateCustomProvider(...args),
deleteCustomProvider: (...args: unknown[]) => mockDeleteCustomProvider(...args),
testNtfyNotification: (...args: unknown[]) => mockTestNtfyNotification(...args),
testNotification: (...args: unknown[]) => mockTestNotification(...args),
fetchBackups: (...args: unknown[]) => mockFetchBackups(...args),
createBackup: (...args: unknown[]) => mockCreateBackup(...args),
fetchMemoryFiles: (...args: unknown[]) => mockFetchMemoryFiles(...args),
fetchMemoryFile: (...args: unknown[]) => mockFetchMemoryFile(...args),
saveMemoryFile: (...args: unknown[]) => mockSaveMemoryFile(...args),
compactMemory: (...args: unknown[]) => mockCompactMemory(...args),
fetchGlobalConcurrency: (...args: unknown[]) => mockFetchGlobalConcurrency(...args),
updateGlobalConcurrency: (...args: unknown[]) => mockUpdateGlobalConcurrency(...args),
fetchMemoryBackendStatus: (...args: unknown[]) => mockFetchMemoryBackendStatus(...args),
testMemoryRetrieval: (...args: unknown[]) => mockTestMemoryRetrieval(...args),
installQmd: (...args: unknown[]) => mockInstallQmd(...args),
fetchGitRemotesDetailed: (...args: unknown[]) => mockFetchGitRemotesDetailed(...args),
fetchDashboardHealth: (...args: unknown[]) => mockFetchDashboardHealth(...args),
checkForUpdates: (...args: unknown[]) => mockCheckForUpdates(...args),
fetchRemoteSettings: (...args: unknown[]) => mockFetchRemoteSettings(...args),
updateRemoteSettings: (...args: unknown[]) => mockUpdateRemoteSettings(...args),
fetchRemoteStatus: (...args: unknown[]) => mockFetchRemoteStatus(...args),
installCloudflared: (...args: unknown[]) => mockInstallCloudflared(...args),
startRemoteTunnel: (...args: unknown[]) => mockStartRemoteTunnel(...args),
stopRemoteTunnel: (...args: unknown[]) => mockStopRemoteTunnel(...args),
killExternalTunnel: (...args: unknown[]) => mockKillExternalTunnel(...args),
regenerateRemotePersistentToken: (...args: unknown[]) => mockRegenerateRemotePersistentToken(...args),
generateShortLivedRemoteToken: (...args: unknown[]) => mockGenerateShortLivedRemoteToken(...args),
fetchRemoteQr: (...args: unknown[]) => mockFetchRemoteQr(...args),
fetchRemoteUrl: (...args: unknown[]) => mockFetchRemoteUrl(...args),
triggerMemoryDreams: (...args: unknown[]) => mockTriggerMemoryDreams(...args),
}));
vi.mock("../../api", async (importOriginal) => {
const { createDashboardApiMock } = await import("../../test/mockApi");
return createDashboardApiMock(() => importOriginal<typeof import("../../api")>(), {
fetchSettings: (...args: unknown[]) => mockFetchSettings(...args),
fetchSettingsByScope: (...args: unknown[]) => mockFetchSettingsByScope(...args),
updateSettings: (...args: unknown[]) => mockUpdateSettings(...args),
updateGlobalSettings: (...args: unknown[]) => mockUpdateGlobalSettings(...args),
exportSettings: (...args: unknown[]) => mockExportSettings(...args),
importSettings: (...args: unknown[]) => mockImportSettings(...args),
fetchAuthStatus: (...args: unknown[]) => mockFetchAuthStatus(...args),
loginProvider: (...args: unknown[]) => mockLoginProvider(...args),
logoutProvider: (...args: unknown[]) => mockLogoutProvider(...args),
saveApiKey: (...args: unknown[]) => mockSaveApiKey(...args),
fetchModels: (...args: unknown[]) => mockFetchModels(...args),
fetchCustomProviders: (...args: unknown[]) => mockFetchCustomProviders(...args),
createCustomProvider: (...args: unknown[]) => mockCreateCustomProvider(...args),
updateCustomProvider: (...args: unknown[]) => mockUpdateCustomProvider(...args),
deleteCustomProvider: (...args: unknown[]) => mockDeleteCustomProvider(...args),
testNtfyNotification: (...args: unknown[]) => mockTestNtfyNotification(...args),
testNotification: (...args: unknown[]) => mockTestNotification(...args),
fetchBackups: (...args: unknown[]) => mockFetchBackups(...args),
createBackup: (...args: unknown[]) => mockCreateBackup(...args),
fetchMemoryFiles: (...args: unknown[]) => mockFetchMemoryFiles(...args),
fetchMemoryFile: (...args: unknown[]) => mockFetchMemoryFile(...args),
saveMemoryFile: (...args: unknown[]) => mockSaveMemoryFile(...args),
compactMemory: (...args: unknown[]) => mockCompactMemory(...args),
fetchGlobalConcurrency: (...args: unknown[]) => mockFetchGlobalConcurrency(...args),
updateGlobalConcurrency: (...args: unknown[]) => mockUpdateGlobalConcurrency(...args),
fetchMemoryBackendStatus: (...args: unknown[]) => mockFetchMemoryBackendStatus(...args),
testMemoryRetrieval: (...args: unknown[]) => mockTestMemoryRetrieval(...args),
installQmd: (...args: unknown[]) => mockInstallQmd(...args),
fetchGitRemotesDetailed: (...args: unknown[]) => mockFetchGitRemotesDetailed(...args),
fetchDashboardHealth: (...args: unknown[]) => mockFetchDashboardHealth(...args),
checkForUpdates: (...args: unknown[]) => mockCheckForUpdates(...args),
fetchRemoteSettings: (...args: unknown[]) => mockFetchRemoteSettings(...args),
updateRemoteSettings: (...args: unknown[]) => mockUpdateRemoteSettings(...args),
fetchRemoteStatus: (...args: unknown[]) => mockFetchRemoteStatus(...args),
installCloudflared: (...args: unknown[]) => mockInstallCloudflared(...args),
startRemoteTunnel: (...args: unknown[]) => mockStartRemoteTunnel(...args),
stopRemoteTunnel: (...args: unknown[]) => mockStopRemoteTunnel(...args),
killExternalTunnel: (...args: unknown[]) => mockKillExternalTunnel(...args),
regenerateRemotePersistentToken: (...args: unknown[]) => mockRegenerateRemotePersistentToken(...args),
generateShortLivedRemoteToken: (...args: unknown[]) => mockGenerateShortLivedRemoteToken(...args),
fetchRemoteQr: (...args: unknown[]) => mockFetchRemoteQr(...args),
fetchRemoteUrl: (...args: unknown[]) => mockFetchRemoteUrl(...args),
triggerMemoryDreams: (...args: unknown[]) => mockTriggerMemoryDreams(...args),
});
});
// Mock the hook
const mockUseMemoryBackendStatus = vi.fn();

View File

@@ -9,32 +9,34 @@ import { TaskDetailModal } from "../TaskDetailModal";
import type { TaskDetail, Column, MergeResult, Task } from "@fusion/core";
import { clearAuthToken } from "../../auth";
vi.mock("../../api", () => ({
uploadAttachment: vi.fn(),
deleteAttachment: vi.fn(),
updateTask: vi.fn().mockResolvedValue({}),
fetchTaskDetail: vi.fn().mockResolvedValue(makeTask()),
fetchAgentLogs: vi.fn().mockResolvedValue([]),
requestSpecRevision: vi.fn().mockResolvedValue({}),
approvePlan: vi.fn().mockResolvedValue({}),
rejectPlan: vi.fn().mockResolvedValue({}),
duplicateTask: vi.fn().mockResolvedValue({}),
refineTask: vi.fn().mockResolvedValue({}),
addSteeringComment: vi.fn(),
assignTask: vi.fn().mockResolvedValue({}),
fetchAgents: vi.fn().mockResolvedValue([]),
fetchAgent: vi.fn(),
// TaskForm dependencies
fetchModels: vi.fn().mockResolvedValue({ models: [], favoriteProviders: [] }),
fetchSettings: vi.fn().mockResolvedValue({ modelPresets: [], autoSelectModelPreset: false, defaultPresetBySize: {} }),
fetchWorkflowSteps: vi.fn().mockResolvedValue([]),
refineText: vi.fn(),
getRefineErrorMessage: vi.fn((err: any) => err?.message || "Failed to refine"),
updateGlobalSettings: vi.fn().mockResolvedValue({}),
pauseTask: vi.fn().mockResolvedValue({}),
unpauseTask: vi.fn().mockResolvedValue({}),
fetchWorkflowResults: vi.fn().mockResolvedValue([]),
}));
vi.mock("../../api", async (importOriginal) => {
const { createDashboardApiMock } = await import("../../test/mockApi");
return createDashboardApiMock(() => importOriginal<typeof import("../../api")>(), {
uploadAttachment: vi.fn(),
deleteAttachment: vi.fn(),
updateTask: vi.fn().mockResolvedValue({}),
fetchTaskDetail: vi.fn().mockResolvedValue(makeTask()),
fetchAgentLogs: vi.fn().mockResolvedValue([]),
requestSpecRevision: vi.fn().mockResolvedValue({}),
approvePlan: vi.fn().mockResolvedValue({}),
rejectPlan: vi.fn().mockResolvedValue({}),
duplicateTask: vi.fn().mockResolvedValue({}),
refineTask: vi.fn().mockResolvedValue({}),
addSteeringComment: vi.fn(),
assignTask: vi.fn().mockResolvedValue({}),
fetchAgents: vi.fn().mockResolvedValue([]),
fetchAgent: vi.fn(),
fetchModels: vi.fn().mockResolvedValue({ models: [], favoriteProviders: [] }),
fetchSettings: vi.fn().mockResolvedValue({ modelPresets: [], autoSelectModelPreset: false, defaultPresetBySize: {} }),
fetchWorkflowSteps: vi.fn().mockResolvedValue([]),
refineText: vi.fn(),
getRefineErrorMessage: vi.fn((err: any) => err?.message || "Failed to refine"),
updateGlobalSettings: vi.fn().mockResolvedValue({}),
pauseTask: vi.fn().mockResolvedValue({}),
unpauseTask: vi.fn().mockResolvedValue({}),
fetchWorkflowResults: vi.fn().mockResolvedValue([]),
});
});
// Mock lucide-react icons used by TaskDetailModal, TaskForm, PrSection, CustomModelDropdown
vi.mock("lucide-react", () => ({

View File

@@ -22,19 +22,22 @@ import * as api from "../../api";
import type { Task, Column } from "@fusion/core";
// Mock the api module
vi.mock("../../api", () => ({
fetchTasks: vi.fn().mockResolvedValue([]),
createTask: vi.fn(),
moveTask: vi.fn(),
deleteTask: vi.fn(),
mergeTask: vi.fn(),
retryTask: vi.fn(),
duplicateTask: vi.fn(),
updateTask: vi.fn(),
archiveTask: vi.fn(),
unarchiveTask: vi.fn(),
archiveAllDone: vi.fn(),
}));
vi.mock("../../api", async (importOriginal) => {
const { createDashboardApiMock } = await import("../../test/mockApi");
return createDashboardApiMock(() => importOriginal<typeof import("../../api")>(), {
fetchTasks: vi.fn().mockResolvedValue([]),
createTask: vi.fn(),
moveTask: vi.fn(),
deleteTask: vi.fn(),
mergeTask: vi.fn(),
retryTask: vi.fn(),
duplicateTask: vi.fn(),
updateTask: vi.fn(),
archiveTask: vi.fn(),
unarchiveTask: vi.fn(),
archiveAllDone: vi.fn(),
});
});
async function flushPromises(): Promise<void> {
await Promise.resolve();

View File

@@ -0,0 +1,61 @@
/**
* Canonical dashboard app API mock helper.
*
* Add new `app/api.ts` or `app/api/legacy.ts` exports here first before
* introducing ad-hoc per-test `vi.mock("../../api", …)` export lists.
*/
import { vi, type Mock } from "vitest";
type AnyFn = Mock;
type AnyModule = Record<string, unknown>;
const fallbackFns = new Map<string, AnyFn>();
function getFallback(name: string): AnyFn {
if (!fallbackFns.has(name)) {
fallbackFns.set(name, vi.fn(async () => undefined));
}
return fallbackFns.get(name)!;
}
export const dashboardApiMocks = {
fetchTasks: vi.fn(async () => []),
fetchSettings: vi.fn(async () => ({})),
updateSettings: vi.fn(async () => ({})),
fetchGlobalSettings: vi.fn(async () => ({})),
fetchAuthStatus: vi.fn(async () => ({ providers: [] })),
fetchModels: vi.fn(async () => ({ models: [], favoriteProviders: [], favoriteModels: [] })),
fetchTaskDetail: vi.fn(),
fetchUnreadCount: vi.fn(async () => ({ unreadCount: 0 })),
} satisfies Record<string, AnyFn>;
export async function createDashboardApiMock(
importActual: () => Promise<AnyModule>,
overrides: Record<string, AnyFn> = {},
): Promise<AnyModule> {
const actual = await importActual();
const mocked: AnyModule = { ...actual, ...dashboardApiMocks, ...overrides };
return new Proxy(mocked, {
get(target, prop, receiver) {
if (typeof prop !== "string") return Reflect.get(target, prop, receiver);
if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
if (["then", "catch", "finally"].includes(prop)) {
return undefined;
}
const actualValue = (actual as AnyModule)[prop];
if (typeof actualValue === "function" || actualValue === undefined) {
const fn = getFallback(prop);
(target as AnyModule)[prop] = fn;
return fn;
}
return actualValue;
},
});
}
export function resetDashboardApiMockState(): void {
Object.values(dashboardApiMocks).forEach((fn) => fn.mockReset());
for (const fn of fallbackFns.values()) fn.mockReset();
}

View File

@@ -0,0 +1,35 @@
import { describe, expect, it } from "vitest";
import { createDashboardApiMock, dashboardApiMocks, resetDashboardApiMockState } from "./mockApi";
import { createLucideMock } from "./mockLucide";
describe("dashboard test mock helpers", () => {
it("creates stable fallback mocks for missing API function exports", async () => {
const module = await createDashboardApiMock(async () => ({ known: () => "ok" }));
const first = module.missingApi as (...args: unknown[]) => Promise<unknown>;
const second = module.missingApi as (...args: unknown[]) => Promise<unknown>;
expect(first).toBe(second);
await first("arg");
expect(first).toHaveBeenCalledWith("arg");
resetDashboardApiMockState();
expect(first).not.toHaveBeenCalled();
});
it("auto-generates missing lucide icons while preserving provided exports", async () => {
const existing = () => "existing";
const module = await createLucideMock(async () => ({ ExistingIcon: existing }));
expect(module.ExistingIcon).toBe(existing);
const Generated = module.BrandNewIcon as (props: Record<string, unknown>) => any;
const rendered = Generated({ className: "x" });
expect(rendered.props["data-testid"]).toBe("icon-brand-new-icon");
expect(rendered.props.className).toBe("x");
});
it("keeps canonical dashboard api mocks resettable", () => {
dashboardApiMocks.fetchSettings.mockResolvedValueOnce({ hello: "world" });
resetDashboardApiMockState();
expect(dashboardApiMocks.fetchSettings).not.toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,30 @@
/**
* Canonical lucide-react mock helper for dashboard app tests.
*
* Add newly used icons here first before creating per-suite lucide export lists.
*/
import React from "react";
type AnyModule = Record<string, unknown>;
function icon(name: string) {
return function MockIcon(props: Record<string, unknown>) {
return React.createElement("span", { "data-testid": `icon-${name}`, ...props });
};
}
export async function createLucideMock(importActual: () => Promise<AnyModule>): Promise<AnyModule> {
const actual = await importActual();
return new Proxy({ ...actual }, {
get(target, prop, receiver) {
if (typeof prop !== "string") return Reflect.get(target, prop, receiver);
if (["then", "catch", "finally"].includes(prop)) return undefined;
if (Reflect.has(target, prop)) return Reflect.get(target, prop, receiver);
const generated = icon(
prop.replace(/[A-Z]/g, (m, idx) => (idx === 0 ? m.toLowerCase() : `-${m.toLowerCase()}`)),
);
(target as AnyModule)[prop] = generated;
return generated;
},
});
}

View File

@@ -13,10 +13,9 @@ import { createServer } from "../server.js";
const mockInit = vi.fn().mockResolvedValue(undefined);
const mockClose = vi.fn().mockResolvedValue(undefined);
vi.mock("@fusion/core", async () => {
const actual = await vi.importActual<typeof import("@fusion/core")>("@fusion/core");
return {
...actual,
vi.mock("@fusion/core", async (importOriginal) => {
const { createCoreMock } = await import("../test/mockCoreEngine.js");
return createCoreMock(() => importOriginal<typeof import("@fusion/core")>(), {
CentralCore: vi.fn().mockImplementation(() => ({
init: mockInit,
close: mockClose,
@@ -42,7 +41,7 @@ vi.mock("@fusion/core", async () => {
]),
updateNode: vi.fn().mockResolvedValue(undefined),
})),
};
});
});
class MockStore extends EventEmitter {

View File

@@ -2,6 +2,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { EventEmitter } from "node:events";
import type { Request, Response } from "express";
import { request } from "../test-request.js";
import { createCoreMock, createEngineMock } from "../test/mockCoreEngine.js";
// ── SSE Test Helpers ────────────────────────────────────────────────────────
@@ -111,7 +112,7 @@ const { mockCreateFnAgent, mockChatStreamManager, mockSendMessage, mockCancelGen
});
// Mock @fusion/engine to prevent createFnAgent resolution
vi.mock("@fusion/engine", () => ({
vi.mock("@fusion/engine", () => createEngineMock({
createFnAgent: mockCreateFnAgent,
}));
@@ -132,8 +133,9 @@ const mockAgentStoreInit = vi.fn().mockResolvedValue(undefined);
const mockAgentStoreGetAgent = vi.fn();
// Mock ChatStore class for vi.mock
vi.mock("@fusion/core", () => {
return {
vi.mock("@fusion/core", async (importOriginal) => createCoreMock(
() => importOriginal<typeof import("@fusion/core")>(),
{
ChatStore: class MockChatStore extends EventEmitter {
init = mockInit;
createSession = mockCreateSession;
@@ -151,8 +153,8 @@ vi.mock("@fusion/core", () => {
init = mockAgentStoreInit;
getAgent = mockAgentStoreGetAgent;
},
};
});
},
));
// Mock chat.js - must mock before importing server
vi.mock("../chat.js", () => {

View File

@@ -65,10 +65,9 @@ vi.mock("node:child_process", async () => {
};
});
vi.mock("@fusion/core", async () => {
const actual = await vi.importActual<typeof import("@fusion/core")>("@fusion/core");
return {
...actual,
vi.mock("@fusion/core", async (importOriginal) => {
const { createCoreMock } = await import("../test/mockCoreEngine.js");
return createCoreMock(() => importOriginal<typeof import("@fusion/core")>(), {
resolveGlobalDir: vi.fn().mockReturnValue("/tmp/fusion-test"),
isGhAvailable: vi.fn(),
isGhAuthenticated: vi.fn(),
@@ -79,10 +78,12 @@ vi.mock("@fusion/core", async () => {
listProjects: mockCentralListProjects,
reconcileProjectStatuses: mockCentralReconcileProjectStatuses,
})),
};
});
});
vi.mock("@fusion/engine", () => ({
vi.mock("@fusion/engine", async () => {
const { createEngineMock } = await import("../test/mockCoreEngine.js");
return createEngineMock({
createFnAgent: vi.fn(async (options?: { onText?: (delta: string) => void }) => ({
session: {
state: {
@@ -122,7 +123,8 @@ vi.mock("@fusion/engine", () => ({
throw new Error("Reflection service unavailable in route tests");
}
},
}));
});
});
import { AgentStore, Database, RoutineStore, isGhAvailable, isGhAuthenticated } from "@fusion/core";
import { createFnAgent } from "@fusion/engine";

View File

@@ -0,0 +1,27 @@
/**
* Canonical @fusion/core and @fusion/engine mock helpers for dashboard server tests.
*
* Add new mocked exports here first before duplicating large per-suite mock objects.
*/
import { vi } from "vitest";
type AnyModule = Record<string, unknown>;
export async function createCoreMock(
importActual: () => Promise<AnyModule>,
overrides: AnyModule = {},
): Promise<AnyModule> {
const actual = await importActual();
return {
...actual,
...overrides,
};
}
export function createEngineMock(overrides: AnyModule = {}): AnyModule {
return {
createFnAgent: vi.fn(),
promptWithFallback: vi.fn(),
...overrides,
};
}

View File

@@ -5,11 +5,9 @@ import {
createTaskDocumentWriteTool,
} from "../agent-tools.js";
vi.mock("@fusion/core", async () => {
const actual = await vi.importActual("@fusion/core");
return {
...actual,
};
vi.mock("@fusion/core", async (importOriginal) => {
const { createEngineCoreMock } = await import("../test/mockCore.js");
return createEngineCoreMock(() => importOriginal<typeof import("@fusion/core")>());
});
const TASK_ID = "FN-1272";

View File

@@ -25,19 +25,18 @@ const mocks = vi.hoisted(() => ({
notificationServiceStop: vi.fn(),
}));
vi.mock("@fusion/core", async () => {
class MockAutomationStore {
constructor(_cwd: string) {}
vi.mock("@fusion/core", async (importOriginal) => {
const { createEngineCoreMock } = await import("../test/mockCore.js");
return createEngineCoreMock(() => importOriginal<typeof import("@fusion/core")>(), {
AutomationStore: class MockAutomationStore {
constructor(_cwd: string) {}
init = mocks.automationStoreInit;
}
return {
AutomationStore: MockAutomationStore,
init = mocks.automationStoreInit;
},
syncInsightExtractionAutomation: mocks.syncInsightExtractionAutomation,
syncAutoSummarizeAutomation: mocks.syncAutoSummarizeAutomation,
syncMemoryDreamsAutomation: mocks.syncMemoryDreamsAutomation,
};
});
});
vi.mock("../cron-runner.js", () => {

View File

@@ -29,12 +29,11 @@ vi.mock("../pi.js", () => ({
promptWithFallback: vi.fn().mockReturnValue("mock-prompt"),
}));
vi.mock("@fusion/core", async () => {
const actual = await vi.importActual("@fusion/core");
return {
...actual,
vi.mock("@fusion/core", async (importOriginal) => {
const { createEngineCoreMock } = await import("../test/mockCore.js");
return createEngineCoreMock(() => importOriginal<typeof import("@fusion/core")>(), {
resolveAgentPrompt: vi.fn().mockReturnValue(null),
};
});
});
async function createTriageFixtureRoot(prefix: string): Promise<string> {

View File

@@ -0,0 +1,21 @@
/**
* Canonical @fusion/core mock helper for engine tests.
*
* Prefer extending this helper over suite-local full export lists.
*/
import type { Mock } from "vitest";
type AnyModule = Record<string, unknown>;
export async function createEngineCoreMock(
importActual: () => Promise<AnyModule>,
overrides: AnyModule = {},
): Promise<AnyModule> {
const actual = await importActual();
return {
...actual,
...overrides,
};
}
export type MockFn = Mock;