test(contracts): split registry-backed channel contract lanes

This commit is contained in:
Vincent Koc 2026-04-04 00:21:07 +09:00
parent f71ef47288
commit ade6b61358
6 changed files with 491 additions and 499 deletions

View File

@ -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,
});
});
}

View File

@ -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<string>([
...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);
}

View File

@ -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;
}

View File

@ -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,
});
});
}

View File

@ -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,
});
});
}

View File

@ -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<T extends { id: string }>(
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,
});
});
}
}