typescript-virtual-container 1.2.3 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +871 -1231
  2. package/benchmark-results.txt +21 -21
  3. package/biome.json +9 -0
  4. package/dist/SSHMimic/index.d.ts +19 -2
  5. package/dist/SSHMimic/index.d.ts.map +1 -1
  6. package/dist/SSHMimic/index.js +127 -15
  7. package/dist/VirtualFileSystem/index.d.ts +115 -88
  8. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  9. package/dist/VirtualFileSystem/index.js +406 -258
  10. package/dist/VirtualShell/index.d.ts +3 -4
  11. package/dist/VirtualShell/index.d.ts.map +1 -1
  12. package/dist/VirtualShell/index.js +5 -23
  13. package/dist/VirtualUserManager/index.d.ts +41 -3
  14. package/dist/VirtualUserManager/index.d.ts.map +1 -1
  15. package/dist/VirtualUserManager/index.js +83 -21
  16. package/dist/commands/chmod.d.ts +3 -0
  17. package/dist/commands/chmod.d.ts.map +1 -0
  18. package/dist/commands/chmod.js +31 -0
  19. package/dist/commands/cp.d.ts +3 -0
  20. package/dist/commands/cp.d.ts.map +1 -0
  21. package/dist/commands/cp.js +68 -0
  22. package/dist/commands/find.d.ts +3 -0
  23. package/dist/commands/find.d.ts.map +1 -0
  24. package/dist/commands/find.js +48 -0
  25. package/dist/commands/grep.d.ts.map +1 -1
  26. package/dist/commands/grep.js +61 -35
  27. package/dist/commands/head.d.ts +3 -0
  28. package/dist/commands/head.d.ts.map +1 -0
  29. package/dist/commands/head.js +30 -0
  30. package/dist/commands/index.d.ts.map +1 -1
  31. package/dist/commands/index.js +25 -35
  32. package/dist/commands/ln.d.ts +3 -0
  33. package/dist/commands/ln.d.ts.map +1 -0
  34. package/dist/commands/ln.js +42 -0
  35. package/dist/commands/mv.d.ts +3 -0
  36. package/dist/commands/mv.d.ts.map +1 -0
  37. package/dist/commands/mv.js +35 -0
  38. package/dist/commands/tail.d.ts +3 -0
  39. package/dist/commands/tail.d.ts.map +1 -0
  40. package/dist/commands/tail.js +33 -0
  41. package/dist/commands/wc.d.ts +3 -0
  42. package/dist/commands/wc.d.ts.map +1 -0
  43. package/dist/commands/wc.js +48 -0
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/standalone.js +7 -9
  47. package/package.json +7 -3
  48. package/scripts/publish-package.sh +70 -0
  49. package/src/SSHMimic/index.ts +159 -17
  50. package/src/VirtualFileSystem/index.ts +500 -280
  51. package/src/VirtualShell/index.ts +5 -33
  52. package/src/VirtualUserManager/index.ts +92 -26
  53. package/src/commands/chmod.ts +33 -0
  54. package/src/commands/cp.ts +76 -0
  55. package/src/commands/find.ts +61 -0
  56. package/src/commands/grep.ts +54 -38
  57. package/src/commands/head.ts +35 -0
  58. package/src/commands/index.ts +25 -43
  59. package/src/commands/ln.ts +47 -0
  60. package/src/commands/mv.ts +43 -0
  61. package/src/commands/tail.ts +37 -0
  62. package/src/commands/wc.ts +48 -0
  63. package/src/index.ts +1 -0
  64. package/src/standalone.ts +12 -9
  65. package/standalone.js +102 -0
  66. package/standalone.js.map +7 -0
  67. package/tests/bun-test-shim.ts +1 -0
  68. package/tests/sftp.test.ts +115 -191
  69. package/tests/users.test.ts +66 -83
@@ -0,0 +1 @@
1
+ export { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from "vitest";
@@ -1,36 +1,28 @@
1
1
  /// <reference types="bun" />
2
2
  import { describe, expect, test } from "bun:test";
3
- import { rmSync } from "node:fs";
4
3
  import type { FileEntryWithStats, SFTPWrapper } from "ssh2";
5
4
  import { Client } from "ssh2";
6
5
  import { SftpMimic } from "../src/SSHMimic/sftp";
7
6
  import VirtualFileSystem from "../src/VirtualFileSystem";
8
7
  import { VirtualUserManager } from "../src/VirtualUserManager";
9
8
 
10
- function makeTempBasePath(): string {
11
- return `./temp-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
12
- }
9
+ // VirtualFileSystem is now pure in-memory — no temp dir or cleanup needed.
13
10
 
14
11
  function connectSftp(port: number): Promise<{ client: Client; sftp: SFTPWrapper }> {
15
12
  return new Promise((resolve, reject) => {
16
13
  const client = new Client();
17
14
  client.on("ready", () => {
18
15
  client.sftp((err, sftp) => {
19
- if (err) {
20
- client.end();
21
- reject(err);
22
- return;
23
- }
16
+ if (err) { client.end(); reject(err); return; }
24
17
  resolve({ client, sftp });
25
18
  });
26
19
  });
27
-
28
20
  client.on("error", reject);
29
21
  client.connect({
30
22
  host: "127.0.0.1",
31
23
  port,
32
24
  username: "root",
33
- password: "root",
25
+ password: "", // root has no password — any value or empty is accepted
34
26
  hostVerifier: () => true,
35
27
  });
36
28
  });
@@ -45,15 +37,10 @@ function connectSftpWithUser(
45
37
  const client = new Client();
46
38
  client.on("ready", () => {
47
39
  client.sftp((err, sftp) => {
48
- if (err) {
49
- client.end();
50
- reject(err);
51
- return;
52
- }
40
+ if (err) { client.end(); reject(err); return; }
53
41
  resolve({ client, sftp });
54
42
  });
55
43
  });
56
-
57
44
  client.on("error", reject);
58
45
  client.connect({
59
46
  host: "127.0.0.1",
@@ -67,253 +54,190 @@ function connectSftpWithUser(
67
54
 
68
55
  describe("SftpMimic", () => {
69
56
  test("authenticates with VirtualUserManager and serves files from the VirtualFileSystem", async () => {
70
- const tempBasePath = makeTempBasePath();
71
- const vfs = new VirtualFileSystem(tempBasePath);
72
- const users = new VirtualUserManager(vfs, "root");
57
+ const vfs = new VirtualFileSystem();
58
+ const users = new VirtualUserManager(vfs);
73
59
 
74
- try {
75
- await users.initialize();
76
-
77
- const rootPath = "/home/root";
78
- if (!vfs.exists(rootPath)) {
79
- vfs.mkdir(rootPath, 0o755);
80
- }
81
- vfs.writeFile(`${rootPath}/TEST.txt`, "hello world");
82
-
83
- const server = new SftpMimic({
84
- port: 0,
85
- hostname: "test-sftp",
86
- vfs,
87
- users,
88
- });
89
- const port = await server.start();
60
+ await users.initialize();
61
+
62
+ const rootPath = "/home/root";
63
+ if (!vfs.exists(rootPath)) {
64
+ vfs.mkdir(rootPath, 0o755);
65
+ }
66
+ vfs.writeFile(`${rootPath}/TEST.txt`, "hello world");
90
67
 
68
+ const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
69
+ const port = await server.start();
70
+
71
+ try {
91
72
  const { client, sftp } = await connectSftp(port);
73
+
92
74
  const list = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
93
- sftp.readdir(
94
- "/home/root",
95
- (err?: Error | null, list?: FileEntryWithStats[]) => {
96
- if (err) {
97
- reject(err);
98
- return;
99
- }
100
- resolve(list || []);
101
- },
102
- );
75
+ sftp.readdir("/home/root", (err?: Error | null, list?: FileEntryWithStats[]) => {
76
+ if (err) { reject(err); return; }
77
+ resolve(list || []);
78
+ });
103
79
  });
104
80
 
105
81
  expect(list.map((entry) => entry.filename)).toContain("TEST.txt");
106
82
 
107
83
  const content = await new Promise<string>((resolve, reject) => {
108
- sftp.readFile(
109
- "/home/root/TEST.txt",
110
- "utf8",
111
- (err?: Error | null, data?: Buffer) => {
112
- if (err) {
113
- reject(err);
114
- return;
115
- }
116
- resolve((data || Buffer.alloc(0)).toString("utf8"));
117
- },
118
- );
84
+ sftp.readFile("/home/root/TEST.txt", "utf8", (err?: Error | null, data?: Buffer) => {
85
+ if (err) { reject(err); return; }
86
+ resolve((data || Buffer.alloc(0)).toString("utf8"));
87
+ });
119
88
  });
120
89
 
121
90
  expect(content).toBe("hello world");
122
91
  client.end();
123
- server.stop();
124
92
  } finally {
125
- rmSync(tempBasePath, { recursive: true, force: true });
93
+ server.stop();
126
94
  }
127
95
  });
128
96
 
129
97
  test("blocks path traversal attempts outside home directory", async () => {
130
- const tempBasePath = makeTempBasePath();
131
- const vfs = new VirtualFileSystem(tempBasePath);
132
- const users = new VirtualUserManager(vfs, "root");
98
+ const vfs = new VirtualFileSystem();
99
+ const users = new VirtualUserManager(vfs);
133
100
 
134
- try {
135
- await users.initialize();
136
-
137
- const rootPath = "/home/root";
138
- if (!vfs.exists(rootPath)) {
139
- vfs.mkdir(rootPath, 0o755);
140
- }
141
-
142
- const server = new SftpMimic({
143
- port: 0,
144
- hostname: "test-sftp",
145
- vfs,
146
- users,
147
- });
148
- const port = await server.start();
101
+ await users.initialize();
149
102
 
103
+ const rootPath = "/home/root";
104
+ if (!vfs.exists(rootPath)) {
105
+ vfs.mkdir(rootPath, 0o755);
106
+ }
107
+
108
+ const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
109
+ const port = await server.start();
110
+
111
+ try {
150
112
  const { client, sftp } = await connectSftp(port);
151
113
 
152
- // Try to read /etc/passwd (outside home directory) - should fail with PERMISSION_DENIED
114
+ // /etc/passwd is outside /home/root should be rejected
153
115
  const traversalAttempt = await new Promise<Error | null>((resolve) => {
154
- sftp.stat(
155
- "/etc/passwd",
156
- (err?: Error | null) => {
157
- resolve(err ?? null);
158
- },
159
- );
116
+ sftp.stat("/etc/passwd", (err?: Error | null) => { resolve(err ?? null); });
160
117
  });
161
118
 
162
119
  expect(traversalAttempt).not.toBeNull();
163
120
  expect(traversalAttempt?.message).toContain("Permission denied");
164
121
 
165
- // Try to access /home/root which should work
166
- const homeAccess = await new Promise<FileEntryWithStats[]>((
167
- resolve,
168
- reject,
169
- ) => {
170
- sftp.readdir(
171
- "/home/root",
172
- (err?: Error | null, list?: FileEntryWithStats[]) => {
173
- if (err) {
174
- reject(err);
175
- return;
176
- }
177
- resolve(list || []);
178
- },
179
- );
122
+ // /home/root itself should work
123
+ const homeAccess = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
124
+ sftp.readdir("/home/root", (err?: Error | null, list?: FileEntryWithStats[]) => {
125
+ if (err) { reject(err); return; }
126
+ resolve(list || []);
127
+ });
180
128
  });
181
-
182
129
  expect(homeAccess).toBeDefined();
183
130
 
184
- // Try to go up with ../ - should fail
131
+ // Path traversal via ../.. should also be rejected
185
132
  const upTraversalAttempt = await new Promise<Error | null>((resolve) => {
186
- sftp.readdir(
187
- "/home/root/../../etc",
188
- (err?: Error | null) => {
189
- resolve(err ?? null);
190
- },
191
- );
133
+ sftp.readdir("/home/root/../../etc", (err?: Error | null) => { resolve(err ?? null); });
192
134
  });
193
-
194
135
  expect(upTraversalAttempt).not.toBeNull();
195
136
 
196
137
  client.end();
197
- server.stop();
198
138
  } finally {
199
- rmSync(tempBasePath, { recursive: true, force: true });
139
+ server.stop();
200
140
  }
201
141
  });
202
142
 
203
- test("auto-creates current system user on initialization", async () => {
204
- // Use a unique temp directory for this test to avoid VFS sharing
205
- const tempPath = `./temp-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
206
- const vfs = new VirtualFileSystem(tempPath);
207
- const users = new VirtualUserManager(vfs, "testpass");
143
+ test("allows a user with a password to authenticate", async () => {
144
+ const vfs = new VirtualFileSystem();
145
+ const users = new VirtualUserManager(vfs);
146
+
208
147
  await users.initialize();
148
+ await users.addUser("alice", "alice-pass");
209
149
 
210
- // Verify that the current system user was created
211
- const currentUser = process.env.USER || process.env.USERNAME;
212
- if (currentUser && currentUser !== "root") {
213
- // Should be able to verify password with the default password (testpass)
214
- const passwordValid = users.verifyPassword(currentUser, "testpass");
215
- expect(passwordValid).toBe(true);
150
+ // Ensure alice's home exists (addUser should create it, but let's be explicit)
151
+ if (!vfs.exists("/home/alice")) {
152
+ vfs.mkdir("/home/alice", 0o755);
153
+ }
154
+ vfs.writeFile("/home/alice/hello.txt", "hi alice");
216
155
 
217
- // Home directory should exist
218
- const homePath = `/home/${currentUser}`;
219
- expect(vfs.exists(homePath)).toBe(true);
156
+ const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
157
+ const port = await server.start();
220
158
 
221
- // README.txt should exist in home
222
- const readmePath = `${homePath}/README.txt`;
223
- expect(vfs.exists(readmePath)).toBe(true);
224
- }
159
+ try {
160
+ const { client, sftp } = await connectSftpWithUser(port, "alice", "alice-pass");
161
+
162
+ const list = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
163
+ sftp.readdir("/home/alice", (err?: Error | null, list?: FileEntryWithStats[]) => {
164
+ if (err) { reject(err); return; }
165
+ resolve(list || []);
166
+ });
167
+ });
168
+
169
+ expect(list.map((e) => e.filename)).toContain("hello.txt");
225
170
 
226
- // Cleanup
227
- const fs = await import("node:fs");
228
- fs.rmSync(tempPath, { recursive: true, force: true });
171
+ client.end();
172
+ } finally {
173
+ server.stop();
174
+ }
229
175
  });
230
176
 
231
- test("allows system user to authenticate and access SFTP", async () => {
232
- const tempBasePath = makeTempBasePath();
233
- const vfs = new VirtualFileSystem(tempBasePath);
234
- const users = new VirtualUserManager(vfs, "root");
177
+ test("rejects a user with a wrong password", async () => {
178
+ const vfs = new VirtualFileSystem();
179
+ const users = new VirtualUserManager(vfs);
180
+
181
+ await users.initialize();
182
+ await users.addUser("bob", "correct-pass");
183
+
184
+ const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
185
+ const port = await server.start();
235
186
 
236
187
  try {
237
- await users.initialize();
238
-
239
- // Verify system user was created with the default password
240
- const currentUser = process.env.USER || process.env.USERNAME;
241
- if (!currentUser || currentUser === "root") {
242
- // Skip test if we can't determine current user
243
- return;
244
- }
245
-
246
- // Ensure a deterministic password for environments where user data may persist.
247
- await users.setPassword(currentUser, "root");
248
-
249
- const server = new SftpMimic({
250
- port: 0,
251
- hostname: "test-sftp",
252
- vfs,
253
- users,
254
- });
255
- const port = await server.start();
188
+ const connectPromise = connectSftpWithUser(port, "bob", "wrong-pass");
189
+ await expect(connectPromise).rejects.toThrow();
190
+ } finally {
191
+ server.stop();
192
+ }
193
+ });
256
194
 
257
- // Connect as the system user (which was auto-created during initialize)
258
- const { client, sftp } = await connectSftpWithUser(
259
- port,
260
- currentUser,
261
- "root",
262
- );
195
+ test("allows writing and reading back a file over SFTP", async () => {
196
+ const vfs = new VirtualFileSystem();
197
+ const users = new VirtualUserManager(vfs);
263
198
 
264
- // User should be able to list their home directory
265
- const list = await new Promise<FileEntryWithStats[]>((resolve, reject) => {
266
- sftp.readdir(
267
- `/home/${currentUser}`,
268
- (err?: Error | null, list?: FileEntryWithStats[]) => {
269
- if (err) {
270
- reject(err);
271
- return;
272
- }
273
- resolve(list || []);
274
- },
275
- );
276
- });
199
+ await users.initialize();
200
+
201
+ if (!vfs.exists("/home/root")) {
202
+ vfs.mkdir("/home/root", 0o755);
203
+ }
277
204
 
278
- // README.txt should be in the home directory
279
- expect(list.map((e) => e.filename)).toContain("README.txt");
205
+ const server = new SftpMimic({ port: 0, hostname: "test-sftp", vfs, users });
206
+ const port = await server.start();
207
+
208
+ try {
209
+ const { client, sftp } = await connectSftp(port);
280
210
 
281
- // Create a file as the system user
282
211
  await new Promise<void>((resolve, reject) => {
283
212
  sftp.writeFile(
284
- `/home/${currentUser}/test-file.txt`,
285
- Buffer.from("WinSCP test"),
213
+ "/home/root/written.txt",
214
+ Buffer.from("written via sftp"),
286
215
  (err?: Error | null) => {
287
- if (err) {
288
- reject(err);
289
- return;
290
- }
216
+ if (err) { reject(err); return; }
291
217
  resolve();
292
218
  },
293
219
  );
294
220
  });
295
221
 
296
- // Read the file back
297
222
  const content = await new Promise<string>((resolve, reject) => {
298
223
  sftp.readFile(
299
- `/home/${currentUser}/test-file.txt`,
224
+ "/home/root/written.txt",
300
225
  "utf8",
301
226
  (err?: Error | null, data?: Buffer) => {
302
- if (err) {
303
- reject(err);
304
- return;
305
- }
227
+ if (err) { reject(err); return; }
306
228
  resolve((data || Buffer.alloc(0)).toString("utf8"));
307
229
  },
308
230
  );
309
231
  });
310
232
 
311
- expect(content).toBe("WinSCP test");
233
+ expect(content).toBe("written via sftp");
234
+
235
+ // Also verify it landed in the in-memory VFS
236
+ expect(vfs.readFile("/home/root/written.txt")).toBe("written via sftp");
312
237
 
313
238
  client.end();
314
- server.stop();
315
239
  } finally {
316
- rmSync(tempBasePath, { recursive: true, force: true });
240
+ server.stop();
317
241
  }
318
242
  });
319
243
  });
@@ -1,114 +1,97 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { mkdtemp, rm } from "node:fs/promises";
3
- import { tmpdir } from "node:os";
4
- import { join } from "node:path";
5
2
  import VirtualFileSystem from "../src/VirtualFileSystem";
6
3
  import { VirtualUserManager } from "../src/VirtualUserManager";
7
4
 
8
- async function withTempVfs(
9
- run: (vfs: VirtualFileSystem) => Promise<void>,
10
- ): Promise<void> {
11
- const tempDir = await mkdtemp(join(tmpdir(), "virtual-env-js-test-"));
12
- try {
13
- const vfs = new VirtualFileSystem(tempDir);
14
- await vfs.restoreMirror();
15
- await run(vfs);
16
- } finally {
17
- await rm(tempDir, { recursive: true, force: true });
18
- }
5
+ // VirtualFileSystem is now pure in-memory — no temp dir needed
6
+ function makeVfs(): VirtualFileSystem {
7
+ return new VirtualFileSystem();
19
8
  }
20
9
 
21
10
  describe("VirtualUserManager auto sudo", () => {
22
11
  test("adds new users to sudoers by default", async () => {
23
- await withTempVfs(async (vfs) => {
24
- const users = new VirtualUserManager(vfs, "root-pass");
25
- await users.initialize();
26
- await users.addUser("alice", "alice-pass");
12
+ const vfs = makeVfs();
13
+ const users = new VirtualUserManager(vfs);
14
+ await users.initialize();
15
+ await users.addUser("alice", "alice-pass");
27
16
 
28
- expect(users.isSudoer("alice")).toBe(true);
29
- });
17
+ expect(users.isSudoer("alice")).toBe(true);
30
18
  });
31
19
 
32
20
  test("does not auto-add sudoers when disabled", async () => {
33
- await withTempVfs(async (vfs) => {
34
- const users = new VirtualUserManager(vfs, "root-pass", false);
35
- await users.initialize();
36
- await users.addUser("bob", "bob-pass");
21
+ const vfs = makeVfs();
22
+ const users = new VirtualUserManager(vfs, false);
23
+ await users.initialize();
24
+ await users.addUser("bob", "bob-pass");
37
25
 
38
- expect(users.isSudoer("bob")).toBe(false);
39
- });
26
+ expect(users.isSudoer("bob")).toBe(false);
40
27
  });
41
28
 
42
29
  test("updates password for existing user", async () => {
43
- await withTempVfs(async (vfs) => {
44
- const users = new VirtualUserManager(vfs, "root-pass");
45
- await users.initialize();
46
- await users.addUser("alice", "alice-pass");
30
+ const vfs = makeVfs();
31
+ const users = new VirtualUserManager(vfs);
32
+ await users.initialize();
33
+ await users.addUser("alice", "alice-pass");
47
34
 
48
- await users.setPassword("alice", "new-pass");
35
+ await users.setPassword("alice", "new-pass");
49
36
 
50
- expect(users.verifyPassword("alice", "new-pass")).toBe(true);
51
- expect(users.verifyPassword("alice", "alice-pass")).toBe(false);
52
- });
37
+ expect(users.verifyPassword("alice", "new-pass")).toBe(true);
38
+ expect(users.verifyPassword("alice", "alice-pass")).toBe(false);
53
39
  });
54
40
  });
55
41
 
56
42
  describe("VirtualUserManager quotas", () => {
57
43
  test("enforces quota for writes inside user home", async () => {
58
- await withTempVfs(async (vfs) => {
59
- const users = new VirtualUserManager(vfs, "root-pass");
60
- await users.initialize();
61
- await users.addUser("alice", "alice-pass");
62
- const startingUsage = users.getUsageBytes("alice");
63
- await users.setQuotaBytes("alice", startingUsage + 5);
64
-
65
- expect(() => {
66
- users.assertWriteWithinQuota("alice", "/home/alice/note.txt", "hello");
67
- }).not.toThrow();
68
-
69
- vfs.writeFile("/home/alice/note.txt", "hello");
70
-
71
- expect(() => {
72
- users.assertWriteWithinQuota(
73
- "alice",
74
- "/home/alice/note.txt",
75
- "this exceeds the configured quota",
76
- );
77
- }).toThrow("quota exceeded for 'alice'");
78
- });
44
+ const vfs = makeVfs();
45
+ const users = new VirtualUserManager(vfs);
46
+ await users.initialize();
47
+ await users.addUser("alice", "alice-pass");
48
+ const startingUsage = users.getUsageBytes("alice");
49
+ await users.setQuotaBytes("alice", startingUsage + 5);
50
+
51
+ expect(() => {
52
+ users.assertWriteWithinQuota("alice", "/home/alice/note.txt", "hello");
53
+ }).not.toThrow();
54
+
55
+ vfs.writeFile("/home/alice/note.txt", "hello");
56
+
57
+ expect(() => {
58
+ users.assertWriteWithinQuota(
59
+ "alice",
60
+ "/home/alice/note.txt",
61
+ "this exceeds the configured quota",
62
+ );
63
+ }).toThrow("quota exceeded for 'alice'");
79
64
  });
80
65
 
81
66
  test("does not enforce home quota outside user home", async () => {
82
- await withTempVfs(async (vfs) => {
83
- const users = new VirtualUserManager(vfs, "root-pass");
84
- await users.initialize();
85
- await users.addUser("bob", "bob-pass");
86
- await users.setQuotaBytes("bob", 1);
87
-
88
- expect(() => {
89
- users.assertWriteWithinQuota("bob", "/tmp/shared.txt", "large-content");
90
- }).not.toThrow();
91
- });
67
+ const vfs = makeVfs();
68
+ const users = new VirtualUserManager(vfs);
69
+ await users.initialize();
70
+ await users.addUser("bob", "bob-pass");
71
+ await users.setQuotaBytes("bob", 1);
72
+
73
+ expect(() => {
74
+ users.assertWriteWithinQuota("bob", "/tmp/shared.txt", "large-content");
75
+ }).not.toThrow();
92
76
  });
93
77
 
94
78
  test("clearQuota removes enforced limit", async () => {
95
- await withTempVfs(async (vfs) => {
96
- const users = new VirtualUserManager(vfs, "root-pass");
97
- await users.initialize();
98
- await users.addUser("charlie", "charlie-pass");
99
- await users.setQuotaBytes("charlie", 2);
100
-
101
- expect(users.getQuotaBytes("charlie")).toBe(2);
102
- await users.clearQuota("charlie");
103
- expect(users.getQuotaBytes("charlie")).toBeNull();
104
-
105
- expect(() => {
106
- users.assertWriteWithinQuota(
107
- "charlie",
108
- "/home/charlie/file.txt",
109
- "long-content",
110
- );
111
- }).not.toThrow();
112
- });
79
+ const vfs = makeVfs();
80
+ const users = new VirtualUserManager(vfs);
81
+ await users.initialize();
82
+ await users.addUser("charlie", "charlie-pass");
83
+ await users.setQuotaBytes("charlie", 2);
84
+
85
+ expect(users.getQuotaBytes("charlie")).toBe(2);
86
+ await users.clearQuota("charlie");
87
+ expect(users.getQuotaBytes("charlie")).toBeNull();
88
+
89
+ expect(() => {
90
+ users.assertWriteWithinQuota(
91
+ "charlie",
92
+ "/home/charlie/file.txt",
93
+ "long-content",
94
+ );
95
+ }).not.toThrow();
113
96
  });
114
97
  });