diff --git a/src/plugins/hook-runner-global.test.ts b/src/plugins/hook-runner-global.test.ts index 826fd6f4a0f..ebc8b87ed5b 100644 --- a/src/plugins/hook-runner-global.test.ts +++ b/src/plugins/hook-runner-global.test.ts @@ -5,6 +5,19 @@ async function importHookRunnerGlobalModule() { return import("./hook-runner-global.js"); } +type HookRunnerGlobalModule = Awaited>; +type HookRunner = NonNullable>; + +function expectGlobalHookRunner( + runner: ReturnType, +): HookRunner { + expect(runner).toEqual(expect.objectContaining({ hasHooks: expect.any(Function) })); + if (runner === null) { + throw new Error("Expected global hook runner"); + } + return runner; +} + async function expectGlobalRunnerState(expected: { hasRunner: boolean; registry?: unknown }) { const mod = await importHookRunnerGlobalModule(); expect(mod.getGlobalHookRunner() === null).toBe(!expected.hasRunner); @@ -29,13 +42,16 @@ describe("hook-runner-global", () => { it("preserves the initialized runner across module reloads", async () => { const { modA, registry } = await createInitializedModule(); - expect(modA.getGlobalHookRunner()?.hasHooks("message_received")).toBe(true); + expect(expectGlobalHookRunner(modA.getGlobalHookRunner()).hasHooks("message_received")).toBe( + true, + ); vi.resetModules(); const modB = await expectGlobalRunnerState({ hasRunner: true, registry }); - expect(modB.getGlobalHookRunner()).not.toBeNull(); - expect(modB.getGlobalHookRunner()?.hasHooks("message_received")).toBe(true); + expect(expectGlobalHookRunner(modB.getGlobalHookRunner()).hasHooks("message_received")).toBe( + true, + ); }); it("clears the shared state across module reloads", async () => { diff --git a/src/plugins/lazy-service-module.test.ts b/src/plugins/lazy-service-module.test.ts index f7419a8c06f..0d1cd9c2c9c 100644 --- a/src/plugins/lazy-service-module.test.ts +++ b/src/plugins/lazy-service-module.test.ts @@ -1,6 +1,10 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { defaultLoadOverrideModule, startLazyPluginServiceModule } from "./lazy-service-module.js"; +type LazyPluginServiceHandle = NonNullable< + Awaited> +>; + function createAsyncHookMock() { return vi.fn(async () => {}); } @@ -38,6 +42,16 @@ async function expectLifecycleStarted(params: { }); } +function expectLazyServiceHandle( + handle: Awaited>, +): LazyPluginServiceHandle { + expect(handle).toEqual(expect.objectContaining({ stop: expect.any(Function) })); + if (handle === null) { + throw new Error("Expected lazy plugin service handle"); + } + return handle; +} + describe("startLazyPluginServiceModule", () => { afterEach(() => { delete process.env.OPENCLAW_LAZY_SERVICE_SKIP; @@ -54,8 +68,7 @@ describe("startLazyPluginServiceModule", () => { }); expect(lifecycle.start).toHaveBeenCalledTimes(1); - expect(handle).not.toBeNull(); - await handle?.stop(); + await expectLazyServiceHandle(handle).stop(); expect(lifecycle.stop).toHaveBeenCalledTimes(1); }); diff --git a/src/plugins/loader.test.ts b/src/plugins/loader.test.ts index b59e8be4066..a37dc2e6a4f 100644 --- a/src/plugins/loader.test.ts +++ b/src/plugins/loader.test.ts @@ -94,6 +94,16 @@ import type { PluginSdkResolutionPreference } from "./sdk-alias.js"; let cachedBundledTelegramDir = ""; let cachedBundledMemoryDir = ""; +type GlobalHookRunner = NonNullable>; + +function expectGlobalHookRunner(runner: ReturnType): GlobalHookRunner { + expect(runner).toEqual(expect.objectContaining({ hasHooks: expect.any(Function) })); + if (runner === null) { + throw new Error("Expected global hook runner"); + } + return runner; +} + function createDetachedTaskRuntimeStub(id: string): DetachedTaskLifecycleRuntime { const fail = (name: string): never => { throw new Error(`detached runtime ${id} should not execute ${name} in this test`); @@ -3274,14 +3284,14 @@ module.exports = { id: "throws-after-import", register() {} };`, }; const first = loadOpenClawPlugins(options); - expect(getGlobalHookRunner()).not.toBeNull(); + expectGlobalHookRunner(getGlobalHookRunner()); resetGlobalHookRunner(); expect(getGlobalHookRunner()).toBeNull(); const second = loadOpenClawPlugins(options); expect(second).toBe(first); - expect(getGlobalHookRunner()).not.toBeNull(); + expectGlobalHookRunner(getGlobalHookRunner()); resetGlobalHookRunner(); }); @@ -3322,7 +3332,7 @@ module.exports = { id: "throws-after-import", register() {} };`, }, }); expect(getGlobalPluginRegistry()).toBe(gatewayRegistry); - expect(getGlobalHookRunner()?.hasHooks("subagent_ended")).toBe(true); + expect(expectGlobalHookRunner(getGlobalHookRunner()).hasHooks("subagent_ended")).toBe(true); const defaultRegistry = loadOpenClawPlugins({ workspaceDir: defaultPlugin.dir, @@ -3342,8 +3352,9 @@ module.exports = { id: "throws-after-import", register() {} };`, expect(getActivePluginRegistry()).toBe(defaultRegistry); expect(getGlobalPluginRegistry()).toBe(gatewayRegistry); - expect(getGlobalHookRunner()?.hasHooks("subagent_ended")).toBe(true); - expect(getGlobalHookRunner()?.hasHooks("message_sent")).toBe(false); + const globalHookRunner = expectGlobalHookRunner(getGlobalHookRunner()); + expect(globalHookRunner.hasHooks("subagent_ended")).toBe(true); + expect(globalHookRunner.hasHooks("message_sent")).toBe(false); }); it.each([ diff --git a/src/plugins/stage-bundled-plugin-runtime.test.ts b/src/plugins/stage-bundled-plugin-runtime.test.ts index c9b8dd27d06..a92c189b1df 100644 --- a/src/plugins/stage-bundled-plugin-runtime.test.ts +++ b/src/plugins/stage-bundled-plugin-runtime.test.ts @@ -331,12 +331,14 @@ describe("stageBundledPluginRuntime", () => { ]); const match = commandsModule.matchPluginCommand("/pair now"); - expect(match).not.toBeNull(); - expect(match?.args).toBe("now"); + expect(match).toEqual(expect.objectContaining({ args: "now" })); + if (match === null) { + throw new Error("Expected plugin command match"); + } await expect( commandsModule.executePluginCommand({ - command: match!.command, - args: match?.args, + command: match.command, + args: match.args, }), ).resolves.toEqual({ text: "paired:now" }); });