| 1 | /** |
| 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. |
| 3 | * |
| 4 | * This source code is licensed under the MIT license found in the |
| 5 | * LICENSE file in the root directory of this source tree. |
| 6 | */ |
| 7 | |
| 8 | import fs from 'node:fs'; |
| 9 | import os from 'node:os'; |
| 10 | import path from 'node:path'; |
| 11 | import {exists} from 'shared/fs'; |
| 12 | import rmtree from '../rmtree'; |
| 13 | |
| 14 | describe('rmtree', () => { |
| 15 | let tmp: string; |
| 16 | |
| 17 | beforeEach(async () => { |
| 18 | tmp = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'rmtree-test')); |
| 19 | }); |
| 20 | |
| 21 | it('does not complain about a non-existent file', async () => { |
| 22 | await rmtree(path.join(tmp, 'foo')); |
| 23 | }); |
| 24 | |
| 25 | it('removes a file', async () => { |
| 26 | const file = path.join(tmp, 'foo'); |
| 27 | await fs.promises.writeFile(file, 'foobar'); |
| 28 | await rmtree(file); |
| 29 | |
| 30 | expect(await exists(file)).toBe(false); |
| 31 | }); |
| 32 | |
| 33 | it('removes an empty folder', async () => { |
| 34 | const folder = path.join(tmp, 'foo'); |
| 35 | await fs.promises.mkdir(folder); |
| 36 | await rmtree(folder); |
| 37 | |
| 38 | expect(await exists(folder)).toBe(false); |
| 39 | }); |
| 40 | |
| 41 | it('removes a folder with files', async () => { |
| 42 | const folder = path.join(tmp, 'foo'); |
| 43 | await fs.promises.mkdir(folder); |
| 44 | await fs.promises.writeFile(path.join(folder, '1'), '1'); |
| 45 | await fs.promises.writeFile(path.join(folder, '2'), '2'); |
| 46 | await fs.promises.writeFile(path.join(folder, '3'), '3'); |
| 47 | await fs.promises.writeFile(path.join(folder, '4'), '4'); |
| 48 | await rmtree(folder); |
| 49 | |
| 50 | expect(await exists(folder)).toBe(false); |
| 51 | }); |
| 52 | |
| 53 | it('removes a deeper tree of folders and files', async () => { |
| 54 | const folder = path.join(tmp, 'tree'); |
| 55 | await fs.promises.mkdir(path.join(folder, '1/2/3/4/5'), {recursive: true}); |
| 56 | await fs.promises.writeFile(path.join(folder, '1/A'), 'A'); |
| 57 | await fs.promises.writeFile(path.join(folder, '1/2/B'), 'B'); |
| 58 | await fs.promises.writeFile(path.join(folder, '1/2/B'), 'B'); |
| 59 | await fs.promises.writeFile(path.join(folder, '1/2/3/C'), 'C'); |
| 60 | await fs.promises.writeFile(path.join(folder, '1/2/3/4/D'), 'D'); |
| 61 | await fs.promises.writeFile(path.join(folder, '1/2/3/4/5/E'), 'E'); |
| 62 | await rmtree(folder); |
| 63 | |
| 64 | expect(await exists(folder)).toBe(false); |
| 65 | }); |
| 66 | |
| 67 | it('does not follow argument if it is a symlink', async () => { |
| 68 | const target = path.join(tmp, 'target'); |
| 69 | const link = path.join(tmp, 'link'); |
| 70 | await fs.promises.writeFile(target, 'target file'); |
| 71 | await fs.promises.symlink(target, link); |
| 72 | expect(await fs.promises.readFile(link, {encoding: 'utf8'})).toBe('target file'); |
| 73 | expect(await exists(link)).toBe(true); |
| 74 | await rmtree(link); |
| 75 | |
| 76 | expect(await exists(link)).toBe(false); |
| 77 | expect(await exists(target)).toBe(true); |
| 78 | }); |
| 79 | |
| 80 | it('does not follow symlink in the tree', async () => { |
| 81 | const target = path.join(tmp, 'target'); |
| 82 | await fs.promises.writeFile(target, 'target file'); |
| 83 | |
| 84 | const folder = path.join(tmp, 'folder'); |
| 85 | await fs.promises.mkdir(folder); |
| 86 | const link = path.join(folder, 'link'); |
| 87 | await fs.promises.symlink(target, link); |
| 88 | expect(await fs.promises.readFile(link, {encoding: 'utf8'})).toBe('target file'); |
| 89 | await rmtree(folder); |
| 90 | |
| 91 | expect(await exists(folder)).toBe(false); |
| 92 | expect(await exists(target)).toBe(true); |
| 93 | }); |
| 94 | }); |
| 95 | |