FN-6367: fix constructible CLI test mocks
Update CLI test mocks so Vitest-spied constructors remain new-able under current mock semantics. - Add constructible mock wrappers for TaskStore and related CLI test constructor mocks. - Replace arrow-function constructor mock implementations with function-based implementations. - Bring task retry fixture data in line with the graph resume retry counter shape. Files changed: .../cli/src/__tests__/experiment-finalize.test.ts | 19 +++++++++++++++++-- .../__tests__/extension-experiment-finalize.test.ts | 19 +++++++++++++++++-- packages/cli/src/__tests__/plugin-dev.test.ts | 19 +++++++++++++++++-- packages/cli/src/__tests__/project-resolver.test.ts | 17 ++++++++++++++++- packages/cli/src/__tests__/task-plan.test.ts | 17 ++++++++++++++++- packages/cli/src/__tests__/task-steer.test.ts | 17 ++++++++++++++++- packages/cli/src/__tests__/update-cache.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/agent.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/backup.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/db.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/desktop.test.ts | 4 +++- packages/cli/src/commands/__tests__/init.test.ts | 17 ++++++++++++++++- .../src/commands/__tests__/memory-backup.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/message.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/node.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/plugin.test.ts | 19 +++++++++++++++++-- packages/cli/src/commands/__tests__/project.test.ts | 21 ++++++++++++++++++--- packages/cli/src/commands/__tests__/serve.test.ts | 17 ++++++++++++++++- .../src/commands/__tests__/settings-export.test.ts | 17 ++++++++++++++++- .../src/commands/__tests__/settings-import.test.ts | 17 ++++++++++++++++- .../cli/src/commands/__tests__/settings.test.ts | 17 ++++++++++++++++- packages/cli/src/commands/__tests__/task.test.ts | 2 ++ 22 files changed, 331 insertions(+), 27 deletions(-) Fusion-Task-Id: FN-6367 Fusion-Task-Lineage: d900dfe0-e286-4175-a5ba-7c319e2527b0
This commit is contained in:
@@ -3,6 +3,21 @@ import { writeFile, mkdtemp } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const previewPlan = vi.fn();
|
||||
const finalize = vi.fn();
|
||||
const init = vi.fn();
|
||||
@@ -18,12 +33,12 @@ const mockErrors = vi.hoisted(() => ({
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn(() => ({ init, getExperimentSessionStore })),
|
||||
TaskStore: makeConstructibleMock(() => ({ init, getExperimentSessionStore })),
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/engine", () => ({
|
||||
defaultGitOps: vi.fn(() => ({})),
|
||||
ExperimentFinalizeService: vi.fn(() => ({ previewPlan, finalize })),
|
||||
ExperimentFinalizeService: makeConstructibleMock(() => ({ previewPlan, finalize })),
|
||||
ExperimentFinalizeStateError: class extends Error { code = "state_error" as const; },
|
||||
ExperimentFinalizeNoKeptRunsError: class extends Error { code = "no_kept_runs" as const; },
|
||||
ExperimentFinalizePlanError: class extends Error { code = "plan_error" as const; },
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const previewPlanMock = vi.hoisted(() => vi.fn());
|
||||
const finalizeMock = vi.hoisted(() => vi.fn());
|
||||
|
||||
@@ -18,7 +33,7 @@ const mockErrors = vi.hoisted(() => ({
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
getExperimentSessionStore: vi.fn(() => ({})),
|
||||
})),
|
||||
@@ -43,7 +58,7 @@ vi.mock("@fusion/engine", () => ({
|
||||
createFnAgent: vi.fn(),
|
||||
fetchWebContent: vi.fn(),
|
||||
defaultGitOps: vi.fn(() => ({})),
|
||||
ExperimentFinalizeService: vi.fn(() => ({ previewPlan: previewPlanMock, finalize: finalizeMock })),
|
||||
ExperimentFinalizeService: makeConstructibleMock(() => ({ previewPlan: previewPlanMock, finalize: finalizeMock })),
|
||||
ExperimentFinalizeStateError: mockErrors.StateError,
|
||||
ExperimentFinalizeNoKeptRunsError: mockErrors.NoKeptError,
|
||||
ExperimentFinalizePlanError: mockErrors.PlanError,
|
||||
|
||||
@@ -3,6 +3,21 @@ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const pluginCommandMocks = vi.hoisted(() => {
|
||||
const store = {
|
||||
registerPlugin: vi.fn(async () => ({ id: "fusion-plugin-dev-test", enabled: true })),
|
||||
@@ -16,8 +31,8 @@ const pluginCommandMocks = vi.hoisted(() => {
|
||||
return {
|
||||
store,
|
||||
loader,
|
||||
createPluginStore: vi.fn(async () => store),
|
||||
createPluginLoader: vi.fn(async () => ({ store, loader })),
|
||||
createPluginStore: makeConstructibleMock(async () => store),
|
||||
createPluginLoader: makeConstructibleMock(async () => ({ store, loader })),
|
||||
resolvePluginEntryFile: vi.fn(async (dir: string) => join(dir, "dist", "index.js")),
|
||||
loadManifestFromPath: vi.fn(async () => ({
|
||||
manifest: {
|
||||
|
||||
@@ -2,6 +2,21 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { existsSync, statSync } from "node:fs";
|
||||
import { TaskStore } from "@fusion/core";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const { mockIsValidSqliteDatabaseFile } = vi.hoisted(() => ({
|
||||
mockIsValidSqliteDatabaseFile: vi.fn(),
|
||||
}));
|
||||
@@ -41,7 +56,7 @@ vi.mock("@fusion/core", async () => {
|
||||
},
|
||||
isValidSqliteDatabaseFile: (...args: Parameters<typeof mockIsValidSqliteDatabaseFile>) =>
|
||||
mockIsValidSqliteDatabaseFile(...args),
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
listTasks: vi.fn().mockResolvedValue([]),
|
||||
})),
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
// Mock node:readline/promises before importing
|
||||
vi.mock("node:readline/promises", () => ({
|
||||
createInterface: vi.fn(),
|
||||
@@ -8,7 +23,7 @@ vi.mock("node:readline/promises", () => ({
|
||||
// Mock @fusion/core before importing
|
||||
vi.mock("@fusion/core", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@fusion/core")>()),
|
||||
TaskStore: vi.fn(),
|
||||
TaskStore: makeConstructibleMock(),
|
||||
COLUMNS: ["triage", "todo", "in-progress", "in-review", "done", "archived"],
|
||||
COLUMN_LABELS: {
|
||||
triage: "Triage",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
// Mock node:readline/promises before importing
|
||||
vi.mock("node:readline/promises", () => ({
|
||||
createInterface: vi.fn(),
|
||||
@@ -8,7 +23,7 @@ vi.mock("node:readline/promises", () => ({
|
||||
// Mock @fusion/core before importing
|
||||
vi.mock("@fusion/core", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@fusion/core")>()),
|
||||
TaskStore: vi.fn(),
|
||||
TaskStore: makeConstructibleMock(),
|
||||
COLUMNS: ["triage", "todo", "in-progress", "in-review", "done", "archived"],
|
||||
COLUMN_LABELS: {
|
||||
triage: "Triage",
|
||||
|
||||
@@ -2,6 +2,21 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const CLI_PACKAGE_VERSION = (
|
||||
JSON.parse(readFileSync(new URL("../../package.json", import.meta.url), "utf-8")) as { version: string }
|
||||
).version;
|
||||
@@ -16,7 +31,7 @@ const { cacheDir, mockResolveGlobalDir } = vi.hoisted(() => {
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
resolveGlobalDir: mockResolveGlobalDir,
|
||||
GlobalSettingsStore: vi.fn(),
|
||||
GlobalSettingsStore: makeConstructibleMock(),
|
||||
}));
|
||||
|
||||
const { getCachedUpdateStatus } = await import("../update-cache.js");
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
// ── Mock AgentStore ──────────────────────────────────────────────────
|
||||
|
||||
const mockGetAgent = vi.fn();
|
||||
@@ -9,7 +24,7 @@ const mockInit = vi.fn().mockResolvedValue(undefined);
|
||||
// AgentStore mock — vi.fn() with mockImplementation works with `new` in vitest.
|
||||
// We return a plain object from the constructor which becomes the instance.
|
||||
vi.mock("@fusion/core", () => ({
|
||||
AgentStore: vi.fn().mockImplementation(() => ({
|
||||
AgentStore: makeConstructibleMock(() => ({
|
||||
init: mockInit,
|
||||
getAgent: mockGetAgent,
|
||||
updateAgentState: mockUpdateAgentState,
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const {
|
||||
mockListBackups,
|
||||
mockListBackupPairs,
|
||||
@@ -20,7 +35,7 @@ const {
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
BackupManager: vi.fn(),
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
getSettings: mockGetSettings,
|
||||
fusionDir: "/cwd/.fusion",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
// Hoist mocks so they are evaluated before module imports
|
||||
const { mockGetDatabase, mockVacuum, mockResolveProject } = vi.hoisted(() => ({
|
||||
mockGetDatabase: vi.fn(),
|
||||
@@ -8,7 +23,7 @@ const { mockGetDatabase, mockVacuum, mockResolveProject } = vi.hoisted(() => ({
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: vi.fn(),
|
||||
getDatabase: mockGetDatabase,
|
||||
})),
|
||||
|
||||
@@ -122,7 +122,9 @@ const mocks = vi.hoisted(() => {
|
||||
server,
|
||||
app,
|
||||
spawn,
|
||||
taskStoreCtor: vi.fn(() => store),
|
||||
taskStoreCtor: vi.fn(function () {
|
||||
return store;
|
||||
}),
|
||||
createServer: vi.fn(() => app),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -11,6 +11,21 @@ import { exec } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { GitRepositoryInitializationError } from "@fusion/core";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
const mockCentralInit = vi.fn();
|
||||
@@ -27,7 +42,7 @@ vi.mock("@fusion/core", async () => {
|
||||
const actual = await vi.importActual<typeof import("@fusion/core")>("@fusion/core");
|
||||
return {
|
||||
...actual,
|
||||
CentralCore: vi.fn().mockImplementation(() => ({
|
||||
CentralCore: makeConstructibleMock(() => ({
|
||||
init: mockCentralInit,
|
||||
close: mockCentralClose,
|
||||
getProjectByPath: mockGetProjectByPath,
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const {
|
||||
mockListBackups,
|
||||
mockRestoreBackup,
|
||||
@@ -15,7 +30,7 @@ const {
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: vi.fn().mockResolvedValue(undefined),
|
||||
getSettings: mockGetSettings,
|
||||
fusionDir: "/cwd/.fusion",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
// ── Mock MessageStore ────────────────────────────────────────────────
|
||||
|
||||
const mockGetInbox = vi.fn();
|
||||
@@ -17,7 +32,7 @@ vi.mock("@fusion/core", () => {
|
||||
};
|
||||
return {
|
||||
createDatabase: vi.fn().mockReturnValue(mockDb),
|
||||
MessageStore: vi.fn().mockImplementation(() => ({
|
||||
MessageStore: makeConstructibleMock(() => ({
|
||||
getInbox: mockGetInbox,
|
||||
getOutbox: mockGetOutbox,
|
||||
getMailbox: mockGetMailbox,
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const mockInit = vi.fn().mockResolvedValue(undefined);
|
||||
const mockClose = vi.fn().mockResolvedValue(undefined);
|
||||
const mockListNodes = vi.fn();
|
||||
@@ -13,7 +28,7 @@ const mockQuestion = vi.fn();
|
||||
const mockRlClose = vi.fn();
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
CentralCore: vi.fn().mockImplementation(() => ({
|
||||
CentralCore: makeConstructibleMock(() => ({
|
||||
init: mockInit,
|
||||
close: mockClose,
|
||||
listNodes: mockListNodes,
|
||||
|
||||
@@ -3,6 +3,21 @@ import { dirname, join, resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const mocks = vi.hoisted(() => {
|
||||
const pluginStoreInstances: Array<{
|
||||
init: ReturnType<typeof vi.fn>;
|
||||
@@ -15,9 +30,9 @@ const mocks = vi.hoisted(() => {
|
||||
let loaderTaskStore: { getRootDir?: () => string } | undefined;
|
||||
let loaderRootDir: string | undefined;
|
||||
|
||||
const PluginStore = vi.fn();
|
||||
const PluginStore = makeConstructibleMock();
|
||||
|
||||
const PluginLoader = vi.fn();
|
||||
const PluginLoader = makeConstructibleMock();
|
||||
|
||||
const setupDefaults = () => {
|
||||
PluginStore.mockImplementation(() => {
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
*/
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const mockListProjects = vi.fn();
|
||||
const mockRegisterProject = vi.fn();
|
||||
const mockEnsureProjectForPath = vi.fn(async (...args: unknown[]) => ({
|
||||
@@ -29,7 +44,7 @@ const mockEnsureMemoryFileWithBackend = vi.fn();
|
||||
|
||||
// Mock @fusion/core
|
||||
vi.mock("@fusion/core", () => ({
|
||||
CentralCore: vi.fn().mockImplementation(() => ({
|
||||
CentralCore: makeConstructibleMock(() => ({
|
||||
init: mockInit.mockResolvedValue(undefined),
|
||||
close: mockClose.mockResolvedValue(undefined),
|
||||
listProjects: mockListProjects,
|
||||
@@ -41,11 +56,11 @@ vi.mock("@fusion/core", () => ({
|
||||
getProjectByPath: mockGetProjectByPath,
|
||||
getProjectHealth: mockGetProjectHealth,
|
||||
})),
|
||||
GlobalSettingsStore: vi.fn().mockImplementation(() => ({
|
||||
GlobalSettingsStore: makeConstructibleMock(() => ({
|
||||
init: mockGlobalInit.mockResolvedValue(undefined),
|
||||
getSettings: mockGetSettings,
|
||||
})),
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: mockTaskStoreInit,
|
||||
listTasks: mockTaskStoreListTasks,
|
||||
})),
|
||||
|
||||
@@ -4,6 +4,21 @@ import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const { mockSyncStartupModels, mockShouldUseHybridExecutor, mockHybridExecutorCtor, mockHybridExecutorInitialize, mockHybridExecutorShutdown } = vi.hoisted(() => ({
|
||||
mockSyncStartupModels: vi.fn().mockResolvedValue(undefined),
|
||||
mockShouldUseHybridExecutor: vi.fn().mockResolvedValue({ enabled: false, reason: "single-project-local-only" }),
|
||||
@@ -590,7 +605,7 @@ vi.mock("@fusion/core", async (importOriginal) => {
|
||||
storeToken: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
}),
|
||||
GlobalSettingsStore: vi.fn().mockImplementation(function () {
|
||||
GlobalSettingsStore: makeConstructibleMock(function () {
|
||||
return {};
|
||||
}),
|
||||
resolveGlobalDir: vi.fn().mockReturnValue("/mock/global"),
|
||||
|
||||
@@ -4,6 +4,21 @@ import { join, resolve } from "node:path";
|
||||
import { TaskStore, exportSettings, generateExportFilename } from "@fusion/core";
|
||||
import { resolveProject } from "../../project-context.js";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const mockStoreInit = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
vi.mock("node:fs/promises", () => ({
|
||||
@@ -11,7 +26,7 @@ vi.mock("node:fs/promises", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: mockStoreInit,
|
||||
})),
|
||||
exportSettings: vi.fn(),
|
||||
|
||||
@@ -3,6 +3,21 @@ import { existsSync } from "node:fs";
|
||||
import { TaskStore, importSettings, readExportFile, validateImportData } from "@fusion/core";
|
||||
import { resolveProject } from "../../project-context.js";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
const mockStoreInit = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
vi.mock("node:fs", () => ({
|
||||
@@ -10,7 +25,7 @@ vi.mock("node:fs", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@fusion/core", () => ({
|
||||
TaskStore: vi.fn().mockImplementation(() => ({
|
||||
TaskStore: makeConstructibleMock(() => ({
|
||||
init: mockStoreInit,
|
||||
})),
|
||||
importSettings: vi.fn(),
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
function makeConstructibleMock<T extends (...args: any[]) => unknown>(impl?: T) {
|
||||
const mock = vi.fn(function () {});
|
||||
const originalMockImplementation = mock.mockImplementation.bind(mock);
|
||||
const originalMockImplementationOnce = mock.mockImplementationOnce.bind(mock);
|
||||
const wrap = (nextImpl: T) => function (this: unknown, ...args: Parameters<T>) {
|
||||
return nextImpl(...args);
|
||||
};
|
||||
mock.mockImplementation = ((nextImpl: T) => originalMockImplementation(wrap(nextImpl))) as typeof mock.mockImplementation;
|
||||
mock.mockImplementationOnce = ((nextImpl: T) => originalMockImplementationOnce(wrap(nextImpl))) as typeof mock.mockImplementationOnce;
|
||||
if (impl) {
|
||||
mock.mockImplementation(impl);
|
||||
}
|
||||
return mock;
|
||||
}
|
||||
|
||||
vi.mock("@fusion/core", () => {
|
||||
const DEFAULT_SETTINGS = {
|
||||
maxConcurrent: 2,
|
||||
@@ -23,7 +38,7 @@ vi.mock("@fusion/core", () => {
|
||||
};
|
||||
|
||||
return {
|
||||
GlobalSettingsStore: vi.fn(),
|
||||
GlobalSettingsStore: makeConstructibleMock(),
|
||||
DEFAULT_SETTINGS,
|
||||
SUPPORTED_LOCALES: ["en", "zh-CN", "zh-TW", "fr", "es", "ko"],
|
||||
resolveWorktrunkSettings: (globalValue: any, projectValue: any) => ({
|
||||
|
||||
@@ -2460,6 +2460,7 @@ describe("runTaskRetry", () => {
|
||||
reviewerContextRetryCount: 0,
|
||||
reviewerFallbackRetryCount: 0,
|
||||
completionHandoffLimboRecoveryCount: 0,
|
||||
graphResumeRetryCount: 0,
|
||||
mergeAuditBounceCount: 0,
|
||||
mergeRetries: 0,
|
||||
resumeLimboCount: 0,
|
||||
@@ -2534,6 +2535,7 @@ describe("runTaskRetry", () => {
|
||||
reviewerContextRetryCount: 0,
|
||||
reviewerFallbackRetryCount: 0,
|
||||
completionHandoffLimboRecoveryCount: 0,
|
||||
graphResumeRetryCount: 0,
|
||||
mergeAuditBounceCount: 0,
|
||||
mergeRetries: 0,
|
||||
resumeLimboCount: 0,
|
||||
|
||||
Reference in New Issue
Block a user