import { describe, expect, it } from "vitest"; import { createScopedAccountConfigAccessors, createScopedChannelConfigAdapter, createScopedChannelConfigBase, createScopedDmSecurityResolver, createHybridChannelConfigAdapter, createTopLevelChannelConfigAdapter, createTopLevelChannelConfigBase, createHybridChannelConfigBase, mapAllowFromEntries, resolveOptionalConfigString, } from "./channel-config-helpers.js"; describe("mapAllowFromEntries", () => { it("coerces allowFrom entries to strings", () => { expect(mapAllowFromEntries(["user", 42])).toEqual(["user", "42"]); }); it("returns empty list for missing input", () => { expect(mapAllowFromEntries(undefined)).toEqual([]); }); }); describe("resolveOptionalConfigString", () => { it("trims and returns string values", () => { expect(resolveOptionalConfigString(" room:123 ")).toBe("room:123"); }); it("coerces numeric values", () => { expect(resolveOptionalConfigString(123)).toBe("123"); }); it("returns undefined for empty values", () => { expect(resolveOptionalConfigString(" ")).toBeUndefined(); expect(resolveOptionalConfigString(undefined)).toBeUndefined(); }); }); describe("createScopedAccountConfigAccessors", () => { it("maps allowFrom and defaultTo from the resolved account", () => { const accessors = createScopedAccountConfigAccessors({ resolveAccount: ({ accountId }) => ({ allowFrom: accountId ? [accountId, 42] : ["fallback"], defaultTo: " room:123 ", }), resolveAllowFrom: (account) => account.allowFrom, formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry).toUpperCase()), resolveDefaultTo: (account) => account.defaultTo, }); expect( accessors.resolveAllowFrom?.({ cfg: {}, accountId: "owner", }), ).toEqual(["owner", "42"]); expect( accessors.formatAllowFrom?.({ cfg: {}, allowFrom: ["owner"], }), ).toEqual(["OWNER"]); expect( accessors.resolveDefaultTo?.({ cfg: {}, accountId: "owner", }), ).toBe("room:123"); }); it("omits resolveDefaultTo when no selector is provided", () => { const accessors = createScopedAccountConfigAccessors({ resolveAccount: () => ({ allowFrom: ["owner"] }), resolveAllowFrom: (account) => account.allowFrom, formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry)), }); expect(accessors.resolveDefaultTo).toBeUndefined(); }); }); describe("createScopedChannelConfigBase", () => { it("wires shared account config CRUD through the section helper", () => { const base = createScopedChannelConfigBase({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default" }), defaultAccountId: () => "default", clearBaseFields: ["token"], }); expect(base.listAccountIds({})).toEqual(["default", "alt"]); expect(base.resolveAccount({}, "alt")).toEqual({ accountId: "alt" }); expect(base.defaultAccountId!({})).toBe("default"); expect( base.setAccountEnabled!({ cfg: {}, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ enabled: true }); expect( base.deleteAccount!({ cfg: { channels: { demo: { token: "secret", }, }, }, accountId: "default", }).channels, ).toBeUndefined(); }); it("can force default account config into accounts.default", () => { const base = createScopedChannelConfigBase({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default" }), defaultAccountId: () => "default", clearBaseFields: [], allowTopLevel: false, }); expect( base.setAccountEnabled!({ cfg: { channels: { demo: { token: "secret", }, }, }, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ token: "secret", accounts: { default: { enabled: true }, }, }); expect( base.deleteAccount!({ cfg: { channels: { demo: { token: "secret", accounts: { default: { enabled: true }, }, }, }, }, accountId: "default", }).channels?.demo, ).toEqual({ token: "secret", accounts: undefined, }); }); }); describe("createScopedChannelConfigAdapter", () => { it("combines scoped CRUD and allowFrom accessors", () => { const adapter = createScopedChannelConfigAdapter({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default", allowFrom: accountId ? [accountId] : ["fallback"], defaultTo: " room:123 ", }), defaultAccountId: () => "default", clearBaseFields: ["token"], resolveAllowFrom: (account) => account.allowFrom, formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry).toUpperCase()), resolveDefaultTo: (account) => account.defaultTo, }); expect(adapter.listAccountIds({})).toEqual(["default", "alt"]); expect(adapter.resolveAccount({}, "alt")).toEqual({ accountId: "alt", allowFrom: ["alt"], defaultTo: " room:123 ", }); expect(adapter.resolveAllowFrom?.({ cfg: {}, accountId: "alt" })).toEqual(["alt"]); expect(adapter.resolveDefaultTo?.({ cfg: {}, accountId: "alt" })).toBe("room:123"); expect( adapter.setAccountEnabled!({ cfg: {}, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ enabled: true }); }); }); describe("createScopedDmSecurityResolver", () => { it("builds account-aware DM policy payloads", () => { const resolveDmPolicy = createScopedDmSecurityResolver<{ accountId?: string | null; dmPolicy?: string; allowFrom?: string[]; }>({ channelKey: "demo", resolvePolicy: (account) => account.dmPolicy, resolveAllowFrom: (account) => account.allowFrom, policyPathSuffix: "dmPolicy", normalizeEntry: (raw) => raw.toLowerCase(), }); expect( resolveDmPolicy({ cfg: { channels: { demo: { accounts: { alt: {}, }, }, }, }, accountId: "alt", account: { accountId: "alt", dmPolicy: "allowlist", allowFrom: ["Owner"], }, }), ).toEqual({ policy: "allowlist", allowFrom: ["Owner"], policyPath: "channels.demo.accounts.alt.dmPolicy", allowFromPath: "channels.demo.accounts.alt.", approveHint: "Approve via: openclaw pairing list demo / openclaw pairing approve demo ", normalizeEntry: expect.any(Function), }); }); }); describe("createTopLevelChannelConfigBase", () => { it("wires top-level enable/delete semantics", () => { const base = createTopLevelChannelConfigBase({ sectionKey: "demo", resolveAccount: () => ({ accountId: "default" }), }); expect(base.listAccountIds({})).toEqual(["default"]); expect(base.defaultAccountId!({})).toBe("default"); expect( base.setAccountEnabled!({ cfg: {}, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ enabled: true }); expect( base.deleteAccount!({ cfg: { channels: { demo: { enabled: true, }, }, }, accountId: "default", }).channels, ).toBeUndefined(); }); it("can clear only account-scoped fields while preserving channel settings", () => { const base = createTopLevelChannelConfigBase({ sectionKey: "demo", resolveAccount: () => ({ accountId: "default" }), deleteMode: "clear-fields", clearBaseFields: ["token", "allowFrom"], }); expect( base.deleteAccount!({ cfg: { channels: { demo: { token: "secret", allowFrom: ["owner"], markdown: { tables: false }, }, }, }, accountId: "default", }).channels?.demo, ).toEqual({ markdown: { tables: false }, }); }); }); describe("createTopLevelChannelConfigAdapter", () => { it("combines top-level CRUD with separate accessor account resolution", () => { const adapter = createTopLevelChannelConfigAdapter< { accountId: string; enabled: boolean }, { allowFrom: string[]; defaultTo: string } >({ sectionKey: "demo", resolveAccount: () => ({ accountId: "default", enabled: true }), resolveAccessorAccount: () => ({ allowFrom: ["owner"], defaultTo: " chat:123 " }), deleteMode: "clear-fields", clearBaseFields: ["token"], resolveAllowFrom: (account) => account.allowFrom, formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry)), resolveDefaultTo: (account) => account.defaultTo, }); expect(adapter.resolveAccount({})).toEqual({ accountId: "default", enabled: true }); expect(adapter.resolveAllowFrom?.({ cfg: {} })).toEqual(["owner"]); expect(adapter.resolveDefaultTo?.({ cfg: {} })).toBe("chat:123"); expect( adapter.deleteAccount!({ cfg: { channels: { demo: { token: "secret", markdown: { tables: false }, }, }, }, accountId: "default", }).channels?.demo, ).toEqual({ markdown: { tables: false }, }); }); }); describe("createHybridChannelConfigBase", () => { it("writes default account enable at the channel root and named accounts under accounts", () => { const base = createHybridChannelConfigBase({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default" }), defaultAccountId: () => "default", clearBaseFields: ["token"], }); expect( base.setAccountEnabled!({ cfg: { channels: { demo: { accounts: { alt: { enabled: false }, }, }, }, }, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ accounts: { alt: { enabled: false }, }, enabled: true, }); expect( base.setAccountEnabled!({ cfg: {}, accountId: "alt", enabled: true, }).channels?.demo, ).toEqual({ accounts: { alt: { enabled: true }, }, }); }); it("can preserve the section when deleting the default account", () => { const base = createHybridChannelConfigBase({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default" }), defaultAccountId: () => "default", clearBaseFields: ["token", "name"], preserveSectionOnDefaultDelete: true, }); expect( base.deleteAccount!({ cfg: { channels: { demo: { token: "secret", name: "bot", accounts: { alt: { enabled: true }, }, }, }, }, accountId: "default", }).channels?.demo, ).toEqual({ accounts: { alt: { enabled: true }, }, }); }); }); describe("createHybridChannelConfigAdapter", () => { it("combines hybrid CRUD with allowFrom/defaultTo accessors", () => { const adapter = createHybridChannelConfigAdapter< { accountId: string; enabled: boolean }, { allowFrom: string[]; defaultTo: string } >({ sectionKey: "demo", listAccountIds: () => ["default", "alt"], resolveAccount: (_cfg, accountId) => ({ accountId: accountId ?? "default", enabled: true, }), resolveAccessorAccount: ({ accountId }) => ({ allowFrom: [accountId ?? "default"], defaultTo: " room:123 ", }), defaultAccountId: () => "default", clearBaseFields: ["token"], preserveSectionOnDefaultDelete: true, resolveAllowFrom: (account) => account.allowFrom, formatAllowFrom: (allowFrom) => allowFrom.map((entry) => String(entry).toUpperCase()), resolveDefaultTo: (account) => account.defaultTo, }); expect(adapter.resolveAllowFrom?.({ cfg: {}, accountId: "alt" })).toEqual(["alt"]); expect(adapter.resolveDefaultTo?.({ cfg: {}, accountId: "alt" })).toBe("room:123"); expect( adapter.setAccountEnabled!({ cfg: {}, accountId: "default", enabled: true, }).channels?.demo, ).toEqual({ enabled: true }); expect( adapter.deleteAccount!({ cfg: { channels: { demo: { token: "secret", markdown: { tables: false }, }, }, }, accountId: "default", }).channels?.demo, ).toEqual({ markdown: { tables: false }, }); }); });