Files
openclaw/src/discord/monitor/thread-bindings.discord-api.test.ts

162 lines
4.6 KiB
TypeScript

import { ChannelType } from "discord-api-types/v10";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { OpenClawConfig } from "../../config/config.js";
import type { ThreadBindingRecord } from "./thread-bindings.types.js";
const hoisted = vi.hoisted(() => {
const restGet = vi.fn();
const sendMessageDiscord = vi.fn();
const sendWebhookMessageDiscord = vi.fn();
const createDiscordRestClient = vi.fn(() => ({
rest: {
get: restGet,
},
}));
return {
restGet,
sendMessageDiscord,
sendWebhookMessageDiscord,
createDiscordRestClient,
};
});
vi.mock("../client.js", () => ({
createDiscordRestClient: hoisted.createDiscordRestClient,
}));
vi.mock("../send.js", () => ({
sendMessageDiscord: (...args: unknown[]) => hoisted.sendMessageDiscord(...args),
sendWebhookMessageDiscord: (...args: unknown[]) => hoisted.sendWebhookMessageDiscord(...args),
}));
const { maybeSendBindingMessage, resolveChannelIdForBinding } =
await import("./thread-bindings.discord-api.js");
describe("resolveChannelIdForBinding", () => {
beforeEach(() => {
hoisted.restGet.mockClear();
hoisted.createDiscordRestClient.mockClear();
hoisted.sendMessageDiscord.mockClear().mockResolvedValue({});
hoisted.sendWebhookMessageDiscord.mockClear().mockResolvedValue({});
});
it("returns explicit channelId without resolving route", async () => {
const resolved = await resolveChannelIdForBinding({
accountId: "default",
threadId: "thread-1",
channelId: "channel-explicit",
});
expect(resolved).toBe("channel-explicit");
expect(hoisted.createDiscordRestClient).not.toHaveBeenCalled();
expect(hoisted.restGet).not.toHaveBeenCalled();
});
it("returns parent channel for thread channels", async () => {
hoisted.restGet.mockResolvedValueOnce({
id: "thread-1",
type: ChannelType.PublicThread,
parent_id: "channel-parent",
});
const resolved = await resolveChannelIdForBinding({
accountId: "default",
threadId: "thread-1",
});
expect(resolved).toBe("channel-parent");
});
it("forwards cfg when resolving channel id through Discord client", async () => {
const cfg = {
channels: { discord: { token: "tok" } },
} as OpenClawConfig;
hoisted.restGet.mockResolvedValueOnce({
id: "thread-1",
type: ChannelType.PublicThread,
parent_id: "channel-parent",
});
await resolveChannelIdForBinding({
cfg,
accountId: "default",
threadId: "thread-1",
});
const createDiscordRestClientCalls = hoisted.createDiscordRestClient.mock.calls as unknown[][];
expect(createDiscordRestClientCalls[0]?.[1]).toBe(cfg);
});
it("keeps non-thread channel id even when parent_id exists", async () => {
hoisted.restGet.mockResolvedValueOnce({
id: "channel-text",
type: ChannelType.GuildText,
parent_id: "category-1",
});
const resolved = await resolveChannelIdForBinding({
accountId: "default",
threadId: "channel-text",
});
expect(resolved).toBe("channel-text");
});
it("keeps forum channel id instead of parent category", async () => {
hoisted.restGet.mockResolvedValueOnce({
id: "forum-1",
type: ChannelType.GuildForum,
parent_id: "category-1",
});
const resolved = await resolveChannelIdForBinding({
accountId: "default",
threadId: "forum-1",
});
expect(resolved).toBe("forum-1");
});
});
describe("maybeSendBindingMessage", () => {
beforeEach(() => {
hoisted.sendMessageDiscord.mockClear().mockResolvedValue({});
hoisted.sendWebhookMessageDiscord.mockClear().mockResolvedValue({});
});
it("forwards cfg to webhook send path", async () => {
const cfg = {
channels: { discord: { token: "tok" } },
} as OpenClawConfig;
const record = {
accountId: "default",
channelId: "parent-1",
threadId: "thread-1",
targetKind: "subagent",
targetSessionKey: "agent:main:subagent:test",
agentId: "main",
boundBy: "test",
boundAt: Date.now(),
lastActivityAt: Date.now(),
webhookId: "wh_1",
webhookToken: "tok_1",
} satisfies ThreadBindingRecord;
await maybeSendBindingMessage({
cfg,
record,
text: "hello webhook",
});
expect(hoisted.sendWebhookMessageDiscord).toHaveBeenCalledTimes(1);
expect(hoisted.sendWebhookMessageDiscord.mock.calls[0]?.[1]).toMatchObject({
cfg,
webhookId: "wh_1",
webhookToken: "tok_1",
accountId: "default",
threadId: "thread-1",
});
expect(hoisted.sendMessageDiscord).not.toHaveBeenCalled();
});
});