From e57628165a196799416a55e67d7089db924d5aa4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 18 Feb 2026 03:38:06 +0000 Subject: [PATCH] test: dedupe shared setup in channel and doctor config tests --- src/channels/plugins/plugins-core.test.ts | 21 +------- src/commands/doctor-config-flow.e2e.test.ts | 43 ++++++----------- src/commands/doctor-gateway-services.test.ts | 50 +++++++++----------- src/config/channel-capabilities.test.ts | 22 ++------- src/cron/service.store-migration.test.ts | 40 ++++++++-------- 5 files changed, 62 insertions(+), 114 deletions(-) diff --git a/src/channels/plugins/plugins-core.test.ts b/src/channels/plugins/plugins-core.test.ts index 9e10f65d2d1..0db23824334 100644 --- a/src/channels/plugins/plugins-core.test.ts +++ b/src/channels/plugins/plugins-core.test.ts @@ -6,7 +6,6 @@ import type { DiscordProbe } from "../../discord/probe.js"; import type { DiscordTokenResolution } from "../../discord/token.js"; import type { IMessageProbe } from "../../imessage/probe.js"; import type { LineProbeResult } from "../../line/types.js"; -import type { PluginRegistry } from "../../plugins/registry.js"; import { setActivePluginRegistry } from "../../plugins/runtime.js"; import type { SignalProbe } from "../../signal/probe.js"; import type { SlackProbe } from "../../slack/probe.js"; @@ -118,23 +117,7 @@ describe("channel plugin catalog", () => { }); }); -const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => ({ - plugins: [], - tools: [], - hooks: [], - typedHooks: [], - commands: [], - channels, - providers: [], - gatewayHandlers: {}, - httpHandlers: [], - httpRoutes: [], - cliRegistrars: [], - services: [], - diagnostics: [], -}); - -const emptyRegistry = createRegistry([]); +const emptyRegistry = createTestRegistry([]); const msteamsOutbound: ChannelOutboundAdapter = { deliveryMode: "direct", @@ -160,7 +143,7 @@ const msteamsPlugin: ChannelPlugin = { outbound: msteamsOutbound, }; -const registryWithMSTeams = createRegistry([ +const registryWithMSTeams = createTestRegistry([ { pluginId: "msteams", plugin: msteamsPlugin, source: "test" }, ]); diff --git a/src/commands/doctor-config-flow.e2e.test.ts b/src/commands/doctor-config-flow.e2e.test.ts index 949f765927f..c60a3bfa626 100644 --- a/src/commands/doctor-config-flow.e2e.test.ts +++ b/src/commands/doctor-config-flow.e2e.test.ts @@ -23,6 +23,19 @@ async function runDoctorConfigWithInput(params: { }); } +function expectGoogleChatDmAllowFromRepaired(cfg: unknown) { + const typed = cfg as { + channels: { + googlechat: { + dm: { allowFrom: string[] }; + allowFrom?: string[]; + }; + }; + }; + expect(typed.channels.googlechat.dm.allowFrom).toEqual(["*"]); + expect(typed.channels.googlechat.allowFrom).toBeUndefined(); +} + describe("doctor config flow", () => { it("preserves invalid config for doctor repairs", async () => { const result = await runDoctorConfigWithInput({ @@ -361,20 +374,7 @@ describe("doctor config flow", () => { }, }); - const cfg = result.cfg as unknown as { - channels: { - googlechat: { - dm: { - policy: string; - allowFrom: string[]; - }; - allowFrom?: string[]; - }; - }; - }; - - expect(cfg.channels.googlechat.dm.allowFrom).toEqual(["*"]); - expect(cfg.channels.googlechat.allowFrom).toBeUndefined(); + expectGoogleChatDmAllowFromRepaired(result.cfg); }); it("repairs googlechat account dm.policy open by setting dm.allowFrom on repair", async () => { @@ -430,19 +430,6 @@ describe("doctor config flow", () => { }, }); - const cfg = result.cfg as unknown as { - channels: { - googlechat: { - dm: { - policy: string; - allowFrom: string[]; - }; - allowFrom?: string[]; - }; - }; - }; - - expect(cfg.channels.googlechat.dm.allowFrom).toEqual(["*"]); - expect(cfg.channels.googlechat.allowFrom).toBeUndefined(); + expectGoogleChatDmAllowFromRepaired(result.cfg); }); }); diff --git a/src/commands/doctor-gateway-services.test.ts b/src/commands/doctor-gateway-services.test.ts index eee37c02df1..4ab73e1f13e 100644 --- a/src/commands/doctor-gateway-services.test.ts +++ b/src/commands/doctor-gateway-services.test.ts @@ -51,6 +51,26 @@ vi.mock("./daemon-install-helpers.js", () => ({ import { maybeRepairGatewayServiceConfig } from "./doctor-gateway-services.js"; +function makeDoctorIo() { + return { log: vi.fn(), error: vi.fn(), exit: vi.fn() }; +} + +function makeDoctorPrompts() { + return { + confirm: vi.fn().mockResolvedValue(true), + confirmRepair: vi.fn().mockResolvedValue(true), + confirmAggressive: vi.fn().mockResolvedValue(true), + confirmSkipInNonInteractive: vi.fn().mockResolvedValue(true), + select: vi.fn().mockResolvedValue("node"), + shouldRepair: false, + shouldForce: false, + }; +} + +async function runRepair(cfg: OpenClawConfig) { + await maybeRepairGatewayServiceConfig(cfg, "local", makeDoctorIo(), makeDoctorPrompts()); +} + describe("maybeRepairGatewayServiceConfig", () => { beforeEach(() => { vi.clearAllMocks(); @@ -91,20 +111,7 @@ describe("maybeRepairGatewayServiceConfig", () => { }, }; - await maybeRepairGatewayServiceConfig( - cfg, - "local", - { log: vi.fn(), error: vi.fn(), exit: vi.fn() }, - { - confirm: vi.fn().mockResolvedValue(true), - confirmRepair: vi.fn().mockResolvedValue(true), - confirmAggressive: vi.fn().mockResolvedValue(true), - confirmSkipInNonInteractive: vi.fn().mockResolvedValue(true), - select: vi.fn().mockResolvedValue("node"), - shouldRepair: false, - shouldForce: false, - }, - ); + await runRepair(cfg); expect(mocks.auditGatewayServiceConfig).toHaveBeenCalledWith( expect.objectContaining({ @@ -164,20 +171,7 @@ describe("maybeRepairGatewayServiceConfig", () => { gateway: {}, }; - await maybeRepairGatewayServiceConfig( - cfg, - "local", - { log: vi.fn(), error: vi.fn(), exit: vi.fn() }, - { - confirm: vi.fn().mockResolvedValue(true), - confirmRepair: vi.fn().mockResolvedValue(true), - confirmAggressive: vi.fn().mockResolvedValue(true), - confirmSkipInNonInteractive: vi.fn().mockResolvedValue(true), - select: vi.fn().mockResolvedValue("node"), - shouldRepair: false, - shouldForce: false, - }, - ); + await runRepair(cfg); expect(mocks.auditGatewayServiceConfig).toHaveBeenCalledWith( expect.objectContaining({ diff --git a/src/config/channel-capabilities.test.ts b/src/config/channel-capabilities.test.ts index 564b420e5f4..cc97a88f3f5 100644 --- a/src/config/channel-capabilities.test.ts +++ b/src/config/channel-capabilities.test.ts @@ -1,7 +1,7 @@ import { afterEach, beforeEach, describe, expect, it } from "vitest"; import type { ChannelPlugin } from "../channels/plugins/types.js"; -import type { PluginRegistry } from "../plugins/registry.js"; import { setActivePluginRegistry } from "../plugins/runtime.js"; +import { createTestRegistry } from "../test-utils/channel-plugins.js"; import { resolveChannelCapabilities } from "./channel-capabilities.js"; import type { OpenClawConfig } from "./config.js"; @@ -86,7 +86,7 @@ describe("resolveChannelCapabilities", () => { it("supports msteams capabilities", () => { setActivePluginRegistry( - createRegistry([ + createTestRegistry([ { pluginId: "msteams", source: "test", @@ -127,22 +127,6 @@ describe("resolveChannelCapabilities", () => { }); }); -const createRegistry = (channels: PluginRegistry["channels"]): PluginRegistry => ({ - plugins: [], - tools: [], - hooks: [], - typedHooks: [], - commands: [], - channels, - providers: [], - gatewayHandlers: {}, - httpHandlers: [], - httpRoutes: [], - cliRegistrars: [], - services: [], - diagnostics: [], -}); - const createStubPlugin = (id: string): ChannelPlugin => ({ id, meta: { @@ -159,7 +143,7 @@ const createStubPlugin = (id: string): ChannelPlugin => ({ }, }); -const baseRegistry = createRegistry([ +const baseRegistry = createTestRegistry([ { pluginId: "telegram", source: "test", plugin: createStubPlugin("telegram") }, { pluginId: "slack", source: "test", plugin: createStubPlugin("slack") }, ]); diff --git a/src/cron/service.store-migration.test.ts b/src/cron/service.store-migration.test.ts index 6c7170060ef..c931d27cbfc 100644 --- a/src/cron/service.store-migration.test.ts +++ b/src/cron/service.store-migration.test.ts @@ -15,6 +15,24 @@ installCronTestHooks({ baseTimeIso: "2026-02-06T17:00:00.000Z", }); +function createStartedCron(storePath: string) { + const cron = new CronService({ + storePath, + cronEnabled: true, + log: noopLogger, + enqueueSystemEvent: vi.fn(), + requestHeartbeatNow: vi.fn(), + runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const, summary: "ok" })), + }); + return { + cron, + start: async () => { + await cron.start(); + return cron; + }, + }; +} + describe("CronService store migrations", () => { it("migrates legacy top-level agentTurn fields and initializes missing state", async () => { const store = await makeStorePath(); @@ -52,16 +70,7 @@ describe("CronService store migrations", () => { "utf-8", ); - const cron = new CronService({ - storePath: store.storePath, - cronEnabled: true, - log: noopLogger, - enqueueSystemEvent: vi.fn(), - requestHeartbeatNow: vi.fn(), - runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const, summary: "ok" })), - }); - - await cron.start(); + const cron = await createStartedCron(store.storePath).start(); const status = await cron.status(); expect(status.enabled).toBe(true); @@ -132,16 +141,7 @@ describe("CronService store migrations", () => { "utf-8", ); - const cron = new CronService({ - storePath: store.storePath, - cronEnabled: true, - log: noopLogger, - enqueueSystemEvent: vi.fn(), - requestHeartbeatNow: vi.fn(), - runIsolatedAgentJob: vi.fn(async () => ({ status: "ok" as const, summary: "ok" })), - }); - - await cron.start(); + const cron = await createStartedCron(store.storePath).start(); const jobs = await cron.list({ includeDisabled: true }); const job = jobs.find((entry) => entry.id === "legacy-agentturn-no-timeout");