// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { assert, assertRejects, assertThrows } from "./test_util.ts"; const REMOVE_METHODS = ["remove", "removeSync"] as const; Deno.test( { permissions: { write: true, read: true } }, async function removeDirSuccess() { for (const method of REMOVE_METHODS) { // REMOVE EMPTY DIRECTORY const path = Deno.makeTempDirSync() + "/subdir"; Deno.mkdirSync(path); const pathInfo = Deno.statSync(path); assert(pathInfo.isDirectory); // check exist first await Deno[method](path); // remove // We then check again after remove assertThrows(() => { Deno.statSync(path); }, Deno.errors.NotFound); } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeFileSuccess() { for (const method of REMOVE_METHODS) { // REMOVE FILE const enc = new TextEncoder(); const data = enc.encode("Hello"); const filename = Deno.makeTempDirSync() + "/test.txt"; Deno.writeFileSync(filename, data, { mode: 0o666 }); const fileInfo = Deno.statSync(filename); assert(fileInfo.isFile); // check exist first await Deno[method](filename); // remove // We then check again after remove assertThrows(() => { Deno.statSync(filename); }, Deno.errors.NotFound); } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeFileByUrl() { for (const method of REMOVE_METHODS) { // REMOVE FILE const enc = new TextEncoder(); const data = enc.encode("Hello"); const tempDir = Deno.makeTempDirSync(); const fileUrl = new URL( `file://${Deno.build.os === "windows" ? "/" : ""}${tempDir}/test.txt`, ); Deno.writeFileSync(fileUrl, data, { mode: 0o666 }); const fileInfo = Deno.statSync(fileUrl); assert(fileInfo.isFile); // check exist first await Deno[method](fileUrl); // remove // We then check again after remove assertThrows(() => { Deno.statSync(fileUrl); }, Deno.errors.NotFound); } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeFail() { for (const method of REMOVE_METHODS) { // NON-EMPTY DIRECTORY const path = Deno.makeTempDirSync() + "/dir/subdir"; const subPath = path + "/subsubdir"; Deno.mkdirSync(path, { recursive: true }); Deno.mkdirSync(subPath); const pathInfo = Deno.statSync(path); assert(pathInfo.isDirectory); // check exist first const subPathInfo = Deno.statSync(subPath); assert(subPathInfo.isDirectory); // check exist first await assertRejects( async () => { await Deno[method](path); }, Error, `remove '${path}'`, ); // TODO(ry) Is Other really the error we should get here? What would Go do? // NON-EXISTENT DIRECTORY/FILE await assertRejects( async () => { await Deno[method]("/baddir"); }, Deno.errors.NotFound, `remove '/baddir'`, ); } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeDanglingSymlinkSuccess() { for (const method of REMOVE_METHODS) { const danglingSymlinkPath = Deno.makeTempDirSync() + "/dangling_symlink"; if (Deno.build.os === "windows") { Deno.symlinkSync("unexistent_file", danglingSymlinkPath, { type: "file", }); } else { Deno.symlinkSync("unexistent_file", danglingSymlinkPath); } const pathInfo = Deno.lstatSync(danglingSymlinkPath); assert(pathInfo.isSymlink); await Deno[method](danglingSymlinkPath); assertThrows(() => { Deno.lstatSync(danglingSymlinkPath); }, Deno.errors.NotFound); } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeValidSymlinkSuccess() { for (const method of REMOVE_METHODS) { const encoder = new TextEncoder(); const data = encoder.encode("Test"); const tempDir = Deno.makeTempDirSync(); const filePath = tempDir + "/test.txt"; const validSymlinkPath = tempDir + "/valid_symlink"; Deno.writeFileSync(filePath, data, { mode: 0o666 }); if (Deno.build.os === "windows") { Deno.symlinkSync(filePath, validSymlinkPath, { type: "file" }); } else { Deno.symlinkSync(filePath, validSymlinkPath); } const symlinkPathInfo = Deno.statSync(validSymlinkPath); assert(symlinkPathInfo.isFile); await Deno[method](validSymlinkPath); assertThrows(() => { Deno.statSync(validSymlinkPath); }, Deno.errors.NotFound); await Deno[method](filePath); } }, ); Deno.test({ permissions: { write: false } }, async function removePerm() { for (const method of REMOVE_METHODS) { await assertRejects(async () => { await Deno[method]("/baddir"); }, Deno.errors.NotCapable); } }); Deno.test( { permissions: { write: true, read: true } }, async function removeAllDirSuccess() { for (const method of REMOVE_METHODS) { // REMOVE EMPTY DIRECTORY let path = Deno.makeTempDirSync() + "/dir/subdir"; Deno.mkdirSync(path, { recursive: true }); let pathInfo = Deno.statSync(path); assert(pathInfo.isDirectory); // check exist first await Deno[method](path, { recursive: true }); // remove // We then check again after remove assertThrows( () => { Deno.statSync(path); }, // Directory is gone Deno.errors.NotFound, ); // REMOVE NON-EMPTY DIRECTORY path = Deno.makeTempDirSync() + "/dir/subdir"; const subPath = path + "/subsubdir"; Deno.mkdirSync(path, { recursive: true }); Deno.mkdirSync(subPath); pathInfo = Deno.statSync(path); assert(pathInfo.isDirectory); // check exist first const subPathInfo = Deno.statSync(subPath); assert(subPathInfo.isDirectory); // check exist first await Deno[method](path, { recursive: true }); // remove // We then check parent directory again after remove assertThrows(() => { Deno.statSync(path); }, Deno.errors.NotFound); // Directory is gone } }, ); Deno.test( { permissions: { write: true, read: true } }, async function removeAllFileSuccess() { for (const method of REMOVE_METHODS) { // REMOVE FILE const enc = new TextEncoder(); const data = enc.encode("Hello"); const filename = Deno.makeTempDirSync() + "/test.txt"; Deno.writeFileSync(filename, data, { mode: 0o666 }); const fileInfo = Deno.statSync(filename); assert(fileInfo.isFile); // check exist first await Deno[method](filename, { recursive: true }); // remove // We then check again after remove assertThrows(() => { Deno.statSync(filename); }, Deno.errors.NotFound); // File is gone } }, ); Deno.test({ permissions: { write: true } }, async function removeAllFail() { for (const method of REMOVE_METHODS) { // NON-EXISTENT DIRECTORY/FILE await assertRejects( async () => { // Non-existent await Deno[method]("/baddir", { recursive: true }); }, Deno.errors.NotFound, `remove '/baddir'`, ); } }); Deno.test({ permissions: { write: false } }, async function removeAllPerm() { for (const method of REMOVE_METHODS) { await assertRejects(async () => { await Deno[method]("/baddir", { recursive: true }); }, Deno.errors.NotCapable); } }); Deno.test( { ignore: Deno.build.os === "windows", permissions: { write: true, read: true }, }, async function removeUnixSocketSuccess() { for (const method of REMOVE_METHODS) { // MAKE TEMPORARY UNIX SOCKET const path = Deno.makeTempDirSync() + "/test.sock"; const listener = Deno.listen({ transport: "unix", path }); listener.close(); Deno.statSync(path); // check if unix socket exists await Deno[method](path); assertThrows(() => Deno.statSync(path), Deno.errors.NotFound); } }, ); if (Deno.build.os === "windows") { Deno.test( { permissions: { run: true, write: true, read: true } }, async function removeFileSymlink() { const { success } = await new Deno.Command("cmd", { args: ["/c", "mklink", "file_link", "bar"], stdout: "null", }).output(); assert(success); await Deno.remove("file_link"); await assertRejects(async () => { await Deno.lstat("file_link"); }, Deno.errors.NotFound); }, ); Deno.test( { permissions: { run: true, write: true, read: true } }, async function removeDirSymlink() { const { success } = await new Deno.Command("cmd", { args: ["/c", "mklink", "/d", "dir_link", "bar"], stdout: "null", }).output(); assert(success); await Deno.remove("dir_link"); await assertRejects(async () => { await Deno.lstat("dir_link"); }, Deno.errors.NotFound); }, ); }