import { describe, expect, it } from "vitest";
import { createDavFixture, davRequest } from "@tests/worker/helpers/dav";
import { fetchWorker } from "@tests/worker/helpers/http";
import { name } from "./files-helpers";
describe("WebDAV file operations", () => {
it("copies, moves, and deletes metadata while preserving blob bytes", async () => {
const fixture = await createDavFixture();
const directory = name("copy-move");
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toMatchObject({
status: 201,
});
expect(
await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`, { method: "PUT", body: "copy me" })),
).toHaveProperty("status", 201);
const copy = await fetchWorker(
davRequest(fixture, `/files/${directory}/a.txt`, {
method: "COPY",
headers: { destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/b.txt` },
}),
);
expect(copy.status).toBe(201);
const move = await fetchWorker(
davRequest(fixture, `/files/${directory}/b.txt`, {
method: "MOVE",
headers: { destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/c.txt` },
}),
);
expect(move.status).toBe(201);
const moved = await fetchWorker(davRequest(fixture, `/files/${directory}/c.txt`));
expect(moved.status).toBe(200);
await expect(moved.text()).resolves.toBe("copy me");
const deleted = await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "DELETE" }));
expect(deleted.status).toBe(204);
const missing = await fetchWorker(davRequest(fixture, `/files/${directory}/c.txt`));
expect(missing.status).toBe(404);
});
it("rejects invalid DELETE, COPY, and MOVE Depth headers before mutation", async () => {
const fixture = await createDavFixture();
const directory = name("invalid-depth");
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toHaveProperty(
"status",
201,
);
expect(
await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`, { method: "PUT", body: "depth" })),
).toHaveProperty("status", 201);
const invalidCopy = await fetchWorker(
davRequest(fixture, `/files/${directory}/a.txt`, {
method: "COPY",
headers: {
depth: "1",
destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/copy.txt`,
},
}),
);
expect(invalidCopy.status).toBe(400);
await expect(invalidCopy.text()).resolves.toContain("COPY Depth must be 0 or infinity");
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/copy.txt`))).toHaveProperty("status", 404);
const invalidMove = await fetchWorker(
davRequest(fixture, `/files/${directory}/a.txt`, {
method: "MOVE",
headers: {
depth: "0",
destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/moved.txt`,
},
}),
);
expect(invalidMove.status).toBe(400);
await expect(invalidMove.text()).resolves.toContain("MOVE Depth must be infinity");
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`))).toHaveProperty("status", 200);
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/moved.txt`))).toHaveProperty("status", 404);
const invalidDelete = await fetchWorker(
davRequest(fixture, `/files/${directory}/a.txt`, { method: "DELETE", headers: { depth: "0" } }),
);
expect(invalidDelete.status).toBe(400);
await expect(invalidDelete.text()).resolves.toContain("DELETE Depth must be infinity");
expect(await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`))).toHaveProperty("status", 200);
});
it("rejects file self-copy without changing body or dead properties", async () => {
const fixture = await createDavFixture();
const file = `${name("self-copy")}.txt`;
const href = `/files/${file}`;
expect(await fetchWorker(davRequest(fixture, href, { method: "PUT", body: "before" }))).toHaveProperty(
"status",
201,
);
expect(
await fetchWorker(
davRequest(fixture, href, {
method: "PROPPATCH",
headers: { "content-type": "application/xml" },
body: `blue`,
}),
),
).toHaveProperty("status", 207);
const copied = await fetchWorker(
davRequest(fixture, href, {
method: "COPY",
headers: { destination: `https://${fixture.hostLabel}.dav.example.com${href}` },
}),
);
expect(copied.status).toBe(403);
const get = await fetchWorker(davRequest(fixture, href));
expect(get.status).toBe(200);
await expect(get.text()).resolves.toBe("before");
const propfind = await fetchWorker(
davRequest(fixture, href, {
method: "PROPFIND",
headers: { depth: "0", "content-type": "application/xml" },
body: ``,
}),
);
expect(propfind.status).toBe(207);
await expect(propfind.text()).resolves.toContain("blue");
});
});