openclaw/test/helpers/channels/plugins-core-extension-cont...

389 lines
12 KiB
TypeScript

import { describe, expect, expectTypeOf, it } from "vitest";
import {
listDiscordDirectoryGroupsFromConfig,
listDiscordDirectoryPeersFromConfig,
type DiscordProbe,
type DiscordTokenResolution,
} from "../../../extensions/discord/api.js";
import type { IMessageProbe } from "../../../extensions/imessage/api.js";
import type { SignalProbe } from "../../../extensions/signal/api.js";
import {
listSlackDirectoryGroupsFromConfig,
listSlackDirectoryPeersFromConfig,
type SlackProbe,
} from "../../../extensions/slack/api.js";
import {
listTelegramDirectoryGroupsFromConfig,
listTelegramDirectoryPeersFromConfig,
type TelegramProbe,
type TelegramTokenResolution,
} from "../../../extensions/telegram/api.js";
import {
listWhatsAppDirectoryGroupsFromConfig,
listWhatsAppDirectoryPeersFromConfig,
} from "../../../extensions/whatsapp/api.js";
import type {
BaseProbeResult,
BaseTokenResolution,
ChannelDirectoryEntry,
} from "../../../src/channels/plugins/types.js";
import type { OpenClawConfig } from "../../../src/config/config.js";
import type { LineProbeResult } from "../../../src/plugin-sdk/line.js";
import { withEnvAsync } from "../../../src/test-utils/env.js";
type DirectoryListFn = (params: {
cfg: OpenClawConfig;
accountId?: string;
query?: string | null;
limit?: number | null;
}) => Promise<ChannelDirectoryEntry[]>;
async function listDirectoryEntriesWithDefaults(listFn: DirectoryListFn, cfg: OpenClawConfig) {
return await listFn({
cfg,
accountId: "default",
query: null,
limit: null,
});
}
async function expectDirectoryIds(
listFn: DirectoryListFn,
cfg: OpenClawConfig,
expected: string[],
options?: { sorted?: boolean },
) {
const entries = await listDirectoryEntriesWithDefaults(listFn, cfg);
const ids = entries.map((entry) => entry.id);
expect(options?.sorted ? ids.toSorted() : ids).toEqual(expected);
}
export function describeDiscordPluginsCoreExtensionContract() {
describe("discord plugins-core extension contract", () => {
it("DiscordProbe satisfies BaseProbeResult", () => {
expectTypeOf<DiscordProbe>().toMatchTypeOf<BaseProbeResult>();
});
it("Discord token resolution satisfies BaseTokenResolution", () => {
expectTypeOf<DiscordTokenResolution>().toMatchTypeOf<BaseTokenResolution>();
});
it("lists peers/groups from config (numeric ids only)", async () => {
const cfg = {
channels: {
discord: {
token: "discord-test",
dm: { allowFrom: ["<@111>", "<@!333>", "nope"] },
dms: { "222": {} },
guilds: {
"123": {
users: ["<@12345>", " discord:444 ", "not-an-id"],
channels: {
"555": {},
"<#777>": {},
"channel:666": {},
general: {},
},
},
},
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(
listDiscordDirectoryPeersFromConfig,
cfg,
["user:111", "user:12345", "user:222", "user:333", "user:444"],
{ sorted: true },
);
await expectDirectoryIds(
listDiscordDirectoryGroupsFromConfig,
cfg,
["channel:555", "channel:666", "channel:777"],
{ sorted: true },
);
});
it("keeps directories readable when tokens are unresolved SecretRefs", async () => {
const envSecret = {
source: "env",
provider: "default",
id: "MISSING_TEST_SECRET",
} as const;
const cfg = {
channels: {
discord: {
token: envSecret,
dm: { allowFrom: ["<@111>"] },
guilds: {
"123": {
channels: {
"555": {},
},
},
},
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listDiscordDirectoryPeersFromConfig, cfg, ["user:111"]);
await expectDirectoryIds(listDiscordDirectoryGroupsFromConfig, cfg, ["channel:555"]);
});
it("applies query and limit filtering for config-backed directories", async () => {
const cfg = {
channels: {
discord: {
token: "discord-test",
guilds: {
"123": {
channels: {
"555": {},
"666": {},
"777": {},
},
},
},
},
},
} as unknown as OpenClawConfig;
const groups = await listDiscordDirectoryGroupsFromConfig({
cfg,
accountId: "default",
query: "666",
limit: 5,
});
expect(groups.map((entry) => entry.id)).toEqual(["channel:666"]);
});
});
}
export function describeSlackPluginsCoreExtensionContract() {
describe("slack plugins-core extension contract", () => {
it("SlackProbe satisfies BaseProbeResult", () => {
expectTypeOf<SlackProbe>().toMatchTypeOf<BaseProbeResult>();
});
it("lists peers/groups from config", async () => {
const cfg = {
channels: {
slack: {
botToken: "xoxb-test",
appToken: "xapp-test",
dm: { allowFrom: ["U123", "user:U999"] },
dms: { U234: {} },
channels: { C111: { users: ["U777"] } },
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(
listSlackDirectoryPeersFromConfig,
cfg,
["user:u123", "user:u234", "user:u777", "user:u999"],
{ sorted: true },
);
await expectDirectoryIds(listSlackDirectoryGroupsFromConfig, cfg, ["channel:c111"]);
});
it("keeps directories readable when tokens are unresolved SecretRefs", async () => {
const envSecret = {
source: "env",
provider: "default",
id: "MISSING_TEST_SECRET",
} as const;
const cfg = {
channels: {
slack: {
botToken: envSecret,
appToken: envSecret,
dm: { allowFrom: ["U123"] },
channels: { C111: {} },
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listSlackDirectoryPeersFromConfig, cfg, ["user:u123"]);
await expectDirectoryIds(listSlackDirectoryGroupsFromConfig, cfg, ["channel:c111"]);
});
it("applies query and limit filtering for config-backed directories", async () => {
const cfg = {
channels: {
slack: {
botToken: "xoxb-test",
appToken: "xapp-test",
dm: { allowFrom: ["U100", "U200"] },
dms: { U300: {} },
},
},
} as unknown as OpenClawConfig;
const peers = await listSlackDirectoryPeersFromConfig({
cfg,
accountId: "default",
query: "user:u",
limit: 2,
});
expect(peers).toHaveLength(2);
expect(peers.every((entry) => entry.id.startsWith("user:u"))).toBe(true);
});
});
}
export function describeTelegramPluginsCoreExtensionContract() {
describe("telegram plugins-core extension contract", () => {
it("TelegramProbe satisfies BaseProbeResult", () => {
expectTypeOf<TelegramProbe>().toMatchTypeOf<BaseProbeResult>();
});
it("Telegram token resolution satisfies BaseTokenResolution", () => {
expectTypeOf<TelegramTokenResolution>().toMatchTypeOf<BaseTokenResolution>();
});
it("lists peers/groups from config", async () => {
const cfg = {
channels: {
telegram: {
botToken: "telegram-test",
allowFrom: ["123", "alice", "tg:@bob"],
dms: { "456": {} },
groups: { "-1001": {}, "*": {} },
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(
listTelegramDirectoryPeersFromConfig,
cfg,
["123", "456", "@alice", "@bob"],
{ sorted: true },
);
await expectDirectoryIds(listTelegramDirectoryGroupsFromConfig, cfg, ["-1001"]);
});
it("keeps fallback semantics when accountId is omitted", async () => {
await withEnvAsync({ TELEGRAM_BOT_TOKEN: "tok-env" }, async () => {
const cfg = {
channels: {
telegram: {
allowFrom: ["alice"],
groups: { "-1001": {} },
accounts: {
work: {
botToken: "tok-work",
allowFrom: ["bob"],
groups: { "-2002": {} },
},
},
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listTelegramDirectoryPeersFromConfig, cfg, ["@alice"]);
await expectDirectoryIds(listTelegramDirectoryGroupsFromConfig, cfg, ["-1001"]);
});
});
it("keeps directories readable when tokens are unresolved SecretRefs", async () => {
const envSecret = {
source: "env",
provider: "default",
id: "MISSING_TEST_SECRET",
} as const;
const cfg = {
channels: {
telegram: {
botToken: envSecret,
allowFrom: ["alice"],
groups: { "-1001": {} },
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listTelegramDirectoryPeersFromConfig, cfg, ["@alice"]);
await expectDirectoryIds(listTelegramDirectoryGroupsFromConfig, cfg, ["-1001"]);
});
it("applies query and limit filtering for config-backed directories", async () => {
const cfg = {
channels: {
telegram: {
botToken: "telegram-test",
groups: { "-1001": {}, "-1002": {}, "-2001": {} },
},
},
} as unknown as OpenClawConfig;
const groups = await listTelegramDirectoryGroupsFromConfig({
cfg,
accountId: "default",
query: "-100",
limit: 1,
});
expect(groups.map((entry) => entry.id)).toEqual(["-1001"]);
});
});
}
export function describeWhatsAppPluginsCoreExtensionContract() {
describe("whatsapp plugins-core extension contract", () => {
it("lists peers/groups from config", async () => {
const cfg = {
channels: {
whatsapp: {
allowFrom: ["+15550000000", "*", "123@g.us"],
groups: { "999@g.us": { requireMention: true }, "*": {} },
},
},
} as unknown as OpenClawConfig;
await expectDirectoryIds(listWhatsAppDirectoryPeersFromConfig, cfg, ["+15550000000"]);
await expectDirectoryIds(listWhatsAppDirectoryGroupsFromConfig, cfg, ["999@g.us"]);
});
it("applies query and limit filtering for config-backed directories", async () => {
const cfg = {
channels: {
whatsapp: {
groups: { "111@g.us": {}, "222@g.us": {}, "333@s.whatsapp.net": {} },
},
},
} as unknown as OpenClawConfig;
const groups = await listWhatsAppDirectoryGroupsFromConfig({
cfg,
accountId: "default",
query: "@g.us",
limit: 1,
});
expect(groups.map((entry) => entry.id)).toEqual(["111@g.us"]);
});
});
}
export function describeSignalPluginsCoreExtensionContract() {
describe("signal plugins-core extension contract", () => {
it("SignalProbe satisfies BaseProbeResult", () => {
expectTypeOf<SignalProbe>().toMatchTypeOf<BaseProbeResult>();
});
});
}
export function describeIMessagePluginsCoreExtensionContract() {
describe("imessage plugins-core extension contract", () => {
it("IMessageProbe satisfies BaseProbeResult", () => {
expectTypeOf<IMessageProbe>().toMatchTypeOf<BaseProbeResult>();
});
});
}
export function describeLinePluginsCoreExtensionContract() {
describe("line plugins-core extension contract", () => {
it("LineProbeResult satisfies BaseProbeResult", () => {
expectTypeOf<LineProbeResult>().toMatchTypeOf<BaseProbeResult>();
});
});
}