openclaw/src/discord/monitor/agent-components.test.ts

100 lines
3.7 KiB
TypeScript

import type { ButtonInteraction, ComponentData, StringSelectMenuInteraction } from "@buape/carbon";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import { createAgentComponentButton, createAgentSelectMenu } from "./agent-components.js";
const readAllowFromStoreMock = vi.hoisted(() => vi.fn());
const upsertPairingRequestMock = vi.hoisted(() => vi.fn());
const enqueueSystemEventMock = vi.hoisted(() => vi.fn());
vi.mock("../../pairing/pairing-store.js", () => ({
readChannelAllowFromStore: (...args: unknown[]) => readAllowFromStoreMock(...args),
upsertChannelPairingRequest: (...args: unknown[]) => upsertPairingRequestMock(...args),
}));
vi.mock("../../infra/system-events.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("../../infra/system-events.js")>();
return {
...actual,
enqueueSystemEvent: (...args: unknown[]) => enqueueSystemEventMock(...args),
};
});
const createCfg = (): OpenClawConfig => ({}) as OpenClawConfig;
const createDmButtonInteraction = (overrides: Partial<ButtonInteraction> = {}) => {
const reply = vi.fn().mockResolvedValue(undefined);
const interaction = {
rawData: { channel_id: "dm-channel" },
user: { id: "123456789", username: "Alice", discriminator: "1234" },
reply,
...overrides,
} as unknown as ButtonInteraction;
return { interaction, reply };
};
const createDmSelectInteraction = (overrides: Partial<StringSelectMenuInteraction> = {}) => {
const reply = vi.fn().mockResolvedValue(undefined);
const interaction = {
rawData: { channel_id: "dm-channel" },
user: { id: "123456789", username: "Alice", discriminator: "1234" },
values: ["alpha"],
reply,
...overrides,
} as unknown as StringSelectMenuInteraction;
return { interaction, reply };
};
beforeEach(() => {
readAllowFromStoreMock.mockReset().mockResolvedValue([]);
upsertPairingRequestMock.mockReset().mockResolvedValue({ code: "PAIRCODE", created: true });
enqueueSystemEventMock.mockReset();
});
describe("agent components", () => {
it("sends pairing reply when DM sender is not allowlisted", async () => {
const button = createAgentComponentButton({
cfg: createCfg(),
accountId: "default",
dmPolicy: "pairing",
});
const { interaction, reply } = createDmButtonInteraction();
await button.run(interaction, { componentId: "hello" } as ComponentData);
expect(reply).toHaveBeenCalledTimes(1);
expect(reply.mock.calls[0]?.[0]?.content).toContain("Pairing code: PAIRCODE");
expect(enqueueSystemEventMock).not.toHaveBeenCalled();
});
it("allows DM interactions when pairing store allowlist matches", async () => {
readAllowFromStoreMock.mockResolvedValue(["123456789"]);
const button = createAgentComponentButton({
cfg: createCfg(),
accountId: "default",
dmPolicy: "allowlist",
});
const { interaction, reply } = createDmButtonInteraction();
await button.run(interaction, { componentId: "hello" } as ComponentData);
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
expect(enqueueSystemEventMock).toHaveBeenCalled();
});
it("matches tag-based allowlist entries for DM select menus", async () => {
const select = createAgentSelectMenu({
cfg: createCfg(),
accountId: "default",
dmPolicy: "allowlist",
allowFrom: ["Alice#1234"],
});
const { interaction, reply } = createDmSelectInteraction();
await select.run(interaction, { componentId: "hello" } as ComponentData);
expect(reply).toHaveBeenCalledWith({ content: "✓", ephemeral: true });
expect(enqueueSystemEventMock).toHaveBeenCalled();
});
});