openclaw/src/channels/plugins/actions/discord.test.ts

138 lines
4.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type { ClawdbotConfig } from "../../../config/config.js";
type SendMessageDiscord = typeof import("../../../discord/send.js").sendMessageDiscord;
type SendPollDiscord = typeof import("../../../discord/send.js").sendPollDiscord;
type ReactMessageDiscord = typeof import("../../../discord/send.js").reactMessageDiscord;
const sendMessageDiscord = vi.fn<Parameters<SendMessageDiscord>, ReturnType<SendMessageDiscord>>(
async () => ({ ok: true }) as Awaited<ReturnType<SendMessageDiscord>>,
);
const sendPollDiscord = vi.fn<Parameters<SendPollDiscord>, ReturnType<SendPollDiscord>>(
async () => ({ ok: true }) as Awaited<ReturnType<SendPollDiscord>>,
);
const reactMessageDiscord = vi.fn<Parameters<ReactMessageDiscord>, ReturnType<ReactMessageDiscord>>(
async () => ({ ok: true }) as Awaited<ReturnType<ReactMessageDiscord>>,
);
vi.mock("../../../discord/send.js", async () => {
const actual = await vi.importActual<typeof import("../../../discord/send.js")>(
"../../../discord/send.js",
);
return {
...actual,
sendMessageDiscord: (...args: Parameters<SendMessageDiscord>) => sendMessageDiscord(...args),
sendPollDiscord: (...args: Parameters<SendPollDiscord>) => sendPollDiscord(...args),
reactMessageDiscord: (...args: Parameters<ReactMessageDiscord>) => reactMessageDiscord(...args),
};
});
const loadHandleDiscordMessageAction = async () => {
const mod = await import("./discord/handle-action.js");
return mod.handleDiscordMessageAction;
};
const loadDiscordMessageActions = async () => {
const mod = await import("./discord.js");
return mod.discordMessageActions;
};
describe("discord message actions", () => {
it("lists channel and upload actions by default", async () => {
const cfg = { channels: { discord: { token: "d0" } } } as ClawdbotConfig;
const discordMessageActions = await loadDiscordMessageActions();
const actions = discordMessageActions.listActions?.({ cfg }) ?? [];
expect(actions).toContain("emoji-upload");
expect(actions).toContain("sticker-upload");
expect(actions).toContain("channel-create");
});
it("respects disabled channel actions", async () => {
const cfg = {
channels: { discord: { token: "d0", actions: { channels: false } } },
} as ClawdbotConfig;
const discordMessageActions = await loadDiscordMessageActions();
const actions = discordMessageActions.listActions?.({ cfg }) ?? [];
expect(actions).not.toContain("channel-create");
});
});
describe("handleDiscordMessageAction", () => {
it("forwards context accountId for send", async () => {
sendMessageDiscord.mockClear();
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
await handleDiscordMessageAction({
action: "send",
params: {
to: "channel:123",
message: "hi",
},
cfg: {} as ClawdbotConfig,
accountId: "ops",
});
expect(sendMessageDiscord).toHaveBeenCalledWith(
"channel:123",
"hi",
expect.objectContaining({
accountId: "ops",
}),
);
});
it("falls back to params accountId when context missing", async () => {
sendPollDiscord.mockClear();
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
await handleDiscordMessageAction({
action: "poll",
params: {
to: "channel:123",
pollQuestion: "Ready?",
pollOption: ["Yes", "No"],
accountId: "marve",
},
cfg: {} as ClawdbotConfig,
});
expect(sendPollDiscord).toHaveBeenCalledWith(
"channel:123",
expect.objectContaining({
question: "Ready?",
options: ["Yes", "No"],
}),
expect.objectContaining({
accountId: "marve",
}),
);
});
it("forwards accountId for reaction actions", async () => {
reactMessageDiscord.mockClear();
const handleDiscordMessageAction = await loadHandleDiscordMessageAction();
await handleDiscordMessageAction({
action: "react",
params: {
channelId: "123",
messageId: "m1",
emoji: "👍",
},
cfg: {} as ClawdbotConfig,
accountId: "ops",
});
expect(reactMessageDiscord).toHaveBeenCalledWith(
"123",
"m1",
"👍",
expect.objectContaining({
accountId: "ops",
}),
);
});
});