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