import { eventToFullCalendarFormat, getCalendarVisibility, } from "@/components/Calendar/utils/calendarUtils"; import { Calendar, DelegationAccess } from "@/features/Calendars/CalendarTypes"; import { AclEntry } from "@/features/Calendars/types/CalendarData"; import { CalendarEvent } from "@/features/Events/EventsTypes"; import { userOrganiser } from "@/features/User/userDataTypes"; describe("getCalendarVisibility", () => { it("returns 'public' when {DAV:}authenticated has {DAV:}read", () => { const acl: AclEntry[] = [ { privilege: "{DAV:}read", principal: "{DAV:}authenticated", protected: true, }, ]; expect(getCalendarVisibility(acl)).toBe("public"); }); it("returns 'private' when {DAV:}authenticated only has read-free-busy", () => { const acl: AclEntry[] = [ { privilege: "{urn:ietf:params:xml:ns:caldav}read-free-busy", principal: "{DAV:}authenticated", protected: true, }, ]; expect(getCalendarVisibility(acl)).toBe("private"); }); it("returns 'private' when {DAV:}authenticated is not present", () => { const acl: AclEntry[] = [ { privilege: "{DAV:}read", principal: "principals/users/123", protected: true, }, ]; expect(getCalendarVisibility(acl)).toBe("private"); }); it("ignores non-authenticated principals and still returns 'private'", () => { const acl: AclEntry[] = [ { privilege: "{DAV:}read", principal: "principals/users/other", protected: true, }, { privilege: "{DAV:}write", principal: "principals/users/other", protected: true, }, ]; expect(getCalendarVisibility(acl)).toBe("private"); }); it("stops early when it finds a {DAV:}read for {DAV:}authenticated", () => { const acl: AclEntry[] = [ { privilege: "{DAV:}read", principal: "{DAV:}authenticated", protected: true, }, { privilege: "{DAV:}write", principal: "{DAV:}authenticated", protected: true, }, ]; expect(getCalendarVisibility(acl)).toBe("public"); }); }); jest.mock("twake-i18n", () => ({ useI18n: () => ({ t: (key: string) => key }), })); const baseEvent: CalendarEvent = { uid: "event-1", calId: "user1/cal1", title: "Test Event", start: "2024-01-15T10:00:00", end: "2024-01-15T11:00:00", allday: false, organizer: { cal_address: "owner@example.com" } as userOrganiser, color: { light: "#FF0000" }, class: "PUBLIC", } as unknown as CalendarEvent; const makeCalendar = (overrides: Partial = {}): Calendar => ({ id: "user1/cal1", name: "Test Calendar", delegated: false, access: {} as DelegationAccess, owner: { emails: ["alice@example.com"], firstname: "Alice" }, color: { light: "#FF0000" }, events: {}, ...overrides, }) as Calendar; const callFormat = ( event: CalendarEvent, calendars: Record, userAddress = "alice@example.com", userId = "user1" ) => eventToFullCalendarFormat([event], [], userId, userAddress, false, calendars); describe("eventToFullCalendarFormat - editable flag", () => { describe("personal calendar (non-delegated)", () => { const calendar = makeCalendar({ delegated: false }); it("is editable when user is organizer", () => { const event = { ...baseEvent, organizer: { cal_address: "alice@example.com" }, } as CalendarEvent; const [result] = callFormat(event, { "user1/cal1": calendar }); expect(result.editable).toBe(true); }); it("is not editable when user is not organizer", () => { const event = { ...baseEvent, organizer: { cal_address: "other@example.com" }, } as CalendarEvent; const [result] = callFormat(event, { "user1/cal1": calendar }); expect(result.editable).toBe(false); }); it("is not editable when pending", () => { const event = { ...baseEvent, organizer: { cal_address: "alice@example.com" }, } as CalendarEvent; const [result] = eventToFullCalendarFormat( [event], [], "user1", "alice@example.com", true, { "user1/cal1": calendar } ); expect(result.editable).toBe(false); }); }); describe("delegated calendar", () => { const writeDelegatedCalendar = makeCalendar({ id: "user2/cal1", delegated: true, access: { write: true } as DelegationAccess, owner: { emails: ["owner@example.com"], firstname: "Owner" }, }); const readDelegatedCalendar = makeCalendar({ id: "user2/cal1", delegated: true, access: { read: true } as DelegationAccess, owner: { emails: ["owner@example.com"], firstname: "Owner" }, }); const event = { ...baseEvent, calId: "user2/cal1" }; it("is editable when owner is organizer and delegated user has write rights", () => { const [result] = callFormat( { ...event, organizer: { cal_address: "owner@example.com" }, } as CalendarEvent, { "user2/cal1": writeDelegatedCalendar } ); expect(result.editable).toBe(true); }); it("is not editable when owner is organizer but delegated user only has read access", () => { const [result] = callFormat( { ...event, organizer: { cal_address: "owner@example.com" }, } as CalendarEvent, { "user2/cal1": readDelegatedCalendar } ); expect(result.editable).toBe(false); }); it("is not editable when owner is not organizer", () => { const [result] = callFormat( { ...event, organizer: { cal_address: "someone-else@example.com" }, } as CalendarEvent, { "user2/cal1": writeDelegatedCalendar } ); expect(result.editable).toBe(false); }); it("is not editable when pending even if owner is organizer", () => { const [result] = eventToFullCalendarFormat( [ { ...event, organizer: { cal_address: "owner@example.com" }, } as CalendarEvent, ], [], "user1", "alice@example.com", true, { "user2/cal1": writeDelegatedCalendar } ); expect(result.editable).toBe(false); }); it("uses owner email not logged-in user email for organizer check", () => { // logged-in user is alice, owner is owner@example.com // organizer matches owner → editable const [result] = callFormat( { ...event, organizer: { cal_address: "owner@example.com" }, } as CalendarEvent, { "user2/cal1": writeDelegatedCalendar }, "alice@example.com" // logged-in user, should NOT be used for delegated ); expect(result.editable).toBe(true); }); }); });