import { describe, expect, it } from "vitest" ;
import {
formatAllowFromLowercase,
formatNormalizedAllowFromEntries,
isAllowedParsedChatSender,
isNormalizedSenderAllowed,
mapAllowlistResolutionInputs,
} from "./allow-from.js" ;
function parseAllowTarget(
entry: string,
):
| { kind: "chat_id" ; chatId: number }
| { kind: "chat_guid" ; chatGuid: string }
| { kind: "chat_identifier" ; chatIdentifier: string }
| { kind: "handle" ; handle: string } {
const trimmed = entry.trim();
const lower = trimmed.toLowerCase();
if (lower.startsWith("chat_id:" )) {
return { kind: "chat_id" , chatId: Number.parseInt(trimmed.slice("chat_id:" .length), 10 ) };
}
if (lower.startsWith("chat_guid:" )) {
return { kind: "chat_guid" , chatGuid: trimmed.slice("chat_guid:" .length) };
}
if (lower.startsWith("chat_identifier:" )) {
return {
kind: "chat_identifier" ,
chatIdentifier: trimmed.slice("chat_identifier:" .length),
};
}
return { kind: "handle" , handle: lower };
}
describe("isAllowedParsedChatSender" , () => {
it.each([
{
name: "denies when allowFrom is empty" ,
input: {
allowFrom: [],
sender: "+15551234567" ,
normalizeSender: (sender: string) => sender,
parseAllowTarget,
},
expected: false ,
},
{
name: "allows wildcard entries" ,
input: {
allowFrom: ["*" ],
sender: "user@example.com" ,
normalizeSender: (sender: string) => sender.toLowerCase(),
parseAllowTarget,
},
expected: true ,
},
{
name: "matches normalized handles" ,
input: {
allowFrom: ["User@Example.com" ],
sender: "user@example.com" ,
normalizeSender: (sender: string) => sender.toLowerCase(),
parseAllowTarget,
},
expected: true ,
},
{
name: "matches chat IDs when provided" ,
input: {
allowFrom: ["chat_id:42" ],
sender: "+15551234567" ,
chatId: 42 ,
normalizeSender: (sender: string) => sender,
parseAllowTarget,
},
expected: true ,
},
])("$name" , ({ input, expected }) => {
expect(isAllowedParsedChatSender(input)).toBe(expected);
});
});
describe("isNormalizedSenderAllowed" , () => {
it.each([
{
name: "allows wildcard" ,
input: {
senderId: "attacker" ,
allowFrom: ["*" ],
},
expected: true ,
},
{
name: "normalizes case and strips prefixes" ,
input: {
senderId: "12345" ,
allowFrom: ["ZALO:12345" , "zl:777" ],
stripPrefixRe: /^(zalo|zl):/i,
},
expected: true ,
},
{
name: "rejects when sender is missing" ,
input: {
senderId: "999" ,
allowFrom: ["zl:12345" ],
stripPrefixRe: /^(zalo|zl):/i,
},
expected: false ,
},
])("$name" , ({ input, expected }) => {
expect(isNormalizedSenderAllowed(input)).toBe(expected);
});
});
describe("formatAllowFromLowercase" , () => {
it("trims, strips prefixes, and lowercases entries" , () => {
expect(
formatAllowFromLowercase({
allowFrom: [" Telegram:UserA " , "tg:UserB" , " " ],
stripPrefixRe: /^(telegram|tg):/i,
}),
).toEqual(["usera" , "userb" ]);
});
});
describe("formatNormalizedAllowFromEntries" , () => {
it.each([
{
name: "applies custom normalization after trimming" ,
input: {
allowFrom: [" @Alice " , "" , " @Bob " ],
normalizeEntry: (entry: string) => entry.replace(/^@/, "" ).toLowerCase(),
},
expected: ["alice" , "bob" ],
},
{
name: "filters empty normalized entries" ,
input: {
allowFrom: ["@" , "valid" ],
normalizeEntry: (entry: string) => entry.replace(/^@$/, "" ),
},
expected: ["valid" ],
},
])("$name" , ({ input, expected }) => {
expect(formatNormalizedAllowFromEntries(input)).toEqual(expected);
});
});
describe("mapAllowlistResolutionInputs" , () => {
it("maps inputs sequentially and preserves order" , async () => {
const visited: string[] = [];
const result = await mapAllowlistResolutionInputs({
inputs: ["one" , "two" , "three" ],
mapInput: async (input) => {
visited.push(input);
return input.toUpperCase();
},
});
expect(visited).toEqual(["one" , "two" , "three" ]);
expect(result).toEqual(["ONE" , "TWO" , "THREE" ]);
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.33 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland