From 734d0bd6474721f545b8a08f4c0b53fbc84e3cc6 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 13 Mar 2026 16:16:47 -0500 Subject: [PATCH] test: stabilize vitest helper export types --- src/cli/daemon-cli/lifecycle-core.test.ts | 2 ++ .../test-helpers/lifecycle-core-harness.ts | 36 ++++++++++++++----- .../test-helpers/schtasks-base-mocks.ts | 4 +-- src/daemon/test-helpers/schtasks-fixtures.ts | 8 +++-- .../bot-native-commands.test-helpers.ts | 28 +++++++++++---- 5 files changed, 60 insertions(+), 18 deletions(-) diff --git a/src/cli/daemon-cli/lifecycle-core.test.ts b/src/cli/daemon-cli/lifecycle-core.test.ts index 7503e21ae5e..6e86ad0d23a 100644 --- a/src/cli/daemon-cli/lifecycle-core.test.ts +++ b/src/cli/daemon-cli/lifecycle-core.test.ts @@ -46,6 +46,7 @@ describe("runServiceRestart token drift", () => { }); resetLifecycleServiceMocks(); service.readCommand.mockResolvedValue({ + programArguments: [], environment: { OPENCLAW_GATEWAY_TOKEN: "service-token" }, }); stubEmptyGatewayEnv(); @@ -77,6 +78,7 @@ describe("runServiceRestart token drift", () => { }, }); service.readCommand.mockResolvedValue({ + programArguments: [], environment: { OPENCLAW_GATEWAY_TOKEN: "env-token" }, }); vi.stubEnv("OPENCLAW_GATEWAY_TOKEN", "env-token"); diff --git a/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts b/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts index 8e91db61664..6e2a93d5633 100644 --- a/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts +++ b/src/cli/daemon-cli/test-helpers/lifecycle-core-harness.ts @@ -1,16 +1,36 @@ import { vi } from "vitest"; +import type { GatewayService } from "../../../daemon/service.js"; +import type { RuntimeEnv } from "../../../runtime.js"; +import type { MockFn } from "../../../test-utils/vitest-mock-fn.js"; export const runtimeLogs: string[] = []; -export const defaultRuntime = { - log: (message: string) => runtimeLogs.push(message), - error: vi.fn(), - exit: (code: number) => { - throw new Error(`__exit__:${code}`); - }, +type LifecycleRuntimeHarness = RuntimeEnv & { + error: MockFn; + exit: MockFn; }; -export const service = { +type LifecycleServiceHarness = GatewayService & { + install: MockFn; + uninstall: MockFn; + stop: MockFn; + isLoaded: MockFn; + readCommand: MockFn; + readRuntime: MockFn; + restart: MockFn; +}; + +export const defaultRuntime: LifecycleRuntimeHarness = { + log: (...args: unknown[]) => { + runtimeLogs.push(args.map((arg) => String(arg)).join(" ")); + }, + error: vi.fn(), + exit: vi.fn((code: number) => { + throw new Error(`__exit__:${code}`); + }), +}; + +export const service: LifecycleServiceHarness = { label: "TestService", loadedText: "loaded", notLoadedText: "not loaded", @@ -32,7 +52,7 @@ export function resetLifecycleServiceMocks() { service.readCommand.mockClear(); service.restart.mockClear(); service.isLoaded.mockResolvedValue(true); - service.readCommand.mockResolvedValue({ environment: {} }); + service.readCommand.mockResolvedValue({ programArguments: [], environment: {} }); service.restart.mockResolvedValue({ outcome: "completed" }); } diff --git a/src/daemon/test-helpers/schtasks-base-mocks.ts b/src/daemon/test-helpers/schtasks-base-mocks.ts index 48933ecdd1c..e3f0f950482 100644 --- a/src/daemon/test-helpers/schtasks-base-mocks.ts +++ b/src/daemon/test-helpers/schtasks-base-mocks.ts @@ -14,9 +14,9 @@ vi.mock("../schtasks-exec.js", () => ({ })); vi.mock("../../infra/ports.js", () => ({ - inspectPortUsage: (...args: unknown[]) => inspectPortUsage(...args), + inspectPortUsage: (port: number) => inspectPortUsage(port), })); vi.mock("../../process/kill-tree.js", () => ({ - killProcessTree: (...args: unknown[]) => killProcessTree(...args), + killProcessTree: (pid: number, opts?: { graceMs?: number }) => killProcessTree(pid, opts), })); diff --git a/src/daemon/test-helpers/schtasks-fixtures.ts b/src/daemon/test-helpers/schtasks-fixtures.ts index a89d7a0eb2e..4762b7543eb 100644 --- a/src/daemon/test-helpers/schtasks-fixtures.ts +++ b/src/daemon/test-helpers/schtasks-fixtures.ts @@ -2,11 +2,15 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { vi } from "vitest"; +import type { PortUsage } from "../../infra/ports-types.js"; +import type { killProcessTree as killProcessTreeImpl } from "../../process/kill-tree.js"; +import type { MockFn } from "../../test-utils/vitest-mock-fn.js"; export const schtasksResponses: Array<{ code: number; stdout: string; stderr: string }> = []; export const schtasksCalls: string[][] = []; -export const inspectPortUsage = vi.fn(); -export const killProcessTree = vi.fn(); + +export const inspectPortUsage: MockFn<(port: number) => Promise> = vi.fn(); +export const killProcessTree: MockFn = vi.fn(); export async function withWindowsEnv( prefix: string, diff --git a/src/telegram/bot-native-commands.test-helpers.ts b/src/telegram/bot-native-commands.test-helpers.ts index 02f1028becf..eef028c8315 100644 --- a/src/telegram/bot-native-commands.test-helpers.ts +++ b/src/telegram/bot-native-commands.test-helpers.ts @@ -3,12 +3,28 @@ import type { OpenClawConfig } from "../config/config.js"; import type { ChannelGroupPolicy } from "../config/group-policy.js"; import type { TelegramAccountConfig } from "../config/types.js"; import type { RuntimeEnv } from "../runtime.js"; +import type { MockFn } from "../test-utils/vitest-mock-fn.js"; import { registerTelegramNativeCommands } from "./bot-native-commands.js"; type RegisterTelegramNativeCommandsParams = Parameters[0]; type GetPluginCommandSpecsFn = typeof import("../plugins/commands.js").getPluginCommandSpecs; type MatchPluginCommandFn = typeof import("../plugins/commands.js").matchPluginCommand; type ExecutePluginCommandFn = typeof import("../plugins/commands.js").executePluginCommand; +type AnyMock = MockFn<(...args: unknown[]) => unknown>; +type AnyAsyncMock = MockFn<(...args: unknown[]) => Promise>; +type NativeCommandHarness = { + handlers: Record Promise>; + sendMessage: AnyAsyncMock; + setMyCommands: AnyAsyncMock; + log: AnyMock; + bot: { + api: { + setMyCommands: AnyAsyncMock; + sendMessage: AnyAsyncMock; + }; + command: (name: string, handler: (ctx: unknown) => Promise) => void; + }; +}; const pluginCommandMocks = vi.hoisted(() => ({ getPluginCommandSpecs: vi.fn(() => []), @@ -86,12 +102,12 @@ export function createNativeCommandsHarness(params?: { nativeEnabled?: boolean; groupConfig?: Record; resolveGroupPolicy?: () => ChannelGroupPolicy; -}) { +}): NativeCommandHarness { const handlers: Record Promise> = {}; - const sendMessage = vi.fn().mockResolvedValue(undefined); - const setMyCommands = vi.fn().mockResolvedValue(undefined); - const log = vi.fn(); - const bot = { + const sendMessage: AnyAsyncMock = vi.fn(async () => undefined); + const setMyCommands: AnyAsyncMock = vi.fn(async () => undefined); + const log: AnyMock = vi.fn(); + const bot: NativeCommandHarness["bot"] = { api: { setMyCommands, sendMessage, @@ -153,7 +169,7 @@ export function createTelegramGroupCommandContext(params?: { }; } -export function findNotAuthorizedCalls(sendMessage: ReturnType) { +export function findNotAuthorizedCalls(sendMessage: AnyAsyncMock) { return sendMessage.mock.calls.filter( (call) => typeof call[1] === "string" && call[1].includes("not authorized"), );