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