test(whatsapp): cover inbound admission contract

This commit is contained in:
Marcus Castro
2026-06-14 17:50:13 -03:00
committed by Shakker
parent 2758140607
commit 2cc25aa909
2 changed files with 214 additions and 0 deletions

View File

@@ -1,5 +1,9 @@
// Whatsapp tests cover access control plugin behavior.
import { beforeAll, describe, expect, it } from "vitest";
import type {
AcceptedInboundAccessControlResult,
InboundAccessControlResult,
} from "./access-control.js";
import {
readAllowFromStoreMock,
sendMessageMock,
@@ -19,6 +23,15 @@ beforeAll(async () => {
({ resolveWhatsAppCommandAuthorized } = await import("../inbound-policy.js"));
});
function expectAccepted(
result: InboundAccessControlResult,
): asserts result is AcceptedInboundAccessControlResult {
expect(result.allowed).toBe(true);
if (!result.allowed) {
throw new Error("Expected accepted inbound access result");
}
}
async function checkUnauthorizedWorkDmSender() {
return checkInboundAccessControl({
cfg: getAccessControlTestConfig() as never,
@@ -92,6 +105,190 @@ async function checkCommandAuthorizedForGroup(params: {
});
}
describe("checkInboundAccessControl admission contract", () => {
it("keeps blocked results on the legacy flat access shape", async () => {
const cfg = {
channels: {
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15559999999"],
},
},
};
setAccessControlTestConfig(cfg);
const result = await checkInboundAccessControl({
cfg: getAccessControlTestConfig() as never,
accountId: "default",
from: "+15550001111",
selfE164: "+15550009999",
senderE164: "+15550001111",
group: false,
pushName: "Stranger",
isFromMe: false,
sock: { sendMessage: sendMessageMock },
remoteJid: "15550001111@s.whatsapp.net",
});
expect(result).toMatchObject({
allowed: false,
shouldMarkRead: false,
resolvedAccountId: "default",
isSelfChat: false,
});
expect("admission" in result).toBe(false);
});
it("returns accepted facts through admission while preserving legacy access fields", async () => {
const cfg = {
channels: {
whatsapp: {
dmPolicy: "allowlist",
contextVisibility: "allowlist_quote",
allowFrom: ["+15550001111"],
direct: {
"+15550001111": {
systemPrompt: "direct prompt",
},
},
},
},
};
setAccessControlTestConfig(cfg);
const result = await checkInboundAccessControl({
cfg: getAccessControlTestConfig() as never,
accountId: "default",
from: "+15550001111",
selfE164: "+15550009999",
senderE164: "+15550001111",
group: false,
pushName: "Sam",
isFromMe: false,
sock: { sendMessage: sendMessageMock },
remoteJid: "15550001111@s.whatsapp.net",
});
expectAccepted(result);
expect(result.resolvedAccountId).toBe(result.admission.accountId);
expect(result.isSelfChat).toBe(result.admission.isSelfChat);
expect(result.shouldMarkRead).toBe(true);
expect(result.admission).toMatchObject({
accountId: "default",
account: {
accountId: "default",
enabled: true,
sendReadReceipts: true,
},
conversation: {
kind: "direct",
id: "+15550001111",
groupSessionId: "+15550001111",
},
sender: {
id: "+15550001111",
dmSenderId: "+15550001111",
isSamePhone: false,
isDmSenderSamePhone: false,
},
ingress: {
admission: "dispatch",
decision: "allow",
reasonCode: "activation_allowed",
},
senderAccess: {
allowed: true,
decision: "allow",
providerMissingFallbackApplied: false,
reasonCode: "dm_policy_allowlisted",
},
commandAccess: {
requested: false,
authorized: false,
shouldBlockControlCommand: false,
reasonCode: "command_authorized",
},
activationAccess: {
ran: true,
allowed: true,
shouldSkip: false,
reasonCode: "activation_allowed",
},
});
expect(result.admission.account).not.toHaveProperty("authDir");
expect(result.admission.conversation).not.toHaveProperty("requireMention");
expect(result.admission.senderAccess).not.toHaveProperty("effectiveAllowFrom");
expect(result.admission.senderAccess).not.toHaveProperty("effectiveGroupAllowFrom");
expect(result.admission).not.toHaveProperty("resolvedPolicy");
});
it("uses group participant JID as the admission sender fallback", async () => {
const groupJid = "120363401234567890@g.us";
const participantJid = "15550001111@s.whatsapp.net";
const cfg = {
channels: {
whatsapp: {
groupPolicy: "open",
},
},
};
setAccessControlTestConfig(cfg);
const result = await checkInboundAccessControl({
cfg: getAccessControlTestConfig() as never,
accountId: "default",
from: groupJid,
selfE164: "+15550009999",
senderE164: null,
senderJid: participantJid,
group: true,
pushName: "Sam",
isFromMe: false,
sock: { sendMessage: sendMessageMock },
remoteJid: groupJid,
});
expectAccepted(result);
expect(result.admission.conversation).toMatchObject({
kind: "group",
id: groupJid,
groupSessionId: groupJid,
});
expect(result.admission.sender.id).toBe(participantJid);
expect(result.admission.sender.dmSenderId).toBe(groupJid);
});
it("does not authorize unresolved group participant JIDs as phone allowlist entries", async () => {
const groupJid = "120363401234567890@g.us";
const cfg = {
channels: {
whatsapp: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15550001111"],
},
},
};
setAccessControlTestConfig(cfg);
const result = await checkInboundAccessControl({
cfg: getAccessControlTestConfig() as never,
accountId: "default",
from: groupJid,
selfE164: "+15550009999",
senderE164: null,
senderJid: "15550001111@lid",
group: true,
pushName: "Sam",
isFromMe: false,
sock: { sendMessage: sendMessageMock },
remoteJid: groupJid,
});
expect(result.allowed).toBe(false);
expect("admission" in result).toBe(false);
});
});
describe("checkInboundAccessControl pairing grace", () => {
async function runPairingGraceCase(messageTimestampMs: number) {
const connectedAtMs = 1_000_000;

View File

@@ -225,6 +225,23 @@ describe("web monitor inbox", () => {
expect(inbound.payload.body).toBe("ping");
expect(inbound.from).toBe("+999");
expect(inbound.platform.recipientJid).toBe("+123");
expect(inbound.admission).toMatchObject({
accountId: DEFAULT_ACCOUNT_ID,
conversation: {
kind: "direct",
id: "+999",
},
sender: {
id: "+999",
dmSenderId: "+999",
},
senderAccess: {
allowed: true,
decision: "allow",
},
});
expect(inbound.accountId).toBe(inbound.admission?.accountId);
expect(inbound.chatType).toBe(inbound.admission?.conversation.kind);
expect(sock.readMessages).toHaveBeenCalledWith([
{
remoteJid: "999@s.whatsapp.net",