From d3a36cc3b070ac48f55960989dd9ecc9ef42db95 Mon Sep 17 00:00:00 2001 From: cpojer Date: Tue, 17 Feb 2026 10:12:49 +0900 Subject: [PATCH] chore: Fix remaining extension test types, enable type checking for extension tests. --- .../diagnostics-otel/src/service.test.ts | 9 ++- extensions/feishu/src/bot.test.ts | 32 ++++++++-- .../src/monitor.webhook-routing.test.ts | 6 +- .../googlechat/src/resolve-target.test.ts | 6 ++ extensions/irc/src/onboarding.test.ts | 16 ++++- extensions/irc/src/policy.test.ts | 2 +- extensions/line/src/channel.logout.test.ts | 45 +++++++++++++- .../line/src/channel.sendPayload.test.ts | 20 ++++--- extensions/line/src/channel.startup.test.ts | 60 ++++++++++++++----- tsconfig.json | 2 +- 10 files changed, 161 insertions(+), 37 deletions(-) diff --git a/extensions/diagnostics-otel/src/service.test.ts b/extensions/diagnostics-otel/src/service.test.ts index c379dc7a9f..ea32fc3ea5 100644 --- a/extensions/diagnostics-otel/src/service.test.ts +++ b/extensions/diagnostics-otel/src/service.test.ts @@ -105,6 +105,7 @@ vi.mock("openclaw/plugin-sdk", async () => { }); import { emitDiagnosticEvent } from "openclaw/plugin-sdk"; +import type { OpenClawPluginServiceContext } from "openclaw/plugin-sdk"; import { createDiagnosticsOtelService } from "./service.js"; describe("diagnostics-otel service", () => { @@ -130,7 +131,7 @@ describe("diagnostics-otel service", () => { }); const service = createDiagnosticsOtelService(); - await service.start({ + const ctx: OpenClawPluginServiceContext = { config: { diagnostics: { enabled: true, @@ -150,7 +151,9 @@ describe("diagnostics-otel service", () => { error: vi.fn(), debug: vi.fn(), }, - }); + stateDir: "/tmp/openclaw-diagnostics-otel-test", + }; + await service.start(ctx); emitDiagnosticEvent({ type: "webhook.received", @@ -222,6 +225,6 @@ describe("diagnostics-otel service", () => { }); expect(logEmit).toHaveBeenCalled(); - await service.stop?.(); + await service.stop?.(ctx); }); }); diff --git a/extensions/feishu/src/bot.test.ts b/extensions/feishu/src/bot.test.ts index 63a2af835c..b9cd691cbb 100644 --- a/extensions/feishu/src/bot.test.ts +++ b/extensions/feishu/src/bot.test.ts @@ -99,7 +99,13 @@ describe("handleFeishuMessage command authorization", () => { await handleFeishuMessage({ cfg, event, - runtime: { log: vi.fn(), error: vi.fn() } as RuntimeEnv, + runtime: { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + } as RuntimeEnv, }); expect(mockResolveCommandAuthorizedFromAuthorizers).toHaveBeenCalledWith({ @@ -148,7 +154,13 @@ describe("handleFeishuMessage command authorization", () => { await handleFeishuMessage({ cfg, event, - runtime: { log: vi.fn(), error: vi.fn() } as RuntimeEnv, + runtime: { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + } as RuntimeEnv, }); expect(mockReadAllowFromStore).toHaveBeenCalledWith("feishu"); @@ -189,7 +201,13 @@ describe("handleFeishuMessage command authorization", () => { await handleFeishuMessage({ cfg, event, - runtime: { log: vi.fn(), error: vi.fn() } as RuntimeEnv, + runtime: { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + } as RuntimeEnv, }); expect(mockUpsertPairingRequest).toHaveBeenCalledWith({ @@ -247,7 +265,13 @@ describe("handleFeishuMessage command authorization", () => { await handleFeishuMessage({ cfg, event, - runtime: { log: vi.fn(), error: vi.fn() } as RuntimeEnv, + runtime: { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + } as RuntimeEnv, }); expect(mockResolveCommandAuthorizedFromAuthorizers).toHaveBeenCalledWith({ diff --git a/extensions/googlechat/src/monitor.webhook-routing.test.ts b/extensions/googlechat/src/monitor.webhook-routing.test.ts index 31a9181eae..adf21bf98b 100644 --- a/extensions/googlechat/src/monitor.webhook-routing.test.ts +++ b/extensions/googlechat/src/monitor.webhook-routing.test.ts @@ -16,7 +16,10 @@ function createWebhookRequest(params: { payload: unknown; path?: string; }): IncomingMessage { - const req = new EventEmitter() as IncomingMessage & { destroyed?: boolean; destroy: () => void }; + const req = new EventEmitter() as IncomingMessage & { + destroyed?: boolean; + destroy: (error?: Error) => IncomingMessage; + }; req.method = "POST"; req.url = params.path ?? "/googlechat"; req.headers = { @@ -26,6 +29,7 @@ function createWebhookRequest(params: { req.destroyed = false; req.destroy = () => { req.destroyed = true; + return req; }; void Promise.resolve().then(() => { diff --git a/extensions/googlechat/src/resolve-target.test.ts b/extensions/googlechat/src/resolve-target.test.ts index 6173ed4be9..d4b53036f1 100644 --- a/extensions/googlechat/src/resolve-target.test.ts +++ b/extensions/googlechat/src/resolve-target.test.ts @@ -79,6 +79,9 @@ describe("googlechat resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("spaces/AAA"); }); @@ -90,6 +93,9 @@ describe("googlechat resolveTarget", () => { }); expect(result.ok).toBe(true); + if (!result.ok) { + throw result.error; + } expect(result.to).toBe("users/user@example.com"); }); diff --git a/extensions/irc/src/onboarding.test.ts b/extensions/irc/src/onboarding.test.ts index aef7fe8d41..e0493f270c 100644 --- a/extensions/irc/src/onboarding.test.ts +++ b/extensions/irc/src/onboarding.test.ts @@ -3,13 +3,21 @@ import { describe, expect, it, vi } from "vitest"; import { ircOnboardingAdapter } from "./onboarding.js"; import type { CoreConfig } from "./types.js"; +const selectFirstOption = async (params: { options: Array<{ value: T }> }): Promise => { + const first = params.options[0]; + if (!first) { + throw new Error("no options"); + } + return first.value; +}; + describe("irc onboarding", () => { it("configures host and nick via onboarding prompts", async () => { const prompter: WizardPrompter = { intro: vi.fn(async () => {}), outro: vi.fn(async () => {}), note: vi.fn(async () => {}), - select: vi.fn(async () => "allowlist"), + select: selectFirstOption as WizardPrompter["select"], multiselect: vi.fn(async () => []), text: vi.fn(async ({ message }: { message: string }) => { if (message === "IRC server host") { @@ -50,7 +58,9 @@ describe("irc onboarding", () => { const runtime: RuntimeEnv = { log: vi.fn(), error: vi.fn(), - exit: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), }; const result = await ircOnboardingAdapter.configure({ @@ -78,7 +88,7 @@ describe("irc onboarding", () => { intro: vi.fn(async () => {}), outro: vi.fn(async () => {}), note: vi.fn(async () => {}), - select: vi.fn(async () => "allowlist"), + select: selectFirstOption as WizardPrompter["select"], multiselect: vi.fn(async () => []), text: vi.fn(async ({ message }: { message: string }) => { if (message === "IRC allowFrom (nick or nick!user@host)") { diff --git a/extensions/irc/src/policy.test.ts b/extensions/irc/src/policy.test.ts index cd617c8619..be3f65e617 100644 --- a/extensions/irc/src/policy.test.ts +++ b/extensions/irc/src/policy.test.ts @@ -127,6 +127,6 @@ describe("irc policy", () => { groupIdCaseInsensitive: true, }); expect(sharedDisabled.allowed).toBe(inboundDisabled.allowed); - expect(sharedDisabled.groupConfig?.enabled).toBe(inboundDisabled.groupConfig?.enabled); + expect(inboundDisabled.groupConfig?.enabled).toBe(false); }); }); diff --git a/extensions/line/src/channel.logout.test.ts b/extensions/line/src/channel.logout.test.ts index 44642d7cac..dbceacee7d 100644 --- a/extensions/line/src/channel.logout.test.ts +++ b/extensions/line/src/channel.logout.test.ts @@ -1,4 +1,9 @@ -import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; +import type { + OpenClawConfig, + PluginRuntime, + ResolvedLineAccount, + RuntimeEnv, +} from "openclaw/plugin-sdk"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { linePlugin } from "./channel.js"; import { setLineRuntime } from "./runtime.js"; @@ -59,10 +64,27 @@ describe("linePlugin gateway.logoutAccount", () => { }, }, }; + const runtimeEnv: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + }; + const resolveAccount = mocks.resolveLineAccount as unknown as (params: { + cfg: OpenClawConfig; + accountId?: string; + }) => ResolvedLineAccount; + const account = resolveAccount({ + cfg, + accountId: DEFAULT_ACCOUNT_ID, + }); - const result = await linePlugin.gateway.logoutAccount({ + const result = await linePlugin.gateway!.logoutAccount!({ accountId: DEFAULT_ACCOUNT_ID, cfg, + account, + runtime: runtimeEnv, }); expect(result.cleared).toBe(true); @@ -86,10 +108,27 @@ describe("linePlugin gateway.logoutAccount", () => { }, }, }; + const runtimeEnv: RuntimeEnv = { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + }; + const resolveAccount = mocks.resolveLineAccount as unknown as (params: { + cfg: OpenClawConfig; + accountId?: string; + }) => ResolvedLineAccount; + const account = resolveAccount({ + cfg, + accountId: "primary", + }); - const result = await linePlugin.gateway.logoutAccount({ + const result = await linePlugin.gateway!.logoutAccount!({ accountId: "primary", cfg, + account, + runtime: runtimeEnv, }); expect(result.cleared).toBe(true); diff --git a/extensions/line/src/channel.sendPayload.test.ts b/extensions/line/src/channel.sendPayload.test.ts index 94bbe9e8c4..3f91f27c51 100644 --- a/extensions/line/src/channel.sendPayload.test.ts +++ b/extensions/line/src/channel.sendPayload.test.ts @@ -105,8 +105,9 @@ describe("linePlugin outbound.sendPayload", () => { }, }; - await linePlugin.outbound.sendPayload({ + await linePlugin.outbound!.sendPayload!({ to: "line:group:1", + text: payload.text, payload, accountId: "default", cfg, @@ -140,8 +141,9 @@ describe("linePlugin outbound.sendPayload", () => { }, }; - await linePlugin.outbound.sendPayload({ + await linePlugin.outbound!.sendPayload!({ to: "line:user:1", + text: payload.text, payload, accountId: "default", cfg, @@ -172,8 +174,9 @@ describe("linePlugin outbound.sendPayload", () => { }, }; - await linePlugin.outbound.sendPayload({ + await linePlugin.outbound!.sendPayload!({ to: "line:user:2", + text: "", payload, accountId: "default", cfg, @@ -210,8 +213,9 @@ describe("linePlugin outbound.sendPayload", () => { }, }; - await linePlugin.outbound.sendPayload({ + await linePlugin.outbound!.sendPayload!({ to: "line:user:3", + text: payload.text, payload, accountId: "default", cfg, @@ -250,8 +254,9 @@ describe("linePlugin outbound.sendPayload", () => { }, }; - await linePlugin.outbound.sendPayload({ + await linePlugin.outbound!.sendPayload!({ to: "line:user:3", + text: payload.text, payload, accountId: "primary", cfg, @@ -266,7 +271,8 @@ describe("linePlugin outbound.sendPayload", () => { describe("linePlugin config.formatAllowFrom", () => { it("strips line:user: prefixes without lowercasing", () => { - const formatted = linePlugin.config.formatAllowFrom({ + const formatted = linePlugin.config.formatAllowFrom!({ + cfg: {} as OpenClawConfig, allowFrom: ["line:user:UABC", "line:UDEF"], }); expect(formatted).toEqual(["UABC", "UDEF"]); @@ -295,7 +301,7 @@ describe("linePlugin groups.resolveRequireMention", () => { }, } as OpenClawConfig; - const requireMention = linePlugin.groups.resolveRequireMention({ + const requireMention = linePlugin.groups!.resolveRequireMention!({ cfg, accountId: "primary", groupId: "group-1", diff --git a/extensions/line/src/channel.startup.test.ts b/extensions/line/src/channel.startup.test.ts index fa04e6ca6d..abd1aedf17 100644 --- a/extensions/line/src/channel.startup.test.ts +++ b/extensions/line/src/channel.startup.test.ts @@ -1,4 +1,11 @@ -import type { OpenClawConfig, PluginRuntime } from "openclaw/plugin-sdk"; +import type { + ChannelGatewayContext, + ChannelAccountSnapshot, + OpenClawConfig, + PluginRuntime, + ResolvedLineAccount, + RuntimeEnv, +} from "openclaw/plugin-sdk"; import { describe, expect, it, vi } from "vitest"; import { linePlugin } from "./channel.js"; import { setLineRuntime } from "./runtime.js"; @@ -26,18 +33,43 @@ function createRuntime() { return { runtime, probeLineBot, monitorLineProvider }; } -function createStartAccountCtx(params: { token: string; secret: string; runtime: unknown }) { +function createRuntimeEnv(): RuntimeEnv { return { + log: vi.fn(), + error: vi.fn(), + exit: vi.fn((code: number): never => { + throw new Error(`exit ${code}`); + }), + }; +} + +function createStartAccountCtx(params: { + token: string; + secret: string; + runtime: RuntimeEnv; +}): ChannelGatewayContext { + const snapshot: ChannelAccountSnapshot = { + accountId: "default", + configured: true, + enabled: true, + running: false, + }; + return { + accountId: "default", account: { accountId: "default", + enabled: true, channelAccessToken: params.token, channelSecret: params.secret, - config: {}, + tokenSource: "config" as const, + config: {} as ResolvedLineAccount["config"], }, cfg: {} as OpenClawConfig, runtime: params.runtime, - abortSignal: undefined, - log: { info: vi.fn(), debug: vi.fn() }, + abortSignal: new AbortController().signal, + log: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() }, + getStatus: () => snapshot, + setStatus: vi.fn(), }; } @@ -47,12 +79,12 @@ describe("linePlugin gateway.startAccount", () => { setLineRuntime(runtime); await expect( - linePlugin.gateway.startAccount( + linePlugin.gateway!.startAccount!( createStartAccountCtx({ token: "token", secret: " ", - runtime: {}, - }) as never, + runtime: createRuntimeEnv(), + }), ), ).rejects.toThrow( 'LINE webhook mode requires a non-empty channel secret for account "default".', @@ -65,12 +97,12 @@ describe("linePlugin gateway.startAccount", () => { setLineRuntime(runtime); await expect( - linePlugin.gateway.startAccount( + linePlugin.gateway!.startAccount!( createStartAccountCtx({ token: " ", secret: "secret", - runtime: {}, - }) as never, + runtime: createRuntimeEnv(), + }), ), ).rejects.toThrow( 'LINE webhook mode requires a non-empty channel access token for account "default".', @@ -82,12 +114,12 @@ describe("linePlugin gateway.startAccount", () => { const { runtime, monitorLineProvider } = createRuntime(); setLineRuntime(runtime); - await linePlugin.gateway.startAccount( + await linePlugin.gateway!.startAccount!( createStartAccountCtx({ token: "token", secret: "secret", - runtime: {}, - }) as never, + runtime: createRuntimeEnv(), + }), ); expect(monitorLineProvider).toHaveBeenCalledWith( diff --git a/tsconfig.json b/tsconfig.json index d2ec3b5948..ab588f96dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,5 +25,5 @@ } }, "include": ["src/**/*", "ui/**/*", "extensions/**/*"], - "exclude": ["node_modules", "dist", "src/**/*.test.ts", "extensions/**/*.test.ts"] + "exclude": ["node_modules", "dist", "src/**/*.test.ts"] }