import { describe, expect, it } from "vitest"; import { createDavFixture, davRequest } from "@tests/worker/helpers/dav"; import { fetchWorker } from "@tests/worker/helpers/http"; import { lock, name } from "./locks-helpers"; describe("WebDAV lock refresh and edge cases", () => { it("treats missing LOCK Depth as infinity and rejects invalid Depth values", async () => { const fixture = await createDavFixture(); const directory = name("default-depth"); expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toHaveProperty( "status", 201, ); const response = await fetchWorker( davRequest(fixture, `/files/${directory}/`, { method: "LOCK", headers: { timeout: "Second-600", "content-type": "application/xml" }, body: ``, }), ); expect(response.status).toBe(200); const token = response.headers.get("lock-token"); expect(token).toMatch(/^]+>$/); expect( await fetchWorker(davRequest(fixture, `/files/${directory}/child.txt`, { method: "PUT", body: "blocked" })), ).toHaveProperty("status", 423); const invalid = await fetchWorker( davRequest(fixture, `/files/${directory}/`, { method: "LOCK", headers: { depth: "1", timeout: "Second-600", "content-type": "application/xml", if: `(${token})` }, body: ``, }), ); expect(invalid.status).toBe(400); }); // DAV-VERIFY-004: empty-body LOCK is refresh only; unknown tokens fail; new // exclusive LOCK over an existing exclusive lock fails even with the token. it("fails empty-body LOCK with an unknown If token instead of creating a new lock", async () => { const fixture = await createDavFixture(); const file = `${name("refresh-unknown")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const response = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: "()" }, }), ); expect(response.status).toBe(412); expect(response.headers.get("lock-token")).toBeNull(); }); it("rejects empty-body LOCK refresh that names multiple applicable tokens", async () => { const fixture = await createDavFixture(); const file = `${name("refresh-many")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const tokenA = await lock(fixture, `/files/${file}`); const second = `${name("refresh-many-other")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${second}`, { method: "PUT", body: "y" }))).toHaveProperty( "status", 201, ); // Token A applies to /files/; we send it alongside an unrelated token // that does not apply. Only one token resolves and applies, so refresh succeeds. const onlyOneApplies = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: `(${tokenA}) ()`, }, }), ); // The unknown second token is in a separate list; the first list still resolves. expect(onlyOneApplies.status).toBe(200); }); it("does not return a Lock-Token header on successful LOCK refresh", async () => { const fixture = await createDavFixture(); const file = `${name("refresh-header")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const token = await lock(fixture, `/files/${file}`); const refreshed = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: `(${token})` } }), ); expect(refreshed.status).toBe(200); // RFC 4918 9.10.1: Lock-Token header is only for new lock creation. expect(refreshed.headers.get("lock-token")).toBeNull(); }); it("accepts LOCK refresh even when Depth is missing or invalid", async () => { const fixture = await createDavFixture(); const file = `${name("refresh-depth")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const token = await lock(fixture, `/files/${file}`); const refreshed = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: `(${token})`, depth: "garbage" }, }), ); expect(refreshed.status).toBe(200); }); it("rejects a new exclusive LOCK even when the existing exclusive token is supplied", async () => { const fixture = await createDavFixture(); const file = `${name("new-lock-conflict")}.txt`; expect(await fetchWorker(davRequest(fixture, `/files/${file}`, { method: "PUT", body: "x" }))).toHaveProperty( "status", 201, ); const token = await lock(fixture, `/files/${file}`); const second = await fetchWorker( davRequest(fixture, `/files/${file}`, { method: "LOCK", headers: { if: `(${token})`, "content-type": "application/xml" }, body: ``, }), ); expect(second.status).toBe(423); }); });