Skip to content
File

Blob: src/workerd/api/node/tests/fs-stat-test.js

javascript452 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4import { deepStrictEqual, ok, rejects, strictEqual, throws } from 'node:assert';
5 
6import {
7 openSync,
8 closeSync,
9 symlinkSync,
10 statSync,
11 lstatSync,
12 fstatSync,
13 stat,
14 lstat,
15 fstat,
16 promises,
17 statfsSync,
18 statfs,
19 Stats,
20} from 'node:fs';
21 
22strictEqual(typeof openSync, 'function');
23strictEqual(typeof closeSync, 'function');
24strictEqual(typeof statSync, 'function');
25strictEqual(typeof lstatSync, 'function');
26strictEqual(typeof fstatSync, 'function');
27strictEqual(typeof stat, 'function');
28strictEqual(typeof lstat, 'function');
29strictEqual(typeof fstat, 'function');
30strictEqual(typeof symlinkSync, 'function');
31strictEqual(typeof statfsSync, 'function');
32strictEqual(typeof statfs, 'function');
33strictEqual(typeof promises.stat, 'function');
34strictEqual(typeof promises.lstat, 'function');
35 
36const kInvalidArgTypeError = { code: 'ERR_INVALID_ARG_TYPE' };
37 
38function checkRootStat(stat, bigint = false) {
39 ok(stat instanceof Stats);
40 strictEqual(stat.isDirectory(), true);
41 strictEqual(stat.isFile(), false);
42 strictEqual(stat.isBlockDevice(), false);
43 strictEqual(stat.isCharacterDevice(), false);
44 strictEqual(stat.isSymbolicLink(), false);
45 strictEqual(stat.isFIFO(), false);
46 strictEqual(stat.isSocket(), false);
47 strictEqual(stat.dev, bigint ? 0n : 0);
48 strictEqual(stat.ino, bigint ? 0n : 0);
49 strictEqual(stat.mode, bigint ? 16676n : 16676);
50 strictEqual(stat.nlink, bigint ? 1n : 1);
51 strictEqual(stat.uid, bigint ? 0n : 0);
52 strictEqual(stat.gid, bigint ? 0n : 0);
53 strictEqual(stat.rdev, bigint ? 0n : 0);
54 strictEqual(stat.size, bigint ? 0n : 0);
55 strictEqual(stat.blksize, bigint ? 0n : 0);
56 strictEqual(stat.blocks, bigint ? 0n : 0);
57 strictEqual(stat.atime.getTime(), 0);
58 strictEqual(stat.mtime.getTime(), 0);
59 strictEqual(stat.ctime.getTime(), 0);
60 strictEqual(stat.birthtime.getTime(), 0);
61 strictEqual(stat.atimeMs, bigint ? 0n : 0);
62 strictEqual(stat.mtimeMs, bigint ? 0n : 0);
63 strictEqual(stat.ctimeMs, bigint ? 0n : 0);
64 strictEqual(stat.birthtimeMs, bigint ? 0n : 0);
65 if (bigint) {
66 strictEqual(stat.atimeNs, 0n);
67 strictEqual(stat.mtimeNs, 0n);
68 strictEqual(stat.ctimeNs, 0n);
69 strictEqual(stat.birthtimeNs, 0n);
70 }
71}
72 
73function checkFileStat(stat, bigint = false) {
74 ok(stat instanceof Stats);
75 strictEqual(stat.isDirectory(), false);
76 strictEqual(stat.isFile(), true);
77 strictEqual(stat.isBlockDevice(), false);
78 strictEqual(stat.isCharacterDevice(), false);
79 strictEqual(stat.isSymbolicLink(), false);
80 strictEqual(stat.isFIFO(), false);
81 strictEqual(stat.isSocket(), false);
82 strictEqual(stat.dev, bigint ? 0n : 0);
83 strictEqual(stat.ino, bigint ? 0n : 0);
84 strictEqual(stat.mode, bigint ? 33060n : 33060);
85 strictEqual(stat.nlink, bigint ? 1n : 1);
86 strictEqual(stat.uid, bigint ? 0n : 0);
87 strictEqual(stat.gid, bigint ? 0n : 0);
88 strictEqual(stat.rdev, bigint ? 0n : 0);
89 ok(stat.size > 0n || stat.size > 0);
90 strictEqual(stat.blksize, bigint ? 0n : 0);
91 strictEqual(stat.blocks, bigint ? 0n : 0);
92 // The size is going to be constantly changing for this test file,
93 // so let's just check that it's not zero.
94}
95 
96function checkCharacterDeviceStat(stat, bigint = false) {
97 ok(stat instanceof Stats);
98 strictEqual(stat.isDirectory(), false);
99 strictEqual(stat.isFile(), false);
100 strictEqual(stat.isBlockDevice(), false);
101 strictEqual(stat.isCharacterDevice(), true);
102 strictEqual(stat.isSymbolicLink(), false);
103 strictEqual(stat.isFIFO(), false);
104 strictEqual(stat.isSocket(), false);
105 strictEqual(stat.dev, bigint ? 1n : 1);
106 strictEqual(stat.ino, bigint ? 0n : 0);
107 strictEqual(stat.mode, bigint ? 8630n : 8630);
108 strictEqual(stat.nlink, bigint ? 1n : 1);
109 strictEqual(stat.uid, bigint ? 0n : 0);
110 strictEqual(stat.gid, bigint ? 0n : 0);
111 strictEqual(stat.rdev, bigint ? 0n : 0);
112}
113 
114function checkLinkStat(stat, bigint = false) {
115 ok(stat instanceof Stats);
116 strictEqual(stat.isDirectory(), false);
117 strictEqual(stat.isFile(), false);
118 strictEqual(stat.isBlockDevice(), false);
119 strictEqual(stat.isCharacterDevice(), false);
120 strictEqual(stat.isSymbolicLink(), true);
121 strictEqual(stat.isFIFO(), false);
122 strictEqual(stat.isSocket(), false);
123 strictEqual(stat.dev, bigint ? 0n : 0);
124 strictEqual(stat.ino, bigint ? 0n : 0);
125 strictEqual(stat.mode, bigint ? 41252n : 41252);
126 strictEqual(stat.nlink, bigint ? 1n : 1);
127 strictEqual(stat.uid, bigint ? 0n : 0);
128 strictEqual(stat.gid, bigint ? 0n : 0);
129 strictEqual(stat.rdev, bigint ? 0n : 0);
130}
131 
132export const statSyncTest = {
133 async test() {
134 throws(() => statSync(), kInvalidArgTypeError);
135 throws(() => statSync('/', 'foo'), kInvalidArgTypeError);
136 throws(() => statSync('/', { bigint: 'yes' }), kInvalidArgTypeError);
137 throws(() => lstatSync(), kInvalidArgTypeError);
138 throws(() => lstatSync('/', 'foo'), kInvalidArgTypeError);
139 throws(() => lstatSync('/', { bigint: 'yes' }), kInvalidArgTypeError);
140 throws(() => fstatSync(), kInvalidArgTypeError);
141 throws(() => fstatSync(123, 'foo'), kInvalidArgTypeError);
142 throws(() => fstatSync(123, { bigint: 'yes' }), kInvalidArgTypeError);
143 
144 symlinkSync('/', '/tmp/link-root');
145 symlinkSync('/bundle/worker', '/tmp/link-bundle-worker');
146 symlinkSync('/dev/null', '/tmp/link-dev-null');
147 
148 const fd1 = openSync('/', 'r');
149 const fd2 = openSync('/bundle/worker', 'r');
150 const fd3 = openSync('/dev/null', 'r');
151 
152 checkRootStat(statSync('/'));
153 checkRootStat(statSync('/', { bigint: true }), true);
154 checkRootStat(lstatSync('/'));
155 checkRootStat(lstatSync('/', { bigint: true }), true);
156 checkRootStat(fstatSync(fd1));
157 checkRootStat(fstatSync(fd1, { bigint: true }), true);
158 
159 checkFileStat(statSync('/bundle/worker'));
160 checkFileStat(statSync('/bundle/worker', { bigint: true }), true);
161 checkFileStat(lstatSync('/bundle/worker'));
162 checkFileStat(lstatSync('/bundle/worker', { bigint: true }), true);
163 checkFileStat(fstatSync(fd2));
164 
165 checkCharacterDeviceStat(statSync('/dev/null'));
166 checkCharacterDeviceStat(statSync('/dev/null', { bigint: true }), true);
167 checkCharacterDeviceStat(lstatSync('/dev/null'));
168 checkCharacterDeviceStat(lstatSync('/dev/null', { bigint: true }), true);
169 checkCharacterDeviceStat(fstatSync(fd3));
170 
171 // Check that symlinks are followed by default with statSync.
172 checkRootStat(statSync('/tmp/link-root'));
173 checkRootStat(statSync('/tmp/link-root', { bigint: true }), true);
174 checkFileStat(statSync('/tmp/link-bundle-worker'));
175 checkFileStat(statSync('/tmp/link-bundle-worker', { bigint: true }), true);
176 checkCharacterDeviceStat(statSync('/tmp/link-dev-null'));
177 checkCharacterDeviceStat(
178 statSync('/tmp/link-dev-null', { bigint: true }),
179 true
180 );
181 
182 // But lstatSync does not follow symlinks.
183 checkLinkStat(lstatSync('/tmp/link-root'));
184 checkLinkStat(lstatSync('/tmp/link-root', { bigint: true }), true);
185 checkLinkStat(lstatSync('/tmp/link-bundle-worker'));
186 checkLinkStat(lstatSync('/tmp/link-bundle-worker', { bigint: true }), true);
187 checkLinkStat(lstatSync('/tmp/link-dev-null'));
188 checkLinkStat(lstatSync('/tmp/link-dev-null', { bigint: true }), true);
189 
190 // Non-existent paths and file descriptors
191 throws(() => statSync('/non-existent-path'), { code: 'ENOENT' });
192 throws(() => lstatSync('/non-existent-path'), { code: 'ENOENT' });
193 throws(() => fstatSync(123), { code: 'EBADF' });
194 
195 strictEqual(
196 statSync('/non-existent-path', { throwIfNoEntry: false }),
197 undefined
198 );
199 strictEqual(
200 lstatSync('/non-existent-path', { throwIfNoEntry: false }),
201 undefined
202 );
203 
204 closeSync(fd1);
205 closeSync(fd2);
206 closeSync(fd3);
207 },
208};
209 
210export const statTest = {
211 async test() {
212 throws(() => stat(), kInvalidArgTypeError);
213 throws(() => stat('/', 'foo'), kInvalidArgTypeError);
214 throws(() => stat('/', { bigint: 'yes' }), kInvalidArgTypeError);
215 throws(() => stat('/'), kInvalidArgTypeError);
216 throws(() => lstat(), kInvalidArgTypeError);
217 throws(() => lstat('/', 'foo'), kInvalidArgTypeError);
218 throws(() => lstat('/', { bigint: 'yes' }), kInvalidArgTypeError);
219 throws(() => lstat('/'), kInvalidArgTypeError);
220 throws(() => fstat(), kInvalidArgTypeError);
221 throws(() => fstat(123, 'foo'), kInvalidArgTypeError);
222 throws(() => fstat(123, { bigint: 'yes' }), kInvalidArgTypeError);
223 throws(() => fstat(123), kInvalidArgTypeError);
224 
225 symlinkSync('/', '/tmp/link-root');
226 symlinkSync('/bundle/worker', '/tmp/link-bundle-worker');
227 symlinkSync('/dev/null', '/tmp/link-dev-null');
228 
229 const fd1 = openSync('/', 'r');
230 const fd2 = openSync('/bundle/worker', 'r');
231 const fd3 = openSync('/dev/null', 'r');
232 
233 const doStatTest = async (statFn, path, bigint, checkFn) => {
234 const { promise, resolve, reject } = Promise.withResolvers();
235 statFn(path, { bigint }, (err, stat) => {
236 if (err) {
237 reject(err);
238 } else {
239 try {
240 checkFn(stat, bigint);
241 resolve();
242 } catch (e) {
243 reject(e);
244 }
245 }
246 });
247 await promise;
248 };
249 
250 const doFailTest = async (statFn, path, code) => {
251 const { promise, resolve, reject } = Promise.withResolvers();
252 statFn(path, (err) => {
253 if (err) {
254 strictEqual(err.code, code);
255 resolve();
256 } else {
257 reject(new Error(`Expected error for path: ${path}`));
258 }
259 });
260 await promise;
261 };
262 
263 await doStatTest(stat, '/', false, checkRootStat);
264 await doStatTest(stat, '/', true, checkRootStat);
265 await doStatTest(stat, '/bundle/worker', false, checkFileStat);
266 await doStatTest(stat, '/bundle/worker', true, checkFileStat);
267 await doStatTest(stat, '/dev/null', false, checkCharacterDeviceStat);
268 await doStatTest(stat, '/dev/null', true, checkCharacterDeviceStat);
269 
270 await doStatTest(stat, '/tmp/link-root', false, checkRootStat);
271 await doStatTest(stat, '/tmp/link-root', true, checkRootStat);
272 await doStatTest(stat, '/tmp/link-bundle-worker', false, checkFileStat);
273 await doStatTest(stat, '/tmp/link-bundle-worker', true, checkFileStat);
274 await doStatTest(
275 stat,
276 '/tmp/link-dev-null',
277 false,
278 checkCharacterDeviceStat
279 );
280 await doStatTest(
281 stat,
282 '/tmp/link-dev-null',
283 true,
284 checkCharacterDeviceStat
285 );
286 
287 await doStatTest(lstat, '/', false, checkRootStat);
288 await doStatTest(lstat, '/', true, checkRootStat);
289 await doStatTest(lstat, '/bundle/worker', false, checkFileStat);
290 await doStatTest(lstat, '/bundle/worker', true, checkFileStat);
291 await doStatTest(lstat, '/dev/null', false, checkCharacterDeviceStat);
292 await doStatTest(lstat, '/dev/null', true, checkCharacterDeviceStat);
293 
294 await doStatTest(lstat, '/tmp/link-root', false, checkLinkStat);
295 await doStatTest(lstat, '/tmp/link-root', true, checkLinkStat);
296 await doStatTest(lstat, '/tmp/link-bundle-worker', false, checkLinkStat);
297 await doStatTest(lstat, '/tmp/link-bundle-worker', true, checkLinkStat);
298 await doStatTest(lstat, '/tmp/link-dev-null', false, checkLinkStat);
299 await doStatTest(lstat, '/tmp/link-dev-null', true, checkLinkStat);
300 
301 await doStatTest(fstat, fd1, false, checkRootStat);
302 await doStatTest(fstat, fd1, true, checkRootStat);
303 await doStatTest(fstat, fd2, false, checkFileStat);
304 await doStatTest(fstat, fd2, true, checkFileStat);
305 await doStatTest(fstat, fd3, false, checkCharacterDeviceStat);
306 await doStatTest(fstat, fd3, true, checkCharacterDeviceStat);
307 
308 // // Non-existent paths and file descriptors
309 await doFailTest(stat, '/non-existent-path', 'ENOENT');
310 await doFailTest(lstat, '/non-existent-path', 'ENOENT');
311 await doFailTest(fstat, 123, 'EBADF');
312 
313 closeSync(fd1);
314 closeSync(fd2);
315 closeSync(fd3);
316 },
317};
318 
319export const statPromisesTest = {
320 async test() {
321 await rejects(promises.stat(), kInvalidArgTypeError);
322 await rejects(promises.stat('/', 'foo'), kInvalidArgTypeError);
323 await rejects(promises.stat('/', { bigint: 'yes' }), kInvalidArgTypeError);
324 await rejects(promises.lstat(), kInvalidArgTypeError);
325 await rejects(promises.lstat('/', 'foo'), kInvalidArgTypeError);
326 await rejects(promises.lstat('/', { bigint: 'yes' }), kInvalidArgTypeError);
327 
328 symlinkSync('/', '/tmp/link-root');
329 symlinkSync('/bundle/worker', '/tmp/link-bundle-worker');
330 symlinkSync('/dev/null', '/tmp/link-dev-null');
331 
332 await checkRootStat(await promises.stat('/'));
333 await checkRootStat(await promises.stat('/', { bigint: true }), true);
334 await checkRootStat(await promises.lstat('/'));
335 await checkRootStat(await promises.lstat('/', { bigint: true }), true);
336 
337 await checkFileStat(await promises.stat('/bundle/worker'));
338 await checkFileStat(
339 await promises.stat('/bundle/worker', { bigint: true }),
340 true
341 );
342 await checkFileStat(await promises.lstat('/bundle/worker'));
343 await checkFileStat(
344 await promises.lstat('/bundle/worker', { bigint: true }),
345 true
346 );
347 
348 await checkCharacterDeviceStat(await promises.stat('/dev/null'));
349 await checkCharacterDeviceStat(
350 await promises.stat('/dev/null', { bigint: true }),
351 true
352 );
353 await checkCharacterDeviceStat(await promises.lstat('/dev/null'));
354 await checkCharacterDeviceStat(
355 await promises.lstat('/dev/null', { bigint: true }),
356 true
357 );
358 
359 await checkRootStat(await promises.stat('/tmp/link-root'));
360 await checkRootStat(
361 await promises.stat('/tmp/link-root', { bigint: true }),
362 true
363 );
364 await checkFileStat(await promises.stat('/tmp/link-bundle-worker'));
365 await checkFileStat(
366 await promises.stat('/tmp/link-bundle-worker', { bigint: true }),
367 true
368 );
369 await checkCharacterDeviceStat(await promises.stat('/tmp/link-dev-null'));
370 await checkCharacterDeviceStat(
371 await promises.stat('/tmp/link-dev-null', { bigint: true }),
372 true
373 );
374 
375 await checkLinkStat(await promises.lstat('/tmp/link-root'));
376 await checkLinkStat(
377 await promises.lstat('/tmp/link-root', { bigint: true }),
378 true
379 );
380 await checkLinkStat(await promises.lstat('/tmp/link-bundle-worker'));
381 await checkLinkStat(
382 await promises.lstat('/tmp/link-bundle-worker', { bigint: true }),
383 true
384 );
385 await checkLinkStat(await promises.lstat('/tmp/link-dev-null'));
386 await checkLinkStat(
387 await promises.lstat('/tmp/link-dev-null', { bigint: true }),
388 true
389 );
390 },
391};
392 
393export const statfsTest = {
394 async test() {
395 const check = {
396 type: 0,
397 bsize: 0,
398 blocks: 0,
399 bfree: 0,
400 bavail: 0,
401 files: 0,
402 ffree: 0,
403 };
404 const checkBn = {
405 type: 0n,
406 bsize: 0n,
407 blocks: 0n,
408 bfree: 0n,
409 bavail: 0n,
410 files: 0n,
411 ffree: 0n,
412 };
413 
414 deepStrictEqual(statfsSync('/'), check);
415 deepStrictEqual(statfsSync('/bundle', { bigint: true }), checkBn);
416 
417 async function callStatfs(path, fn, bigint = false) {
418 const { promise, resolve, reject } = Promise.withResolvers();
419 statfs(path, { bigint }, (err, stat) => {
420 if (err) return reject(err);
421 try {
422 fn(stat);
423 resolve();
424 } catch (err) {
425 reject(err);
426 }
427 });
428 await promise;
429 }
430 
431 await callStatfs('/', (stat) => {
432 deepStrictEqual(stat, check);
433 });
434 await callStatfs(
435 '/',
436 (stat) => {
437 deepStrictEqual(stat, checkBn);
438 },
439 true /* bigint */
440 );
441 
442 // Throws if the path is invalid / wrong type
443 throws(() => statfs(123), {
444 code: /ERR_INVALID_ARG_TYPE/,
445 });
446 
447 throws(() => statfs('/', { bigint: 'yes' }), {
448 code: /ERR_INVALID_ARG_TYPE/,
449 });
450 },
451};