Skip to content
File

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

javascript921 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 {
5 deepStrictEqual,
6 ok,
7 match,
8 rejects,
9 strictEqual,
10 throws,
11} from 'node:assert';
12 
13import {
14 existsSync,
15 writeFileSync,
16 mkdirSync,
17 mkdtempSync,
18 rmSync,
19 rmdirSync,
20 readdirSync,
21 mkdtemp,
22 mkdir,
23 rm,
24 rmdir,
25 readdir,
26 promises,
27 opendirSync,
28 opendir,
29} from 'node:fs';
30 
31strictEqual(typeof existsSync, 'function');
32strictEqual(typeof writeFileSync, 'function');
33strictEqual(typeof mkdirSync, 'function');
34strictEqual(typeof mkdtempSync, 'function');
35strictEqual(typeof rmSync, 'function');
36strictEqual(typeof rmdirSync, 'function');
37strictEqual(typeof readdirSync, 'function');
38strictEqual(typeof mkdtemp, 'function');
39strictEqual(typeof mkdir, 'function');
40strictEqual(typeof rm, 'function');
41strictEqual(typeof rmdir, 'function');
42strictEqual(typeof readdir, 'function');
43strictEqual(typeof opendirSync, 'function');
44strictEqual(typeof opendir, 'function');
45strictEqual(typeof promises.mkdir, 'function');
46strictEqual(typeof promises.mkdtemp, 'function');
47strictEqual(typeof promises.rm, 'function');
48strictEqual(typeof promises.rmdir, 'function');
49strictEqual(typeof promises.readdir, 'function');
50strictEqual(typeof promises.opendir, 'function');
51 
52const kInvalidArgTypeError = { code: 'ERR_INVALID_ARG_TYPE' };
53const kInvalidArgValueError = { code: 'ERR_INVALID_ARG_VALUE' };
54const kEPermError = { code: 'EPERM' };
55const kENoEntError = { code: 'ENOENT' };
56const kEExistError = { code: 'EEXIST' };
57const kENotDirError = { code: 'ENOTDIR' };
58const kENotEmptyError = { code: 'ENOTEMPTY' };
59 
60export const mkdirSyncTest = {
61 test() {
62 throws(() => mkdirSync(), kInvalidArgTypeError);
63 throws(() => mkdirSync(123), kInvalidArgTypeError);
64 throws(() => mkdirSync('/tmp/testdir', 'hello'), kInvalidArgTypeError);
65 throws(
66 () => mkdirSync('/tmp/testdir', { recursive: 123 }),
67 kInvalidArgTypeError
68 );
69 
70 // Make a directory.
71 ok(!existsSync('/tmp/testdir'));
72 strictEqual(mkdirSync('/tmp/testdir'), undefined);
73 ok(existsSync('/tmp/testdir'));
74 
75 // Making a subdirectory in a non-existing path fails by default
76 ok(!existsSync('/tmp/testdir/a/b/c'));
77 throws(() => mkdirSync('/tmp/testdir/a/b/c'), kENoEntError);
78 
79 // But passing the recursive option allows the entire path to be created.
80 ok(!existsSync('/tmp/testdir/a/b/c'));
81 strictEqual(
82 mkdirSync('/tmp/testdir/a/b/c', { recursive: true }),
83 '/tmp/testdir/a'
84 );
85 ok(existsSync('/tmp/testdir/a/b/c'));
86 
87 // Cannot make a directory in a read-only location
88 throws(() => mkdirSync('/bundle/a'), kEPermError);
89 
90 // Making a directory that already exists is a non-op
91 mkdirSync('/tmp/testdir');
92 
93 // Attempting to create a directory that already exists as a file throws
94 writeFileSync('/tmp/abc', 'Hello World');
95 throws(() => mkdirSync('/tmp/abc'), kEExistError);
96 
97 // Attempting to create a directory recursively when a parent is a file
98 // throws
99 throws(() => mkdirSync('/tmp/abc/foo', { recursive: true }), kENotDirError);
100 },
101};
102 
103export const mkdirAsyncCallbackTest = {
104 async test() {
105 throws(() => mkdir(), kInvalidArgTypeError);
106 throws(() => mkdir(123), kInvalidArgTypeError);
107 throws(() => mkdir('/tmp/testdir', 'hello'), kInvalidArgTypeError);
108 throws(
109 () => mkdir('/tmp/testdir', { recursive: 123 }),
110 kInvalidArgTypeError
111 );
112 
113 // Make a directory.
114 ok(!existsSync('/tmp/testdir'));
115 await new Promise((resolve, reject) => {
116 mkdir('/tmp/testdir', (err) => {
117 if (err) reject(err);
118 else resolve();
119 });
120 });
121 ok(existsSync('/tmp/testdir'));
122 
123 // Making a subdirectory in a non-existing path fails by default
124 ok(!existsSync('/tmp/testdir/a/b/c'));
125 await new Promise((resolve, reject) => {
126 mkdir('/tmp/testdir/a/b/c', (err) => {
127 if (err && err.code === kENoEntError.code) resolve();
128 else reject(err);
129 });
130 });
131 
132 // But passing the recursive option allows the entire path to be created.
133 ok(!existsSync('/tmp/testdir/a/b/c'));
134 await new Promise((resolve, reject) => {
135 mkdir('/tmp/testdir/a/b/c', { recursive: true }, (err) => {
136 if (err) reject(err);
137 else resolve();
138 });
139 });
140 ok(existsSync('/tmp/testdir/a/b/c'));
141 
142 // Cannot make a directory in a read-only location
143 await new Promise((resolve, reject) => {
144 mkdir('/bundle/a', (err) => {
145 if (err && err.code === kEPermError.code) resolve();
146 else reject(err);
147 });
148 });
149 
150 // Making a directory that already exists is a non-op
151 await new Promise((resolve, reject) => {
152 mkdir('/tmp/testdir', (err) => {
153 if (err) reject(err);
154 else resolve();
155 });
156 });
157 
158 // Attempting to create a directory that already exists as a file throws
159 writeFileSync('/tmp/abc', 'Hello World');
160 await new Promise((resolve, reject) => {
161 mkdir('/tmp/abc', (err) => {
162 if (err && err.code === kEExistError.code) resolve();
163 else reject(err);
164 });
165 });
166 
167 // Attempting to create a directory recursively when a parent is a file
168 // throws
169 await new Promise((resolve, reject) => {
170 mkdir('/tmp/abc/foo', { recursive: true }, (err) => {
171 if (err && err.code === kENotDirError.code) resolve();
172 else reject(err);
173 });
174 });
175 },
176};
177 
178export const mkdirAsyncPromiseTest = {
179 async test() {
180 await rejects(promises.mkdir(), kInvalidArgTypeError);
181 await rejects(promises.mkdir(123), kInvalidArgTypeError);
182 await rejects(
183 promises.mkdir('/tmp/testdir', 'hello'),
184 kInvalidArgTypeError
185 );
186 await rejects(
187 promises.mkdir('/tmp/testdir', { recursive: 123 }),
188 kInvalidArgTypeError
189 );
190 
191 // Make a directory.
192 ok(!existsSync('/tmp/testdir'));
193 await promises.mkdir('/tmp/testdir');
194 ok(existsSync('/tmp/testdir'));
195 
196 // Making a subdirectory in a non-existing path fails by default
197 ok(!existsSync('/tmp/testdir/a/b/c'));
198 await rejects(promises.mkdir('/tmp/testdir/a/b/c'), kENoEntError);
199 
200 // But passing the recursive option allows the entire path to be created.
201 ok(!existsSync('/tmp/testdir/a/b/c'));
202 await promises.mkdir('/tmp/testdir/a/b/c', { recursive: true });
203 ok(existsSync('/tmp/testdir/a/b/c'));
204 
205 // Cannot make a directory in a read-only location
206 await rejects(promises.mkdir('/bundle/a'), kEPermError);
207 
208 // Making a directory that already exists is a non-op
209 await promises.mkdir('/tmp/testdir');
210 
211 // Attempting to create a directory that already exists as a file throws
212 writeFileSync('/tmp/abc', 'Hello World');
213 await rejects(promises.mkdir('/tmp/abc'), kEExistError);
214 
215 // Attempting to create a directory recursively when a parent is a file
216 // throws
217 await rejects(
218 promises.mkdir('/tmp/abc/foo', { recursive: true }),
219 kENotDirError
220 );
221 },
222};
223 
224export const mkdtempSyncTest = {
225 test() {
226 throws(() => mkdtempSync(), kInvalidArgTypeError);
227 const ret1 = mkdtempSync('/tmp/testdir-');
228 const ret2 = mkdtempSync('/tmp/testdir-');
229 match(ret1, /\/tmp\/testdir-\d+/);
230 match(ret2, /\/tmp\/testdir-\d+/);
231 ok(existsSync(ret1));
232 ok(existsSync(ret2));
233 throws(() => mkdtempSync('/bundle/testdir-'), kEPermError);
234 },
235};
236 
237export const mkdtempAsyncCallbackTest = {
238 async test() {
239 throws(() => mkdtemp(), kInvalidArgTypeError);
240 const ret1 = await new Promise((resolve, reject) => {
241 mkdtemp('/tmp/testdir-', (err, dir) => {
242 if (err) reject(err);
243 else resolve(dir);
244 });
245 });
246 const ret2 = await new Promise((resolve, reject) => {
247 mkdtemp('/tmp/testdir-', (err, dir) => {
248 if (err) reject(err);
249 else resolve(dir);
250 });
251 });
252 match(ret1, /\/tmp\/testdir-\d+/);
253 match(ret2, /\/tmp\/testdir-\d+/);
254 ok(existsSync(ret1));
255 ok(existsSync(ret2));
256 await new Promise((resolve, reject) => {
257 mkdtemp('/bundle/testdir-', (err) => {
258 if (err && err.code === kEPermError.code) resolve();
259 else reject(err);
260 });
261 });
262 },
263};
264 
265export const mkdtempAsyncPromiseTest = {
266 async test() {
267 await rejects(promises.mkdtemp(), kInvalidArgTypeError);
268 const ret1 = await promises.mkdtemp('/tmp/testdir-');
269 const ret2 = await promises.mkdtemp('/tmp/testdir-');
270 match(ret1, /\/tmp\/testdir-\d+/);
271 match(ret2, /\/tmp\/testdir-\d+/);
272 ok(existsSync(ret1));
273 ok(existsSync(ret2));
274 await rejects(promises.mkdtemp('/bundle/testdir-'), kEPermError);
275 },
276};
277 
278export const rmSyncTest = {
279 test() {
280 // Passing incorrect types for options throws
281 throws(
282 () => rmSync('/tmp/testdir', { recursive: 'yes' }),
283 kInvalidArgTypeError
284 );
285 throws(() => rmSync('/tmp/testdir', 'abc'), kInvalidArgTypeError);
286 throws(
287 () => rmSync('/tmp/testdir', { force: 'yes' }),
288 kInvalidArgTypeError
289 );
290 throws(
291 () => rmSync('/tmp/testdir', { maxRetries: 'yes' }),
292 kInvalidArgTypeError
293 );
294 throws(
295 () => rmSync('/tmp/testdir', { retryDelay: 'yes' }),
296 kInvalidArgTypeError
297 );
298 throws(
299 () => rmSync('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
300 kInvalidArgTypeError
301 );
302 throws(
303 () => rmSync('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
304 kInvalidArgTypeError
305 );
306 throws(
307 () =>
308 rmSync('/tmp/testdir', { maxRetries: 1, retryDelay: 1, force: 'yes' }),
309 kInvalidArgTypeError
310 );
311 
312 throws(
313 () => rmdirSync('/tmp/testdir', { recursive: 'yes' }),
314 kInvalidArgTypeError
315 );
316 throws(() => rmdirSync('/tmp/testdir', 'abc'), kInvalidArgTypeError);
317 throws(
318 () => rmdirSync('/tmp/testdir', { maxRetries: 'yes' }),
319 kInvalidArgTypeError
320 );
321 throws(
322 () => rmdirSync('/tmp/testdir', { retryDelay: 'yes' }),
323 kInvalidArgTypeError
324 );
325 throws(
326 () => rmdirSync('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
327 kInvalidArgTypeError
328 );
329 throws(
330 () => rmdirSync('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
331 kInvalidArgTypeError
332 );
333 
334 ok(!existsSync('/tmp/testdir'));
335 mkdirSync('/tmp/testdir');
336 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
337 
338 // When the recusive option is not set, then removing a directory
339 // with children throws...
340 throws(() => rmdirSync('/tmp/testdir'), kENotEmptyError);
341 ok(existsSync('/tmp/testdir'));
342 
343 // But works when the recursive option is set
344 rmdirSync('/tmp/testdir', { recursive: true });
345 ok(!existsSync('/tmp/testdir'));
346 
347 mkdirSync('/tmp/testdir');
348 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
349 writeFileSync('/tmp/testdir/b.txt', 'Hello World');
350 ok(existsSync('/tmp/testdir/a.txt'));
351 
352 // trying to remove a file with rmdir throws
353 throws(() => rmdirSync('/tmp/testdir/a.txt'), kENotDirError);
354 
355 // removing a file with rm works
356 rmSync('/tmp/testdir/a.txt');
357 ok(!existsSync('/tmp/testdir/a.txt'));
358 
359 // Calling rmSync when the directory is not empty throws
360 throws(() => rmSync('/tmp/testdir'), kENotEmptyError);
361 ok(existsSync('/tmp/testdir'));
362 
363 // But works when the recursive option is set
364 throws(() => rmSync('/tmp/testdir'));
365 rmSync('/tmp/testdir', { recursive: true });
366 ok(!existsSync('/tmp/testdir'));
367 },
368};
369 
370export const rmAsyncCallbackTest = {
371 async test() {
372 // Passing incorrect types for options throws
373 throws(
374 () => rm('/tmp/testdir', { recursive: 'yes' }),
375 kInvalidArgTypeError
376 );
377 throws(() => rm('/tmp/testdir', 'abc'), kInvalidArgTypeError);
378 throws(() => rm('/tmp/testdir', { force: 'yes' }), kInvalidArgTypeError);
379 throws(
380 () => rm('/tmp/testdir', { maxRetries: 'yes' }),
381 kInvalidArgTypeError
382 );
383 throws(
384 () => rm('/tmp/testdir', { retryDelay: 'yes' }),
385 kInvalidArgTypeError
386 );
387 throws(
388 () => rm('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
389 kInvalidArgTypeError
390 );
391 throws(
392 () => rm('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
393 kInvalidArgTypeError
394 );
395 throws(
396 () => rm('/tmp/testdir', { maxRetries: 1, retryDelay: 1, force: 'yes' }),
397 kInvalidArgTypeError
398 );
399 
400 throws(
401 () => rmdir('/tmp/testdir', { recursive: 'yes' }),
402 kInvalidArgTypeError
403 );
404 throws(() => rmdir('/tmp/testdir', 'abc'), kInvalidArgTypeError);
405 throws(
406 () => rmdir('/tmp/testdir', { maxRetries: 'yes' }),
407 kInvalidArgTypeError
408 );
409 throws(
410 () => rmdir('/tmp/testdir', { retryDelay: 'yes' }),
411 kInvalidArgTypeError
412 );
413 throws(
414 () => rmdir('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
415 kInvalidArgTypeError
416 );
417 throws(
418 () => rmdir('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
419 kInvalidArgTypeError
420 );
421 
422 ok(!existsSync('/tmp/testdir'));
423 mkdirSync('/tmp/testdir');
424 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
425 
426 // When the recusive option is not set, then removing a directory
427 // with children throws...
428 await new Promise((resolve, reject) => {
429 rmdir('/tmp/testdir', (err) => {
430 if (err && err.code === kENotEmptyError.code) resolve();
431 else reject(err);
432 });
433 });
434 
435 ok(existsSync('/tmp/testdir'));
436 // But works when the recursive option is set
437 await new Promise((resolve, reject) => {
438 rmdir('/tmp/testdir', { recursive: true }, (err) => {
439 if (err) reject(err);
440 else resolve();
441 });
442 });
443 ok(!existsSync('/tmp/testdir'));
444 mkdirSync('/tmp/testdir');
445 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
446 writeFileSync('/tmp/testdir/b.txt', 'Hello World');
447 
448 ok(existsSync('/tmp/testdir/a.txt'));
449 // trying to remove a file with rmdir throws
450 await new Promise((resolve, reject) => {
451 rmdir('/tmp/testdir/a.txt', (err) => {
452 if (err && err.code === kENotDirError.code) resolve();
453 else reject(err);
454 });
455 });
456 // removing a file with rm works
457 await new Promise((resolve, reject) => {
458 rm('/tmp/testdir/a.txt', (err) => {
459 if (err) reject(err);
460 else resolve();
461 });
462 });
463 ok(!existsSync('/tmp/testdir/a.txt'));
464 // Calling rm when the directory is not empty throws
465 await new Promise((resolve, reject) => {
466 rm('/tmp/testdir', (err) => {
467 if (err && err.code === kENotEmptyError.code) resolve();
468 else reject(err);
469 });
470 });
471 ok(existsSync('/tmp/testdir'));
472 // But works when the recursive option is set
473 await new Promise((resolve, reject) => {
474 rm('/tmp/testdir', { recursive: true }, (err) => {
475 if (err) reject(err);
476 else resolve();
477 });
478 });
479 ok(!existsSync('/tmp/testdir'));
480 },
481};
482 
483export const rmAsyncPromiseTest = {
484 async test() {
485 // Passing incorrect types for options throws
486 await rejects(
487 promises.rm('/tmp/testdir', { recursive: 'yes' }),
488 kInvalidArgTypeError
489 );
490 await rejects(promises.rm('/tmp/testdir', 'abc'), kInvalidArgTypeError);
491 await rejects(
492 promises.rm('/tmp/testdir', { force: 'yes' }),
493 kInvalidArgTypeError
494 );
495 await rejects(
496 promises.rm('/tmp/testdir', { maxRetries: 'yes' }),
497 kInvalidArgTypeError
498 );
499 await rejects(
500 promises.rm('/tmp/testdir', { retryDelay: 'yes' }),
501 kInvalidArgTypeError
502 );
503 await rejects(
504 promises.rm('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
505 kInvalidArgTypeError
506 );
507 await rejects(
508 promises.rm('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
509 kInvalidArgTypeError
510 );
511 await rejects(
512 promises.rm('/tmp/testdir', {
513 maxRetries: 1,
514 retryDelay: 1,
515 force: 'yes',
516 }),
517 kInvalidArgTypeError
518 );
519 
520 await rejects(
521 promises.rmdir('/tmp/testdir', { recursive: 'yes' }),
522 kInvalidArgTypeError
523 );
524 await rejects(promises.rmdir('/tmp/testdir', 'abc'), kInvalidArgTypeError);
525 await rejects(
526 promises.rmdir('/tmp/testdir', { maxRetries: 'yes' }),
527 kInvalidArgTypeError
528 );
529 await rejects(
530 promises.rmdir('/tmp/testdir', { retryDelay: 'yes' }),
531 kInvalidArgTypeError
532 );
533 await rejects(
534 promises.rmdir('/tmp/testdir', { maxRetries: 1, retryDelay: 'yes' }),
535 kInvalidArgTypeError
536 );
537 await rejects(
538 promises.rmdir('/tmp/testdir', { maxRetries: 'yes', retryDelay: 1 }),
539 kInvalidArgTypeError
540 );
541 
542 ok(!existsSync('/tmp/testdir'));
543 mkdirSync('/tmp/testdir');
544 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
545 
546 // When the recusive option is not set, then removing a directory
547 // with children throws...
548 await rejects(promises.rmdir('/tmp/testdir'), kENotEmptyError);
549 
550 ok(existsSync('/tmp/testdir'));
551 // But works when the recursive option is set
552 await promises.rmdir('/tmp/testdir', { recursive: true });
553 ok(!existsSync('/tmp/testdir'));
554 mkdirSync('/tmp/testdir');
555 writeFileSync('/tmp/testdir/a.txt', 'Hello World');
556 writeFileSync('/tmp/testdir/b.txt', 'Hello World');
557 ok(existsSync('/tmp/testdir/a.txt'));
558 // trying to remove a file with rmdir throws
559 await rejects(promises.rmdir('/tmp/testdir/a.txt'), kENotDirError);
560 // removing a file with rm works
561 await promises.rm('/tmp/testdir/a.txt');
562 ok(!existsSync('/tmp/testdir/a.txt'));
563 // Calling rm when the directory is not empty throws
564 await rejects(promises.rm('/tmp/testdir'), kENotEmptyError);
565 ok(existsSync('/tmp/testdir'));
566 // But works when the recursive option is set
567 await promises.rm('/tmp/testdir', { recursive: true });
568 ok(!existsSync('/tmp/testdir'));
569 },
570};
571 
572export const readdirSyncTest = {
573 test() {
574 throws(() => readdirSync(), kInvalidArgTypeError);
575 throws(() => readdirSync(123), kInvalidArgTypeError);
576 throws(
577 () => readdirSync('/tmp/testdir', { withFileTypes: 123 }),
578 kInvalidArgTypeError
579 );
580 throws(
581 () => readdirSync('/tmp/testdir', { recursive: 123 }),
582 kInvalidArgTypeError
583 );
584 throws(
585 () =>
586 readdirSync('/tmp/testdir', { withFileTypes: true, recursive: 123 }),
587 kInvalidArgTypeError
588 );
589 
590 deepStrictEqual(readdirSync('/'), ['bundle', 'tmp', 'dev']);
591 
592 deepStrictEqual(readdirSync('/', 'buffer'), [
593 Buffer.from('bundle'),
594 Buffer.from('tmp'),
595 Buffer.from('dev'),
596 ]);
597 
598 {
599 const ents = readdirSync('/', { withFileTypes: true });
600 strictEqual(ents.length, 3);
601 
602 strictEqual(ents[0].name, 'bundle');
603 strictEqual(ents[0].isDirectory(), true);
604 strictEqual(ents[0].isFile(), false);
605 strictEqual(ents[0].isBlockDevice(), false);
606 strictEqual(ents[0].isCharacterDevice(), false);
607 strictEqual(ents[0].isFIFO(), false);
608 strictEqual(ents[0].isSocket(), false);
609 strictEqual(ents[0].isSymbolicLink(), false);
610 strictEqual(ents[0].parentPath, '/');
611 }
612 
613 {
614 const ents = readdirSync('/', {
615 withFileTypes: true,
616 encoding: 'buffer',
617 });
618 strictEqual(ents.length, 3);
619 
620 deepStrictEqual(ents[0].name, Buffer.from('bundle'));
621 strictEqual(ents[0].isDirectory(), true);
622 strictEqual(ents[0].isFile(), false);
623 strictEqual(ents[0].isBlockDevice(), false);
624 strictEqual(ents[0].isCharacterDevice(), false);
625 strictEqual(ents[0].isFIFO(), false);
626 strictEqual(ents[0].isSocket(), false);
627 strictEqual(ents[0].isSymbolicLink(), false);
628 strictEqual(ents[0].parentPath, '/');
629 }
630 
631 {
632 const ents = readdirSync('/', { withFileTypes: true, recursive: true });
633 strictEqual(ents.length, 8);
634 
635 strictEqual(ents[0].name, 'bundle');
636 strictEqual(ents[0].isDirectory(), true);
637 strictEqual(ents[0].isFile(), false);
638 strictEqual(ents[0].isBlockDevice(), false);
639 strictEqual(ents[0].isCharacterDevice(), false);
640 strictEqual(ents[0].isFIFO(), false);
641 strictEqual(ents[0].isSocket(), false);
642 strictEqual(ents[0].isSymbolicLink(), false);
643 strictEqual(ents[0].parentPath, '/');
644 
645 strictEqual(ents[1].name, 'bundle/worker');
646 strictEqual(ents[1].isDirectory(), false);
647 strictEqual(ents[1].isFile(), true);
648 strictEqual(ents[1].isBlockDevice(), false);
649 strictEqual(ents[1].isCharacterDevice(), false);
650 strictEqual(ents[1].isFIFO(), false);
651 strictEqual(ents[1].isSocket(), false);
652 strictEqual(ents[1].isSymbolicLink(), false);
653 strictEqual(ents[1].parentPath, '/bundle');
654 
655 strictEqual(ents[4].name, 'dev/null');
656 strictEqual(ents[4].isDirectory(), false);
657 strictEqual(ents[4].isFile(), false);
658 strictEqual(ents[4].isBlockDevice(), false);
659 strictEqual(ents[4].isCharacterDevice(), true);
660 strictEqual(ents[4].isFIFO(), false);
661 strictEqual(ents[4].isSocket(), false);
662 strictEqual(ents[4].isSymbolicLink(), false);
663 strictEqual(ents[4].parentPath, '/dev');
664 }
665 },
666};
667 
668export const readdirAsyncCallbackTest = {
669 async test() {
670 deepStrictEqual(
671 await new Promise((resolve, reject) => {
672 readdir('/', (err, files) => {
673 if (err) reject(err);
674 else resolve(files);
675 });
676 }),
677 ['bundle', 'tmp', 'dev']
678 );
679 
680 {
681 const ents = await new Promise((resolve, reject) => {
682 readdir('/', { withFileTypes: true }, (err, files) => {
683 if (err) reject(err);
684 else resolve(files);
685 });
686 });
687 strictEqual(ents.length, 3);
688 
689 strictEqual(ents[0].name, 'bundle');
690 strictEqual(ents[0].isDirectory(), true);
691 strictEqual(ents[0].isFile(), false);
692 strictEqual(ents[0].isBlockDevice(), false);
693 strictEqual(ents[0].isCharacterDevice(), false);
694 strictEqual(ents[0].isFIFO(), false);
695 strictEqual(ents[0].isSocket(), false);
696 strictEqual(ents[0].isSymbolicLink(), false);
697 strictEqual(ents[0].parentPath, '/');
698 }
699 
700 {
701 const ents = await new Promise((resolve, reject) => {
702 readdir(
703 '/',
704 { withFileTypes: true, encoding: 'buffer' },
705 (err, files) => {
706 if (err) reject(err);
707 else resolve(files);
708 }
709 );
710 });
711 strictEqual(ents.length, 3);
712 
713 deepStrictEqual(ents[0].name, Buffer.from('bundle'));
714 strictEqual(ents[0].isDirectory(), true);
715 strictEqual(ents[0].isFile(), false);
716 strictEqual(ents[0].isBlockDevice(), false);
717 strictEqual(ents[0].isCharacterDevice(), false);
718 strictEqual(ents[0].isFIFO(), false);
719 strictEqual(ents[0].isSocket(), false);
720 strictEqual(ents[0].isSymbolicLink(), false);
721 strictEqual(ents[0].parentPath, '/');
722 }
723 
724 {
725 const ents = await new Promise((resolve, reject) => {
726 readdir('/', { withFileTypes: true, recursive: true }, (err, files) => {
727 if (err) reject(err);
728 else resolve(files);
729 });
730 });
731 strictEqual(ents.length, 8);
732 
733 strictEqual(ents[0].name, 'bundle');
734 strictEqual(ents[0].isDirectory(), true);
735 strictEqual(ents[0].isFile(), false);
736 strictEqual(ents[0].isBlockDevice(), false);
737 strictEqual(ents[0].isCharacterDevice(), false);
738 strictEqual(ents[0].isFIFO(), false);
739 strictEqual(ents[0].isSocket(), false);
740 strictEqual(ents[0].isSymbolicLink(), false);
741 strictEqual(ents[0].parentPath, '/');
742 
743 strictEqual(ents[1].name, 'bundle/worker');
744 strictEqual(ents[1].isDirectory(), false);
745 strictEqual(ents[1].isFile(), true);
746 strictEqual(ents[1].isBlockDevice(), false);
747 strictEqual(ents[1].isCharacterDevice(), false);
748 strictEqual(ents[1].isFIFO(), false);
749 strictEqual(ents[1].isSocket(), false);
750 strictEqual(ents[1].isSymbolicLink(), false);
751 strictEqual(ents[1].parentPath, '/bundle');
752 
753 strictEqual(ents[4].name, 'dev/null');
754 strictEqual(ents[4].isDirectory(), false);
755 strictEqual(ents[4].isFile(), false);
756 strictEqual(ents[4].isBlockDevice(), false);
757 strictEqual(ents[4].isCharacterDevice(), true);
758 strictEqual(ents[4].isFIFO(), false);
759 strictEqual(ents[4].isSocket(), false);
760 strictEqual(ents[4].isSymbolicLink(), false);
761 strictEqual(ents[4].parentPath, '/dev');
762 }
763 },
764};
765 
766export const readdirAsyncPromiseTest = {
767 async test() {
768 deepStrictEqual(await promises.readdir('/'), ['bundle', 'tmp', 'dev']);
769 
770 {
771 const ents = await promises.readdir('/', { withFileTypes: true });
772 strictEqual(ents.length, 3);
773 
774 strictEqual(ents[0].name, 'bundle');
775 strictEqual(ents[0].isDirectory(), true);
776 strictEqual(ents[0].isFile(), false);
777 strictEqual(ents[0].isBlockDevice(), false);
778 strictEqual(ents[0].isCharacterDevice(), false);
779 strictEqual(ents[0].isFIFO(), false);
780 strictEqual(ents[0].isSocket(), false);
781 strictEqual(ents[0].isSymbolicLink(), false);
782 strictEqual(ents[0].parentPath, '/');
783 }
784 
785 {
786 const ents = await promises.readdir('/', {
787 withFileTypes: true,
788 encoding: 'buffer',
789 });
790 strictEqual(ents.length, 3);
791 
792 deepStrictEqual(ents[0].name, Buffer.from('bundle'));
793 strictEqual(ents[0].isDirectory(), true);
794 strictEqual(ents[0].isFile(), false);
795 strictEqual(ents[0].isBlockDevice(), false);
796 strictEqual(ents[0].isCharacterDevice(), false);
797 strictEqual(ents[0].isFIFO(), false);
798 strictEqual(ents[0].isSocket(), false);
799 strictEqual(ents[0].isSymbolicLink(), false);
800 strictEqual(ents[0].parentPath, '/');
801 }
802 
803 {
804 const ents = await promises.readdir('/', {
805 withFileTypes: true,
806 recursive: true,
807 });
808 strictEqual(ents.length, 8);
809 
810 strictEqual(ents[0].name, 'bundle');
811 strictEqual(ents[0].isDirectory(), true);
812 strictEqual(ents[0].isFile(), false);
813 strictEqual(ents[0].isBlockDevice(), false);
814 strictEqual(ents[0].isCharacterDevice(), false);
815 strictEqual(ents[0].isFIFO(), false);
816 strictEqual(ents[0].isSocket(), false);
817 strictEqual(ents[0].isSymbolicLink(), false);
818 strictEqual(ents[0].parentPath, '/');
819 
820 strictEqual(ents[1].name, 'bundle/worker');
821 strictEqual(ents[1].isDirectory(), false);
822 strictEqual(ents[1].isFile(), true);
823 strictEqual(ents[1].isBlockDevice(), false);
824 strictEqual(ents[1].isCharacterDevice(), false);
825 strictEqual(ents[1].isFIFO(), false);
826 strictEqual(ents[1].isSocket(), false);
827 strictEqual(ents[1].isSymbolicLink(), false);
828 strictEqual(ents[1].parentPath, '/bundle');
829 strictEqual(ents[4].name, 'dev/null');
830 strictEqual(ents[4].isDirectory(), false);
831 strictEqual(ents[4].isFile(), false);
832 strictEqual(ents[4].isBlockDevice(), false);
833 strictEqual(ents[4].isCharacterDevice(), true);
834 strictEqual(ents[4].isFIFO(), false);
835 strictEqual(ents[4].isSocket(), false);
836 strictEqual(ents[4].isSymbolicLink(), false);
837 strictEqual(ents[4].parentPath, '/dev');
838 }
839 },
840};
841 
842export const opendirSyncTest = {
843 test() {
844 throws(() => opendirSync(), kInvalidArgTypeError);
845 throws(() => opendirSync(123), kInvalidArgTypeError);
846 throws(() => opendirSync('/tmp', { encoding: 123 }), kInvalidArgValueError);
847 
848 const dir = opendirSync('/', { recursive: true });
849 strictEqual(dir.path, '/');
850 strictEqual(dir.readSync().name, 'bundle');
851 strictEqual(dir.readSync().name, 'bundle/worker');
852 strictEqual(dir.readSync().name, 'tmp');
853 strictEqual(dir.readSync().name, 'dev');
854 strictEqual(dir.readSync().name, 'dev/null');
855 strictEqual(dir.readSync().name, 'dev/zero');
856 strictEqual(dir.readSync().name, 'dev/full');
857 strictEqual(dir.readSync().name, 'dev/random');
858 strictEqual(dir.readSync(), null); // All done.
859 dir.closeSync();
860 
861 // Closing again throws
862 throws(() => dir.closeSync(), { code: 'ERR_DIR_CLOSED' });
863 // Reading again throws
864 throws(() => dir.readSync(), { code: 'ERR_DIR_CLOSED' });
865 },
866};
867 
868export const opendirSyncAndAsyncTest = {
869 async test() {
870 throws(() => opendir(), kInvalidArgTypeError);
871 throws(() => opendir(123), kInvalidArgTypeError);
872 throws(() => opendir('/tmp', { encoding: 123 }), kInvalidArgValueError);
873 
874 const { promise, resolve, reject } = Promise.withResolvers();
875 opendir('/', { recursive: true }, (err, dir) => {
876 if (err) reject(err);
877 else resolve(dir);
878 });
879 
880 await using dir = await promise;
881 
882 strictEqual((await dir.read()).name, 'bundle');
883 strictEqual((await dir.read()).name, 'bundle/worker');
884 strictEqual((await dir.read()).name, 'tmp');
885 strictEqual((await dir.read()).name, 'dev');
886 strictEqual((await dir.read()).name, 'dev/null');
887 strictEqual((await dir.read()).name, 'dev/zero');
888 strictEqual((await dir.read()).name, 'dev/full');
889 strictEqual((await dir.read()).name, 'dev/random');
890 strictEqual(await dir.read(), null); // All done.
891 },
892};
893 
894export const opendirSyncAndAsyncTest2 = {
895 async test() {
896 throws(() => opendir(), kInvalidArgTypeError);
897 throws(() => opendir(123), kInvalidArgTypeError);
898 throws(() => opendir('/tmp', { encoding: 123 }), kInvalidArgValueError);
899 
900 const { promise, resolve, reject } = Promise.withResolvers();
901 opendir('/', { recursive: true }, (err, dir) => {
902 if (err) reject(err);
903 else resolve(dir);
904 });
905 
906 await using dir = await promise;
907 
908 const entries = await Array.fromAsync(dir);
909 
910 strictEqual(entries.length, 8);
911 strictEqual(entries[0].name, 'bundle');
912 strictEqual(entries[1].name, 'bundle/worker');
913 strictEqual(entries[2].name, 'tmp');
914 strictEqual(entries[3].name, 'dev');
915 strictEqual(entries[4].name, 'dev/null');
916 strictEqual(entries[5].name, 'dev/zero');
917 strictEqual(entries[6].name, 'dev/full');
918 strictEqual(entries[7].name, 'dev/random');
919 },
920};