aboutsummaryrefslogtreecommitdiff
path: root/test/js/node/fs/cp.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'test/js/node/fs/cp.test.ts')
-rw-r--r--test/js/node/fs/cp.test.ts255
1 files changed, 255 insertions, 0 deletions
diff --git a/test/js/node/fs/cp.test.ts b/test/js/node/fs/cp.test.ts
new file mode 100644
index 000000000..37da58843
--- /dev/null
+++ b/test/js/node/fs/cp.test.ts
@@ -0,0 +1,255 @@
+import fs from "fs";
+import { describe, test, expect, jest } from "bun:test";
+import { tempDirWithFiles } from "harness";
+
+const impls = [
+ // ["cpSync", fs.cpSync],
+ ["cp", fs.promises.cp],
+] as const;
+
+for (const [name, copy] of impls) {
+ async function copyShouldThrow(...args: Parameters<typeof copy>) {
+ try {
+ await (copy as any)(...args);
+ } catch (e: any) {
+ if (e?.code?.toUpperCase() === "TODO") {
+ throw new Error("Expected " + name + "() to throw non TODO error");
+ }
+ return e;
+ }
+ throw new Error("Expected " + name + "() to throw");
+ }
+
+ function assertContent(path: string, content: string) {
+ expect(fs.readFileSync(path, "utf8")).toBe(content);
+ }
+
+ describe("fs." + name, () => {
+ test("single file", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ });
+
+ await copy(basename + "/from/a.txt", basename + "/to.txt");
+
+ expect(fs.readFileSync(basename + "/to.txt", "utf8")).toBe("a");
+ });
+
+ test("refuse to copy directory with 'recursive: false'", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ });
+
+ await copyShouldThrow(basename + "/from", basename + "/result");
+ });
+
+ test("recursive directory structure - no destination", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b/e.txt": "e",
+ "from/c.txt": "c",
+ "from/w/y/x/z.txt": "z",
+ });
+
+ await copy(basename + "/from", basename + "/result", { recursive: true });
+
+ assertContent(basename + "/result/a.txt", "a");
+ assertContent(basename + "/result/b/e.txt", "e");
+ assertContent(basename + "/result/c.txt", "c");
+ assertContent(basename + "/result/w/y/x/z.txt", "z");
+ });
+
+ test("recursive directory structure - overwrite existing files by default", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b/e.txt": "e",
+ "from/c.txt": "c",
+ "from/w/y/x/z.txt": "z",
+
+ "result/a.txt": "fail",
+ "result/w/y/x/z.txt": "lose",
+ "result/w/y/v.txt": "keep this",
+ });
+
+ await copy(basename + "/from", basename + "/result", { recursive: true });
+
+ assertContent(basename + "/result/a.txt", "a");
+ assertContent(basename + "/result/b/e.txt", "e");
+ assertContent(basename + "/result/c.txt", "c");
+ assertContent(basename + "/result/w/y/x/z.txt", "z");
+ assertContent(basename + "/result/w/y/v.txt", "keep this");
+ });
+
+ test("recursive directory structure - 'force: false' does not overwrite existing files", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "lose",
+ "from/b/e.txt": "e",
+ "from/c.txt": "c",
+ "from/w/y/x/z.txt": "lose",
+
+ "result/a.txt": "win",
+ "result/w/y/x/z.txt": "win",
+ "result/w/y/v.txt": "keep this",
+ });
+
+ await copy(basename + "/from", basename + "/result", { recursive: true, force: false });
+
+ assertContent(basename + "/result/a.txt", "win");
+ assertContent(basename + "/result/b/e.txt", "e");
+ assertContent(basename + "/result/c.txt", "c");
+ assertContent(basename + "/result/w/y/x/z.txt", "win");
+ assertContent(basename + "/result/w/y/v.txt", "keep this");
+ });
+
+ test("'force: false' on a single file doesn't override", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "lose",
+ "result/a.txt": "win",
+ });
+
+ await copy(basename + "/from/a.txt", basename + "/result/a.txt", { force: false });
+
+ assertContent(basename + "/result/a.txt", "win");
+ });
+
+ test("'force: true' on a single file does override", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "win",
+ "result/a.txt": "lose",
+ });
+
+ await copy(basename + "/from/a.txt", basename + "/result/a.txt", { force: true });
+
+ assertContent(basename + "/result/a.txt", "win");
+ });
+
+ test("'force: false' + 'errorOnExist: true' can throw", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "lose",
+ "result/a.txt": "win",
+ });
+
+ await copyShouldThrow(basename + "/from/a.txt", basename + "/result/a.txt", { force: false, errorOnExist: true });
+
+ assertContent(basename + "/result/a.txt", "win");
+ });
+
+ test("symlinks - single file", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ });
+
+ fs.symlinkSync(basename + "/from/a.txt", basename + "/from/a_symlink.txt");
+
+ await copy(basename + "/from/a_symlink.txt", basename + "/result.txt");
+ await copy(basename + "/from/a_symlink.txt", basename + "/result2.txt", { recursive: false });
+
+ const stats = fs.lstatSync(basename + "/result.txt");
+ expect(stats.isSymbolicLink()).toBe(true);
+ expect(fs.readFileSync(basename + "/result.txt", "utf8")).toBe("a");
+
+ const stats2 = fs.lstatSync(basename + "/result2.txt");
+ expect(stats2.isSymbolicLink()).toBe(true);
+ expect(fs.readFileSync(basename + "/result2.txt", "utf8")).toBe("a");
+ });
+
+ test("symlinks - single file recursive", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ });
+
+ fs.symlinkSync(basename + "/from/a.txt", basename + "/from/a_symlink.txt");
+
+ await copy(basename + "/from/a_symlink.txt", basename + "/result.txt", { recursive: true });
+
+ const stats = fs.lstatSync(basename + "/result.txt");
+ expect(stats.isSymbolicLink()).toBe(true);
+ expect(fs.readFileSync(basename + "/result.txt", "utf8")).toBe("a");
+ });
+
+ test("symlinks - directory recursive", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b.txt": "b",
+ "from/dir/c.txt": "c",
+ });
+
+ fs.symlinkSync(basename + "/from/a.txt", basename + "/from/a_symlink.txt");
+ fs.symlinkSync(basename + "/from/dir", basename + "/from/dir_symlink");
+
+ await copy(basename + "/from", basename + "/result", { recursive: true });
+
+ const statsFile = fs.lstatSync(basename + "/result/a_symlink.txt");
+ expect(statsFile.isSymbolicLink()).toBe(true);
+ expect(fs.readFileSync(basename + "/result/a_symlink.txt", "utf8")).toBe("a");
+
+ const statsDir = fs.lstatSync(basename + "/result/dir_symlink");
+ expect(statsDir.isSymbolicLink()).toBe(true);
+ expect(fs.readdirSync(basename + "/result/dir_symlink")).toEqual(["c.txt"]);
+ });
+
+ test("symlinks - directory recursive 2", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b.txt": "b",
+ "from/dir/c.txt": "c",
+ });
+
+ fs.symlinkSync(basename + "/from/a.txt", basename + "/from/a_symlink.txt");
+ fs.symlinkSync(basename + "/from/dir", basename + "/from/dir_symlink");
+ fs.mkdirSync(basename + "/result");
+
+ await copy(basename + "/from", basename + "/result", { recursive: true });
+
+ const statsFile = fs.lstatSync(basename + "/result/a_symlink.txt");
+ expect(statsFile.isSymbolicLink()).toBe(true);
+ expect(fs.readFileSync(basename + "/result/a_symlink.txt", "utf8")).toBe("a");
+
+ const statsDir = fs.lstatSync(basename + "/result/dir_symlink");
+ expect(statsDir.isSymbolicLink()).toBe(true);
+ expect(fs.readdirSync(basename + "/result/dir_symlink")).toEqual(["c.txt"]);
+ });
+
+ test("filter - works", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b.txt": "b",
+ });
+
+ await copy(basename + "/from", basename + "/result", {
+ filter: (src: string) => {
+ return src.endsWith("/from") || src.includes("a.txt");
+ },
+ recursive: true,
+ });
+
+ expect(fs.existsSync(basename + "/result/a.txt")).toBe(true);
+ expect(fs.existsSync(basename + "/result/b.txt")).toBe(false);
+ });
+
+ test("filter - paths given are correct and relative", async () => {
+ const basename = tempDirWithFiles("cp", {
+ "from/a.txt": "a",
+ "from/b.txt": "b",
+ });
+
+ const filter = jest.fn((src: string) => true);
+
+ let prev = process.cwd();
+ process.chdir(basename);
+
+ await copy(basename + "/from", basename + "/result", {
+ filter,
+ recursive: true,
+ });
+
+ process.chdir(prev);
+
+ expect(filter.mock.calls.sort((a, b) => a[0].localeCompare(b[0]))).toEqual([
+ [basename + "/from", basename + "/result"],
+ [basename + "/from/a.txt", basename + "/result/a.txt"],
+ [basename + "/from/b.txt", basename + "/result/b.txt"],
+ ]);
+ });
+ });
+}