From dcef96e6d452d17283f9d291a38b4922a95ab92e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 22 Mar 2026 17:38:25 -0700 Subject: [PATCH] test(nextcloud-talk): cover signature and format helpers --- extensions/nextcloud-talk/src/format.test.ts | 36 ++++++++++ .../nextcloud-talk/src/signature.test.ts | 66 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 extensions/nextcloud-talk/src/format.test.ts create mode 100644 extensions/nextcloud-talk/src/signature.test.ts diff --git a/extensions/nextcloud-talk/src/format.test.ts b/extensions/nextcloud-talk/src/format.test.ts new file mode 100644 index 00000000000..a376e39f39e --- /dev/null +++ b/extensions/nextcloud-talk/src/format.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "vitest"; +import { + escapeNextcloudTalkMarkdown, + formatNextcloudTalkCodeBlock, + formatNextcloudTalkInlineCode, + formatNextcloudTalkMention, + markdownToNextcloudTalk, + stripNextcloudTalkFormatting, + truncateNextcloudTalkText, +} from "./format.js"; + +describe("nextcloud talk format helpers", () => { + it("keeps markdown mostly intact while trimming outer whitespace", () => { + expect(markdownToNextcloudTalk(" **hello** ")).toBe("**hello**"); + }); + + it("escapes markdown-sensitive characters", () => { + expect(escapeNextcloudTalkMarkdown("*hello* [x](y)")).toBe("\\*hello\\* \\[x\\]\\(y\\)"); + }); + + it("formats mentions and code consistently", () => { + expect(formatNextcloudTalkMention("@alice")).toBe("@alice"); + expect(formatNextcloudTalkMention("bob")).toBe("@bob"); + expect(formatNextcloudTalkCodeBlock("const x = 1;", "ts")).toBe("```ts\nconst x = 1;\n```"); + expect(formatNextcloudTalkInlineCode("x")).toBe("`x`"); + expect(formatNextcloudTalkInlineCode("x ` y")).toBe("`` x ` y ``"); + }); + + it("strips markdown formatting and truncates on word boundaries", () => { + expect( + stripNextcloudTalkFormatting("**bold** [link](https://example.com) `code`"), + ).toBe("bold link"); + expect(truncateNextcloudTalkText("alpha beta gamma delta", 14)).toBe("alpha beta..."); + expect(truncateNextcloudTalkText("short", 14)).toBe("short"); + }); +}); diff --git a/extensions/nextcloud-talk/src/signature.test.ts b/extensions/nextcloud-talk/src/signature.test.ts new file mode 100644 index 00000000000..71bac8ea908 --- /dev/null +++ b/extensions/nextcloud-talk/src/signature.test.ts @@ -0,0 +1,66 @@ +import { describe, expect, it } from "vitest"; +import { + extractNextcloudTalkHeaders, + generateNextcloudTalkSignature, + verifyNextcloudTalkSignature, +} from "./signature.js"; + +describe("nextcloud talk signature helpers", () => { + it("verifies generated signatures against the same body and secret", () => { + const body = JSON.stringify({ hello: "world" }); + const generated = generateNextcloudTalkSignature({ + body, + secret: "secret-123", + }); + + expect(generated.random).toMatch(/^[0-9a-f]{64}$/); + expect(generated.signature).toMatch(/^[0-9a-f]{64}$/); + expect( + verifyNextcloudTalkSignature({ + signature: generated.signature, + random: generated.random, + body, + secret: "secret-123", + }), + ).toBe(true); + }); + + it("rejects missing fields and mismatched signatures", () => { + expect( + verifyNextcloudTalkSignature({ + signature: "", + random: "abc", + body: "body", + secret: "secret", + }), + ).toBe(false); + expect( + verifyNextcloudTalkSignature({ + signature: "deadbeef", + random: "abc", + body: "body", + secret: "secret", + }), + ).toBe(false); + }); + + it("extracts normalized webhook headers", () => { + expect( + extractNextcloudTalkHeaders({ + "x-nextcloud-talk-signature": "sig", + "x-nextcloud-talk-random": "rand", + "x-nextcloud-talk-backend": "backend", + }), + ).toEqual({ + signature: "sig", + random: "rand", + backend: "backend", + }); + + expect( + extractNextcloudTalkHeaders({ + "X-Nextcloud-Talk-Signature": "sig", + }), + ).toBeNull(); + }); +});