diff --git a/extensions/discord/src/channel.contract.test.ts b/extensions/discord/src/channel.contract.test.ts deleted file mode 100644 index 3a651e6e26b..00000000000 --- a/extensions/discord/src/channel.contract.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { OpenClawConfig } from "openclaw/plugin-sdk/discord"; -import { afterEach, describe, vi } from "vitest"; -import { installChannelActionsContractSuite } from "../../../src/test-utils/channel-actions-contract.js"; -import { installChannelPluginContractSuite } from "../../../src/test-utils/channel-plugin-contract.js"; - -const discordListActionsMock = vi.fn(); -const discordGetCapabilitiesMock = vi.fn(); - -vi.mock("./runtime.js", () => ({ - getDiscordRuntime: () => ({ - channel: { - discord: { - messageActions: { - listActions: discordListActionsMock, - getCapabilities: discordGetCapabilitiesMock, - }, - }, - }, - }), -})); - -const { discordPlugin } = await import("./channel.js"); - -describe("discordPlugin contract", () => { - afterEach(() => { - discordListActionsMock.mockReset(); - discordGetCapabilitiesMock.mockReset(); - }); - - installChannelPluginContractSuite({ - plugin: discordPlugin, - }); - - installChannelActionsContractSuite({ - plugin: discordPlugin, - cases: [ - { - name: "forwards runtime-backed Discord actions and capabilities", - cfg: {} as OpenClawConfig, - expectedActions: ["send", "react", "poll"], - expectedCapabilities: ["interactive", "components"], - beforeTest: () => { - discordListActionsMock.mockReturnValue(["send", "react", "poll"]); - discordGetCapabilitiesMock.mockReturnValue(["interactive", "components"]); - }, - }, - ], - }); -}); diff --git a/extensions/mattermost/src/channel.contract.test.ts b/extensions/mattermost/src/channel.contract.test.ts deleted file mode 100644 index 96f5fe9ed4a..00000000000 --- a/extensions/mattermost/src/channel.contract.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import type { OpenClawConfig } from "openclaw/plugin-sdk/mattermost"; -import { describe } from "vitest"; -import { installChannelActionsContractSuite } from "../../../src/test-utils/channel-actions-contract.js"; -import { installChannelPluginContractSuite } from "../../../src/test-utils/channel-plugin-contract.js"; -import { mattermostPlugin } from "./channel.js"; - -describe("mattermostPlugin contract", () => { - installChannelPluginContractSuite({ - plugin: mattermostPlugin, - }); - - installChannelActionsContractSuite({ - plugin: mattermostPlugin, - unsupportedAction: "poll", - cases: [ - { - name: "configured account exposes send and react", - cfg: { - channels: { - mattermost: { - enabled: true, - botToken: "test-token", - baseUrl: "https://chat.example.com", - }, - }, - } as OpenClawConfig, - expectedActions: ["send", "react"], - expectedCapabilities: ["buttons"], - }, - { - name: "reactions can be disabled while send stays available", - cfg: { - channels: { - mattermost: { - enabled: true, - botToken: "test-token", - baseUrl: "https://chat.example.com", - actions: { reactions: false }, - }, - }, - } as OpenClawConfig, - expectedActions: ["send"], - expectedCapabilities: ["buttons"], - }, - { - name: "missing bot credentials disables the actions surface", - cfg: { - channels: { - mattermost: { - enabled: true, - }, - }, - } as OpenClawConfig, - expectedActions: [], - expectedCapabilities: [], - }, - ], - }); -}); diff --git a/extensions/slack/src/channel.contract.test.ts b/extensions/slack/src/channel.contract.test.ts deleted file mode 100644 index 3fd0e23dab5..00000000000 --- a/extensions/slack/src/channel.contract.test.ts +++ /dev/null @@ -1,85 +0,0 @@ -import type { OpenClawConfig } from "openclaw/plugin-sdk/slack"; -import { describe } from "vitest"; -import { installChannelActionsContractSuite } from "../../../src/test-utils/channel-actions-contract.js"; -import { installChannelPluginContractSuite } from "../../../src/test-utils/channel-plugin-contract.js"; -import { slackPlugin } from "./channel.js"; - -describe("slackPlugin contract", () => { - installChannelPluginContractSuite({ - plugin: slackPlugin, - }); - - installChannelActionsContractSuite({ - plugin: slackPlugin, - unsupportedAction: "poll", - cases: [ - { - name: "configured account exposes default Slack actions", - cfg: { - channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", - }, - }, - } as OpenClawConfig, - expectedActions: [ - "send", - "react", - "reactions", - "read", - "edit", - "delete", - "download-file", - "pin", - "unpin", - "list-pins", - "member-info", - "emoji-list", - ], - expectedCapabilities: ["blocks"], - }, - { - name: "interactive replies add the shared interactive capability", - cfg: { - channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", - capabilities: { - interactiveReplies: true, - }, - }, - }, - } as OpenClawConfig, - expectedActions: [ - "send", - "react", - "reactions", - "read", - "edit", - "delete", - "download-file", - "pin", - "unpin", - "list-pins", - "member-info", - "emoji-list", - ], - expectedCapabilities: ["blocks", "interactive"], - }, - { - name: "missing tokens disables the actions surface", - cfg: { - channels: { - slack: { - enabled: true, - }, - }, - } as OpenClawConfig, - expectedActions: [], - expectedCapabilities: [], - }, - ], - }); -}); diff --git a/extensions/telegram/src/channel.contract.test.ts b/extensions/telegram/src/channel.contract.test.ts deleted file mode 100644 index 164f862949d..00000000000 --- a/extensions/telegram/src/channel.contract.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { OpenClawConfig } from "openclaw/plugin-sdk/telegram"; -import { afterEach, describe, vi } from "vitest"; -import { installChannelActionsContractSuite } from "../../../src/test-utils/channel-actions-contract.js"; -import { installChannelPluginContractSuite } from "../../../src/test-utils/channel-plugin-contract.js"; - -const telegramListActionsMock = vi.fn(); -const telegramGetCapabilitiesMock = vi.fn(); - -vi.mock("./runtime.js", () => ({ - getTelegramRuntime: () => ({ - channel: { - telegram: { - messageActions: { - listActions: telegramListActionsMock, - getCapabilities: telegramGetCapabilitiesMock, - }, - }, - }, - }), -})); - -const { telegramPlugin } = await import("./channel.js"); - -describe("telegramPlugin contract", () => { - afterEach(() => { - telegramListActionsMock.mockReset(); - telegramGetCapabilitiesMock.mockReset(); - }); - - installChannelPluginContractSuite({ - plugin: telegramPlugin, - }); - - installChannelActionsContractSuite({ - plugin: telegramPlugin, - cases: [ - { - name: "forwards runtime-backed Telegram actions and capabilities", - cfg: {} as OpenClawConfig, - expectedActions: ["send", "poll", "react"], - expectedCapabilities: ["interactive", "buttons"], - beforeTest: () => { - telegramListActionsMock.mockReturnValue(["send", "poll", "react"]); - telegramGetCapabilitiesMock.mockReturnValue(["interactive", "buttons"]); - }, - }, - ], - }); -}); diff --git a/src/test-utils/channel-actions-contract.ts b/src/test-utils/channel-actions-contract.ts deleted file mode 100644 index 12d7d7046f5..00000000000 --- a/src/test-utils/channel-actions-contract.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { expect, it } from "vitest"; -import type { ChannelMessageCapability } from "../channels/plugins/message-capabilities.js"; -import type { ChannelMessageActionName, ChannelPlugin } from "../channels/plugins/types.js"; -import type { OpenClawConfig } from "../config/config.js"; - -type ChannelActionsContractCase = { - name: string; - cfg: OpenClawConfig; - expectedActions: readonly ChannelMessageActionName[]; - expectedCapabilities?: readonly ChannelMessageCapability[]; - beforeTest?: () => void; -}; - -export function installChannelActionsContractSuite(params: { - plugin: Pick; - cases: readonly ChannelActionsContractCase[]; - unsupportedAction?: ChannelMessageActionName; -}) { - it("exposes the base message actions contract", () => { - expect(params.plugin.actions).toBeDefined(); - expect(typeof params.plugin.actions?.listActions).toBe("function"); - }); - - for (const testCase of params.cases) { - it(`actions contract: ${testCase.name}`, () => { - testCase.beforeTest?.(); - - const actions = params.plugin.actions?.listActions?.({ cfg: testCase.cfg }) ?? []; - const capabilities = params.plugin.actions?.getCapabilities?.({ cfg: testCase.cfg }) ?? []; - - expect(actions).toEqual([...new Set(actions)]); - expect(capabilities).toEqual([...new Set(capabilities)]); - expect(actions.toSorted()).toEqual([...testCase.expectedActions].toSorted()); - expect(capabilities.toSorted()).toEqual( - [...(testCase.expectedCapabilities ?? [])].toSorted(), - ); - - if (params.plugin.actions?.supportsAction) { - for (const action of testCase.expectedActions) { - expect(params.plugin.actions.supportsAction({ action })).toBe(true); - } - if ( - params.unsupportedAction && - !testCase.expectedActions.includes(params.unsupportedAction) - ) { - expect(params.plugin.actions.supportsAction({ action: params.unsupportedAction })).toBe( - false, - ); - } - } - }); - } -} diff --git a/src/test-utils/channel-plugin-contract.ts b/src/test-utils/channel-plugin-contract.ts deleted file mode 100644 index b2912befd0b..00000000000 --- a/src/test-utils/channel-plugin-contract.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { expect, it } from "vitest"; -import type { ChannelPlugin } from "../channels/plugins/types.js"; - -export function installChannelPluginContractSuite(params: { - plugin: Pick; -}) { - it("satisfies the base channel plugin contract", () => { - const { plugin } = params; - - expect(typeof plugin.id).toBe("string"); - expect(plugin.id.trim()).not.toBe(""); - - expect(plugin.meta.id).toBe(plugin.id); - expect(plugin.meta.label.trim()).not.toBe(""); - expect(plugin.meta.selectionLabel.trim()).not.toBe(""); - expect(plugin.meta.docsPath).toMatch(/^\/channels\//); - expect(plugin.meta.blurb.trim()).not.toBe(""); - - expect(plugin.capabilities.chatTypes.length).toBeGreaterThan(0); - - expect(typeof plugin.config.listAccountIds).toBe("function"); - expect(typeof plugin.config.resolveAccount).toBe("function"); - }); -}