diff --git a/src/channels/plugins/contracts/plugin-actions.registry-backed.contract.test.ts b/src/channels/plugins/contracts/plugin-actions.registry-backed.contract.test.ts new file mode 100644 index 00000000000..761e8ed65c1 --- /dev/null +++ b/src/channels/plugins/contracts/plugin-actions.registry-backed.contract.test.ts @@ -0,0 +1,21 @@ +import { describe } from "vitest"; +import { getActionContractRegistry, getPluginContractRegistry } from "./registry.js"; +import { installChannelActionsContractSuite, installChannelPluginContractSuite } from "./suites.js"; + +for (const entry of getPluginContractRegistry()) { + describe(`${entry.id} plugin contract`, () => { + installChannelPluginContractSuite({ + plugin: entry.plugin, + }); + }); +} + +for (const entry of getActionContractRegistry()) { + describe(`${entry.id} actions contract`, () => { + installChannelActionsContractSuite({ + plugin: entry.plugin, + cases: entry.cases as never, + unsupportedAction: entry.unsupportedAction as never, + }); + }); +} diff --git a/src/channels/plugins/contracts/registry-backed.contract.test.ts b/src/channels/plugins/contracts/registry-backed.contract.test.ts deleted file mode 100644 index b5939c5fa78..00000000000 --- a/src/channels/plugins/contracts/registry-backed.contract.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { describeChannelRegistryBackedContracts } from "../../../../test/helpers/channels/registry-backed-contract.js"; -import { - actionContractRegistry, - directoryContractRegistry, - pluginContractRegistry, - setupContractRegistry, - statusContractRegistry, - surfaceContractRegistry, - threadingContractRegistry, -} from "./registry.js"; - -const registryIds = new Set([ - ...pluginContractRegistry.map((entry) => entry.id), - ...actionContractRegistry.map((entry) => entry.id), - ...setupContractRegistry.map((entry) => entry.id), - ...statusContractRegistry.map((entry) => entry.id), - ...surfaceContractRegistry.map((entry) => entry.id), - ...threadingContractRegistry.map((entry) => entry.id), - ...directoryContractRegistry.map((entry) => entry.id), -]); - -for (const id of [...registryIds].toSorted()) { - describeChannelRegistryBackedContracts(id); -} diff --git a/src/channels/plugins/contracts/registry.ts b/src/channels/plugins/contracts/registry.ts index 8234f05e3e6..aa679df78a0 100644 --- a/src/channels/plugins/contracts/registry.ts +++ b/src/channels/plugins/contracts/registry.ts @@ -123,401 +123,434 @@ vi.mock(buildBundledPluginModuleId("matrix", "runtime-api.js"), async () => { }; }); -export const pluginContractRegistry: PluginContractEntry[] = listBundledChannelPlugins().map( - (plugin) => ({ +let pluginContractRegistryCache: PluginContractEntry[] | undefined; +let actionContractRegistryCache: ActionsContractEntry[] | undefined; +let setupContractRegistryCache: SetupContractEntry[] | undefined; +let statusContractRegistryCache: StatusContractEntry[] | undefined; +let surfaceContractRegistryCache: SurfaceContractEntry[] | undefined; +let threadingContractRegistryCache: ThreadingContractEntry[] | undefined; +let directoryContractRegistryCache: DirectoryContractEntry[] | undefined; + +export function getPluginContractRegistry(): PluginContractEntry[] { + pluginContractRegistryCache ??= listBundledChannelPlugins().map((plugin) => ({ id: plugin.id, plugin, - }), -); + })); + return pluginContractRegistryCache; +} -export const actionContractRegistry: ActionsContractEntry[] = [ - { - id: "slack", - plugin: requireBundledChannelPlugin("slack"), - 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", - "upload-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, +export function getActionContractRegistry(): ActionsContractEntry[] { + actionContractRegistryCache ??= [ + { + id: "slack", + plugin: requireBundledChannelPlugin("slack"), + 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", - "upload-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: [], - }, - ], - }, - { - id: "mattermost", - plugin: requireBundledChannelPlugin("mattermost"), - 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: [], - }, - ], - }, - { - id: "telegram", - plugin: requireBundledChannelPlugin("telegram"), - cases: [ - { - name: "exposes configured Telegram actions and capabilities", - cfg: { - channels: { - telegram: { - botToken: "123:telegram-test-token", - }, - }, - } as OpenClawConfig, - expectedActions: ["send", "poll", "react", "delete", "edit", "topic-create", "topic-edit"], - expectedCapabilities: ["interactive", "buttons"], - }, - ], - }, - { - id: "discord", - plugin: requireBundledChannelPlugin("discord"), - cases: [ - { - name: "describes configured Discord actions and capabilities", - cfg: { - channels: { - discord: { - token: "Bot token-main", - actions: { - polls: true, - reactions: true, - permissions: false, - messages: false, - pins: false, - threads: false, - search: false, - stickers: false, - memberInfo: false, - roleInfo: false, - emojiUploads: false, - stickerUploads: false, - channelInfo: false, - channels: false, - voiceStatus: false, - events: false, - roles: false, - moderation: false, - presence: false, + } as OpenClawConfig, + expectedActions: [ + "send", + "react", + "reactions", + "read", + "edit", + "delete", + "download-file", + "upload-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", "poll", "react", "reactions", "emoji-list"], - expectedCapabilities: ["interactive", "components"], - }, - ], - }, -]; - -export const setupContractRegistry: SetupContractEntry[] = [ - { - id: "slack", - plugin: requireBundledChannelPlugin("slack"), - cases: [ - { - name: "default account stores tokens and enables the channel", - cfg: {} as OpenClawConfig, - input: { - botToken: "xoxb-test", - appToken: "xapp-test", + } as OpenClawConfig, + expectedActions: [ + "send", + "react", + "reactions", + "read", + "edit", + "delete", + "download-file", + "upload-file", + "pin", + "unpin", + "list-pins", + "member-info", + "emoji-list", + ], + expectedCapabilities: ["blocks", "interactive"], }, - expectedAccountId: "default", - assertPatchedConfig: (cfg) => { - expect(cfg.channels?.slack?.enabled).toBe(true); - expect(cfg.channels?.slack?.botToken).toBe("xoxb-test"); - expect(cfg.channels?.slack?.appToken).toBe("xapp-test"); - }, - }, - { - name: "non-default env setup is rejected", - cfg: {} as OpenClawConfig, - accountId: "ops", - input: { - useEnv: true, - }, - expectedAccountId: "ops", - expectedValidation: "Slack env tokens can only be used for the default account.", - }, - ], - }, - { - id: "mattermost", - plugin: requireBundledChannelPlugin("mattermost"), - cases: [ - { - name: "default account stores token and normalized base URL", - cfg: {} as OpenClawConfig, - input: { - botToken: "test-token", - httpUrl: "https://chat.example.com/", - }, - expectedAccountId: "default", - assertPatchedConfig: (cfg) => { - expect(cfg.channels?.mattermost?.enabled).toBe(true); - expect(cfg.channels?.mattermost?.botToken).toBe("test-token"); - expect(cfg.channels?.mattermost?.baseUrl).toBe("https://chat.example.com"); - }, - }, - { - name: "missing credentials are rejected", - cfg: {} as OpenClawConfig, - input: { - httpUrl: "", - }, - expectedAccountId: "default", - expectedValidation: "Mattermost requires --bot-token and --http-url (or --use-env).", - }, - ], - }, - { - id: "line", - plugin: requireBundledChannelPlugin("line"), - cases: [ - { - name: "default account stores token and secret", - cfg: {} as OpenClawConfig, - input: { - channelAccessToken: "line-token", - channelSecret: "line-secret", - }, - expectedAccountId: "default", - assertPatchedConfig: (cfg) => { - expect(cfg.channels?.line?.enabled).toBe(true); - expect(cfg.channels?.line?.channelAccessToken).toBe("line-token"); - expect(cfg.channels?.line?.channelSecret).toBe("line-secret"); - }, - }, - { - name: "non-default env setup is rejected", - cfg: {} as OpenClawConfig, - accountId: "ops", - input: { - useEnv: true, - }, - expectedAccountId: "ops", - expectedValidation: "LINE_CHANNEL_ACCESS_TOKEN can only be used for the default account.", - }, - ], - }, -]; - -export const statusContractRegistry: StatusContractEntry[] = [ - { - id: "slack", - plugin: requireBundledChannelPlugin("slack"), - cases: [ - { - name: "configured account produces a configured status snapshot", - cfg: { - channels: { - slack: { - botToken: "xoxb-test", - appToken: "xapp-test", + { + name: "missing tokens disables the actions surface", + cfg: { + channels: { + slack: { + enabled: true, + }, }, - }, - } as OpenClawConfig, - runtime: { - accountId: "default", - connected: true, - running: true, + } as OpenClawConfig, + expectedActions: [], + expectedCapabilities: [], }, - probe: { ok: true }, - assertSnapshot: (snapshot) => { - expect(snapshot.accountId).toBe("default"); - expect(snapshot.enabled).toBe(true); - expect(snapshot.configured).toBe(true); - }, - }, - ], - }, - { - id: "mattermost", - plugin: requireBundledChannelPlugin("mattermost"), - cases: [ - { - name: "configured account preserves connectivity details in the snapshot", - cfg: { - channels: { - mattermost: { - enabled: true, - botToken: "test-token", - baseUrl: "https://chat.example.com", + ], + }, + { + id: "mattermost", + plugin: requireBundledChannelPlugin("mattermost"), + 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, - runtime: { - accountId: "default", - connected: true, - lastConnectedAt: 1234, + } as OpenClawConfig, + expectedActions: ["send", "react"], + expectedCapabilities: ["buttons"], }, - probe: { ok: true }, - assertSnapshot: (snapshot) => { - expect(snapshot.accountId).toBe("default"); - expect(snapshot.enabled).toBe(true); - expect(snapshot.configured).toBe(true); - expect(snapshot.connected).toBe(true); - expect(snapshot.baseUrl).toBe("https://chat.example.com"); - }, - }, - ], - }, - { - id: "line", - plugin: requireBundledChannelPlugin("line"), - cases: [ - { - name: "configured account produces a webhook status snapshot", - cfg: { - channels: { - line: { - enabled: true, - channelAccessToken: "line-token", - channelSecret: "line-secret", + { + 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, - runtime: { - accountId: "default", - running: true, + } as OpenClawConfig, + expectedActions: ["send"], + expectedCapabilities: ["buttons"], }, - probe: { ok: true }, - assertSnapshot: (snapshot) => { - expect(snapshot.accountId).toBe("default"); - expect(snapshot.enabled).toBe(true); - expect(snapshot.configured).toBe(true); - expect(snapshot.mode).toBe("webhook"); + { + name: "missing bot credentials disables the actions surface", + cfg: { + channels: { + mattermost: { + enabled: true, + }, + }, + } as OpenClawConfig, + expectedActions: [], + expectedCapabilities: [], }, - }, - ], - }, -]; + ], + }, + { + id: "telegram", + plugin: requireBundledChannelPlugin("telegram"), + cases: [ + { + name: "exposes configured Telegram actions and capabilities", + cfg: { + channels: { + telegram: { + botToken: "123:telegram-test-token", + }, + }, + } as OpenClawConfig, + expectedActions: [ + "send", + "poll", + "react", + "delete", + "edit", + "topic-create", + "topic-edit", + ], + expectedCapabilities: ["interactive", "buttons"], + }, + ], + }, + { + id: "discord", + plugin: requireBundledChannelPlugin("discord"), + cases: [ + { + name: "describes configured Discord actions and capabilities", + cfg: { + channels: { + discord: { + token: "Bot token-main", + actions: { + polls: true, + reactions: true, + permissions: false, + messages: false, + pins: false, + threads: false, + search: false, + stickers: false, + memberInfo: false, + roleInfo: false, + emojiUploads: false, + stickerUploads: false, + channelInfo: false, + channels: false, + voiceStatus: false, + events: false, + roles: false, + moderation: false, + presence: false, + }, + }, + }, + } as OpenClawConfig, + expectedActions: ["send", "poll", "react", "reactions", "emoji-list"], + expectedCapabilities: ["interactive", "components"], + }, + ], + }, + ]; + return actionContractRegistryCache; +} -export const surfaceContractRegistry: SurfaceContractEntry[] = listBundledChannelPlugins().map( - (plugin) => ({ +export function getSetupContractRegistry(): SetupContractEntry[] { + setupContractRegistryCache ??= [ + { + id: "slack", + plugin: requireBundledChannelPlugin("slack"), + cases: [ + { + name: "default account stores tokens and enables the channel", + cfg: {} as OpenClawConfig, + input: { + botToken: "xoxb-test", + appToken: "xapp-test", + }, + expectedAccountId: "default", + assertPatchedConfig: (cfg) => { + expect(cfg.channels?.slack?.enabled).toBe(true); + expect(cfg.channels?.slack?.botToken).toBe("xoxb-test"); + expect(cfg.channels?.slack?.appToken).toBe("xapp-test"); + }, + }, + { + name: "non-default env setup is rejected", + cfg: {} as OpenClawConfig, + accountId: "ops", + input: { + useEnv: true, + }, + expectedAccountId: "ops", + expectedValidation: "Slack env tokens can only be used for the default account.", + }, + ], + }, + { + id: "mattermost", + plugin: requireBundledChannelPlugin("mattermost"), + cases: [ + { + name: "default account stores token and normalized base URL", + cfg: {} as OpenClawConfig, + input: { + botToken: "test-token", + httpUrl: "https://chat.example.com/", + }, + expectedAccountId: "default", + assertPatchedConfig: (cfg) => { + expect(cfg.channels?.mattermost?.enabled).toBe(true); + expect(cfg.channels?.mattermost?.botToken).toBe("test-token"); + expect(cfg.channels?.mattermost?.baseUrl).toBe("https://chat.example.com"); + }, + }, + { + name: "missing credentials are rejected", + cfg: {} as OpenClawConfig, + input: { + httpUrl: "", + }, + expectedAccountId: "default", + expectedValidation: "Mattermost requires --bot-token and --http-url (or --use-env).", + }, + ], + }, + { + id: "line", + plugin: requireBundledChannelPlugin("line"), + cases: [ + { + name: "default account stores token and secret", + cfg: {} as OpenClawConfig, + input: { + channelAccessToken: "line-token", + channelSecret: "line-secret", + }, + expectedAccountId: "default", + assertPatchedConfig: (cfg) => { + expect(cfg.channels?.line?.enabled).toBe(true); + expect(cfg.channels?.line?.channelAccessToken).toBe("line-token"); + expect(cfg.channels?.line?.channelSecret).toBe("line-secret"); + }, + }, + { + name: "non-default env setup is rejected", + cfg: {} as OpenClawConfig, + accountId: "ops", + input: { + useEnv: true, + }, + expectedAccountId: "ops", + expectedValidation: "LINE_CHANNEL_ACCESS_TOKEN can only be used for the default account.", + }, + ], + }, + ]; + return setupContractRegistryCache; +} + +export function getStatusContractRegistry(): StatusContractEntry[] { + statusContractRegistryCache ??= [ + { + id: "slack", + plugin: requireBundledChannelPlugin("slack"), + cases: [ + { + name: "configured account produces a configured status snapshot", + cfg: { + channels: { + slack: { + botToken: "xoxb-test", + appToken: "xapp-test", + }, + }, + } as OpenClawConfig, + runtime: { + accountId: "default", + connected: true, + running: true, + }, + probe: { ok: true }, + assertSnapshot: (snapshot) => { + expect(snapshot.accountId).toBe("default"); + expect(snapshot.enabled).toBe(true); + expect(snapshot.configured).toBe(true); + }, + }, + ], + }, + { + id: "mattermost", + plugin: requireBundledChannelPlugin("mattermost"), + cases: [ + { + name: "configured account preserves connectivity details in the snapshot", + cfg: { + channels: { + mattermost: { + enabled: true, + botToken: "test-token", + baseUrl: "https://chat.example.com", + }, + }, + } as OpenClawConfig, + runtime: { + accountId: "default", + connected: true, + lastConnectedAt: 1234, + }, + probe: { ok: true }, + assertSnapshot: (snapshot) => { + expect(snapshot.accountId).toBe("default"); + expect(snapshot.enabled).toBe(true); + expect(snapshot.configured).toBe(true); + expect(snapshot.connected).toBe(true); + expect(snapshot.baseUrl).toBe("https://chat.example.com"); + }, + }, + ], + }, + { + id: "line", + plugin: requireBundledChannelPlugin("line"), + cases: [ + { + name: "configured account produces a webhook status snapshot", + cfg: { + channels: { + line: { + enabled: true, + channelAccessToken: "line-token", + channelSecret: "line-secret", + }, + }, + } as OpenClawConfig, + runtime: { + accountId: "default", + running: true, + }, + probe: { ok: true }, + assertSnapshot: (snapshot) => { + expect(snapshot.accountId).toBe("default"); + expect(snapshot.enabled).toBe(true); + expect(snapshot.configured).toBe(true); + expect(snapshot.mode).toBe("webhook"); + }, + }, + ], + }, + ]; + return statusContractRegistryCache; +} + +export function getSurfaceContractRegistry(): SurfaceContractEntry[] { + surfaceContractRegistryCache ??= listBundledChannelPlugins().map((plugin) => ({ id: plugin.id, plugin, surfaces: channelPluginSurfaceKeys.filter((surface) => Boolean(plugin[surface])), - }), -); - -export const threadingContractRegistry: ThreadingContractEntry[] = surfaceContractRegistry - .filter((entry) => entry.surfaces.includes("threading")) - .map((entry) => ({ - id: entry.id, - plugin: entry.plugin, })); + return surfaceContractRegistryCache; +} + +export function getThreadingContractRegistry(): ThreadingContractEntry[] { + threadingContractRegistryCache ??= getSurfaceContractRegistry() + .filter((entry) => entry.surfaces.includes("threading")) + .map((entry) => ({ + id: entry.id, + plugin: entry.plugin, + })); + return threadingContractRegistryCache; +} const directoryPresenceOnlyIds = new Set(["whatsapp", "zalouser"]); -export const directoryContractRegistry: DirectoryContractEntry[] = surfaceContractRegistry - .filter((entry) => entry.surfaces.includes("directory")) - .map((entry) => ({ - id: entry.id, - plugin: entry.plugin, - coverage: directoryPresenceOnlyIds.has(entry.id) ? "presence" : "lookups", - })); +export function getDirectoryContractRegistry(): DirectoryContractEntry[] { + directoryContractRegistryCache ??= getSurfaceContractRegistry() + .filter((entry) => entry.surfaces.includes("directory")) + .map((entry) => ({ + id: entry.id, + plugin: entry.plugin, + coverage: directoryPresenceOnlyIds.has(entry.id) ? "presence" : "lookups", + })); + return directoryContractRegistryCache; +} diff --git a/src/channels/plugins/contracts/setup-status.registry-backed.contract.test.ts b/src/channels/plugins/contracts/setup-status.registry-backed.contract.test.ts new file mode 100644 index 00000000000..c37e47974ee --- /dev/null +++ b/src/channels/plugins/contracts/setup-status.registry-backed.contract.test.ts @@ -0,0 +1,21 @@ +import { describe } from "vitest"; +import { getSetupContractRegistry, getStatusContractRegistry } from "./registry.js"; +import { installChannelSetupContractSuite, installChannelStatusContractSuite } from "./suites.js"; + +for (const entry of getSetupContractRegistry()) { + describe(`${entry.id} setup contract`, () => { + installChannelSetupContractSuite({ + plugin: entry.plugin, + cases: entry.cases as never, + }); + }); +} + +for (const entry of getStatusContractRegistry()) { + describe(`${entry.id} status contract`, () => { + installChannelStatusContractSuite({ + plugin: entry.plugin, + cases: entry.cases as never, + }); + }); +} diff --git a/src/channels/plugins/contracts/surfaces.registry-backed.contract.test.ts b/src/channels/plugins/contracts/surfaces.registry-backed.contract.test.ts new file mode 100644 index 00000000000..8285cbc1000 --- /dev/null +++ b/src/channels/plugins/contracts/surfaces.registry-backed.contract.test.ts @@ -0,0 +1,41 @@ +import { describe } from "vitest"; +import { + getDirectoryContractRegistry, + getSurfaceContractRegistry, + getThreadingContractRegistry, +} from "./registry.js"; +import { + installChannelDirectoryContractSuite, + installChannelSurfaceContractSuite, + installChannelThreadingContractSuite, +} from "./suites.js"; + +for (const entry of getSurfaceContractRegistry()) { + for (const surface of entry.surfaces) { + describe(`${entry.id} ${surface} surface contract`, () => { + installChannelSurfaceContractSuite({ + plugin: entry.plugin, + surface, + }); + }); + } +} + +for (const entry of getThreadingContractRegistry()) { + describe(`${entry.id} threading contract`, () => { + installChannelThreadingContractSuite({ + plugin: entry.plugin, + }); + }); +} + +for (const entry of getDirectoryContractRegistry()) { + describe(`${entry.id} directory contract`, () => { + installChannelDirectoryContractSuite({ + plugin: entry.plugin, + coverage: entry.coverage, + cfg: entry.cfg, + accountId: entry.accountId, + }); + }); +} diff --git a/test/helpers/channels/registry-backed-contract.ts b/test/helpers/channels/registry-backed-contract.ts deleted file mode 100644 index 1fb6942bce0..00000000000 --- a/test/helpers/channels/registry-backed-contract.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { describe } from "vitest"; -import { - actionContractRegistry, - directoryContractRegistry, - pluginContractRegistry, - setupContractRegistry, - statusContractRegistry, - surfaceContractRegistry, - threadingContractRegistry, -} from "../../../src/channels/plugins/contracts/registry.js"; -import { - installChannelActionsContractSuite, - installChannelDirectoryContractSuite, - installChannelPluginContractSuite, - installChannelSetupContractSuite, - installChannelStatusContractSuite, - installChannelSurfaceContractSuite, - installChannelThreadingContractSuite, -} from "../../../src/channels/plugins/contracts/suites.js"; - -function hasEntries( - entries: readonly T[], - id: string, -): entries is readonly T[] { - return entries.some((entry) => entry.id === id); -} - -export function describeChannelRegistryBackedContracts(id: string) { - if (hasEntries(pluginContractRegistry, id)) { - const entry = pluginContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} plugin contract`, () => { - installChannelPluginContractSuite({ - plugin: entry.plugin, - }); - }); - } - - if (hasEntries(actionContractRegistry, id)) { - const entry = actionContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} actions contract`, () => { - installChannelActionsContractSuite({ - plugin: entry.plugin, - cases: entry.cases as never, - unsupportedAction: entry.unsupportedAction as never, - }); - }); - } - - if (hasEntries(setupContractRegistry, id)) { - const entry = setupContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} setup contract`, () => { - installChannelSetupContractSuite({ - plugin: entry.plugin, - cases: entry.cases as never, - }); - }); - } - - if (hasEntries(statusContractRegistry, id)) { - const entry = statusContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} status contract`, () => { - installChannelStatusContractSuite({ - plugin: entry.plugin, - cases: entry.cases as never, - }); - }); - } - - for (const entry of surfaceContractRegistry.filter((item) => item.id === id)) { - for (const surface of entry.surfaces) { - describe(`${entry.id} ${surface} surface contract`, () => { - installChannelSurfaceContractSuite({ - plugin: entry.plugin, - surface, - }); - }); - } - } - - if (hasEntries(threadingContractRegistry, id)) { - const entry = threadingContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} threading contract`, () => { - installChannelThreadingContractSuite({ - plugin: entry.plugin, - }); - }); - } - - if (hasEntries(directoryContractRegistry, id)) { - const entry = directoryContractRegistry.find((item) => item.id === id)!; - describe(`${entry.id} directory contract`, () => { - installChannelDirectoryContractSuite({ - plugin: entry.plugin, - coverage: entry.coverage, - cfg: entry.cfg, - accountId: entry.accountId, - }); - }); - } -}