Files
ihasmail/web/src/store/__tests__/participants.test.ts
T
jcoffey-dev a2337f6ad8 Use an example address, and the right name, in the test fixtures
Two things, one of them not what it looked like.

An organizer fixture was built from a real, routable address. Every other
fixture in the codebase uses example.org or example.com, and this
repository is public, so that one was a personal address sitting in
public source for no reason -- the test asserts roles and participation
status and never reads either value. It is [email protected] now.

The names were wrong in the other direction. Three fixtures across two
files said "John Ellis", which is not the maintainer's name; it is
John Coffey. Being a name rather than a routable address, it leaked
nothing, but it was simply incorrect, and incorrect in the sort of place
nobody rereads.

The address and the name are separate questions and got separate answers:
the address is fictional because it is an address, and the name is real
because it is right. A message from [email protected] signed John Coffey
is exactly what these tests mean.

Found while checking, at the maintainer's prompting, whether the repo
leaked anything about the host it runs on. It does not -- the nginx and
deploy files here are the generic examples they claim to be, and the real
ones live in a private repository.
2026-08-27 14:15:00 -07:00

59 lines
3.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { participantAddresses, participantEmail, isAttendee, eventRule, makeParticipant } from "@/store/calendar";
import type { CalendarEvent, JSCalendarParticipant } from "@/jmap/types";
/**
* Stalwart 0.16.19 and RFC 8984 disagree about where a participant's address
* lives. Sent the RFC's way, Stalwart keeps the event and drops the participant
* map without a word — guests vanished and no invitation was ever sent (#26).
* Shapes below are what a live 0.16.19 returned.
*/
const p = (o: Partial<JSCalendarParticipant>): JSCalendarParticipant => ({ roles: {}, ...o });
const ev = (o: Partial<CalendarEvent>): CalendarEvent => ({ id: "e1", "@type": "Event", uid: "u1", calendarIds: { c1: true }, start: "2030-01-01T10:00:00", ...o } as CalendarEvent);
describe("participant addresses", () => {
it("reads Stalwart's calendarAddress", () => {
expect(participantEmail(p({ calendarAddress: "mailto:[email protected]" }))).toBe("[email protected]");
});
it("still reads the RFC 8984 spellings, for events written by other clients", () => {
expect(participantEmail(p({ sendTo: { imip: "mailto:[email protected]" } }))).toBe("[email protected]");
expect(participantEmail(p({ email: "[email protected]" }))).toBe("[email protected]");
expect(participantAddresses(p({ calendarAddress: "mailto:[email protected]", email: "[email protected]" }))).toEqual(["mailto:[email protected]", "mailto:[email protected]"]);
});
it("has no address to offer when the participant carries none", () => {
expect(participantEmail(p({ name: "Nameless" }))).toBe("");
});
it("counts a participant as attending under either role name", () => {
expect(isAttendee(p({ roles: { attendee: true } }))).toBe(true);
expect(isAttendee(p({ roles: { required: true } }))).toBe(true); // what Stalwart writes for REQ-PARTICIPANT
expect(isAttendee(p({ roles: { optional: true } }))).toBe(true);
expect(isAttendee(p({ roles: { owner: true } }))).toBe(false);
});
});
describe("makeParticipant", () => {
it("addresses a guest the way Stalwart stores them", () => {
const guest = makeParticipant("[email protected]", "Guest", "attendee");
expect(guest.calendarAddress).toBe("mailto:[email protected]");
expect(guest.sendTo).toBeUndefined();
expect(guest.roles).toEqual({ attendee: true, required: true });
expect(guest.participationStatus).toBe("needs-action");
expect(guest.expectReply).toBe(true);
});
it("marks the organizer as owner and keeps a status already given", () => {
const me = makeParticipant("[email protected]", "John Coffey", "owner");
expect(me.roles).toEqual({ owner: true, attendee: true });
expect(me.participationStatus).toBe("accepted");
expect(me.expectReply).toBe(false);
expect(makeParticipant("[email protected]", null, "attendee", "declined").participationStatus).toBe("declined");
});
});
describe("eventRule", () => {
it("reads Stalwart's singular rule and the RFC's array", () => {
expect(eventRule(ev({ recurrenceRule: { "@type": "RecurrenceRule", frequency: "weekly" } }))?.frequency).toBe("weekly");
expect(eventRule(ev({ recurrenceRules: [{ "@type": "RecurrenceRule", frequency: "daily" }] }))?.frequency).toBe("daily");
expect(eventRule(ev({}))).toBeUndefined();
});
});