chore: Fix remaining extension test types, enable type checking for extension tests.

This commit is contained in:
cpojer
2026-02-17 10:12:49 +09:00
parent a741985574
commit d3a36cc3b0
10 changed files with 161 additions and 37 deletions

View File

@@ -105,6 +105,7 @@ vi.mock("openclaw/plugin-sdk", async () => {
}); });
import { emitDiagnosticEvent } from "openclaw/plugin-sdk"; import { emitDiagnosticEvent } from "openclaw/plugin-sdk";
import type { OpenClawPluginServiceContext } from "openclaw/plugin-sdk";
import { createDiagnosticsOtelService } from "./service.js"; import { createDiagnosticsOtelService } from "./service.js";
describe("diagnostics-otel service", () => { describe("diagnostics-otel service", () => {
@@ -130,7 +131,7 @@ describe("diagnostics-otel service", () => {
}); });
const service = createDiagnosticsOtelService(); const service = createDiagnosticsOtelService();
await service.start({ const ctx: OpenClawPluginServiceContext = {
config: { config: {
diagnostics: { diagnostics: {
enabled: true, enabled: true,
@@ -150,7 +151,9 @@ describe("diagnostics-otel service", () => {
error: vi.fn(), error: vi.fn(),
debug: vi.fn(), debug: vi.fn(),
}, },
}); stateDir: "/tmp/openclaw-diagnostics-otel-test",
};
await service.start(ctx);
emitDiagnosticEvent({ emitDiagnosticEvent({
type: "webhook.received", type: "webhook.received",
@@ -222,6 +225,6 @@ describe("diagnostics-otel service", () => {
}); });
expect(logEmit).toHaveBeenCalled(); expect(logEmit).toHaveBeenCalled();
await service.stop?.(); await service.stop?.(ctx);
}); });
}); });

View File

@@ -99,7 +99,13 @@ describe("handleFeishuMessage command authorization", () => {
await handleFeishuMessage({ await handleFeishuMessage({
cfg, cfg,
event, 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({ expect(mockResolveCommandAuthorizedFromAuthorizers).toHaveBeenCalledWith({
@@ -148,7 +154,13 @@ describe("handleFeishuMessage command authorization", () => {
await handleFeishuMessage({ await handleFeishuMessage({
cfg, cfg,
event, 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"); expect(mockReadAllowFromStore).toHaveBeenCalledWith("feishu");
@@ -189,7 +201,13 @@ describe("handleFeishuMessage command authorization", () => {
await handleFeishuMessage({ await handleFeishuMessage({
cfg, cfg,
event, 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({ expect(mockUpsertPairingRequest).toHaveBeenCalledWith({
@@ -247,7 +265,13 @@ describe("handleFeishuMessage command authorization", () => {
await handleFeishuMessage({ await handleFeishuMessage({
cfg, cfg,
event, 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({ expect(mockResolveCommandAuthorizedFromAuthorizers).toHaveBeenCalledWith({

View File

@@ -16,7 +16,10 @@ function createWebhookRequest(params: {
payload: unknown; payload: unknown;
path?: string; path?: string;
}): IncomingMessage { }): 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.method = "POST";
req.url = params.path ?? "/googlechat"; req.url = params.path ?? "/googlechat";
req.headers = { req.headers = {
@@ -26,6 +29,7 @@ function createWebhookRequest(params: {
req.destroyed = false; req.destroyed = false;
req.destroy = () => { req.destroy = () => {
req.destroyed = true; req.destroyed = true;
return req;
}; };
void Promise.resolve().then(() => { void Promise.resolve().then(() => {

View File

@@ -79,6 +79,9 @@ describe("googlechat resolveTarget", () => {
}); });
expect(result.ok).toBe(true); expect(result.ok).toBe(true);
if (!result.ok) {
throw result.error;
}
expect(result.to).toBe("spaces/AAA"); expect(result.to).toBe("spaces/AAA");
}); });
@@ -90,6 +93,9 @@ describe("googlechat resolveTarget", () => {
}); });
expect(result.ok).toBe(true); expect(result.ok).toBe(true);
if (!result.ok) {
throw result.error;
}
expect(result.to).toBe("users/user@example.com"); expect(result.to).toBe("users/user@example.com");
}); });

View File

@@ -3,13 +3,21 @@ import { describe, expect, it, vi } from "vitest";
import { ircOnboardingAdapter } from "./onboarding.js"; import { ircOnboardingAdapter } from "./onboarding.js";
import type { CoreConfig } from "./types.js"; import type { CoreConfig } from "./types.js";
const selectFirstOption = async <T>(params: { options: Array<{ value: T }> }): Promise<T> => {
const first = params.options[0];
if (!first) {
throw new Error("no options");
}
return first.value;
};
describe("irc onboarding", () => { describe("irc onboarding", () => {
it("configures host and nick via onboarding prompts", async () => { it("configures host and nick via onboarding prompts", async () => {
const prompter: WizardPrompter = { const prompter: WizardPrompter = {
intro: vi.fn(async () => {}), intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}), outro: vi.fn(async () => {}),
note: vi.fn(async () => {}), note: vi.fn(async () => {}),
select: vi.fn(async () => "allowlist"), select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []), multiselect: vi.fn(async () => []),
text: vi.fn(async ({ message }: { message: string }) => { text: vi.fn(async ({ message }: { message: string }) => {
if (message === "IRC server host") { if (message === "IRC server host") {
@@ -50,7 +58,9 @@ describe("irc onboarding", () => {
const runtime: RuntimeEnv = { const runtime: RuntimeEnv = {
log: vi.fn(), log: vi.fn(),
error: vi.fn(), error: vi.fn(),
exit: vi.fn(), exit: vi.fn((code: number): never => {
throw new Error(`exit ${code}`);
}),
}; };
const result = await ircOnboardingAdapter.configure({ const result = await ircOnboardingAdapter.configure({
@@ -78,7 +88,7 @@ describe("irc onboarding", () => {
intro: vi.fn(async () => {}), intro: vi.fn(async () => {}),
outro: vi.fn(async () => {}), outro: vi.fn(async () => {}),
note: vi.fn(async () => {}), note: vi.fn(async () => {}),
select: vi.fn(async () => "allowlist"), select: selectFirstOption as WizardPrompter["select"],
multiselect: vi.fn(async () => []), multiselect: vi.fn(async () => []),
text: vi.fn(async ({ message }: { message: string }) => { text: vi.fn(async ({ message }: { message: string }) => {
if (message === "IRC allowFrom (nick or nick!user@host)") { if (message === "IRC allowFrom (nick or nick!user@host)") {

View File

@@ -127,6 +127,6 @@ describe("irc policy", () => {
groupIdCaseInsensitive: true, groupIdCaseInsensitive: true,
}); });
expect(sharedDisabled.allowed).toBe(inboundDisabled.allowed); expect(sharedDisabled.allowed).toBe(inboundDisabled.allowed);
expect(sharedDisabled.groupConfig?.enabled).toBe(inboundDisabled.groupConfig?.enabled); expect(inboundDisabled.groupConfig?.enabled).toBe(false);
}); });
}); });

View File

@@ -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 { beforeEach, describe, expect, it, vi } from "vitest";
import { linePlugin } from "./channel.js"; import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.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, accountId: DEFAULT_ACCOUNT_ID,
cfg, cfg,
account,
runtime: runtimeEnv,
}); });
expect(result.cleared).toBe(true); 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", accountId: "primary",
cfg, cfg,
account,
runtime: runtimeEnv,
}); });
expect(result.cleared).toBe(true); expect(result.cleared).toBe(true);

View File

@@ -105,8 +105,9 @@ describe("linePlugin outbound.sendPayload", () => {
}, },
}; };
await linePlugin.outbound.sendPayload({ await linePlugin.outbound!.sendPayload!({
to: "line:group:1", to: "line:group:1",
text: payload.text,
payload, payload,
accountId: "default", accountId: "default",
cfg, cfg,
@@ -140,8 +141,9 @@ describe("linePlugin outbound.sendPayload", () => {
}, },
}; };
await linePlugin.outbound.sendPayload({ await linePlugin.outbound!.sendPayload!({
to: "line:user:1", to: "line:user:1",
text: payload.text,
payload, payload,
accountId: "default", accountId: "default",
cfg, cfg,
@@ -172,8 +174,9 @@ describe("linePlugin outbound.sendPayload", () => {
}, },
}; };
await linePlugin.outbound.sendPayload({ await linePlugin.outbound!.sendPayload!({
to: "line:user:2", to: "line:user:2",
text: "",
payload, payload,
accountId: "default", accountId: "default",
cfg, cfg,
@@ -210,8 +213,9 @@ describe("linePlugin outbound.sendPayload", () => {
}, },
}; };
await linePlugin.outbound.sendPayload({ await linePlugin.outbound!.sendPayload!({
to: "line:user:3", to: "line:user:3",
text: payload.text,
payload, payload,
accountId: "default", accountId: "default",
cfg, cfg,
@@ -250,8 +254,9 @@ describe("linePlugin outbound.sendPayload", () => {
}, },
}; };
await linePlugin.outbound.sendPayload({ await linePlugin.outbound!.sendPayload!({
to: "line:user:3", to: "line:user:3",
text: payload.text,
payload, payload,
accountId: "primary", accountId: "primary",
cfg, cfg,
@@ -266,7 +271,8 @@ describe("linePlugin outbound.sendPayload", () => {
describe("linePlugin config.formatAllowFrom", () => { describe("linePlugin config.formatAllowFrom", () => {
it("strips line:user: prefixes without lowercasing", () => { 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"], allowFrom: ["line:user:UABC", "line:UDEF"],
}); });
expect(formatted).toEqual(["UABC", "UDEF"]); expect(formatted).toEqual(["UABC", "UDEF"]);
@@ -295,7 +301,7 @@ describe("linePlugin groups.resolveRequireMention", () => {
}, },
} as OpenClawConfig; } as OpenClawConfig;
const requireMention = linePlugin.groups.resolveRequireMention({ const requireMention = linePlugin.groups!.resolveRequireMention!({
cfg, cfg,
accountId: "primary", accountId: "primary",
groupId: "group-1", groupId: "group-1",

View File

@@ -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 { describe, expect, it, vi } from "vitest";
import { linePlugin } from "./channel.js"; import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js"; import { setLineRuntime } from "./runtime.js";
@@ -26,18 +33,43 @@ function createRuntime() {
return { runtime, probeLineBot, monitorLineProvider }; return { runtime, probeLineBot, monitorLineProvider };
} }
function createStartAccountCtx(params: { token: string; secret: string; runtime: unknown }) { function createRuntimeEnv(): RuntimeEnv {
return { 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<ResolvedLineAccount> {
const snapshot: ChannelAccountSnapshot = {
accountId: "default",
configured: true,
enabled: true,
running: false,
};
return {
accountId: "default",
account: { account: {
accountId: "default", accountId: "default",
enabled: true,
channelAccessToken: params.token, channelAccessToken: params.token,
channelSecret: params.secret, channelSecret: params.secret,
config: {}, tokenSource: "config" as const,
config: {} as ResolvedLineAccount["config"],
}, },
cfg: {} as OpenClawConfig, cfg: {} as OpenClawConfig,
runtime: params.runtime, runtime: params.runtime,
abortSignal: undefined, abortSignal: new AbortController().signal,
log: { info: vi.fn(), debug: vi.fn() }, 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); setLineRuntime(runtime);
await expect( await expect(
linePlugin.gateway.startAccount( linePlugin.gateway!.startAccount!(
createStartAccountCtx({ createStartAccountCtx({
token: "token", token: "token",
secret: " ", secret: " ",
runtime: {}, runtime: createRuntimeEnv(),
}) as never, }),
), ),
).rejects.toThrow( ).rejects.toThrow(
'LINE webhook mode requires a non-empty channel secret for account "default".', 'LINE webhook mode requires a non-empty channel secret for account "default".',
@@ -65,12 +97,12 @@ describe("linePlugin gateway.startAccount", () => {
setLineRuntime(runtime); setLineRuntime(runtime);
await expect( await expect(
linePlugin.gateway.startAccount( linePlugin.gateway!.startAccount!(
createStartAccountCtx({ createStartAccountCtx({
token: " ", token: " ",
secret: "secret", secret: "secret",
runtime: {}, runtime: createRuntimeEnv(),
}) as never, }),
), ),
).rejects.toThrow( ).rejects.toThrow(
'LINE webhook mode requires a non-empty channel access token for account "default".', '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(); const { runtime, monitorLineProvider } = createRuntime();
setLineRuntime(runtime); setLineRuntime(runtime);
await linePlugin.gateway.startAccount( await linePlugin.gateway!.startAccount!(
createStartAccountCtx({ createStartAccountCtx({
token: "token", token: "token",
secret: "secret", secret: "secret",
runtime: {}, runtime: createRuntimeEnv(),
}) as never, }),
); );
expect(monitorLineProvider).toHaveBeenCalledWith( expect(monitorLineProvider).toHaveBeenCalledWith(

View File

@@ -25,5 +25,5 @@
} }
}, },
"include": ["src/**/*", "ui/**/*", "extensions/**/*"], "include": ["src/**/*", "ui/**/*", "extensions/**/*"],
"exclude": ["node_modules", "dist", "src/**/*.test.ts", "extensions/**/*.test.ts"] "exclude": ["node_modules", "dist", "src/**/*.test.ts"]
} }