From 50e71daaa0619fdb88739af2d36d63748bb2e51a Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Fri, 17 Apr 2026 15:48:32 -0400 Subject: [PATCH] test: keep inbound group policy tests hermetic --- ...ound.group-require-mention-test-plugins.ts | 118 ++++++++++++++++++ src/auto-reply/inbound.test.ts | 16 ++- 2 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 src/auto-reply/inbound.group-require-mention-test-plugins.ts diff --git a/src/auto-reply/inbound.group-require-mention-test-plugins.ts b/src/auto-reply/inbound.group-require-mention-test-plugins.ts new file mode 100644 index 00000000000..13c78cb0c14 --- /dev/null +++ b/src/auto-reply/inbound.group-require-mention-test-plugins.ts @@ -0,0 +1,118 @@ +import type { OpenClawConfig } from "../config/config.js"; +import { setActivePluginRegistry } from "../plugins/runtime.js"; +import { createChannelTestPluginBase, createTestRegistry } from "../test-utils/channel-plugins.js"; + +type TestChannelGroupContext = { + cfg: OpenClawConfig; + groupId?: string | null; + groupChannel?: string | null; + groupSpace?: string | null; + accountId?: string | null; +}; + +function normalizeTestSlug(raw?: string | null): string { + return raw?.trim().replace(/^#/, "").toLowerCase() ?? ""; +} + +function resolveDiscordRequireMentionForTest(params: TestChannelGroupContext): boolean { + const discordCfg = params.cfg.channels?.discord as + | { + guilds?: Record< + string, + { + requireMention?: boolean; + slug?: string; + channels?: Record; + } + >; + } + | undefined; + const guilds = discordCfg?.guilds; + if (!guilds) { + return true; + } + const space = params.groupSpace?.trim() ?? ""; + const spaceSlug = normalizeTestSlug(space); + const guild = + (space ? guilds[space] : undefined) ?? + (spaceSlug ? guilds[spaceSlug] : undefined) ?? + Object.values(guilds).find((entry) => normalizeTestSlug(entry?.slug) === spaceSlug) ?? + guilds["*"]; + const channelSlug = normalizeTestSlug(params.groupChannel); + const channel = + (params.groupId ? guild?.channels?.[params.groupId] : undefined) ?? + (channelSlug ? guild?.channels?.[channelSlug] : undefined) ?? + (channelSlug ? guild?.channels?.[`#${channelSlug}`] : undefined); + return channel?.requireMention ?? guild?.requireMention ?? true; +} + +function resolveSlackRequireMentionForTest(params: TestChannelGroupContext): boolean { + const slackCfg = params.cfg.channels?.slack as + | { + defaultAccount?: string; + channels?: Record; + accounts?: Record }>; + } + | undefined; + if (!slackCfg) { + return true; + } + const accountId = params.accountId ?? slackCfg.defaultAccount; + const channels = + (accountId ? slackCfg.accounts?.[accountId]?.channels : undefined) ?? slackCfg.channels; + if (!channels) { + return true; + } + const channelName = params.groupChannel?.trim().replace(/^#/, ""); + const channelSlug = normalizeTestSlug(channelName); + const candidates = [ + params.groupId?.trim(), + channelName ? `#${channelName}` : undefined, + channelName, + channelSlug, + "*", + ]; + for (const candidate of candidates) { + if (!candidate) { + continue; + } + const entry = channels[candidate]; + if (typeof entry?.requireMention === "boolean") { + return entry.requireMention; + } + } + return true; +} + +export function installGroupRequireMentionTestPlugins() { + setActivePluginRegistry( + createTestRegistry([ + { + pluginId: "discord", + plugin: { + ...createChannelTestPluginBase({ id: "discord" }), + groups: { resolveRequireMention: resolveDiscordRequireMentionForTest }, + }, + source: "test", + }, + { + pluginId: "slack", + plugin: { + ...createChannelTestPluginBase({ id: "slack" }), + groups: { resolveRequireMention: resolveSlackRequireMentionForTest }, + }, + source: "test", + }, + { + pluginId: "line", + plugin: createChannelTestPluginBase({ id: "line" }), + source: "test", + }, + { + pluginId: "bluebubbles", + plugin: createChannelTestPluginBase({ id: "bluebubbles" }), + source: "test", + }, + ]), + ); +} diff --git a/src/auto-reply/inbound.test.ts b/src/auto-reply/inbound.test.ts index d8bd1d76be9..c83cc4bfb89 100644 --- a/src/auto-reply/inbound.test.ts +++ b/src/auto-reply/inbound.test.ts @@ -1,11 +1,12 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { describe, expect, it, vi } from "vitest"; +import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import type { GroupKeyResolution } from "../config/sessions.js"; import { resetPluginRuntimeStateForTest } from "../plugins/runtime.js"; import { createInboundDebouncer } from "./inbound-debounce.js"; +import { installGroupRequireMentionTestPlugins } from "./inbound.group-require-mention-test-plugins.js"; import { resolveGroupRequireMention } from "./reply/groups.js"; import { finalizeInboundContext } from "./reply/inbound-context.js"; import { @@ -809,8 +810,12 @@ describe("mention helpers", () => { }); describe("resolveGroupRequireMention", () => { - it("respects Discord guild/channel requireMention settings", async () => { + beforeEach(() => { resetPluginRuntimeStateForTest(); + installGroupRequireMentionTestPlugins(); + }); + + it("respects Discord guild/channel requireMention settings", async () => { const cfg: OpenClawConfig = { channels: { discord: { @@ -841,7 +846,6 @@ describe("resolveGroupRequireMention", () => { }); it("respects Slack channel requireMention settings", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { slack: { @@ -867,7 +871,6 @@ describe("resolveGroupRequireMention", () => { }); it("uses Slack fallback resolver semantics for default-account wildcard channels", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { slack: { @@ -898,7 +901,6 @@ describe("resolveGroupRequireMention", () => { }); it("keeps core reply-stage resolution aligned for Slack default-account wildcard fallbacks", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { slack: { @@ -929,7 +931,6 @@ describe("resolveGroupRequireMention", () => { }); it("uses Discord fallback resolver semantics for guild slug matches", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { discord: { @@ -959,7 +960,6 @@ describe("resolveGroupRequireMention", () => { }); it("keeps core reply-stage resolution aligned for Discord slug + wildcard guild fallbacks", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { discord: { @@ -991,7 +991,6 @@ describe("resolveGroupRequireMention", () => { }); it("respects LINE prefixed group keys in reply-stage requireMention resolution", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { line: { @@ -1016,7 +1015,6 @@ describe("resolveGroupRequireMention", () => { }); it("preserves plugin-backed channel requireMention resolution", async () => { - resetPluginRuntimeStateForTest(); const cfg: OpenClawConfig = { channels: { bluebubbles: {