From 1d4fcb6a019697adaed330942a433470511d5b0f Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 4 Apr 2026 04:09:41 +0900 Subject: [PATCH] test(contracts): split group policy lanes --- .../group-policy.fallback.contract.test.ts | 95 +++++++++++++++++++ ...oup-policy.provider-owned.contract.test.ts | 37 ++++++++ .../channels/group-policy-contract-suites.ts | 49 ++++++++++ 3 files changed, 181 insertions(+) create mode 100644 src/channels/plugins/contracts/group-policy.fallback.contract.test.ts create mode 100644 src/channels/plugins/contracts/group-policy.provider-owned.contract.test.ts create mode 100644 test/helpers/channels/group-policy-contract-suites.ts diff --git a/src/channels/plugins/contracts/group-policy.fallback.contract.test.ts b/src/channels/plugins/contracts/group-policy.fallback.contract.test.ts new file mode 100644 index 00000000000..4d54bdf64d0 --- /dev/null +++ b/src/channels/plugins/contracts/group-policy.fallback.contract.test.ts @@ -0,0 +1,95 @@ +import { describe, it } from "vitest"; +import { whatsappAccessControlTesting } from "../../../../extensions/whatsapp/api.js"; +import { resolveZaloRuntimeGroupPolicy } from "../../../../extensions/zalo/api.js"; +import { + expectResolvedGroupPolicyCase, + installChannelRuntimeGroupPolicyFallbackSuite, +} from "../../../../test/helpers/channels/group-policy-contract-suites.js"; +import { resolveOpenProviderRuntimeGroupPolicy } from "../../../config/runtime-group-policy.js"; + +type ResolvedGroupPolicy = ReturnType; + +function expectResolvedDiscordGroupPolicyCase(params: { + providerConfigPresent: Parameters< + typeof resolveOpenProviderRuntimeGroupPolicy + >[0]["providerConfigPresent"]; + groupPolicy: Parameters[0]["groupPolicy"]; + expected: Pick; +}) { + expectResolvedGroupPolicyCase(resolveOpenProviderRuntimeGroupPolicy(params), params.expected); +} + +describe("channel runtime group policy fallback contract", () => { + describe("slack", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: resolveOpenProviderRuntimeGroupPolicy, + 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: resolveOpenProviderRuntimeGroupPolicy, + 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: whatsappAccessControlTesting.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: resolveOpenProviderRuntimeGroupPolicy, + 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: resolveOpenProviderRuntimeGroupPolicy, + 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.each([ + { + providerConfigPresent: false, + groupPolicy: "disabled", + expected: { + groupPolicy: "disabled", + providerMissingFallbackApplied: false, + }, + }, + ] as const)("respects explicit provider policy %#", (testCase) => { + expectResolvedDiscordGroupPolicyCase(testCase); + }); + }); + + describe("zalo", () => { + installChannelRuntimeGroupPolicyFallbackSuite({ + resolve: 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", + }); + }); +}); diff --git a/src/channels/plugins/contracts/group-policy.provider-owned.contract.test.ts b/src/channels/plugins/contracts/group-policy.provider-owned.contract.test.ts new file mode 100644 index 00000000000..daed6d1c562 --- /dev/null +++ b/src/channels/plugins/contracts/group-policy.provider-owned.contract.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; +import { evaluateZaloGroupAccess } from "../../../../extensions/zalo/api.js"; + +function expectAllowedZaloGroupAccess(params: Parameters[0]) { + expect(evaluateZaloGroupAccess(params)).toMatchObject({ + allowed: true, + groupPolicy: "allowlist", + reason: "allowed", + }); +} + +function expectAllowedZaloGroupAccessCase( + params: Omit[0], "groupAllowFrom"> & { + groupAllowFrom: readonly string[]; + }, +) { + expectAllowedZaloGroupAccess({ + ...params, + groupAllowFrom: [...params.groupAllowFrom], + }); +} + +describe("channel runtime group policy provider-owned contract", () => { + describe("zalo", () => { + it.each([ + { + providerConfigPresent: true, + configuredGroupPolicy: "allowlist", + defaultGroupPolicy: "open", + groupAllowFrom: ["zl:12345"], + senderId: "12345", + }, + ] as const)("keeps provider-owned group access evaluation %#", (testCase) => { + expectAllowedZaloGroupAccessCase(testCase); + }); + }); +}); diff --git a/test/helpers/channels/group-policy-contract-suites.ts b/test/helpers/channels/group-policy-contract-suites.ts new file mode 100644 index 00000000000..5bff04d4c76 --- /dev/null +++ b/test/helpers/channels/group-policy-contract-suites.ts @@ -0,0 +1,49 @@ +import { expect, it } from "vitest"; +import { resolveOpenProviderRuntimeGroupPolicy } from "../../../src/config/runtime-group-policy.js"; + +type ResolvedGroupPolicy = ReturnType; + +export type RuntimeGroupPolicyResolver = ( + params: Parameters[0], +) => ReturnType; + +export function installChannelRuntimeGroupPolicyFallbackSuite(params: { + configuredLabel: string; + defaultGroupPolicyUnderTest: "allowlist" | "disabled" | "open"; + 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); + }); +} + +export function expectResolvedGroupPolicyCase( + resolved: Pick, + expected: Pick, +) { + expect(resolved.groupPolicy).toBe(expected.groupPolicy); + expect(resolved.providerMissingFallbackApplied).toBe(expected.providerMissingFallbackApplied); +}