import { describe, expect, it } from "vitest"; import { createDavFixture, davRequest } from "@tests/worker/helpers/dav"; import { fetchWorker } from "@tests/worker/helpers/http"; describe("WebDAV If header preconditions", () => { it("rejects false If entity-tag preconditions on mutating WebDAV methods", async () => { const fixture = await createDavFixture(); const file = `if-${crypto.randomUUID()}.txt`; const source = `if-source-${crypto.randomUUID()}.txt`; const prop = `if-prop-${crypto.randomUUID()}.txt`; const falseIf = '(["definitely-not-the-current-etag"])'; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); expect(await fetchWorker(davRequest(fixture, `/files/${source}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); expect(await fetchWorker(davRequest(fixture, `/files/${prop}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); expect( await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "DELETE", headers: { if: falseIf } })), ).toHaveProperty("status", 412); expect( await fetchWorker( davRequest(fixture, `/files/${source}`, { method: "COPY", headers: { if: falseIf, destination: `https://${fixture.hostLabel}.dav.example.com/files/copied.txt` }, }), ), ).toHaveProperty("status", 412); expect( await fetchWorker( davRequest(fixture, `/files/${source}`, { method: "MOVE", headers: { if: falseIf, destination: `https://${fixture.hostLabel}.dav.example.com/files/moved.txt` }, }), ), ).toHaveProperty("status", 412); expect( await fetchWorker( davRequest(fixture, `/files/${prop}`, { method: "PROPPATCH", headers: { if: falseIf, "content-type": "application/xml" }, body: `y`, }), ), ).toHaveProperty("status", 412); expect( await fetchWorker(davRequest(fixture, `/files/new-if-dir/`, { method: "MKCOL", headers: { if: falseIf } })), ).toHaveProperty("status", 412); }); it("honors HTTP If-Match on DELETE, COPY, MOVE, and PROPPATCH", async () => { const fixture = await createDavFixture(); const file = `if-match-${crypto.randomUUID()}.txt`; const copySource = `if-match-copy-${crypto.randomUUID()}.txt`; const moveSource = `if-match-move-${crypto.randomUUID()}.txt`; const propSource = `if-match-prop-${crypto.randomUUID()}.txt`; const stale = '"definitely-stale"'; for (const name of [file, copySource, moveSource, propSource]) { expect(await fetchWorker(davRequest(fixture, `/files/${name}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); } expect( await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "DELETE", headers: { "if-match": stale } })), ).toHaveProperty("status", 412); expect(await fetchWorker(davRequest(fixture, `/files/${file}`))).toHaveProperty("status", 200); expect( await fetchWorker( davRequest(fixture, `/files/${copySource}`, { method: "COPY", headers: { "if-match": stale, destination: `https://${fixture.hostLabel}.dav.example.com/files/if-match-copy-dest.txt`, }, }), ), ).toHaveProperty("status", 412); expect(await fetchWorker(davRequest(fixture, "/files/if-match-copy-dest.txt"))).toHaveProperty("status", 404); expect( await fetchWorker( davRequest(fixture, `/files/${moveSource}`, { method: "MOVE", headers: { "if-match": stale, destination: `https://${fixture.hostLabel}.dav.example.com/files/if-match-move-dest.txt`, }, }), ), ).toHaveProperty("status", 412); expect(await fetchWorker(davRequest(fixture, `/files/${moveSource}`))).toHaveProperty("status", 200); expect( await fetchWorker( davRequest(fixture, `/files/${propSource}`, { method: "PROPPATCH", headers: { "if-match": stale, "content-type": "application/xml" }, body: `y`, }), ), ).toHaveProperty("status", 412); }); it("applies tagged If entity-tag conditions to the tagged resource", async () => { const fixture = await createDavFixture(); const target = `tag-target-${crypto.randomUUID()}.txt`; const tagged = `tagged-${crypto.randomUUID()}.txt`; expect( await fetchWorker(davRequest(fixture, `/files/${target}`, { method: "PUT", body: "target" })), ).toHaveProperty("status", 201); const taggedPut = await fetchWorker(davRequest(fixture, `/files/${tagged}`, { method: "PUT", body: "tagged" })); expect(taggedPut.status).toBe(201); const etag = taggedPut.headers.get("etag"); expect(etag).toMatch(/^"[0-9a-f]+"$/); const response = await fetchWorker( davRequest(fixture, `/files/${target}`, { method: "PROPPATCH", headers: { if: ` ([${etag}])`, "content-type": "application/xml", }, body: `y`, }), ); expect(response.status).toBe(207); }); it("rejects false If preconditions on LOCK refresh", async () => { const fixture = await createDavFixture(); const file = `refresh-${crypto.randomUUID()}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const locked = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { depth: "0", timeout: "Second-600", "content-type": "application/xml" }, body: ``, }), ); expect(locked.status).toBe(200); const token = locked.headers.get("lock-token"); expect(token).toMatch(/^]+>$/); expect( await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: `(${token} ["definitely-not-the-current-etag"])` }, }), ), ).toHaveProperty("status", 412); }); });