From 79a8905fa468933a5fbdc524d7faf1d45645ca35 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Mon, 16 Mar 2026 01:32:33 -0700 Subject: [PATCH] Channels: centralize group policy contracts --- .../src/monitor/provider.group-policy.test.ts | 22 ----- .../src/monitor/provider.group-policy.test.ts | 13 --- .../src/monitor/provider.group-policy.test.ts | 13 --- .../src/group-access.group-policy.test.ts | 13 --- .../access-control.group-policy.test.ts | 13 --- .../zalo/src/monitor.group-policy.test.ts | 12 --- .../contracts/group-policy.contract.test.ts | 94 +++++++++++++++++++ .../runtime-group-policy-contract.ts | 43 --------- 8 files changed, 94 insertions(+), 129 deletions(-) delete mode 100644 extensions/discord/src/monitor/provider.group-policy.test.ts delete mode 100644 extensions/imessage/src/monitor/provider.group-policy.test.ts delete mode 100644 extensions/slack/src/monitor/provider.group-policy.test.ts delete mode 100644 extensions/telegram/src/group-access.group-policy.test.ts delete mode 100644 extensions/whatsapp/src/inbound/access-control.group-policy.test.ts create mode 100644 src/channels/plugins/contracts/group-policy.contract.test.ts delete mode 100644 src/test-utils/runtime-group-policy-contract.ts diff --git a/extensions/discord/src/monitor/provider.group-policy.test.ts b/extensions/discord/src/monitor/provider.group-policy.test.ts deleted file mode 100644 index 995c6f66e31..00000000000 --- a/extensions/discord/src/monitor/provider.group-policy.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { describe, expect, it } from "vitest"; -import { installProviderRuntimeGroupPolicyFallbackSuite } from "../../../../src/test-utils/runtime-group-policy-contract.js"; -import { __testing } from "./provider.js"; - -describe("resolveDiscordRuntimeGroupPolicy", () => { - installProviderRuntimeGroupPolicyFallbackSuite({ - resolve: __testing.resolveDiscordRuntimeGroupPolicy, - configuredLabel: "keeps open default when channels.discord is configured", - defaultGroupPolicyUnderTest: "open", - missingConfigLabel: "fails closed when channels.discord is missing and no defaults are set", - missingDefaultLabel: "ignores explicit global defaults when provider config is missing", - }); - - it("respects explicit provider policy", () => { - const resolved = __testing.resolveDiscordRuntimeGroupPolicy({ - providerConfigPresent: false, - groupPolicy: "disabled", - }); - expect(resolved.groupPolicy).toBe("disabled"); - expect(resolved.providerMissingFallbackApplied).toBe(false); - }); -}); diff --git a/extensions/imessage/src/monitor/provider.group-policy.test.ts b/extensions/imessage/src/monitor/provider.group-policy.test.ts deleted file mode 100644 index d6a7b1f880b..00000000000 --- a/extensions/imessage/src/monitor/provider.group-policy.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe } from "vitest"; -import { installProviderRuntimeGroupPolicyFallbackSuite } from "../../../../src/test-utils/runtime-group-policy-contract.js"; -import { __testing } from "./monitor-provider.js"; - -describe("resolveIMessageRuntimeGroupPolicy", () => { - installProviderRuntimeGroupPolicyFallbackSuite({ - resolve: __testing.resolveIMessageRuntimeGroupPolicy, - configuredLabel: "keeps open fallback when channels.imessage is configured", - defaultGroupPolicyUnderTest: "disabled", - missingConfigLabel: "fails closed when channels.imessage is missing and no defaults are set", - missingDefaultLabel: "ignores explicit global defaults when provider config is missing", - }); -}); diff --git a/extensions/slack/src/monitor/provider.group-policy.test.ts b/extensions/slack/src/monitor/provider.group-policy.test.ts deleted file mode 100644 index 392003ad5f5..00000000000 --- a/extensions/slack/src/monitor/provider.group-policy.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe } from "vitest"; -import { installProviderRuntimeGroupPolicyFallbackSuite } from "../../../../src/test-utils/runtime-group-policy-contract.js"; -import { __testing } from "./provider.js"; - -describe("resolveSlackRuntimeGroupPolicy", () => { - installProviderRuntimeGroupPolicyFallbackSuite({ - resolve: __testing.resolveSlackRuntimeGroupPolicy, - configuredLabel: "keeps open default when channels.slack is configured", - defaultGroupPolicyUnderTest: "open", - missingConfigLabel: "fails closed when channels.slack is missing and no defaults are set", - missingDefaultLabel: "ignores explicit global defaults when provider config is missing", - }); -}); diff --git a/extensions/telegram/src/group-access.group-policy.test.ts b/extensions/telegram/src/group-access.group-policy.test.ts deleted file mode 100644 index 8b93c52d160..00000000000 --- a/extensions/telegram/src/group-access.group-policy.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe } from "vitest"; -import { installProviderRuntimeGroupPolicyFallbackSuite } from "../../../src/test-utils/runtime-group-policy-contract.js"; -import { resolveTelegramRuntimeGroupPolicy } from "./group-access.js"; - -describe("resolveTelegramRuntimeGroupPolicy", () => { - installProviderRuntimeGroupPolicyFallbackSuite({ - resolve: resolveTelegramRuntimeGroupPolicy, - configuredLabel: "keeps open fallback when channels.telegram is configured", - defaultGroupPolicyUnderTest: "disabled", - missingConfigLabel: "fails closed when channels.telegram is missing and no defaults are set", - missingDefaultLabel: "ignores explicit defaults when provider config is missing", - }); -}); diff --git a/extensions/whatsapp/src/inbound/access-control.group-policy.test.ts b/extensions/whatsapp/src/inbound/access-control.group-policy.test.ts deleted file mode 100644 index 0a508f9739b..00000000000 --- a/extensions/whatsapp/src/inbound/access-control.group-policy.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { describe } from "vitest"; -import { installProviderRuntimeGroupPolicyFallbackSuite } from "../../../../src/test-utils/runtime-group-policy-contract.js"; -import { __testing } from "./access-control.js"; - -describe("resolveWhatsAppRuntimeGroupPolicy", () => { - installProviderRuntimeGroupPolicyFallbackSuite({ - resolve: __testing.resolveWhatsAppRuntimeGroupPolicy, - configuredLabel: "keeps open fallback when channels.whatsapp is configured", - defaultGroupPolicyUnderTest: "disabled", - missingConfigLabel: "fails closed when channels.whatsapp is missing and no defaults are set", - missingDefaultLabel: "ignores explicit default policy when provider config is missing", - }); -}); diff --git a/extensions/zalo/src/monitor.group-policy.test.ts b/extensions/zalo/src/monitor.group-policy.test.ts index 2ce0b1be2a2..7a44caab83d 100644 --- a/extensions/zalo/src/monitor.group-policy.test.ts +++ b/extensions/zalo/src/monitor.group-policy.test.ts @@ -2,18 +2,6 @@ import { describe, expect, it } from "vitest"; import { __testing } from "./monitor.js"; describe("zalo group policy access", () => { - it("defaults missing provider config to allowlist", () => { - const resolved = __testing.resolveZaloRuntimeGroupPolicy({ - providerConfigPresent: false, - groupPolicy: undefined, - defaultGroupPolicy: "open", - }); - expect(resolved).toEqual({ - groupPolicy: "allowlist", - providerMissingFallbackApplied: true, - }); - }); - it("blocks all group messages when policy is disabled", () => { const decision = __testing.evaluateZaloGroupAccess({ providerConfigPresent: true, diff --git a/src/channels/plugins/contracts/group-policy.contract.test.ts b/src/channels/plugins/contracts/group-policy.contract.test.ts new file mode 100644 index 00000000000..51a9c178170 --- /dev/null +++ b/src/channels/plugins/contracts/group-policy.contract.test.ts @@ -0,0 +1,94 @@ +import { describe, expect, it } from "vitest"; +import { __testing as discordTesting } from "../../../../extensions/discord/src/monitor/provider.js"; +import { __testing as imessageTesting } from "../../../../extensions/imessage/src/monitor/monitor-provider.js"; +import { __testing as slackTesting } from "../../../../extensions/slack/src/monitor/provider.js"; +import { resolveTelegramRuntimeGroupPolicy } from "../../../../extensions/telegram/src/group-access.js"; +import { __testing as whatsappTesting } from "../../../../extensions/whatsapp/src/inbound/access-control.js"; +import { __testing as zaloTesting } from "../../../../extensions/zalo/src/monitor.js"; +import { installChannelRuntimeGroupPolicyFallbackSuite } from "./suites.js"; + +describe("channel runtime group policy contract", () => { + describe("slack", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: slackTesting.resolveSlackRuntimeGroupPolicy, + configuredLabel: "keeps open default when channels.slack is configured", + defaultGroupPolicyUnderTest: "open", + missingConfigLabel: "fails closed when channels.slack is missing and no defaults are set", + missingDefaultLabel: "ignores explicit global defaults when provider config is missing", + }); + }); + + describe("telegram", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: resolveTelegramRuntimeGroupPolicy, + configuredLabel: "keeps open fallback when channels.telegram is configured", + defaultGroupPolicyUnderTest: "disabled", + missingConfigLabel: "fails closed when channels.telegram is missing and no defaults are set", + missingDefaultLabel: "ignores explicit defaults when provider config is missing", + }); + }); + + describe("whatsapp", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: whatsappTesting.resolveWhatsAppRuntimeGroupPolicy, + configuredLabel: "keeps open fallback when channels.whatsapp is configured", + defaultGroupPolicyUnderTest: "disabled", + missingConfigLabel: "fails closed when channels.whatsapp is missing and no defaults are set", + missingDefaultLabel: "ignores explicit global defaults when provider config is missing", + }); + }); + + describe("imessage", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: imessageTesting.resolveIMessageRuntimeGroupPolicy, + configuredLabel: "keeps open fallback when channels.imessage is configured", + defaultGroupPolicyUnderTest: "disabled", + missingConfigLabel: "fails closed when channels.imessage is missing and no defaults are set", + missingDefaultLabel: "ignores explicit global defaults when provider config is missing", + }); + }); + + describe("discord", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: discordTesting.resolveDiscordRuntimeGroupPolicy, + configuredLabel: "keeps open default when channels.discord is configured", + defaultGroupPolicyUnderTest: "open", + missingConfigLabel: "fails closed when channels.discord is missing and no defaults are set", + missingDefaultLabel: "ignores explicit global defaults when provider config is missing", + }); + + it("respects explicit provider policy", () => { + const resolved = discordTesting.resolveDiscordRuntimeGroupPolicy({ + providerConfigPresent: false, + groupPolicy: "disabled", + }); + expect(resolved.groupPolicy).toBe("disabled"); + expect(resolved.providerMissingFallbackApplied).toBe(false); + }); + }); + + describe("zalo", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: zaloTesting.resolveZaloRuntimeGroupPolicy, + configuredLabel: "keeps open fallback when channels.zalo is configured", + defaultGroupPolicyUnderTest: "open", + missingConfigLabel: "fails closed when channels.zalo is missing and no defaults are set", + missingDefaultLabel: "ignores explicit global defaults when provider config is missing", + }); + + it("keeps provider-owned group access evaluation", () => { + const decision = zaloTesting.evaluateZaloGroupAccess({ + providerConfigPresent: true, + configuredGroupPolicy: "allowlist", + defaultGroupPolicy: "open", + groupAllowFrom: ["zl:12345"], + senderId: "12345", + }); + expect(decision).toMatchObject({ + allowed: true, + groupPolicy: "allowlist", + reason: "allowed", + }); + }); + }); +}); diff --git a/src/test-utils/runtime-group-policy-contract.ts b/src/test-utils/runtime-group-policy-contract.ts deleted file mode 100644 index 65a0e0b8ef3..00000000000 --- a/src/test-utils/runtime-group-policy-contract.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { expect, it } from "vitest"; -import type { - ResolveProviderRuntimeGroupPolicyParams, - RuntimeGroupPolicyResolution, -} from "../config/runtime-group-policy.js"; -import type { GroupPolicy } from "../config/types.base.js"; - -type RuntimeGroupPolicyResolver = ( - params: ResolveProviderRuntimeGroupPolicyParams, -) => RuntimeGroupPolicyResolution; - -export function installProviderRuntimeGroupPolicyFallbackSuite(params: { - configuredLabel: string; - defaultGroupPolicyUnderTest: GroupPolicy; - missingConfigLabel: string; - missingDefaultLabel: string; - resolve: RuntimeGroupPolicyResolver; -}) { - it(params.missingConfigLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: false, - }); - expect(resolved.groupPolicy).toBe("allowlist"); - expect(resolved.providerMissingFallbackApplied).toBe(true); - }); - - it(params.configuredLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: true, - }); - expect(resolved.groupPolicy).toBe("open"); - expect(resolved.providerMissingFallbackApplied).toBe(false); - }); - - it(params.missingDefaultLabel, () => { - const resolved = params.resolve({ - providerConfigPresent: false, - defaultGroupPolicy: params.defaultGroupPolicyUnderTest, - }); - expect(resolved.groupPolicy).toBe("allowlist"); - expect(resolved.providerMissingFallbackApplied).toBe(true); - }); -}