import { describe, expect, it } from "vitest"; import { createDavFixture, davRequest } from "@tests/worker/helpers/dav"; import { fetchWorker } from "@tests/worker/helpers/http"; import { mkcolCalendarBody, propfindBody } from "./helpers"; describe("CalDAV calendar collections", () => { it("creates calendars with MKCOL calendar resource type", async () => { const fixture = await createDavFixture(["caldav.full"]); const calendarName = `work-${crypto.randomUUID()}`; const created = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "MKCOL", headers: { "content-type": "application/xml", depth: "1" }, body: mkcolCalendarBody("Work Calendar"), }), ); expect(created.status).toBe(201); const propfind = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(""), }), ); expect(propfind.status).toBe(207); const xml = await propfind.text(); expect(xml).toContain(""), }), ), ).toMatchObject({ status: 404 }); const recreated = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "MKCOL", headers: { "content-type": "application/xml", depth: "1" }, body: mkcolCalendarBody("Recreated Work Calendar"), }), ); expect(recreated.status).toBe(201); const reprobe = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(""), }), ); expect(reprobe.status).toBe(207); const reprobeXml = await reprobe.text(); expect(reprobeXml).toContain(" { const fixture = await createDavFixture(["caldav.full"]); const calendarName = `fresh-ctag-${crypto.randomUUID()}`; expect( await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "MKCOL", headers: { "content-type": "application/xml" }, body: mkcolCalendarBody("Fresh Ctag"), }), ), ).toMatchObject({ status: 201 }); const propfind = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(""), }), ); expect(propfind.status).toBe(207); expect(await propfind.text()).toContain('sync:0'); }); it("returns per-property PROPPATCH failures atomically for protected CalDAV properties", async () => { const fixture = await createDavFixture(["caldav.full"]); const patch = await fetchWorker( davRequest(fixture, "/calendars/default/", { method: "PROPPATCH", headers: { "content-type": "application/xml", depth: "1" }, body: [ '', '', "Family", "bad", "", ].join(""), }), ); expect(patch.status).toBe(207); const patchXml = await patch.text(); expect(patchXml).toContain("HTTP/1.1 403 Forbidden"); expect(patchXml).toContain("HTTP/1.1 424 Failed Dependency"); const propfind = await fetchWorker( davRequest(fixture, "/calendars/default/", { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(''), }), ); expect(propfind.status).toBe(207); expect(await propfind.text()).toContain("HTTP/1.1 404 Not Found"); }); it("stores MKCOL calendar creation timezone atomically and returns no-cache", async () => { const fixture = await createDavFixture(["caldav.full"]); const calendarName = `timezone-${crypto.randomUUID()}`; const timezone = ["BEGIN:VTIMEZONE", "TZID:America/New_York", "END:VTIMEZONE"].join("\r\n"); const created = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "MKCOL", headers: { "content-type": "application/xml", depth: "1" }, body: mkcolCalendarBody("Timezone Calendar", timezone), }), ); expect(created.status).toBe(201); expect(created.headers.get("cache-control")).toBe("no-cache"); const propfind = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(""), }), ); expect(propfind.status).toBe(207); const xml = await propfind.text(); expect(xml).toContain("Timezone Calendar"); expect(xml).toContain("TZID:America/New_York"); const badName = `bad-timezone-${crypto.randomUUID()}`; const invalid = await fetchWorker( davRequest(fixture, `/calendars/${badName}/`, { method: "MKCOL", headers: { "content-type": "application/xml", depth: "1" }, body: mkcolCalendarBody("Bad Timezone", "BEGIN:VTIMEZONE\r\nTZID:Not/A_Zone\r\nEND:VTIMEZONE"), }), ); expect(invalid.status).toBe(400); expect( await fetchWorker( davRequest(fixture, `/calendars/${badName}/`, { method: "PROPFIND", headers: { depth: "0", "content-type": "application/xml" }, body: propfindBody(""), }), ), ).toMatchObject({ status: 404 }); }); it("rejects MKCOL calendar bodies from the wrong namespace", async () => { const fixture = await createDavFixture(["caldav.full"]); const calendarName = `bad-mkcol-${crypto.randomUUID()}`; const created = await fetchWorker( davRequest(fixture, `/calendars/${calendarName}/`, { method: "MKCOL", headers: { "content-type": "application/xml", depth: "1" }, body: [ '', '', "", "", ].join(""), }), ); expect(created.status).toBe(400); }); });