Skip to content
File

Blob: tests/worker/protocol/webdav/files-operations.workers.test.ts

typescript130 lines
1import { describe, expect, it } from "vitest";
2 
3import { createDavFixture, davRequest } from "@tests/worker/helpers/dav";
4import { fetchWorker } from "@tests/worker/helpers/http";
5import { name } from "./files-helpers";
6 
7describe("WebDAV file operations", () => {
8 it("copies, moves, and deletes metadata while preserving blob bytes", async () => {
9 const fixture = await createDavFixture();
10 const directory = name("copy-move");
11 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toMatchObject({
12 status: 201,
13 });
14 expect(
15 await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`, { method: "PUT", body: "copy me" })),
16 ).toHaveProperty("status", 201);
17 
18 const copy = await fetchWorker(
19 davRequest(fixture, `/files/${directory}/a.txt`, {
20 method: "COPY",
21 headers: { destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/b.txt` },
22 }),
23 );
24 expect(copy.status).toBe(201);
25 
26 const move = await fetchWorker(
27 davRequest(fixture, `/files/${directory}/b.txt`, {
28 method: "MOVE",
29 headers: { destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/c.txt` },
30 }),
31 );
32 expect(move.status).toBe(201);
33 
34 const moved = await fetchWorker(davRequest(fixture, `/files/${directory}/c.txt`));
35 expect(moved.status).toBe(200);
36 await expect(moved.text()).resolves.toBe("copy me");
37 
38 const deleted = await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "DELETE" }));
39 expect(deleted.status).toBe(204);
40 const missing = await fetchWorker(davRequest(fixture, `/files/${directory}/c.txt`));
41 expect(missing.status).toBe(404);
42 });
43 
44 it("rejects invalid DELETE, COPY, and MOVE Depth headers before mutation", async () => {
45 const fixture = await createDavFixture();
46 const directory = name("invalid-depth");
47 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/`, { method: "MKCOL" }))).toHaveProperty(
48 "status",
49 201,
50 );
51 expect(
52 await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`, { method: "PUT", body: "depth" })),
53 ).toHaveProperty("status", 201);
54 
55 const invalidCopy = await fetchWorker(
56 davRequest(fixture, `/files/${directory}/a.txt`, {
57 method: "COPY",
58 headers: {
59 depth: "1",
60 destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/copy.txt`,
61 },
62 }),
63 );
64 expect(invalidCopy.status).toBe(400);
65 await expect(invalidCopy.text()).resolves.toContain("COPY Depth must be 0 or infinity");
66 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/copy.txt`))).toHaveProperty("status", 404);
67 
68 const invalidMove = await fetchWorker(
69 davRequest(fixture, `/files/${directory}/a.txt`, {
70 method: "MOVE",
71 headers: {
72 depth: "0",
73 destination: `https://${fixture.hostLabel}.dav.example.com/files/${directory}/moved.txt`,
74 },
75 }),
76 );
77 expect(invalidMove.status).toBe(400);
78 await expect(invalidMove.text()).resolves.toContain("MOVE Depth must be infinity");
79 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`))).toHaveProperty("status", 200);
80 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/moved.txt`))).toHaveProperty("status", 404);
81 
82 const invalidDelete = await fetchWorker(
83 davRequest(fixture, `/files/${directory}/a.txt`, { method: "DELETE", headers: { depth: "0" } }),
84 );
85 expect(invalidDelete.status).toBe(400);
86 await expect(invalidDelete.text()).resolves.toContain("DELETE Depth must be infinity");
87 expect(await fetchWorker(davRequest(fixture, `/files/${directory}/a.txt`))).toHaveProperty("status", 200);
88 });
89 
90 it("rejects file self-copy without changing body or dead properties", async () => {
91 const fixture = await createDavFixture();
92 const file = `${name("self-copy")}.txt`;
93 const href = `/files/${file}`;
94 expect(await fetchWorker(davRequest(fixture, href, { method: "PUT", body: "before" }))).toHaveProperty(
95 "status",
96 201,
97 );
98 expect(
99 await fetchWorker(
100 davRequest(fixture, href, {
101 method: "PROPPATCH",
102 headers: { "content-type": "application/xml" },
103 body: `<?xml version="1.0"?><D:propertyupdate xmlns:D="DAV:" xmlns:Z="urn:test"><D:set><D:prop><Z:color>blue</Z:color></D:prop></D:set></D:propertyupdate>`,
104 }),
105 ),
106 ).toHaveProperty("status", 207);
107 
108 const copied = await fetchWorker(
109 davRequest(fixture, href, {
110 method: "COPY",
111 headers: { destination: `https://${fixture.hostLabel}.dav.example.com${href}` },
112 }),
113 );
114 expect(copied.status).toBe(403);
115 
116 const get = await fetchWorker(davRequest(fixture, href));
117 expect(get.status).toBe(200);
118 await expect(get.text()).resolves.toBe("before");
119 const propfind = await fetchWorker(
120 davRequest(fixture, href, {
121 method: "PROPFIND",
122 headers: { depth: "0", "content-type": "application/xml" },
123 body: `<?xml version="1.0"?><D:propfind xmlns:D="DAV:" xmlns:Z="urn:test"><D:prop><Z:color/></D:prop></D:propfind>`,
124 }),
125 );
126 expect(propfind.status).toBe(207);
127 await expect(propfind.text()).resolves.toContain("blue");
128 });
129});