From e98dc178662f590f73fb15ee633050a1f518f040 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 8 Apr 2026 14:18:51 +0100 Subject: [PATCH] refactor: dedupe plugin test harnesses --- src/plugins/doctor-contract-registry.test.ts | 38 +++------------- .../runtime/runtime-task-test-harness.ts | 42 +++++++++++++++++ src/plugins/runtime/runtime-taskflow.test.ts | 43 ++++-------------- src/plugins/runtime/runtime-tasks.test.ts | 45 +++++-------------- src/plugins/setup-registry.test.ts | 38 +++------------- .../test-helpers/registry-jiti-mocks.ts | 41 +++++++++++++++++ 6 files changed, 113 insertions(+), 134 deletions(-) create mode 100644 src/plugins/runtime/runtime-task-test-harness.ts create mode 100644 src/plugins/test-helpers/registry-jiti-mocks.ts diff --git a/src/plugins/doctor-contract-registry.test.ts b/src/plugins/doctor-contract-registry.test.ts index 2b764c214ca..3e3662e05a7 100644 --- a/src/plugins/doctor-contract-registry.test.ts +++ b/src/plugins/doctor-contract-registry.test.ts @@ -2,28 +2,13 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; +import { + getRegistryJitiMocks, + resetRegistryJitiMocks, +} from "./test-helpers/registry-jiti-mocks.js"; const tempDirs: string[] = []; - -const mocks = vi.hoisted(() => ({ - createJiti: vi.fn(), - discoverOpenClawPlugins: vi.fn(), - loadPluginManifestRegistry: vi.fn(), -})); - -vi.mock("jiti", () => ({ - createJiti: (...args: Parameters) => mocks.createJiti(...args), -})); - -vi.mock("./discovery.js", () => ({ - discoverOpenClawPlugins: (...args: Parameters) => - mocks.discoverOpenClawPlugins(...args), -})); - -vi.mock("./manifest-registry.js", () => ({ - loadPluginManifestRegistry: (...args: Parameters) => - mocks.loadPluginManifestRegistry(...args), -})); +const mocks = getRegistryJitiMocks(); let clearPluginDoctorContractRegistryCache: typeof import("./doctor-contract-registry.js").clearPluginDoctorContractRegistryCache; let listPluginDoctorLegacyConfigRules: typeof import("./doctor-contract-registry.js").listPluginDoctorLegacyConfigRules; @@ -38,18 +23,7 @@ afterEach(() => { describe("doctor-contract-registry getJiti", () => { beforeEach(async () => { - mocks.createJiti.mockReset(); - mocks.discoverOpenClawPlugins.mockReset(); - mocks.loadPluginManifestRegistry.mockReset(); - mocks.discoverOpenClawPlugins.mockReturnValue({ - candidates: [], - diagnostics: [], - }); - mocks.createJiti.mockImplementation( - (_modulePath: string, _options?: Record) => { - return () => ({ default: {} }); - }, - ); + resetRegistryJitiMocks(); vi.resetModules(); ({ clearPluginDoctorContractRegistryCache, listPluginDoctorLegacyConfigRules } = await import("./doctor-contract-registry.js")); diff --git a/src/plugins/runtime/runtime-task-test-harness.ts b/src/plugins/runtime/runtime-task-test-harness.ts new file mode 100644 index 00000000000..12e4732cadb --- /dev/null +++ b/src/plugins/runtime/runtime-task-test-harness.ts @@ -0,0 +1,42 @@ +import { vi } from "vitest"; +import { resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js"; +import { + resetTaskRegistryDeliveryRuntimeForTests, + resetTaskRegistryForTests, + setTaskRegistryDeliveryRuntimeForTests, +} from "../../tasks/task-registry.js"; + +const runtimeTaskMocks = vi.hoisted(() => ({ + sendMessageMock: vi.fn(), + cancelSessionMock: vi.fn(), + killSubagentRunAdminMock: vi.fn(), +})); + +vi.mock("../../acp/control-plane/manager.js", () => ({ + getAcpSessionManager: () => ({ + cancelSession: runtimeTaskMocks.cancelSessionMock, + }), +})); + +vi.mock("../../agents/subagent-control.js", () => ({ + killSubagentRunAdmin: (params: unknown) => runtimeTaskMocks.killSubagentRunAdminMock(params), +})); + +export function getRuntimeTaskMocks() { + return runtimeTaskMocks; +} + +export function installRuntimeTaskDeliveryMock(): void { + setTaskRegistryDeliveryRuntimeForTests({ + sendMessage: runtimeTaskMocks.sendMessageMock, + }); +} + +export function resetRuntimeTaskTestState( + taskRegistryOptions?: Parameters[0], +): void { + resetTaskRegistryDeliveryRuntimeForTests(); + resetTaskRegistryForTests(taskRegistryOptions); + resetTaskFlowRegistryForTests({ persist: false }); + vi.clearAllMocks(); +} diff --git a/src/plugins/runtime/runtime-taskflow.test.ts b/src/plugins/runtime/runtime-taskflow.test.ts index 701ebfc4074..dfda51349f8 100644 --- a/src/plugins/runtime/runtime-taskflow.test.ts +++ b/src/plugins/runtime/runtime-taskflow.test.ts @@ -1,46 +1,19 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { getTaskFlowById, resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { getTaskFlowById } from "../../tasks/task-flow-registry.js"; +import { getTaskById } from "../../tasks/task-registry.js"; import { - getTaskById, - resetTaskRegistryDeliveryRuntimeForTests, - resetTaskRegistryForTests, - setTaskRegistryDeliveryRuntimeForTests, -} from "../../tasks/task-registry.js"; + installRuntimeTaskDeliveryMock, + resetRuntimeTaskTestState, +} from "./runtime-task-test-harness.js"; import { createRuntimeTaskFlow } from "./runtime-taskflow.js"; -const hoisted = vi.hoisted(() => { - const sendMessageMock = vi.fn(); - const cancelSessionMock = vi.fn(); - const killSubagentRunAdminMock = vi.fn(); - return { - sendMessageMock, - cancelSessionMock, - killSubagentRunAdminMock, - }; -}); - -vi.mock("../../acp/control-plane/manager.js", () => ({ - getAcpSessionManager: () => ({ - cancelSession: hoisted.cancelSessionMock, - }), -})); - -vi.mock("../../agents/subagent-control.js", () => ({ - killSubagentRunAdmin: (params: unknown) => hoisted.killSubagentRunAdminMock(params), -})); - afterEach(() => { - resetTaskRegistryDeliveryRuntimeForTests(); - resetTaskRegistryForTests({ persist: false }); - resetTaskFlowRegistryForTests({ persist: false }); - vi.clearAllMocks(); + resetRuntimeTaskTestState({ persist: false }); }); describe("runtime TaskFlow", () => { beforeEach(() => { - setTaskRegistryDeliveryRuntimeForTests({ - sendMessage: hoisted.sendMessageMock, - }); + installRuntimeTaskDeliveryMock(); }); it("binds managed TaskFlow operations to a session key", () => { diff --git a/src/plugins/runtime/runtime-tasks.test.ts b/src/plugins/runtime/runtime-tasks.test.ts index 6efcd4e039c..2bc7d0f81a5 100644 --- a/src/plugins/runtime/runtime-tasks.test.ts +++ b/src/plugins/runtime/runtime-tasks.test.ts @@ -1,46 +1,21 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { resetTaskFlowRegistryForTests } from "../../tasks/task-flow-registry.js"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { - resetTaskRegistryDeliveryRuntimeForTests, - resetTaskRegistryForTests, - setTaskRegistryDeliveryRuntimeForTests, -} from "../../tasks/task-registry.js"; + getRuntimeTaskMocks, + installRuntimeTaskDeliveryMock, + resetRuntimeTaskTestState, +} from "./runtime-task-test-harness.js"; import { createRuntimeTaskFlow } from "./runtime-taskflow.js"; import { createRuntimeTaskFlows, createRuntimeTaskRuns } from "./runtime-tasks.js"; -const hoisted = vi.hoisted(() => { - const sendMessageMock = vi.fn(); - const cancelSessionMock = vi.fn(); - const killSubagentRunAdminMock = vi.fn(); - return { - sendMessageMock, - cancelSessionMock, - killSubagentRunAdminMock, - }; -}); - -vi.mock("../../acp/control-plane/manager.js", () => ({ - getAcpSessionManager: () => ({ - cancelSession: hoisted.cancelSessionMock, - }), -})); - -vi.mock("../../agents/subagent-control.js", () => ({ - killSubagentRunAdmin: (params: unknown) => hoisted.killSubagentRunAdminMock(params), -})); +const runtimeTaskMocks = getRuntimeTaskMocks(); afterEach(() => { - resetTaskRegistryDeliveryRuntimeForTests(); - resetTaskRegistryForTests(); - resetTaskFlowRegistryForTests({ persist: false }); - vi.clearAllMocks(); + resetRuntimeTaskTestState(); }); describe("runtime tasks", () => { beforeEach(() => { - setTaskRegistryDeliveryRuntimeForTests({ - sendMessage: hoisted.sendMessageMock, - }); + installRuntimeTaskDeliveryMock(); }); it("exposes canonical task and TaskFlow DTOs without leaking raw registry fields", () => { @@ -185,7 +160,7 @@ describe("runtime tasks", () => { cfg: {} as never, }); - expect(hoisted.cancelSessionMock).toHaveBeenCalledWith({ + expect(runtimeTaskMocks.cancelSessionMock).toHaveBeenCalledWith({ cfg: {}, sessionKey: "agent:main:subagent:child", reason: "task-cancel", @@ -232,7 +207,7 @@ describe("runtime tasks", () => { cfg: {} as never, }); - expect(hoisted.cancelSessionMock).not.toHaveBeenCalled(); + expect(runtimeTaskMocks.cancelSessionMock).not.toHaveBeenCalled(); expect(result).toEqual({ found: false, cancelled: false, diff --git a/src/plugins/setup-registry.test.ts b/src/plugins/setup-registry.test.ts index 68368691324..e9a1eed46b9 100644 --- a/src/plugins/setup-registry.test.ts +++ b/src/plugins/setup-registry.test.ts @@ -2,28 +2,13 @@ import fs from "node:fs"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanupTrackedTempDirs, makeTrackedTempDir } from "./test-helpers/fs-fixtures.js"; +import { + getRegistryJitiMocks, + resetRegistryJitiMocks, +} from "./test-helpers/registry-jiti-mocks.js"; const tempDirs: string[] = []; - -const mocks = vi.hoisted(() => ({ - createJiti: vi.fn(), - discoverOpenClawPlugins: vi.fn(), - loadPluginManifestRegistry: vi.fn(), -})); - -vi.mock("jiti", () => ({ - createJiti: (...args: Parameters) => mocks.createJiti(...args), -})); - -vi.mock("./discovery.js", () => ({ - discoverOpenClawPlugins: (...args: Parameters) => - mocks.discoverOpenClawPlugins(...args), -})); - -vi.mock("./manifest-registry.js", () => ({ - loadPluginManifestRegistry: (...args: Parameters) => - mocks.loadPluginManifestRegistry(...args), -})); +const mocks = getRegistryJitiMocks(); let clearPluginSetupRegistryCache: typeof import("./setup-registry.js").clearPluginSetupRegistryCache; let resolvePluginSetupRegistry: typeof import("./setup-registry.js").resolvePluginSetupRegistry; @@ -39,22 +24,11 @@ afterEach(() => { describe("setup-registry getJiti", () => { beforeEach(async () => { + resetRegistryJitiMocks(); vi.resetModules(); ({ clearPluginSetupRegistryCache, resolvePluginSetupRegistry, runPluginSetupConfigMigrations } = await import("./setup-registry.js")); clearPluginSetupRegistryCache(); - mocks.createJiti.mockReset(); - mocks.discoverOpenClawPlugins.mockReset(); - mocks.loadPluginManifestRegistry.mockReset(); - mocks.discoverOpenClawPlugins.mockReturnValue({ - candidates: [], - diagnostics: [], - }); - mocks.createJiti.mockImplementation( - (_modulePath: string, _options?: Record) => { - return () => ({ default: {} }); - }, - ); }); it("disables native jiti loading on Windows for setup-api modules", () => { diff --git a/src/plugins/test-helpers/registry-jiti-mocks.ts b/src/plugins/test-helpers/registry-jiti-mocks.ts new file mode 100644 index 00000000000..8b0a7f15ede --- /dev/null +++ b/src/plugins/test-helpers/registry-jiti-mocks.ts @@ -0,0 +1,41 @@ +import { vi } from "vitest"; + +const registryJitiMocks = vi.hoisted(() => ({ + createJiti: vi.fn(), + discoverOpenClawPlugins: vi.fn(), + loadPluginManifestRegistry: vi.fn(), +})); + +vi.mock("jiti", () => ({ + createJiti: (...args: Parameters) => + registryJitiMocks.createJiti(...args), +})); + +vi.mock("../discovery.js", () => ({ + discoverOpenClawPlugins: ( + ...args: Parameters + ) => registryJitiMocks.discoverOpenClawPlugins(...args), +})); + +vi.mock("../manifest-registry.js", () => ({ + loadPluginManifestRegistry: ( + ...args: Parameters + ) => registryJitiMocks.loadPluginManifestRegistry(...args), +})); + +export function resetRegistryJitiMocks(): void { + registryJitiMocks.createJiti.mockReset(); + registryJitiMocks.discoverOpenClawPlugins.mockReset(); + registryJitiMocks.loadPluginManifestRegistry.mockReset(); + registryJitiMocks.discoverOpenClawPlugins.mockReturnValue({ + candidates: [], + diagnostics: [], + }); + registryJitiMocks.createJiti.mockImplementation( + (_modulePath: string, _options?: Record) => () => ({ default: {} }), + ); +} + +export function getRegistryJitiMocks() { + return registryJitiMocks; +}