diff --git a/src/channels/channel-helpers.test.ts b/src/channels/channel-helpers.test.ts index 89837fe42ec..b6d3ff4fbd8 100644 --- a/src/channels/channel-helpers.test.ts +++ b/src/channels/channel-helpers.test.ts @@ -88,62 +88,71 @@ describe("channel targets", () => { }); describe("resolveConversationLabel", () => { - it("prefers ConversationLabel when present", () => { - const ctx: MsgContext = { ConversationLabel: "Pinned Label", ChatType: "group" }; - expect(resolveConversationLabel(ctx)).toBe("Pinned Label"); - }); + const cases: Array<{ name: string; ctx: MsgContext; expected: string }> = [ + { + name: "prefers ConversationLabel when present", + ctx: { ConversationLabel: "Pinned Label", ChatType: "group" }, + expected: "Pinned Label", + }, + { + name: "prefers ThreadLabel over derived chat labels", + ctx: { + ThreadLabel: "Thread Alpha", + ChatType: "group", + GroupSubject: "Ops", + From: "telegram:group:42", + }, + expected: "Thread Alpha", + }, + { + name: "uses SenderName for direct chats when available", + ctx: { ChatType: "direct", SenderName: "Ada", From: "telegram:99" }, + expected: "Ada", + }, + { + name: "falls back to From for direct chats when SenderName is missing", + ctx: { ChatType: "direct", From: "telegram:99" }, + expected: "telegram:99", + }, + { + name: "derives Telegram-like group labels with numeric id suffix", + ctx: { ChatType: "group", GroupSubject: "Ops", From: "telegram:group:42" }, + expected: "Ops id:42", + }, + { + name: "does not append ids for #rooms/channels", + ctx: { + ChatType: "channel", + GroupSubject: "#general", + From: "slack:channel:C123", + }, + expected: "#general", + }, + { + name: "does not append ids when the base already contains the id", + ctx: { + ChatType: "group", + GroupSubject: "Family id:123@g.us", + From: "whatsapp:group:123@g.us", + }, + expected: "Family id:123@g.us", + }, + { + name: "appends ids for WhatsApp-like group ids when a subject exists", + ctx: { + ChatType: "group", + GroupSubject: "Family", + From: "whatsapp:group:123@g.us", + }, + expected: "Family id:123@g.us", + }, + ]; - it("prefers ThreadLabel over derived chat labels", () => { - const ctx: MsgContext = { - ThreadLabel: "Thread Alpha", - ChatType: "group", - GroupSubject: "Ops", - From: "telegram:group:42", - }; - expect(resolveConversationLabel(ctx)).toBe("Thread Alpha"); - }); - - it("uses SenderName for direct chats when available", () => { - const ctx: MsgContext = { ChatType: "direct", SenderName: "Ada", From: "telegram:99" }; - expect(resolveConversationLabel(ctx)).toBe("Ada"); - }); - - it("falls back to From for direct chats when SenderName is missing", () => { - const ctx: MsgContext = { ChatType: "direct", From: "telegram:99" }; - expect(resolveConversationLabel(ctx)).toBe("telegram:99"); - }); - - it("derives Telegram-like group labels with numeric id suffix", () => { - const ctx: MsgContext = { ChatType: "group", GroupSubject: "Ops", From: "telegram:group:42" }; - expect(resolveConversationLabel(ctx)).toBe("Ops id:42"); - }); - - it("does not append ids for #rooms/channels", () => { - const ctx: MsgContext = { - ChatType: "channel", - GroupSubject: "#general", - From: "slack:channel:C123", - }; - expect(resolveConversationLabel(ctx)).toBe("#general"); - }); - - it("does not append ids when the base already contains the id", () => { - const ctx: MsgContext = { - ChatType: "group", - GroupSubject: "Family id:123@g.us", - From: "whatsapp:group:123@g.us", - }; - expect(resolveConversationLabel(ctx)).toBe("Family id:123@g.us"); - }); - - it("appends ids for WhatsApp-like group ids when a subject exists", () => { - const ctx: MsgContext = { - ChatType: "group", - GroupSubject: "Family", - From: "whatsapp:group:123@g.us", - }; - expect(resolveConversationLabel(ctx)).toBe("Family id:123@g.us"); - }); + for (const testCase of cases) { + it(testCase.name, () => { + expect(resolveConversationLabel(testCase.ctx)).toBe(testCase.expected); + }); + } }); describe("createTypingCallbacks", () => { diff --git a/src/channels/channels-misc.test.ts b/src/channels/channels-misc.test.ts index 3eb51c509ac..1bc3e74db2f 100644 --- a/src/channels/channels-misc.test.ts +++ b/src/channels/channels-misc.test.ts @@ -16,24 +16,26 @@ describe("channel-web barrel", () => { }); describe("normalizeChatType", () => { - it("normalizes common inputs", () => { - expect(normalizeChatType("direct")).toBe("direct"); - expect(normalizeChatType("dm")).toBe("direct"); - expect(normalizeChatType("group")).toBe("group"); - expect(normalizeChatType("channel")).toBe("channel"); - }); + const cases: Array<{ name: string; value: string | undefined; expected: string | undefined }> = [ + { name: "normalizes direct", value: "direct", expected: "direct" }, + { name: "normalizes dm alias", value: "dm", expected: "direct" }, + { name: "normalizes group", value: "group", expected: "group" }, + { name: "normalizes channel", value: "channel", expected: "channel" }, + { name: "returns undefined for undefined", value: undefined, expected: undefined }, + { name: "returns undefined for empty", value: "", expected: undefined }, + { name: "returns undefined for unknown value", value: "nope", expected: undefined }, + { name: "returns undefined for unsupported room", value: "room", expected: undefined }, + ]; - it("returns undefined for empty/unknown values", () => { - expect(normalizeChatType(undefined)).toBeUndefined(); - expect(normalizeChatType("")).toBeUndefined(); - expect(normalizeChatType("nope")).toBeUndefined(); - expect(normalizeChatType("room")).toBeUndefined(); - }); + for (const testCase of cases) { + it(testCase.name, () => { + expect(normalizeChatType(testCase.value)).toBe(testCase.expected); + }); + } describe("backward compatibility", () => { - it("accepts legacy 'dm' value and normalizes to 'direct'", () => { - // Legacy config/input may use "dm" - ensure smooth upgrade path - expect(normalizeChatType("dm")).toBe("direct"); + it("accepts legacy 'dm' value shape variants and normalizes to 'direct'", () => { + // Legacy config/input may use "dm" with non-canonical casing/spacing. expect(normalizeChatType("DM")).toBe("direct"); expect(normalizeChatType(" dm ")).toBe("direct"); }); diff --git a/src/web/auto-reply/web-auto-reply-utils.test.ts b/src/web/auto-reply/web-auto-reply-utils.test.ts index 8ff11a091ec..ec4d67b591a 100644 --- a/src/web/auto-reply/web-auto-reply-utils.test.ts +++ b/src/web/auto-reply/web-auto-reply-utils.test.ts @@ -224,21 +224,6 @@ describe("web auto-reply util", () => { }); describe("isLikelyWhatsAppCryptoError", () => { - it("returns false for non-matching reasons", () => { - expect(isLikelyWhatsAppCryptoError(new Error("boom"))).toBe(false); - expect(isLikelyWhatsAppCryptoError("boom")).toBe(false); - expect(isLikelyWhatsAppCryptoError({ message: "bad mac" })).toBe(false); - }); - - it("matches known Baileys crypto auth errors (string)", () => { - expect( - isLikelyWhatsAppCryptoError( - "baileys: unsupported state or unable to authenticate data (noise-handler)", - ), - ).toBe(true); - expect(isLikelyWhatsAppCryptoError("bad mac in aesDecryptGCM (baileys)")).toBe(true); - }); - it("matches known Baileys crypto auth errors (Error)", () => { const err = new Error("bad mac"); err.stack = "at something\nat @whiskeysockets/baileys/noise-handler\n"; @@ -251,13 +236,40 @@ describe("web auto-reply util", () => { expect(isLikelyWhatsAppCryptoError(circular)).toBe(false); }); - it("handles non-string reasons without throwing", () => { - expect(isLikelyWhatsAppCryptoError(null)).toBe(false); - expect(isLikelyWhatsAppCryptoError(123)).toBe(false); - expect(isLikelyWhatsAppCryptoError(true)).toBe(false); - expect(isLikelyWhatsAppCryptoError(123n)).toBe(false); - expect(isLikelyWhatsAppCryptoError(Symbol("bad mac"))).toBe(false); - expect(isLikelyWhatsAppCryptoError(function namedFn() {})).toBe(false); - }); + const cases: Array<{ name: string; value: unknown; expected: boolean }> = [ + { name: "returns false for non-matching Error", value: new Error("boom"), expected: false }, + { name: "returns false for non-matching string", value: "boom", expected: false }, + { + name: "returns false for bad-mac object without whatsapp/baileys markers", + value: { message: "bad mac" }, + expected: false, + }, + { + name: "matches known Baileys crypto auth errors (string, unsupported state)", + value: "baileys: unsupported state or unable to authenticate data (noise-handler)", + expected: true, + }, + { + name: "matches known Baileys crypto auth errors (string, bad mac)", + value: "bad mac in aesDecryptGCM (baileys)", + expected: true, + }, + { name: "handles null reason without throwing", value: null, expected: false }, + { name: "handles number reason without throwing", value: 123, expected: false }, + { name: "handles boolean reason without throwing", value: true, expected: false }, + { name: "handles bigint reason without throwing", value: 123n, expected: false }, + { name: "handles symbol reason without throwing", value: Symbol("bad mac"), expected: false }, + { + name: "handles function reason without throwing", + value: function namedFn() {}, + expected: false, + }, + ]; + + for (const testCase of cases) { + it(testCase.name, () => { + expect(isLikelyWhatsAppCryptoError(testCase.value)).toBe(testCase.expected); + }); + } }); }); diff --git a/test/gateway.multi.e2e.test.ts b/test/gateway.multi.e2e.test.ts index 3b033616e59..bca47ced553 100644 --- a/test/gateway.multi.e2e.test.ts +++ b/test/gateway.multi.e2e.test.ts @@ -29,9 +29,12 @@ type NodeListPayload = { nodes?: Array<{ nodeId?: string; connected?: boolean; paired?: boolean }>; }; -const GATEWAY_START_TIMEOUT_MS = 45_000; +const GATEWAY_START_TIMEOUT_MS = 20_000; const GATEWAY_STOP_TIMEOUT_MS = 1_500; const E2E_TIMEOUT_MS = 120_000; +const GATEWAY_CONNECT_STATUS_TIMEOUT_MS = 2_000; +const GATEWAY_NODE_STATUS_TIMEOUT_MS = 4_000; +const GATEWAY_NODE_STATUS_POLL_MS = 20; const getFreePort = async () => { const srv = net.createServer(); @@ -80,7 +83,7 @@ const waitForPortOpen = async ( // keep polling } - await sleep(25); + await sleep(10); } const stdout = chunksOut.join(""); const stderr = chunksErr.join(""); @@ -265,7 +268,7 @@ const connectNode = async ( const connectStatusClient = async ( inst: GatewayInstance, - timeoutMs = 5_000, + timeoutMs = GATEWAY_CONNECT_STATUS_TIMEOUT_MS, ): Promise => { let settled = false; let timer: NodeJS.Timeout | null = null; @@ -312,9 +315,16 @@ const connectStatusClient = async ( }); }; -const waitForNodeStatus = async (inst: GatewayInstance, nodeId: string, timeoutMs = 10_000) => { +const waitForNodeStatus = async ( + inst: GatewayInstance, + nodeId: string, + timeoutMs = GATEWAY_NODE_STATUS_TIMEOUT_MS, +) => { const deadline = Date.now() + timeoutMs; - const client = await connectStatusClient(inst); + const client = await connectStatusClient( + inst, + Math.min(GATEWAY_CONNECT_STATUS_TIMEOUT_MS, timeoutMs), + ); try { while (Date.now() < deadline) { const list = await client.request("node.list", {}); @@ -322,7 +332,7 @@ const waitForNodeStatus = async (inst: GatewayInstance, nodeId: string, timeoutM if (match?.connected && match?.paired) { return; } - await sleep(50); + await sleep(GATEWAY_NODE_STATUS_POLL_MS); } } finally { client.stop();