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);
});
});