Files
calendar/packages/lib/getReplyToHeader.test.ts
T
fbf6510dd8 fix: join Reply-To addresses as string for SMTP compatibility (#28611)
* fix: join multiple Reply-To addresses as comma-separated string

Some SMTP providers (e.g., SendLayer) reject emails when Reply-To is
passed as an array to nodemailer, which serializes it as multiple
Reply-To headers. Using a comma-joined string is RFC 2822 compliant
and works universally across all SMTP providers.

Fixes #28610

* test: add unit tests for getReplyToHeader SMTP compatibility

- Verify replyTo is always returned as comma-separated string, not array
- Test single email, multiple emails, and empty email cases
- Add RFC 5322 compliance test for SMTP compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: add RFC 5322 reference comment to getReplyToHeader tests

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Lawrence Christian <67164412+LCNDevs@users.noreply.github.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
Co-authored-by: Romit <romitgabani1.work@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-28 13:08:52 +00:00

93 lines
3.3 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { getReplyToHeader } from "./getReplyToHeader";
/**
* RFC 5322 (Internet Message Format) specifies that the Reply-To header must be
* a comma-separated list of addresses, not an array. Many SMTP servers reject arrays.
* Spec: https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2
*/
vi.mock("./getReplyToEmail", () => ({
getReplyToEmail: vi.fn((calEvent, excludeOrganizerEmail) => {
if (excludeOrganizerEmail) return null;
return calEvent.organizer?.email || null;
}),
}));
const createMockCalEvent = (organizerEmail: string) => ({
organizer: { email: organizerEmail },
hideOrganizerEmail: false,
});
describe("getReplyToHeader", () => {
describe("return type", () => {
it("always returns replyTo as a string, never an array", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any, ["attendee1@test.com", "attendee2@test.com"]);
expect(result).toHaveProperty("replyTo");
expect(typeof result.replyTo).toBe("string");
// Should NOT be an array
expect(Array.isArray(result.replyTo)).toBe(false);
});
});
describe("with single email", () => {
it("returns single email as string", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any);
expect(result).toEqual({ replyTo: "organizer@test.com" });
});
it("returns additionalEmail as string when provided alone", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any, "additional@test.com", true);
expect(result).toEqual({ replyTo: "additional@test.com" });
});
});
describe("with multiple emails", () => {
it("returns comma-separated string for multiple emails", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any, ["attendee1@test.com", "attendee2@test.com"]);
expect(result).toEqual({
replyTo: "attendee1@test.com, attendee2@test.com, organizer@test.com",
});
});
it("returns comma-separated string when additionalEmails is array", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any, ["a@test.com", "b@test.com", "c@test.com"], true);
expect(result).toEqual({
replyTo: "a@test.com, b@test.com, c@test.com",
});
});
});
describe("with no emails", () => {
it("returns empty object when no emails available", () => {
const calEvent = { organizer: { email: "" }, hideOrganizerEmail: true };
const result = getReplyToHeader(calEvent as any, undefined, true);
expect(result).toEqual({});
});
});
describe("SMTP compatibility", () => {
it("produces RFC 5322 compliant Reply-To header format", () => {
const calEvent = createMockCalEvent("organizer@test.com");
const result = getReplyToHeader(calEvent as any, ["a@test.com", "b@test.com"]);
// RFC 5322 specifies comma-separated list for multiple addresses
expect(result.replyTo).toMatch(/^[^,]+, [^,]+, [^,]+$/);
expect(result.replyTo).not.toContain("[");
expect(result.replyTo).not.toContain("]");
});
});
});