Skip to content

Commit 25262af

Browse files
authored
test: cover filesystem interface contract (#245)
Signed-off-by: EfeDurmaz16 <efebarandurmaz05@gmail.com>
1 parent 1369b77 commit 25262af

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, expect, it } from "vitest";
2+
import { InMemoryFs } from "./in-memory-fs/in-memory-fs.js";
3+
4+
describe("IFileSystem contract", () => {
5+
it("reads, writes, appends, stats, lists, and removes files", async () => {
6+
const fs = new InMemoryFs();
7+
8+
await fs.mkdir("/docs", { recursive: true });
9+
await fs.writeFile("/docs/readme.md", "hello");
10+
await fs.appendFile("/docs/readme.md", " world");
11+
12+
expect(await fs.readFile("/docs/readme.md")).toBe("hello world");
13+
expect(await fs.exists("/docs/readme.md")).toBe(true);
14+
expect((await fs.stat("/docs/readme.md")).isFile).toBe(true);
15+
expect(await fs.readdir("/docs")).toContain("readme.md");
16+
17+
await fs.rm("/docs/readme.md");
18+
expect(await fs.exists("/docs/readme.md")).toBe(false);
19+
});
20+
21+
it("copies and moves files without changing file contents", async () => {
22+
const fs = new InMemoryFs();
23+
24+
await fs.mkdir("/tmp", { recursive: true });
25+
await fs.writeFile("/tmp/source.txt", "contents");
26+
await fs.cp("/tmp/source.txt", "/tmp/copy.txt");
27+
await fs.mv("/tmp/copy.txt", "/tmp/moved.txt");
28+
29+
expect(await fs.readFile("/tmp/source.txt")).toBe("contents");
30+
expect(await fs.readFile("/tmp/moved.txt")).toBe("contents");
31+
expect(await fs.exists("/tmp/copy.txt")).toBe(false);
32+
});
33+
34+
it("rejects null-byte paths for mutating and read operations", async () => {
35+
const fs = new InMemoryFs();
36+
37+
await expect(fs.readFile("/evil\0.txt")).rejects.toThrow("null byte");
38+
await expect(fs.writeFile("/evil\0.txt", "data")).rejects.toThrow(
39+
"null byte",
40+
);
41+
await expect(fs.mkdir("/evil\0dir")).rejects.toThrow("null byte");
42+
await expect(fs.rm("/evil\0.txt")).rejects.toThrow("null byte");
43+
});
44+
45+
it("clamps traversal above the virtual root", async () => {
46+
const fs = new InMemoryFs();
47+
48+
await fs.writeFile("/root.txt", "root");
49+
50+
expect(await fs.readFile("/../../root.txt")).toBe("root");
51+
expect((await fs.stat("/../../")).isDirectory).toBe(true);
52+
});
53+
54+
it("resolves relative paths consistently", () => {
55+
const fs = new InMemoryFs();
56+
57+
expect(fs.resolvePath("/work", "file.txt")).toBe("/work/file.txt");
58+
expect(fs.resolvePath("/work", "../file.txt")).toBe("/file.txt");
59+
expect(fs.resolvePath("/work", "/absolute.txt")).toBe("/absolute.txt");
60+
});
61+
62+
it("creates symlinks and keeps absolute symlink targets virtual", async () => {
63+
const fs = new InMemoryFs();
64+
65+
await fs.writeFile("/target.txt", "target");
66+
await fs.symlink("/target.txt", "/link.txt");
67+
68+
expect(await fs.readlink("/link.txt")).toBe("/target.txt");
69+
expect((await fs.lstat("/link.txt")).isSymbolicLink).toBe(true);
70+
expect(await fs.readFile("/link.txt")).toBe("target");
71+
});
72+
});

0 commit comments

Comments
 (0)