-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpath-encoding.test.ts
More file actions
157 lines (124 loc) · 4.35 KB
/
path-encoding.test.ts
File metadata and controls
157 lines (124 loc) · 4.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import { describe, test, expect } from "bun:test";
import { encodeLabel, decodeLabel, pathToLtree, ltreeToPath, normalizePath } from "../src/path-encoding";
describe("encodeLabel", () => {
test("passes through alphanumeric", () => {
expect(encodeLabel("hello")).toBe("hello");
});
test("passes through hyphens", () => {
expect(encodeLabel("my-file")).toBe("my-file");
});
test("encodes underscores", () => {
expect(encodeLabel("my_file")).toBe("my__5F__file");
});
test("encodes dots", () => {
expect(encodeLabel("readme.md")).toBe("readme__dot__md");
});
test("encodes spaces", () => {
expect(encodeLabel("my file")).toBe("my__sp__file");
});
test("encodes multiple special chars", () => {
expect(encodeLabel("my file.txt")).toBe("my__sp__file__dot__txt");
});
test("encodes other special characters as hex", () => {
expect(encodeLabel("file@name")).toBe("file__40__name");
});
test("encodes hash", () => {
expect(encodeLabel("file#1")).toBe("file__23__1");
});
test("throws on empty string", () => {
expect(() => encodeLabel("")).toThrow("Cannot encode empty filename");
});
test("throws on null byte", () => {
expect(() => encodeLabel("file\0name")).toThrow("null bytes");
});
test("no collision between literal __dot__ and encoded dot", () => {
const dotEncoded = encodeLabel(".");
const literalEncoded = encodeLabel("__dot__");
expect(dotEncoded).not.toBe(literalEncoded);
expect(decodeLabel(dotEncoded)).toBe(".");
expect(decodeLabel(literalEncoded)).toBe("__dot__");
});
});
describe("decodeLabel", () => {
test("passes through alphanumeric", () => {
expect(decodeLabel("hello")).toBe("hello");
});
test("decodes dots", () => {
expect(decodeLabel("readme__dot__md")).toBe("readme.md");
});
test("decodes spaces", () => {
expect(decodeLabel("my__sp__file")).toBe("my file");
});
test("decodes hex", () => {
expect(decodeLabel("file__40__name")).toBe("file@name");
});
test("decodes underscores", () => {
expect(decodeLabel("my__5F__file")).toBe("my_file");
});
test("roundtrips complex names", () => {
const names = ["hello.world.txt", "my file (1).md", "résumé.pdf", "a+b=c.js", "under_score"];
for (const name of names) {
expect(decodeLabel(encodeLabel(name))).toBe(name);
}
});
test("roundtrips emoji and non-BMP unicode", () => {
const names = ["\u{1F4C1}folder", "file\u{1F600}.txt", "\u{1F4DD}notes"];
for (const name of names) {
expect(decodeLabel(encodeLabel(name))).toBe(name);
}
});
});
describe("normalizePath", () => {
test("resolves .. segments", () => {
expect(normalizePath("/home/user/../other")).toBe("/home/other");
});
test("resolves . segments", () => {
expect(normalizePath("/home/./file")).toBe("/home/file");
});
test("normalizes double slashes", () => {
expect(normalizePath("/home//docs")).toBe("/home/docs");
});
test("resolves root", () => {
expect(normalizePath("/")).toBe("/");
});
test("throws on null byte", () => {
expect(() => normalizePath("/foo\0bar")).toThrow("null bytes");
});
});
describe("pathToLtree", () => {
test("converts root path", () => {
expect(pathToLtree("/", 42)).toBe("s42");
});
test("converts simple path", () => {
expect(pathToLtree("/home", 42)).toBe("s42.home");
});
test("converts nested path", () => {
expect(pathToLtree("/home/docs/readme.md", 42)).toBe("s42.home.docs.readme__dot__md");
});
test("handles trailing slash", () => {
expect(pathToLtree("/home/docs/", 42)).toBe("s42.home.docs");
});
test("normalizes double slashes", () => {
expect(pathToLtree("/home//docs", 42)).toBe("s42.home.docs");
});
test("normalizes .. components", () => {
expect(pathToLtree("/home/../etc", 42)).toBe("s42.etc");
});
});
describe("ltreeToPath", () => {
test("converts root", () => {
expect(ltreeToPath("s42")).toBe("/");
});
test("converts simple path", () => {
expect(ltreeToPath("s42.home")).toBe("/home");
});
test("converts nested path with encoded chars", () => {
expect(ltreeToPath("s42.home.docs.readme__dot__md")).toBe("/home/docs/readme.md");
});
test("roundtrips paths", () => {
const paths = ["/", "/home", "/home/docs/readme.md", "/tmp/my file.txt"];
for (const p of paths) {
expect(ltreeToPath(pathToLtree(p, 7))).toBe(p);
}
});
});