import { describe, expect, it } from "vitest"; import { buildChannelKeyCandidates, normalizeChannelSlug, resolveChannelEntryMatch, resolveChannelEntryMatchWithFallback, resolveNestedAllowlistDecision, applyChannelMatchMeta, resolveChannelMatchConfig, } from "./channel-config.js"; describe("buildChannelKeyCandidates", () => { it("dedupes and trims keys", () => { expect(buildChannelKeyCandidates(" a ", "a", "", "b", "b")).toEqual(["a", "b"]); }); }); describe("normalizeChannelSlug", () => { it("normalizes names into slugs", () => { expect(normalizeChannelSlug("My Team")).toBe("my-team"); expect(normalizeChannelSlug("#General Chat")).toBe("general-chat"); expect(normalizeChannelSlug(" Dev__Chat ")).toBe("dev-chat"); }); }); describe("resolveChannelEntryMatch", () => { it("returns matched entry and wildcard metadata", () => { const entries = { a: { allow: true }, "*": { allow: false } }; const match = resolveChannelEntryMatch({ entries, keys: ["missing", "a"], wildcardKey: "*", }); expect(match.entry).toBe(entries.a); expect(match.key).toBe("a"); expect(match.wildcardEntry).toBe(entries["*"]); expect(match.wildcardKey).toBe("*"); }); }); describe("resolveChannelEntryMatchWithFallback", () => { it("prefers direct matches over parent and wildcard", () => { const entries = { a: { allow: true }, parent: { allow: false }, "*": { allow: false } }; const match = resolveChannelEntryMatchWithFallback({ entries, keys: ["a"], parentKeys: ["parent"], wildcardKey: "*", }); expect(match.entry).toBe(entries.a); expect(match.matchSource).toBe("direct"); expect(match.matchKey).toBe("a"); }); it("falls back to parent when direct misses", () => { const entries = { parent: { allow: false }, "*": { allow: true } }; const match = resolveChannelEntryMatchWithFallback({ entries, keys: ["missing"], parentKeys: ["parent"], wildcardKey: "*", }); expect(match.entry).toBe(entries.parent); expect(match.matchSource).toBe("parent"); expect(match.matchKey).toBe("parent"); }); it("falls back to wildcard when no direct or parent match", () => { const entries = { "*": { allow: true } }; const match = resolveChannelEntryMatchWithFallback({ entries, keys: ["missing"], parentKeys: ["still-missing"], wildcardKey: "*", }); expect(match.entry).toBe(entries["*"]); expect(match.matchSource).toBe("wildcard"); expect(match.matchKey).toBe("*"); }); it("matches normalized keys when normalizeKey is provided", () => { const entries = { "My Team": { allow: true } }; const match = resolveChannelEntryMatchWithFallback({ entries, keys: ["my-team"], normalizeKey: normalizeChannelSlug, }); expect(match.entry).toBe(entries["My Team"]); expect(match.matchSource).toBe("direct"); expect(match.matchKey).toBe("My Team"); }); }); describe("applyChannelMatchMeta", () => { it("copies match metadata onto resolved configs", () => { const resolved = applyChannelMatchMeta( { allowed: true }, { matchKey: "general", matchSource: "direct" }, ); expect(resolved.matchKey).toBe("general"); expect(resolved.matchSource).toBe("direct"); }); }); describe("resolveChannelMatchConfig", () => { it("returns null when no entry is matched", () => { const resolved = resolveChannelMatchConfig({ matchKey: "x" }, () => ({ allowed: true })); expect(resolved).toBeNull(); }); it("resolves entry and applies match metadata", () => { const resolved = resolveChannelMatchConfig( { entry: { allow: true }, matchKey: "*", matchSource: "wildcard" }, () => ({ allowed: true }), ); expect(resolved?.matchKey).toBe("*"); expect(resolved?.matchSource).toBe("wildcard"); }); }); describe("resolveNestedAllowlistDecision", () => { it("allows when outer allowlist is disabled", () => { expect( resolveNestedAllowlistDecision({ outerConfigured: false, outerMatched: false, innerConfigured: false, innerMatched: false, }), ).toBe(true); }); it("blocks when outer allowlist is configured but missing match", () => { expect( resolveNestedAllowlistDecision({ outerConfigured: true, outerMatched: false, innerConfigured: false, innerMatched: false, }), ).toBe(false); }); it("requires inner match when inner allowlist is configured", () => { expect( resolveNestedAllowlistDecision({ outerConfigured: true, outerMatched: true, innerConfigured: true, innerMatched: false, }), ).toBe(false); expect( resolveNestedAllowlistDecision({ outerConfigured: true, outerMatched: true, innerConfigured: true, innerMatched: true, }), ).toBe(true); }); });