11cb0ef41Sopenharmony_ci// Flags: --expose-internals 21cb0ef41Sopenharmony_ci'use strict'; 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ciconst tmpdir = require('../common/tmpdir'); 51cb0ef41Sopenharmony_ciconst assert = require('assert'); 61cb0ef41Sopenharmony_ciconst fs = require('fs'); 71cb0ef41Sopenharmony_ciconst path = require('path'); 81cb0ef41Sopenharmony_ciconst { validateRmdirOptions } = require('internal/fs/utils'); 91cb0ef41Sopenharmony_ci 101cb0ef41Sopenharmony_cicommon.expectWarning( 111cb0ef41Sopenharmony_ci 'DeprecationWarning', 121cb0ef41Sopenharmony_ci 'In future versions of Node.js, fs.rmdir(path, { recursive: true }) ' + 131cb0ef41Sopenharmony_ci 'will be removed. Use fs.rm(path, { recursive: true }) instead', 141cb0ef41Sopenharmony_ci 'DEP0147' 151cb0ef41Sopenharmony_ci); 161cb0ef41Sopenharmony_ci 171cb0ef41Sopenharmony_citmpdir.refresh(); 181cb0ef41Sopenharmony_ci 191cb0ef41Sopenharmony_cilet count = 0; 201cb0ef41Sopenharmony_ciconst nextDirPath = (name = 'rmdir-recursive') => 211cb0ef41Sopenharmony_ci path.join(tmpdir.path, `${name}-${count++}`); 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_cifunction makeNonEmptyDirectory(depth, files, folders, dirname, createSymLinks) { 241cb0ef41Sopenharmony_ci fs.mkdirSync(dirname, { recursive: true }); 251cb0ef41Sopenharmony_ci fs.writeFileSync(path.join(dirname, 'text.txt'), 'hello', 'utf8'); 261cb0ef41Sopenharmony_ci 271cb0ef41Sopenharmony_ci const options = { flag: 'wx' }; 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ci for (let f = files; f > 0; f--) { 301cb0ef41Sopenharmony_ci fs.writeFileSync(path.join(dirname, `f-${depth}-${f}`), '', options); 311cb0ef41Sopenharmony_ci } 321cb0ef41Sopenharmony_ci 331cb0ef41Sopenharmony_ci if (createSymLinks) { 341cb0ef41Sopenharmony_ci // Valid symlink 351cb0ef41Sopenharmony_ci fs.symlinkSync( 361cb0ef41Sopenharmony_ci `f-${depth}-1`, 371cb0ef41Sopenharmony_ci path.join(dirname, `link-${depth}-good`), 381cb0ef41Sopenharmony_ci 'file' 391cb0ef41Sopenharmony_ci ); 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci // Invalid symlink 421cb0ef41Sopenharmony_ci fs.symlinkSync( 431cb0ef41Sopenharmony_ci 'does-not-exist', 441cb0ef41Sopenharmony_ci path.join(dirname, `link-${depth}-bad`), 451cb0ef41Sopenharmony_ci 'file' 461cb0ef41Sopenharmony_ci ); 471cb0ef41Sopenharmony_ci } 481cb0ef41Sopenharmony_ci 491cb0ef41Sopenharmony_ci // File with a name that looks like a glob 501cb0ef41Sopenharmony_ci fs.writeFileSync(path.join(dirname, '[a-z0-9].txt'), '', options); 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ci depth--; 531cb0ef41Sopenharmony_ci if (depth <= 0) { 541cb0ef41Sopenharmony_ci return; 551cb0ef41Sopenharmony_ci } 561cb0ef41Sopenharmony_ci 571cb0ef41Sopenharmony_ci for (let f = folders; f > 0; f--) { 581cb0ef41Sopenharmony_ci fs.mkdirSync( 591cb0ef41Sopenharmony_ci path.join(dirname, `folder-${depth}-${f}`), 601cb0ef41Sopenharmony_ci { recursive: true } 611cb0ef41Sopenharmony_ci ); 621cb0ef41Sopenharmony_ci makeNonEmptyDirectory( 631cb0ef41Sopenharmony_ci depth, 641cb0ef41Sopenharmony_ci files, 651cb0ef41Sopenharmony_ci folders, 661cb0ef41Sopenharmony_ci path.join(dirname, `d-${depth}-${f}`), 671cb0ef41Sopenharmony_ci createSymLinks 681cb0ef41Sopenharmony_ci ); 691cb0ef41Sopenharmony_ci } 701cb0ef41Sopenharmony_ci} 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_cifunction removeAsync(dir) { 731cb0ef41Sopenharmony_ci // Removal should fail without the recursive option. 741cb0ef41Sopenharmony_ci fs.rmdir(dir, common.mustCall((err) => { 751cb0ef41Sopenharmony_ci assert.strictEqual(err.syscall, 'rmdir'); 761cb0ef41Sopenharmony_ci 771cb0ef41Sopenharmony_ci // Removal should fail without the recursive option set to true. 781cb0ef41Sopenharmony_ci fs.rmdir(dir, { recursive: false }, common.mustCall((err) => { 791cb0ef41Sopenharmony_ci assert.strictEqual(err.syscall, 'rmdir'); 801cb0ef41Sopenharmony_ci 811cb0ef41Sopenharmony_ci // Recursive removal should succeed. 821cb0ef41Sopenharmony_ci fs.rmdir(dir, { recursive: true }, common.mustSucceed(() => { 831cb0ef41Sopenharmony_ci // An error should occur if recursive and the directory does not exist. 841cb0ef41Sopenharmony_ci fs.rmdir(dir, { recursive: true }, common.mustCall((err) => { 851cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ENOENT'); 861cb0ef41Sopenharmony_ci // Attempted removal should fail now because the directory is gone. 871cb0ef41Sopenharmony_ci fs.rmdir(dir, common.mustCall((err) => { 881cb0ef41Sopenharmony_ci assert.strictEqual(err.syscall, 'rmdir'); 891cb0ef41Sopenharmony_ci })); 901cb0ef41Sopenharmony_ci })); 911cb0ef41Sopenharmony_ci })); 921cb0ef41Sopenharmony_ci })); 931cb0ef41Sopenharmony_ci })); 941cb0ef41Sopenharmony_ci} 951cb0ef41Sopenharmony_ci 961cb0ef41Sopenharmony_ci// Test the asynchronous version 971cb0ef41Sopenharmony_ci{ 981cb0ef41Sopenharmony_ci // Create a 4-level folder hierarchy including symlinks 991cb0ef41Sopenharmony_ci let dir = nextDirPath(); 1001cb0ef41Sopenharmony_ci makeNonEmptyDirectory(4, 10, 2, dir, true); 1011cb0ef41Sopenharmony_ci removeAsync(dir); 1021cb0ef41Sopenharmony_ci 1031cb0ef41Sopenharmony_ci // Create a 2-level folder hierarchy without symlinks 1041cb0ef41Sopenharmony_ci dir = nextDirPath(); 1051cb0ef41Sopenharmony_ci makeNonEmptyDirectory(2, 10, 2, dir, false); 1061cb0ef41Sopenharmony_ci removeAsync(dir); 1071cb0ef41Sopenharmony_ci 1081cb0ef41Sopenharmony_ci // Create a flat folder including symlinks 1091cb0ef41Sopenharmony_ci dir = nextDirPath(); 1101cb0ef41Sopenharmony_ci makeNonEmptyDirectory(1, 10, 2, dir, true); 1111cb0ef41Sopenharmony_ci removeAsync(dir); 1121cb0ef41Sopenharmony_ci} 1131cb0ef41Sopenharmony_ci 1141cb0ef41Sopenharmony_ci// Test the synchronous version. 1151cb0ef41Sopenharmony_ci{ 1161cb0ef41Sopenharmony_ci const dir = nextDirPath(); 1171cb0ef41Sopenharmony_ci makeNonEmptyDirectory(4, 10, 2, dir, true); 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci // Removal should fail without the recursive option set to true. 1201cb0ef41Sopenharmony_ci assert.throws(() => { 1211cb0ef41Sopenharmony_ci fs.rmdirSync(dir); 1221cb0ef41Sopenharmony_ci }, { syscall: 'rmdir' }); 1231cb0ef41Sopenharmony_ci assert.throws(() => { 1241cb0ef41Sopenharmony_ci fs.rmdirSync(dir, { recursive: false }); 1251cb0ef41Sopenharmony_ci }, { syscall: 'rmdir' }); 1261cb0ef41Sopenharmony_ci 1271cb0ef41Sopenharmony_ci // Recursive removal should succeed. 1281cb0ef41Sopenharmony_ci fs.rmdirSync(dir, { recursive: true }); 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci // An error should occur if recursive and the directory does not exist. 1311cb0ef41Sopenharmony_ci assert.throws(() => fs.rmdirSync(dir, { recursive: true }), 1321cb0ef41Sopenharmony_ci { code: 'ENOENT' }); 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci // Attempted removal should fail now because the directory is gone. 1351cb0ef41Sopenharmony_ci assert.throws(() => fs.rmdirSync(dir), { syscall: 'rmdir' }); 1361cb0ef41Sopenharmony_ci} 1371cb0ef41Sopenharmony_ci 1381cb0ef41Sopenharmony_ci// Test the Promises based version. 1391cb0ef41Sopenharmony_ci(async () => { 1401cb0ef41Sopenharmony_ci const dir = nextDirPath(); 1411cb0ef41Sopenharmony_ci makeNonEmptyDirectory(4, 10, 2, dir, true); 1421cb0ef41Sopenharmony_ci 1431cb0ef41Sopenharmony_ci // Removal should fail without the recursive option set to true. 1441cb0ef41Sopenharmony_ci await assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' }); 1451cb0ef41Sopenharmony_ci await assert.rejects(fs.promises.rmdir(dir, { recursive: false }), { 1461cb0ef41Sopenharmony_ci syscall: 'rmdir' 1471cb0ef41Sopenharmony_ci }); 1481cb0ef41Sopenharmony_ci 1491cb0ef41Sopenharmony_ci // Recursive removal should succeed. 1501cb0ef41Sopenharmony_ci await fs.promises.rmdir(dir, { recursive: true }); 1511cb0ef41Sopenharmony_ci 1521cb0ef41Sopenharmony_ci // An error should occur if recursive and the directory does not exist. 1531cb0ef41Sopenharmony_ci await assert.rejects(fs.promises.rmdir(dir, { recursive: true }), 1541cb0ef41Sopenharmony_ci { code: 'ENOENT' }); 1551cb0ef41Sopenharmony_ci 1561cb0ef41Sopenharmony_ci // Attempted removal should fail now because the directory is gone. 1571cb0ef41Sopenharmony_ci await assert.rejects(fs.promises.rmdir(dir), { syscall: 'rmdir' }); 1581cb0ef41Sopenharmony_ci})().then(common.mustCall()); 1591cb0ef41Sopenharmony_ci 1601cb0ef41Sopenharmony_ci// Test input validation. 1611cb0ef41Sopenharmony_ci{ 1621cb0ef41Sopenharmony_ci const defaults = { 1631cb0ef41Sopenharmony_ci retryDelay: 100, 1641cb0ef41Sopenharmony_ci maxRetries: 0, 1651cb0ef41Sopenharmony_ci recursive: false 1661cb0ef41Sopenharmony_ci }; 1671cb0ef41Sopenharmony_ci const modified = { 1681cb0ef41Sopenharmony_ci retryDelay: 953, 1691cb0ef41Sopenharmony_ci maxRetries: 5, 1701cb0ef41Sopenharmony_ci recursive: true 1711cb0ef41Sopenharmony_ci }; 1721cb0ef41Sopenharmony_ci 1731cb0ef41Sopenharmony_ci assert.deepStrictEqual(validateRmdirOptions(), defaults); 1741cb0ef41Sopenharmony_ci assert.deepStrictEqual(validateRmdirOptions({}), defaults); 1751cb0ef41Sopenharmony_ci assert.deepStrictEqual(validateRmdirOptions(modified), modified); 1761cb0ef41Sopenharmony_ci assert.deepStrictEqual(validateRmdirOptions({ 1771cb0ef41Sopenharmony_ci maxRetries: 99 1781cb0ef41Sopenharmony_ci }), { 1791cb0ef41Sopenharmony_ci retryDelay: 100, 1801cb0ef41Sopenharmony_ci maxRetries: 99, 1811cb0ef41Sopenharmony_ci recursive: false 1821cb0ef41Sopenharmony_ci }); 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci [null, 'foo', 5, NaN].forEach((bad) => { 1851cb0ef41Sopenharmony_ci assert.throws(() => { 1861cb0ef41Sopenharmony_ci validateRmdirOptions(bad); 1871cb0ef41Sopenharmony_ci }, { 1881cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_TYPE', 1891cb0ef41Sopenharmony_ci name: 'TypeError', 1901cb0ef41Sopenharmony_ci message: /^The "options" argument must be of type object\./ 1911cb0ef41Sopenharmony_ci }); 1921cb0ef41Sopenharmony_ci }); 1931cb0ef41Sopenharmony_ci 1941cb0ef41Sopenharmony_ci [undefined, null, 'foo', Infinity, function() {}].forEach((bad) => { 1951cb0ef41Sopenharmony_ci assert.throws(() => { 1961cb0ef41Sopenharmony_ci validateRmdirOptions({ recursive: bad }); 1971cb0ef41Sopenharmony_ci }, { 1981cb0ef41Sopenharmony_ci code: 'ERR_INVALID_ARG_TYPE', 1991cb0ef41Sopenharmony_ci name: 'TypeError', 2001cb0ef41Sopenharmony_ci message: /^The "options\.recursive" property must be of type boolean\./ 2011cb0ef41Sopenharmony_ci }); 2021cb0ef41Sopenharmony_ci }); 2031cb0ef41Sopenharmony_ci 2041cb0ef41Sopenharmony_ci assert.throws(() => { 2051cb0ef41Sopenharmony_ci validateRmdirOptions({ retryDelay: -1 }); 2061cb0ef41Sopenharmony_ci }, { 2071cb0ef41Sopenharmony_ci code: 'ERR_OUT_OF_RANGE', 2081cb0ef41Sopenharmony_ci name: 'RangeError', 2091cb0ef41Sopenharmony_ci message: /^The value of "options\.retryDelay" is out of range\./ 2101cb0ef41Sopenharmony_ci }); 2111cb0ef41Sopenharmony_ci 2121cb0ef41Sopenharmony_ci assert.throws(() => { 2131cb0ef41Sopenharmony_ci validateRmdirOptions({ maxRetries: -1 }); 2141cb0ef41Sopenharmony_ci }, { 2151cb0ef41Sopenharmony_ci code: 'ERR_OUT_OF_RANGE', 2161cb0ef41Sopenharmony_ci name: 'RangeError', 2171cb0ef41Sopenharmony_ci message: /^The value of "options\.maxRetries" is out of range\./ 2181cb0ef41Sopenharmony_ci }); 2191cb0ef41Sopenharmony_ci} 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci// It should not pass recursive option to rmdirSync, when called from 2221cb0ef41Sopenharmony_ci// rimraf (see: #35566) 2231cb0ef41Sopenharmony_ci{ 2241cb0ef41Sopenharmony_ci // Make a non-empty directory: 2251cb0ef41Sopenharmony_ci const original = fs.rmdirSync; 2261cb0ef41Sopenharmony_ci const dir = `${nextDirPath()}/foo/bar`; 2271cb0ef41Sopenharmony_ci fs.mkdirSync(dir, { recursive: true }); 2281cb0ef41Sopenharmony_ci fs.writeFileSync(`${dir}/foo.txt`, 'hello world', 'utf8'); 2291cb0ef41Sopenharmony_ci 2301cb0ef41Sopenharmony_ci // When called the second time from rimraf, the recursive option should 2311cb0ef41Sopenharmony_ci // not be set for rmdirSync: 2321cb0ef41Sopenharmony_ci let callCount = 0; 2331cb0ef41Sopenharmony_ci let rmdirSyncOptionsFromRimraf; 2341cb0ef41Sopenharmony_ci fs.rmdirSync = (path, options) => { 2351cb0ef41Sopenharmony_ci if (callCount > 0) { 2361cb0ef41Sopenharmony_ci rmdirSyncOptionsFromRimraf = { ...options }; 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci callCount++; 2391cb0ef41Sopenharmony_ci return original(path, options); 2401cb0ef41Sopenharmony_ci }; 2411cb0ef41Sopenharmony_ci fs.rmdirSync(dir, { recursive: true }); 2421cb0ef41Sopenharmony_ci fs.rmdirSync = original; 2431cb0ef41Sopenharmony_ci assert.strictEqual(rmdirSyncOptionsFromRimraf.recursive, undefined); 2441cb0ef41Sopenharmony_ci} 245