/** * RED test scaffold: broker/write.ts — tsdav PUT/DELETE wrappers (CAL-04, CAL-05, CAL-06) * * Behaviors under test: * 1. createCalendarEvent calls client.createCalendarObject with `${uid}.ics` filename * 2. updateCalendarEvent passes etag into the calendarObject (If-Match for D-08) * 3. deleteCalendarEvent passes etag into the calendarObject (If-Match for D-08) * 4. Each returns the raw Response from the tsdav client mock * * These tests FAIL (RED) because broker/write.ts does not exist yet. * They will turn GREEN in Plan 03-02 when the implementation is added. */ import { describe, it, expect, vi } from 'vitest'; // This import fails (RED) — broker/write.ts does not exist yet. // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore intentional RED import import { createCalendarEvent, updateCalendarEvent, deleteCalendarEvent, } from '../../src/broker/write.js'; const makeMockResponse = (status: number): Response => ({ status, ok: status >= 200 && status < 300, headers: new Headers() }) as unknown as Response; describe('createCalendarEvent', () => { it('calls client.createCalendarObject with uid.ics filename', async () => { const mockClient = { createCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(201)), }; const mockCalendar = { url: 'https://caldav.fastmail.com/dav/calendars/user/test@fm.com/Default/', }; const uid = 'abc-123@familysync'; const icsString = 'BEGIN:VCALENDAR\r\nEND:VCALENDAR'; const result = await createCalendarEvent( mockClient as never, mockCalendar as never, uid, icsString, ); expect(mockClient.createCalendarObject).toHaveBeenCalledWith({ calendar: mockCalendar, filename: `${uid}.ics`, iCalString: icsString, }); expect(result.status).toBe(201); }); it('returns the raw Response from client', async () => { const mockClient = { createCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(204)), }; const mockCalendar = { url: 'https://caldav.fastmail.com/' }; const result = await createCalendarEvent( mockClient as never, mockCalendar as never, 'uid@fs', 'ICS', ); expect(result).toBeDefined(); expect(result.status).toBe(204); }); }); describe('updateCalendarEvent', () => { it('calls client.updateCalendarObject with etag in calendarObject (If-Match)', async () => { const mockClient = { updateCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(204)), }; const calendarObjectUrl = 'https://caldav.fastmail.com/dav/calendars/user/test/Default/uid.ics'; const icsString = 'BEGIN:VCALENDAR\r\nEND:VCALENDAR'; const etag = '"etag-123"'; const result = await updateCalendarEvent( mockClient as never, calendarObjectUrl, icsString, etag, ); expect(mockClient.updateCalendarObject).toHaveBeenCalledWith({ calendarObject: { url: calendarObjectUrl, data: icsString, etag, }, }); expect(result.status).toBe(204); }); it('passes empty string as etag when etag is null (safe for If-Match)', async () => { const mockClient = { updateCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(204)), }; await updateCalendarEvent( mockClient as never, 'https://caldav.fastmail.com/uid.ics', 'ICS', null, ); const callArg = mockClient.updateCalendarObject.mock.calls[0][0]; expect(callArg.calendarObject.etag).toBe(''); }); }); describe('deleteCalendarEvent', () => { it('calls client.deleteCalendarObject with etag in calendarObject (If-Match)', async () => { const mockClient = { deleteCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(204)), }; const calendarObjectUrl = 'https://caldav.fastmail.com/dav/calendars/user/test/Default/uid.ics'; const etag = '"etag-456"'; const result = await deleteCalendarEvent(mockClient as never, calendarObjectUrl, etag); expect(mockClient.deleteCalendarObject).toHaveBeenCalledWith({ calendarObject: { url: calendarObjectUrl, data: '', etag, }, }); expect(result.status).toBe(204); }); it('passes empty string as etag when etag is null', async () => { const mockClient = { deleteCalendarObject: vi.fn().mockResolvedValue(makeMockResponse(204)), }; await deleteCalendarEvent(mockClient as never, 'https://caldav.fastmail.com/uid.ics', null); const callArg = mockClient.deleteCalendarObject.mock.calls[0][0]; expect(callArg.calendarObject.etag).toBe(''); }); });