import { CalendarEvent } from "@/features/Events/EventsTypes"; import { updateAttendeesAfterTimeChange } from "@/features/Events/updateEventHelpers/updateAttendeesAfterTimeChange"; import { userAttendee } from "@/features/User/models/attendee"; import { userOrganiser } from "@/features/User/userDataTypes"; describe("updateAttendeesAfterTimeChange", () => { const mockAttendee: userAttendee = { cal_address: "attendee@example.com", partstat: "ACCEPTED", rsvp: "FALSE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cn: "attendee", }; const mockOrganizer: userOrganiser = { cn: "Organizer", cal_address: "organizer@example.com", }; const organizerAttendee: userAttendee = { cal_address: "organizer@example.com", partstat: "ACCEPTED", rsvp: "FALSE", role: "CHAIR", cutype: "INDIVIDUAL", cn: "organizer", }; const baseEvent: CalendarEvent = { uid: "test-event", URL: "/calendar/667037022b752d0026472254/cal1/test-event.ics", title: "Test Event", calId: "667037022b752d0026472254/cal1", start: "2025-01-15T07:00:00.000Z", end: "2025-01-15T08:00:00.000Z", allday: false, organizer: mockOrganizer, attendee: [mockAttendee, organizerAttendee], timezone: "ETC/UTC", }; describe("early returns", () => { it("should return the event unchanged when attendee list is undefined", () => { const event = { ...baseEvent, attendee: undefined }; const result = updateAttendeesAfterTimeChange( event as unknown as CalendarEvent ); expect(result).toBe(event); }); it("should NOT bail out early when organizer is undefined", () => { const event = { ...baseEvent, organizer: undefined }; const result = updateAttendeesAfterTimeChange(event); expect(result.attendee).toBeDefined(); expect(result.attendee).toHaveLength(2); }); }); describe("without provided attendees list", () => { it("should return attendees unchanged when timeChanged is false", () => { const result = updateAttendeesAfterTimeChange(baseEvent, false); expect(result.attendee?.[0].partstat).toBe("ACCEPTED"); expect(result.attendee?.[0].rsvp).toBe("FALSE"); }); it("should mark non-organizer attendees as NEEDS-ACTION when timeChanged is true", () => { const result = updateAttendeesAfterTimeChange(baseEvent, true); const attendee = result.attendee?.find( (a) => a.cal_address === "attendee@example.com" ); expect(attendee?.partstat).toBe("NEEDS-ACTION"); expect(attendee?.rsvp).toBe("TRUE"); }); it("should NOT mark the organizer as NEEDS-ACTION when timeChanged is true", () => { const result = updateAttendeesAfterTimeChange(baseEvent, true); const organizer = result.attendee?.find( (a) => a.cal_address === "organizer@example.com" ); expect(organizer?.partstat).toBe("ACCEPTED"); expect(organizer?.rsvp).toBe("FALSE"); }); it("should return attendees unchanged when timeChanged is undefined", () => { const result = updateAttendeesAfterTimeChange(baseEvent); expect(result.attendee?.[0].partstat).toBe("ACCEPTED"); }); }); describe("with provided attendees list", () => { const newAttendee: userAttendee = { cal_address: "new@example.com", partstat: "ACCEPTED", rsvp: "FALSE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cn: "new", }; it("should use existing attendee data when the address already exists in the event", () => { const result = updateAttendeesAfterTimeChange(baseEvent, false, [ mockAttendee, ]); const attendee = result.attendee?.find( (a) => a.cal_address === "attendee@example.com" ); expect(attendee?.partstat).toBe("ACCEPTED"); }); it("should mark new attendee as NEEDS-ACTION when not found in existing list", () => { const result = updateAttendeesAfterTimeChange(baseEvent, false, [ newAttendee, ]); const attendee = result.attendee?.find( (a) => a.cal_address === "new@example.com" ); expect(attendee?.partstat).toBe("NEEDS-ACTION"); expect(attendee?.rsvp).toBe("TRUE"); }); it("should mark all attendees as NEEDS-ACTION when timeChanged is true", () => { const result = updateAttendeesAfterTimeChange(baseEvent, true, [ mockAttendee, newAttendee, ]); const nonOrganizerAttendees = result.attendee?.filter( (a) => a.cal_address !== "organizer@example.com" ); nonOrganizerAttendees?.forEach((a) => { expect(a.partstat).toBe("NEEDS-ACTION"); expect(a.rsvp).toBe("TRUE"); }); }); it("should append organizer entry at the end of the attendee list", () => { const result = updateAttendeesAfterTimeChange(baseEvent, false, [ mockAttendee, ]); const last = result.attendee?.[result.attendee.length - 1]; expect(last?.cal_address).toBe("organizer@example.com"); }); it("should use existing organizer attendee data when found in event attendees", () => { const result = updateAttendeesAfterTimeChange(baseEvent, false, [ mockAttendee, ]); const organizer = result.attendee?.find( (a) => a.cal_address === "organizer@example.com" ); expect(organizer?.partstat).toBe("ACCEPTED"); }); it("should fall back to organizer defaults when organizer is not in existing attendees", () => { const eventWithoutOrganizerInAttendees: CalendarEvent = { ...baseEvent, attendee: [mockAttendee], // organizer not in attendee list }; const result = updateAttendeesAfterTimeChange( eventWithoutOrganizerInAttendees, false, [mockAttendee] ); const organizer = result.attendee?.find( (a) => a.cal_address === "organizer@example.com" ); expect(organizer?.role).toBe("CHAIR"); expect(organizer?.partstat).toBe("NEEDS-ACTION"); }); }); describe("organizer undefined", () => { const eventNoOrganizer: CalendarEvent = { ...baseEvent, organizer: undefined, }; it("should return attendees without bailing out", () => { const result = updateAttendeesAfterTimeChange(eventNoOrganizer); expect(result.attendee).toHaveLength(2); }); it("should mark attendees as NEEDS-ACTION when timeChanged is true", () => { const result = updateAttendeesAfterTimeChange(eventNoOrganizer, true); result.attendee?.forEach((a) => { expect(a.partstat).toBe("NEEDS-ACTION"); expect(a.rsvp).toBe("TRUE"); }); }); it("should not mutate attendees when timeChanged is false", () => { const result = updateAttendeesAfterTimeChange(eventNoOrganizer, false); expect(result.attendee?.[0].partstat).toBe("ACCEPTED"); expect(result.attendee?.[0].rsvp).toBe("FALSE"); }); it("should not append an organizer entry when organizer is undefined and attendees list is provided", () => { const newAttendee: userAttendee = { cal_address: "new@example.com", partstat: "ACCEPTED", rsvp: "FALSE", role: "REQ-PARTICIPANT", cutype: "INDIVIDUAL", cn: "new", }; const result = updateAttendeesAfterTimeChange(eventNoOrganizer, false, [ newAttendee, ]); expect(result.attendee).toHaveLength(1); expect(result.attendee?.[0].cal_address).toBe("new@example.com"); }); }); });