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