import { env } from "cloudflare:workers";
import { describe, expect, it } from "vitest";
import { createDavFixture, davRequest } from "@tests/worker/helpers/dav";
import { fetchWorker } from "@tests/worker/helpers/http";
import { propfindBody, storageIdFor } from "./helpers";
describe("CardDAV address book collections", () => {
it("advertises sync:0 getctag for newly created address books", async () => {
const fixture = await createDavFixture(["carddav.full"]);
const bookName = `fresh-ctag-${crypto.randomUUID()}`;
const storageId = await storageIdFor(fixture.subjectId);
const created = await env.CARD_DAV.getByName(storageId).createAddressbook({
subjectId: fixture.subjectId,
name: bookName,
displayName: "Fresh Ctag",
nowMs: Date.now(),
});
expect(created.ok).toBe(true);
const propfind = await fetchWorker(
davRequest(fixture, `/addressbooks/${bookName}/`, {
method: "PROPFIND",
headers: { depth: "0", "content-type": "application/xml" },
body: propfindBody(""),
}),
);
expect(propfind.status).toBe(207);
expect(await propfind.text()).toContain('sync:0');
});
it("stores address book dead properties with PROPPATCH", async () => {
const fixture = await createDavFixture(["carddav.full"]);
const patch = await fetchWorker(
davRequest(fixture, "/addressbooks/default/", {
method: "PROPPATCH",
headers: { "content-type": "application/xml" },
body: [
'',
'',
"Friends",
"",
].join(""),
}),
);
expect(patch.status).toBe(207);
const propfind = await fetchWorker(
davRequest(fixture, "/addressbooks/default/", {
method: "PROPFIND",
headers: { depth: "0", "content-type": "application/xml" },
body: propfindBody(''),
}),
);
expect(propfind.status).toBe(207);
await expect(propfind.text()).resolves.toContain("Friends");
});
it("returns per-property PROPPATCH failures atomically for protected CardDAV properties", async () => {
const fixture = await createDavFixture(["carddav.full"]);
const patch = await fetchWorker(
davRequest(fixture, "/addressbooks/default/", {
method: "PROPPATCH",
headers: { "content-type": "application/xml" },
body: [
'',
'',
"Friends",
"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, "/addressbooks/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");
});
});