Skip to content
File

Blob: tests/worker/protocol/webdav/if-header.test.ts

typescript69 lines
1import { describe, expect, it } from "vitest";
2 
3import { parseIfHeader, positiveEtags, positiveLockTokens } from "@/worker/dav/if-header";
4import { ifHeaderMatches } from "@/worker/dav/locks";
5import type { FileLockRow } from "@/worker/db/file-dav-do/schema";
6 
7function activeLock(input: { token?: string; rootPath: string; depth?: "0" | "infinity" }): FileLockRow {
8 return {
9 token: input.token ?? "opaquelocktoken:abc",
10 rootNodeId: "node-1",
11 rootPath: input.rootPath,
12 ownerXml: '<D:owner xmlns:D="DAV:"/>',
13 principalSubjectId: "subject-1",
14 scope: "exclusive",
15 depth: input.depth ?? "0",
16 createdAtMs: 1,
17 expiresAtMs: 2,
18 };
19}
20 
21describe("WebDAV If header parser", () => {
22 it("parses untagged lock tokens and entity tags", () => {
23 const parsed = parseIfHeader('(<opaquelocktoken:abc> ["etag-1"])');
24 expect(parsed.ok).toBe(true);
25 if (!parsed.ok) return;
26 expect(positiveLockTokens(parsed.header)).toEqual(["opaquelocktoken:abc"]);
27 expect(positiveEtags(parsed.header)).toEqual(['"etag-1"']);
28 });
29 
30 it("parses tagged lists and Not conditions without treating prefixes as text hacks", () => {
31 const parsed = parseIfHeader("</files/a.txt> (Not <opaquelocktoken:abc>) (<opaquelocktoken:def>)");
32 expect(parsed.ok).toBe(true);
33 if (!parsed.ok) return;
34 expect(parsed.header.lists[0]?.resourceHref).toBe("/files/a.txt");
35 expect(parsed.header.lists[0]?.conditions[0]).toMatchObject({ not: true, kind: "lock-token" });
36 expect(positiveLockTokens(parsed.header)).toEqual(["opaquelocktoken:def"]);
37 });
38 
39 it("matches absolute tagged lock roots against descendant writes", () => {
40 const parsed = parseIfHeader("<https://example.test/files/locked/> (<opaquelocktoken:abc>)");
41 expect(parsed.ok).toBe(true);
42 if (!parsed.ok) return;
43 
44 expect(
45 ifHeaderMatches({
46 header: parsed.header,
47 targetHref: "/files/locked/child.txt",
48 targetEtag: null,
49 locks: [activeLock({ rootPath: "/files/locked/", depth: "infinity" })],
50 }),
51 ).toBe(true);
52 });
53 
54 it("honors Not conditions for entity tags", () => {
55 const parsed = parseIfHeader('(Not ["other-etag"])');
56 expect(parsed.ok).toBe(true);
57 if (!parsed.ok) return;
58 
59 expect(
60 ifHeaderMatches({
61 header: parsed.header,
62 targetHref: "/files/a.txt",
63 targetEtag: '"etag-1"',
64 locks: [],
65 }),
66 ).toBe(true);
67 });
68});