import { describe, expect, it } from "vitest"; import { allowListMatches, type DiscordGuildEntryResolved, normalizeDiscordAllowList, normalizeDiscordSlug, resolveDiscordChannelConfig, resolveDiscordGuildEntry, resolveGroupDmAllow, resolveDiscordReplyTarget, } from "./monitor.js"; const fakeGuild = (id: string, name: string) => ({ id, name }) as unknown as import("discord.js").Guild; const makeEntries = ( entries: Record>, ): Record => { const out: Record = {}; for (const [key, value] of Object.entries(entries)) { out[key] = { slug: value.slug, requireMention: value.requireMention, users: value.users, channels: value.channels, }; } return out; }; describe("discord allowlist helpers", () => { it("normalizes slugs", () => { expect(normalizeDiscordSlug("Friends of Clawd")).toBe("friends-of-clawd"); expect(normalizeDiscordSlug("#General")).toBe("general"); expect(normalizeDiscordSlug("Dev__Chat")).toBe("dev-chat"); }); it("matches ids or names", () => { const allow = normalizeDiscordAllowList( ["123", "steipete", "Friends of Clawd"], ["discord:", "user:", "guild:", "channel:"], ); expect(allow).not.toBeNull(); if (!allow) { throw new Error("Expected allow list to be normalized"); } expect(allowListMatches(allow, { id: "123" })).toBe(true); expect(allowListMatches(allow, { name: "steipete" })).toBe(true); expect(allowListMatches(allow, { name: "friends-of-clawd" })).toBe(true); expect(allowListMatches(allow, { name: "other" })).toBe(false); }); }); describe("discord guild/channel resolution", () => { it("resolves guild entry by id", () => { const guildEntries = makeEntries({ "123": { slug: "friends-of-clawd" }, }); const resolved = resolveDiscordGuildEntry({ guild: fakeGuild("123", "Friends of Clawd"), guildEntries, }); expect(resolved?.id).toBe("123"); expect(resolved?.slug).toBe("friends-of-clawd"); }); it("resolves guild entry by slug key", () => { const guildEntries = makeEntries({ "friends-of-clawd": { slug: "friends-of-clawd" }, }); const resolved = resolveDiscordGuildEntry({ guild: fakeGuild("123", "Friends of Clawd"), guildEntries, }); expect(resolved?.id).toBe("123"); expect(resolved?.slug).toBe("friends-of-clawd"); }); it("falls back to wildcard guild entry", () => { const guildEntries = makeEntries({ "*": { requireMention: false }, }); const resolved = resolveDiscordGuildEntry({ guild: fakeGuild("123", "Friends of Clawd"), guildEntries, }); expect(resolved?.id).toBe("123"); expect(resolved?.requireMention).toBe(false); }); it("resolves channel config by slug", () => { const guildInfo: DiscordGuildEntryResolved = { channels: { general: { allow: true }, help: { allow: true, requireMention: true }, }, }; const channel = resolveDiscordChannelConfig({ guildInfo, channelId: "456", channelName: "General", channelSlug: "general", }); expect(channel?.allowed).toBe(true); expect(channel?.requireMention).toBeUndefined(); const help = resolveDiscordChannelConfig({ guildInfo, channelId: "789", channelName: "Help", channelSlug: "help", }); expect(help?.allowed).toBe(true); expect(help?.requireMention).toBe(true); }); it("denies channel when config present but no match", () => { const guildInfo: DiscordGuildEntryResolved = { channels: { general: { allow: true }, }, }; const channel = resolveDiscordChannelConfig({ guildInfo, channelId: "999", channelName: "random", channelSlug: "random", }); expect(channel?.allowed).toBe(false); }); }); describe("discord group DM gating", () => { it("allows all when no allowlist", () => { expect( resolveGroupDmAllow({ channels: undefined, channelId: "1", channelName: "dm", channelSlug: "dm", }), ).toBe(true); }); it("matches group DM allowlist", () => { expect( resolveGroupDmAllow({ channels: ["clawd-dm"], channelId: "1", channelName: "Clawd DM", channelSlug: "clawd-dm", }), ).toBe(true); expect( resolveGroupDmAllow({ channels: ["clawd-dm"], channelId: "1", channelName: "Other", channelSlug: "other", }), ).toBe(false); }); }); describe("discord reply target selection", () => { it("skips replies when mode is off", () => { expect( resolveDiscordReplyTarget({ replyToMode: "off", replyToId: "123", hasReplied: false, }), ).toBeUndefined(); }); it("replies only once when mode is first", () => { expect( resolveDiscordReplyTarget({ replyToMode: "first", replyToId: "123", hasReplied: false, }), ).toBe("123"); expect( resolveDiscordReplyTarget({ replyToMode: "first", replyToId: "123", hasReplied: true, }), ).toBeUndefined(); }); it("replies on every message when mode is all", () => { expect( resolveDiscordReplyTarget({ replyToMode: "all", replyToId: "123", hasReplied: false, }), ).toBe("123"); expect( resolveDiscordReplyTarget({ replyToMode: "all", replyToId: "123", hasReplied: true, }), ).toBe("123"); }); });