mirror of https://github.com/openclaw/openclaw.git
test: dedupe discord preflight helpers
This commit is contained in:
parent
6a44ca9f76
commit
fd340a88d6
|
|
@ -1,4 +1,3 @@
|
|||
import { ChannelType } from "@buape/carbon";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const ensureConfiguredAcpBindingSessionMock = vi.hoisted(() => vi.fn());
|
||||
|
|
@ -13,7 +12,13 @@ vi.mock("../../acp/persistent-bindings.js", () => ({
|
|||
|
||||
import { __testing as sessionBindingTesting } from "../../infra/outbound/session-binding-service.js";
|
||||
import { preflightDiscordMessage } from "./message-handler.preflight.js";
|
||||
import { createNoopThreadBindingManager } from "./thread-bindings.js";
|
||||
import {
|
||||
createDiscordMessage,
|
||||
createDiscordPreflightArgs,
|
||||
createGuildEvent,
|
||||
createGuildTextClient,
|
||||
DEFAULT_PREFLIGHT_CFG,
|
||||
} from "./message-handler.preflight.test-helpers.js";
|
||||
|
||||
const GUILD_ID = "guild-1";
|
||||
const CHANNEL_ID = "channel-1";
|
||||
|
|
@ -48,70 +53,36 @@ function createConfiguredDiscordBinding() {
|
|||
}
|
||||
|
||||
function createBasePreflightParams(overrides?: Record<string, unknown>) {
|
||||
const message = {
|
||||
const message = createDiscordMessage({
|
||||
id: "m-1",
|
||||
content: "<@bot-1> hello",
|
||||
timestamp: new Date().toISOString(),
|
||||
channelId: CHANNEL_ID,
|
||||
attachments: [],
|
||||
content: "<@bot-1> hello",
|
||||
mentionedUsers: [{ id: "bot-1" }],
|
||||
mentionedRoles: [],
|
||||
mentionedEveryone: false,
|
||||
author: {
|
||||
id: "user-1",
|
||||
bot: false,
|
||||
username: "alice",
|
||||
},
|
||||
} as unknown as import("@buape/carbon").Message;
|
||||
|
||||
const client = {
|
||||
fetchChannel: async (channelId: string) => {
|
||||
if (channelId === CHANNEL_ID) {
|
||||
return {
|
||||
id: CHANNEL_ID,
|
||||
type: ChannelType.GuildText,
|
||||
name: "general",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
} as unknown as import("@buape/carbon").Client;
|
||||
});
|
||||
|
||||
return {
|
||||
cfg: {
|
||||
session: {
|
||||
mainKey: "main",
|
||||
scope: "per-sender",
|
||||
},
|
||||
} as import("../../config/config.js").OpenClawConfig,
|
||||
...createDiscordPreflightArgs({
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: {
|
||||
allowBots: true,
|
||||
} as NonNullable<import("../../config/config.js").OpenClawConfig["channels"]>["discord"],
|
||||
data: createGuildEvent({
|
||||
channelId: CHANNEL_ID,
|
||||
guildId: GUILD_ID,
|
||||
author: message.author,
|
||||
message,
|
||||
}),
|
||||
client: createGuildTextClient(CHANNEL_ID),
|
||||
botUserId: "bot-1",
|
||||
}),
|
||||
discordConfig: {
|
||||
allowBots: true,
|
||||
} as NonNullable<import("../../config/config.js").OpenClawConfig["channels"]>["discord"],
|
||||
accountId: "default",
|
||||
token: "token",
|
||||
runtime: {} as import("../../runtime.js").RuntimeEnv,
|
||||
botUserId: "bot-1",
|
||||
guildHistories: new Map(),
|
||||
historyLimit: 0,
|
||||
mediaMaxBytes: 1_000_000,
|
||||
textLimit: 2_000,
|
||||
replyToMode: "all",
|
||||
dmEnabled: true,
|
||||
groupDmEnabled: true,
|
||||
ackReactionScope: "direct",
|
||||
groupPolicy: "open",
|
||||
threadBindings: createNoopThreadBindingManager("default"),
|
||||
data: {
|
||||
channel_id: CHANNEL_ID,
|
||||
guild_id: GUILD_ID,
|
||||
guild: {
|
||||
id: GUILD_ID,
|
||||
name: "Guild One",
|
||||
},
|
||||
author: message.author,
|
||||
message,
|
||||
} as unknown as import("./listeners.js").DiscordMessageEvent,
|
||||
client,
|
||||
...overrides,
|
||||
} satisfies Parameters<typeof preflightDiscordMessage>[0];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
import { ChannelType } from "@buape/carbon";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
import type { preflightDiscordMessage } from "./message-handler.preflight.js";
|
||||
import { createNoopThreadBindingManager } from "./thread-bindings.js";
|
||||
|
||||
export type DiscordConfig = NonNullable<OpenClawConfig["channels"]>["discord"];
|
||||
export type DiscordMessageEvent = import("./listeners.js").DiscordMessageEvent;
|
||||
export type DiscordClient = import("@buape/carbon").Client;
|
||||
|
||||
export const DEFAULT_PREFLIGHT_CFG = {
|
||||
session: {
|
||||
mainKey: "main",
|
||||
scope: "per-sender",
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
export function createGuildTextClient(channelId: string): DiscordClient {
|
||||
return {
|
||||
fetchChannel: async (id: string) => {
|
||||
if (id === channelId) {
|
||||
return {
|
||||
id: channelId,
|
||||
type: ChannelType.GuildText,
|
||||
name: "general",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
} as unknown as DiscordClient;
|
||||
}
|
||||
|
||||
export function createGuildEvent(params: {
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
author: import("@buape/carbon").Message["author"];
|
||||
message: import("@buape/carbon").Message;
|
||||
}): DiscordMessageEvent {
|
||||
return {
|
||||
channel_id: params.channelId,
|
||||
guild_id: params.guildId,
|
||||
guild: {
|
||||
id: params.guildId,
|
||||
name: "Guild One",
|
||||
},
|
||||
author: params.author,
|
||||
message: params.message,
|
||||
} as unknown as DiscordMessageEvent;
|
||||
}
|
||||
|
||||
export function createDiscordMessage(params: {
|
||||
id: string;
|
||||
channelId: string;
|
||||
content: string;
|
||||
author: {
|
||||
id: string;
|
||||
bot: boolean;
|
||||
username?: string;
|
||||
};
|
||||
mentionedUsers?: Array<{ id: string }>;
|
||||
mentionedEveryone?: boolean;
|
||||
attachments?: Array<Record<string, unknown>>;
|
||||
}): import("@buape/carbon").Message {
|
||||
return {
|
||||
id: params.id,
|
||||
content: params.content,
|
||||
timestamp: new Date().toISOString(),
|
||||
channelId: params.channelId,
|
||||
attachments: params.attachments ?? [],
|
||||
mentionedUsers: params.mentionedUsers ?? [],
|
||||
mentionedRoles: [],
|
||||
mentionedEveryone: params.mentionedEveryone ?? false,
|
||||
author: params.author,
|
||||
} as unknown as import("@buape/carbon").Message;
|
||||
}
|
||||
|
||||
export function createDiscordPreflightArgs(params: {
|
||||
cfg: OpenClawConfig;
|
||||
discordConfig: DiscordConfig;
|
||||
data: DiscordMessageEvent;
|
||||
client: DiscordClient;
|
||||
botUserId?: string;
|
||||
}): Parameters<typeof preflightDiscordMessage>[0] {
|
||||
return {
|
||||
cfg: params.cfg,
|
||||
discordConfig: params.discordConfig,
|
||||
accountId: "default",
|
||||
token: "token",
|
||||
runtime: {} as import("../../runtime.js").RuntimeEnv,
|
||||
botUserId: params.botUserId ?? "openclaw-bot",
|
||||
guildHistories: new Map(),
|
||||
historyLimit: 0,
|
||||
mediaMaxBytes: 1_000_000,
|
||||
textLimit: 2_000,
|
||||
replyToMode: "all",
|
||||
dmEnabled: true,
|
||||
groupDmEnabled: true,
|
||||
ackReactionScope: "direct",
|
||||
groupPolicy: "open",
|
||||
threadBindings: createNoopThreadBindingManager("default"),
|
||||
data: params.data,
|
||||
client: params.client,
|
||||
};
|
||||
}
|
||||
|
|
@ -15,25 +15,21 @@ import {
|
|||
resolvePreflightMentionRequirement,
|
||||
shouldIgnoreBoundThreadWebhookMessage,
|
||||
} from "./message-handler.preflight.js";
|
||||
import {
|
||||
createDiscordMessage,
|
||||
createDiscordPreflightArgs,
|
||||
createGuildEvent,
|
||||
createGuildTextClient,
|
||||
DEFAULT_PREFLIGHT_CFG,
|
||||
type DiscordClient,
|
||||
type DiscordConfig,
|
||||
type DiscordMessageEvent,
|
||||
} from "./message-handler.preflight.test-helpers.js";
|
||||
import {
|
||||
__testing as threadBindingTesting,
|
||||
createNoopThreadBindingManager,
|
||||
createThreadBindingManager,
|
||||
} from "./thread-bindings.js";
|
||||
|
||||
type DiscordConfig = NonNullable<
|
||||
import("../../config/config.js").OpenClawConfig["channels"]
|
||||
>["discord"];
|
||||
type DiscordMessageEvent = import("./listeners.js").DiscordMessageEvent;
|
||||
type DiscordClient = import("@buape/carbon").Client;
|
||||
|
||||
const DEFAULT_CFG = {
|
||||
session: {
|
||||
mainKey: "main",
|
||||
scope: "per-sender",
|
||||
},
|
||||
} as import("../../config/config.js").OpenClawConfig;
|
||||
|
||||
function createThreadBinding(
|
||||
overrides?: Partial<
|
||||
import("../../infra/outbound/session-binding-service.js").SessionBindingRecord
|
||||
|
|
@ -67,41 +63,7 @@ function createPreflightArgs(params: {
|
|||
data: DiscordMessageEvent;
|
||||
client: DiscordClient;
|
||||
}): Parameters<typeof preflightDiscordMessage>[0] {
|
||||
return {
|
||||
cfg: params.cfg,
|
||||
discordConfig: params.discordConfig,
|
||||
accountId: "default",
|
||||
token: "token",
|
||||
runtime: {} as import("../../runtime.js").RuntimeEnv,
|
||||
botUserId: "openclaw-bot",
|
||||
guildHistories: new Map(),
|
||||
historyLimit: 0,
|
||||
mediaMaxBytes: 1_000_000,
|
||||
textLimit: 2_000,
|
||||
replyToMode: "all",
|
||||
dmEnabled: true,
|
||||
groupDmEnabled: true,
|
||||
ackReactionScope: "direct",
|
||||
groupPolicy: "open",
|
||||
threadBindings: createNoopThreadBindingManager("default"),
|
||||
data: params.data,
|
||||
client: params.client,
|
||||
};
|
||||
}
|
||||
|
||||
function createGuildTextClient(channelId: string): DiscordClient {
|
||||
return {
|
||||
fetchChannel: async (id: string) => {
|
||||
if (id === channelId) {
|
||||
return {
|
||||
id: channelId,
|
||||
type: ChannelType.GuildText,
|
||||
name: "general",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
} as unknown as DiscordClient;
|
||||
return createDiscordPreflightArgs(params);
|
||||
}
|
||||
|
||||
function createThreadClient(params: { threadId: string; parentId: string }): DiscordClient {
|
||||
|
|
@ -128,50 +90,6 @@ function createThreadClient(params: { threadId: string; parentId: string }): Dis
|
|||
} as unknown as DiscordClient;
|
||||
}
|
||||
|
||||
function createGuildEvent(params: {
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
author: import("@buape/carbon").Message["author"];
|
||||
message: import("@buape/carbon").Message;
|
||||
}): DiscordMessageEvent {
|
||||
return {
|
||||
channel_id: params.channelId,
|
||||
guild_id: params.guildId,
|
||||
guild: {
|
||||
id: params.guildId,
|
||||
name: "Guild One",
|
||||
},
|
||||
author: params.author,
|
||||
message: params.message,
|
||||
} as unknown as DiscordMessageEvent;
|
||||
}
|
||||
|
||||
function createMessage(params: {
|
||||
id: string;
|
||||
channelId: string;
|
||||
content: string;
|
||||
author: {
|
||||
id: string;
|
||||
bot: boolean;
|
||||
username?: string;
|
||||
};
|
||||
mentionedUsers?: Array<{ id: string }>;
|
||||
mentionedEveryone?: boolean;
|
||||
attachments?: Array<Record<string, unknown>>;
|
||||
}): import("@buape/carbon").Message {
|
||||
return {
|
||||
id: params.id,
|
||||
content: params.content,
|
||||
timestamp: new Date().toISOString(),
|
||||
channelId: params.channelId,
|
||||
attachments: params.attachments ?? [],
|
||||
mentionedUsers: params.mentionedUsers ?? [],
|
||||
mentionedRoles: [],
|
||||
mentionedEveryone: params.mentionedEveryone ?? false,
|
||||
author: params.author,
|
||||
} as unknown as import("@buape/carbon").Message;
|
||||
}
|
||||
|
||||
async function runThreadBoundPreflight(params: {
|
||||
threadId: string;
|
||||
parentId: string;
|
||||
|
|
@ -197,7 +115,7 @@ async function runThreadBoundPreflight(params: {
|
|||
|
||||
return preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: DEFAULT_CFG,
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: params.discordConfig,
|
||||
data: createGuildEvent({
|
||||
channelId: params.threadId,
|
||||
|
|
@ -223,7 +141,7 @@ async function runGuildPreflight(params: {
|
|||
}) {
|
||||
return preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: params.cfg ?? DEFAULT_CFG,
|
||||
cfg: params.cfg ?? DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: params.discordConfig,
|
||||
data: createGuildEvent({
|
||||
channelId: params.channelId,
|
||||
|
|
@ -237,6 +155,40 @@ async function runGuildPreflight(params: {
|
|||
});
|
||||
}
|
||||
|
||||
async function runMentionOnlyBotPreflight(params: {
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
message: import("@buape/carbon").Message;
|
||||
}) {
|
||||
return runGuildPreflight({
|
||||
channelId: params.channelId,
|
||||
guildId: params.guildId,
|
||||
message: params.message,
|
||||
discordConfig: {
|
||||
allowBots: "mentions",
|
||||
} as DiscordConfig,
|
||||
});
|
||||
}
|
||||
|
||||
async function runIgnoreOtherMentionsPreflight(params: {
|
||||
channelId: string;
|
||||
guildId: string;
|
||||
message: import("@buape/carbon").Message;
|
||||
}) {
|
||||
return runGuildPreflight({
|
||||
channelId: params.channelId,
|
||||
guildId: params.guildId,
|
||||
message: params.message,
|
||||
discordConfig: {} as DiscordConfig,
|
||||
guildEntries: {
|
||||
[params.guildId]: {
|
||||
requireMention: false,
|
||||
ignoreOtherMentions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe("resolvePreflightMentionRequirement", () => {
|
||||
it("requires mention when config requires mention and thread is not bound", () => {
|
||||
expect(
|
||||
|
|
@ -279,7 +231,7 @@ describe("preflightDiscordMessage", () => {
|
|||
});
|
||||
const threadId = "thread-system-1";
|
||||
const parentId = "channel-parent-1";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-system-1",
|
||||
channelId: threadId,
|
||||
content:
|
||||
|
|
@ -311,7 +263,7 @@ describe("preflightDiscordMessage", () => {
|
|||
});
|
||||
const threadId = "thread-bot-regular-1";
|
||||
const parentId = "channel-parent-regular-1";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-bot-regular-1",
|
||||
channelId: threadId,
|
||||
content: "here is tool output chunk",
|
||||
|
|
@ -342,7 +294,7 @@ describe("preflightDiscordMessage", () => {
|
|||
const threadId = "thread-bot-focus";
|
||||
const parentId = "channel-parent-focus";
|
||||
const client = createThreadClient({ threadId, parentId });
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-bot-1",
|
||||
channelId: threadId,
|
||||
content: "relay message without mention",
|
||||
|
|
@ -363,7 +315,7 @@ describe("preflightDiscordMessage", () => {
|
|||
const result = await preflightDiscordMessage(
|
||||
createPreflightArgs({
|
||||
cfg: {
|
||||
...DEFAULT_CFG,
|
||||
...DEFAULT_PREFLIGHT_CFG,
|
||||
} as import("../../config/config.js").OpenClawConfig,
|
||||
discordConfig: {
|
||||
allowBots: true,
|
||||
|
|
@ -386,7 +338,7 @@ describe("preflightDiscordMessage", () => {
|
|||
it("drops bot messages without mention when allowBots=mentions", async () => {
|
||||
const channelId = "channel-bot-mentions-off";
|
||||
const guildId = "guild-bot-mentions-off";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-bot-mentions-off",
|
||||
channelId,
|
||||
content: "relay chatter",
|
||||
|
|
@ -397,14 +349,7 @@ describe("preflightDiscordMessage", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const result = await runGuildPreflight({
|
||||
channelId,
|
||||
guildId,
|
||||
message,
|
||||
discordConfig: {
|
||||
allowBots: "mentions",
|
||||
} as DiscordConfig,
|
||||
});
|
||||
const result = await runMentionOnlyBotPreflight({ channelId, guildId, message });
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
|
@ -412,7 +357,7 @@ describe("preflightDiscordMessage", () => {
|
|||
it("allows bot messages with explicit mention when allowBots=mentions", async () => {
|
||||
const channelId = "channel-bot-mentions-on";
|
||||
const guildId = "guild-bot-mentions-on";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-bot-mentions-on",
|
||||
channelId,
|
||||
content: "hi <@openclaw-bot>",
|
||||
|
|
@ -424,14 +369,7 @@ describe("preflightDiscordMessage", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const result = await runGuildPreflight({
|
||||
channelId,
|
||||
guildId,
|
||||
message,
|
||||
discordConfig: {
|
||||
allowBots: "mentions",
|
||||
} as DiscordConfig,
|
||||
});
|
||||
const result = await runMentionOnlyBotPreflight({ channelId, guildId, message });
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
});
|
||||
|
|
@ -439,7 +377,7 @@ describe("preflightDiscordMessage", () => {
|
|||
it("drops guild messages that mention another user when ignoreOtherMentions=true", async () => {
|
||||
const channelId = "channel-other-mention-1";
|
||||
const guildId = "guild-other-mention-1";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-other-mention-1",
|
||||
channelId,
|
||||
content: "hello <@999>",
|
||||
|
|
@ -451,18 +389,7 @@ describe("preflightDiscordMessage", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const result = await runGuildPreflight({
|
||||
channelId,
|
||||
guildId,
|
||||
message,
|
||||
discordConfig: {} as DiscordConfig,
|
||||
guildEntries: {
|
||||
[guildId]: {
|
||||
requireMention: false,
|
||||
ignoreOtherMentions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const result = await runIgnoreOtherMentionsPreflight({ channelId, guildId, message });
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
|
@ -470,7 +397,7 @@ describe("preflightDiscordMessage", () => {
|
|||
it("does not drop @everyone messages when ignoreOtherMentions=true", async () => {
|
||||
const channelId = "channel-other-mention-everyone";
|
||||
const guildId = "guild-other-mention-everyone";
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-other-mention-everyone",
|
||||
channelId,
|
||||
content: "@everyone heads up",
|
||||
|
|
@ -482,18 +409,7 @@ describe("preflightDiscordMessage", () => {
|
|||
},
|
||||
});
|
||||
|
||||
const result = await runGuildPreflight({
|
||||
channelId,
|
||||
guildId,
|
||||
message,
|
||||
discordConfig: {} as DiscordConfig,
|
||||
guildEntries: {
|
||||
[guildId]: {
|
||||
requireMention: false,
|
||||
ignoreOtherMentions: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const result = await runIgnoreOtherMentionsPreflight({ channelId, guildId, message });
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.hasAnyMention).toBe(true);
|
||||
|
|
@ -503,7 +419,7 @@ describe("preflightDiscordMessage", () => {
|
|||
const channelId = "channel-everyone-1";
|
||||
const guildId = "guild-everyone-1";
|
||||
const client = createGuildTextClient(channelId);
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-everyone-1",
|
||||
channelId,
|
||||
content: "@everyone heads up",
|
||||
|
|
@ -517,7 +433,7 @@ describe("preflightDiscordMessage", () => {
|
|||
|
||||
const result = await preflightDiscordMessage({
|
||||
...createPreflightArgs({
|
||||
cfg: DEFAULT_CFG,
|
||||
cfg: DEFAULT_PREFLIGHT_CFG,
|
||||
discordConfig: {
|
||||
allowBots: true,
|
||||
} as DiscordConfig,
|
||||
|
|
@ -546,7 +462,7 @@ describe("preflightDiscordMessage", () => {
|
|||
const channelId = "channel-audio-1";
|
||||
const client = createGuildTextClient(channelId);
|
||||
|
||||
const message = createMessage({
|
||||
const message = createDiscordMessage({
|
||||
id: "m-audio-1",
|
||||
channelId,
|
||||
content: "",
|
||||
|
|
@ -568,7 +484,7 @@ describe("preflightDiscordMessage", () => {
|
|||
const result = await preflightDiscordMessage(
|
||||
createPreflightArgs({
|
||||
cfg: {
|
||||
...DEFAULT_CFG,
|
||||
...DEFAULT_PREFLIGHT_CFG,
|
||||
messages: {
|
||||
groupChat: {
|
||||
mentionPatterns: ["openclaw"],
|
||||
|
|
|
|||
Loading…
Reference in New Issue