std/fs/copy_test.ts

549 lines
16 KiB
TypeScript

// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assert, assertEquals, assertRejects, assertThrows } from "@std/assert";
import * as path from "@std/path";
import { copy, copySync } from "./copy.ts";
import { existsSync } from "./exists.ts";
import { ensureDir, ensureDirSync } from "./ensure_dir.ts";
import { ensureFile, ensureFileSync } from "./ensure_file.ts";
import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts";
const moduleDir = path.dirname(path.fromFileUrl(import.meta.url));
const testdataDir = path.resolve(moduleDir, "testdata");
function testCopy(
name: string,
cb: (tempDir: string) => Promise<void>,
ignore = false,
) {
Deno.test({
name,
async fn() {
const tempDir = await Deno.makeTempDir({
prefix: "deno_std_copy_async_test_",
});
await cb(tempDir);
await Deno.remove(tempDir, { recursive: true });
},
ignore,
});
}
function testCopySync(name: string, cb: (tempDir: string) => void) {
Deno.test({
name,
fn: () => {
const tempDir = Deno.makeTempDirSync({
prefix: "deno_std_copy_sync_test_",
});
cb(tempDir);
Deno.removeSync(tempDir, { recursive: true });
},
});
}
testCopy(
"copy() rejects if src does not exist",
async (tempDir: string) => {
const srcFile = path.join(testdataDir, "copy_file_not_exists.txt");
const destFile = path.join(tempDir, "copy_file_not_exists_1.txt");
await assertRejects(
async () => {
await copy(srcFile, destFile);
},
);
},
);
testCopy(
"copy() rejects if src and dest are the same paths",
async (tempDir: string) => {
const srcFile = path.join(tempDir, "copy_file_same.txt");
const destFile = path.join(tempDir, "copy_file_same.txt");
await assertRejects(
async () => {
await copy(srcFile, destFile);
},
Error,
"Source and destination cannot be the same",
);
},
);
testCopy(
"copy() copies file to new destination",
async (tempDir: string) => {
const srcFile = path.join(testdataDir, "copy_file.txt");
const destFile = path.join(tempDir, "copy_file_copy.txt");
const srcContent = await Deno.readTextFile(srcFile);
assert(await Deno.lstat(srcFile), "source should exist before copy");
await assertRejects(
async () => await Deno.lstat(destFile),
"destination should not exist before copy",
);
await copy(srcFile, destFile);
assert(await Deno.lstat(srcFile), "source should exist after copy");
assert(await Deno.lstat(destFile), "destination should exist after copy");
const destContent = await Deno.readTextFile(destFile);
assertEquals(
srcContent,
destContent,
"source and destination should have the same content",
);
// Copy again and it should throw an error.
await assertRejects(
async () => {
await copy(srcFile, destFile);
},
Error,
`'${destFile}' already exists`,
);
// Modify destination file.
await Deno.writeTextFile(destFile, "txt copy");
assertEquals(await Deno.readTextFile(destFile), "txt copy");
// Copy again with overwrite option.
await copy(srcFile, destFile, { overwrite: true });
// Make sure the file has been overwritten.
assertEquals(await Deno.readTextFile(destFile), "txt");
},
);
testCopy(
"copy() copies with preserve timestamps",
async (tempDir: string) => {
{
const srcFile = path.join(testdataDir, "copy_file.txt");
const destFile = path.join(tempDir, "copy_file_copy.txt");
const srcStatInfo = await Deno.stat(srcFile);
assert(srcStatInfo.atime instanceof Date);
assert(srcStatInfo.mtime instanceof Date);
// Copy with overwrite and preserve timestamps options.
await copy(srcFile, destFile, {
overwrite: true,
preserveTimestamps: true,
});
const destStatInfo = await Deno.stat(destFile);
assert(destStatInfo.atime instanceof Date);
assert(destStatInfo.mtime instanceof Date);
assertEquals(destStatInfo.atime, srcStatInfo.atime);
assertEquals(destStatInfo.mtime, srcStatInfo.mtime);
}
// copy dir with preserve timestamps
{
const srcDir = path.join(testdataDir, "copy_dir");
const destDir = path.join(tempDir, "copy_dir");
const srcFile = path.join(srcDir, "0.txt");
const destFile = path.join(destDir, "0.txt");
const srcNestFile = path.join(srcDir, "nest", "0.txt");
const destNestFile = path.join(destDir, "nest", "0.txt");
await copy(srcDir, destDir, { preserveTimestamps: true });
const srcDirInfo = await Deno.stat(srcFile);
const destDirInfo = await Deno.stat(destFile);
const srcFileInfo = await Deno.stat(srcFile);
const destFileInfo = await Deno.stat(destFile);
const srcNestFileInfo = await Deno.stat(srcNestFile);
const destNestFileInfo = await Deno.stat(destNestFile);
assertEquals(srcDirInfo.atime, destDirInfo.atime);
assertEquals(srcDirInfo.mtime, destDirInfo.mtime);
assertEquals(srcFileInfo.atime, destFileInfo.atime);
assertEquals(srcFileInfo.mtime, destFileInfo.mtime);
assertEquals(srcNestFileInfo.atime, destNestFileInfo.atime);
assertEquals(srcNestFileInfo.mtime, destNestFileInfo.mtime);
}
},
);
testCopy(
"copy() rejects if destination is its own subdirectory",
async (tempDir: string) => {
const srcDir = path.join(tempDir, "parent");
const destDir = path.join(srcDir, "child");
await ensureDir(srcDir);
await assertRejects(
async () => {
await copy(srcDir, destDir);
},
Error,
`Cannot copy '${srcDir}' to a subdirectory of itself: '${destDir}'`,
);
},
);
testCopy(
"copy() rejects when copying a directory to an existent destination that is not a directory",
async (tempDir: string) => {
const srcDir = path.join(tempDir, "parent");
const destDir = path.join(tempDir, "child.txt");
await ensureDir(srcDir);
await ensureFile(destDir);
await assertRejects(
async () => {
await copy(srcDir, destDir);
},
Error,
`Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'`,
);
},
);
testCopy(
"copy() copies a directory",
async (tempDir: string) => {
const srcDir = path.join(testdataDir, "copy_dir");
const destDir = path.join(tempDir, "copy_dir");
const srcFile = path.join(srcDir, "0.txt");
const destFile = path.join(destDir, "0.txt");
const srcNestFile = path.join(srcDir, "nest", "0.txt");
const destNestFile = path.join(destDir, "nest", "0.txt");
await copy(srcDir, destDir);
assert(await Deno.lstat(destFile));
assert(await Deno.lstat(destNestFile));
// After copy. The source and destination should have the same content.
assertEquals(
await Deno.readTextFile(srcFile),
await Deno.readTextFile(destFile),
);
assertEquals(
await Deno.readTextFile(srcNestFile),
await Deno.readTextFile(destNestFile),
);
// Copy again without overwrite option and it should throw an error.
await assertRejects(
async () => {
await copy(srcDir, destDir);
},
Error,
`'${destDir}' already exists`,
);
// Modify the file in the destination directory.
await Deno.writeTextFile(destNestFile, "nest copy");
assertEquals(await Deno.readTextFile(destNestFile), "nest copy");
// Copy again with overwrite option.
await copy(srcDir, destDir, { overwrite: true });
// Make sure the file has been overwritten.
assertEquals(await Deno.readTextFile(destNestFile), "nest");
},
);
testCopy(
"copy() copies a symlink file",
async (tempDir: string) => {
const dir = path.join(testdataDir, "copy_dir_link_file");
const srcLink = path.join(dir, "0.txt");
const destLink = path.join(tempDir, "0_copy.txt");
assert(
(await Deno.lstat(srcLink)).isSymlink,
`'${srcLink}' should be symlink type`,
);
await copy(srcLink, destLink);
const statInfo = await Deno.lstat(destLink);
assert(statInfo.isSymlink, `'${destLink}' should be symlink type`);
},
);
testCopy(
"copy() copies a symlink directory",
async (tempDir: string) => {
const srcDir = path.join(testdataDir, "copy_dir");
const srcLink = path.join(tempDir, "copy_dir_link");
const destLink = path.join(tempDir, "copy_dir_link_copy");
await ensureSymlink(srcDir, srcLink);
assert(
(await Deno.lstat(srcLink)).isSymlink,
`'${srcLink}' should be symlink type`,
);
await copy(srcLink, destLink);
const statInfo = await Deno.lstat(destLink);
assert(statInfo.isSymlink);
},
);
testCopySync(
"copySync() throws if src does not exist",
(tempDir: string) => {
const srcFile = path.join(testdataDir, "copy_file_not_exists_sync.txt");
const destFile = path.join(tempDir, "copy_file_not_exists_1_sync.txt");
assertThrows(() => {
copySync(srcFile, destFile);
});
},
);
testCopySync(
"copySync() copies with preserve timestamps",
(tempDir: string) => {
{
const srcFile = path.join(testdataDir, "copy_file.txt");
const destFile = path.join(tempDir, "copy_file_copy.txt");
const srcStatInfo = Deno.statSync(srcFile);
assert(srcStatInfo.atime instanceof Date);
assert(srcStatInfo.mtime instanceof Date);
// Copy with overwrite and preserve timestamps options.
copySync(srcFile, destFile, {
overwrite: true,
preserveTimestamps: true,
});
const destStatInfo = Deno.statSync(destFile);
assert(destStatInfo.atime instanceof Date);
assert(destStatInfo.mtime instanceof Date);
assertEquals(destStatInfo.atime, srcStatInfo.atime);
assertEquals(destStatInfo.mtime, srcStatInfo.mtime);
}
// copy dir with preserve timestamps
{
const srcDir = path.join(testdataDir, "copy_dir");
const destDir = path.join(tempDir, "copy_dir");
const srcFile = path.join(srcDir, "0.txt");
const destFile = path.join(destDir, "0.txt");
const srcNestFile = path.join(srcDir, "nest", "0.txt");
const destNestFile = path.join(destDir, "nest", "0.txt");
copySync(srcDir, destDir, { preserveTimestamps: true });
const srcDirInfo = Deno.statSync(srcFile);
const destDirInfo = Deno.statSync(destFile);
const srcFileInfo = Deno.statSync(srcFile);
const destFileInfo = Deno.statSync(destFile);
const srcNestFileInfo = Deno.statSync(srcNestFile);
const destNestFileInfo = Deno.statSync(destNestFile);
assertEquals(srcDirInfo.atime, destDirInfo.atime);
assertEquals(srcDirInfo.mtime, destDirInfo.mtime);
assertEquals(srcFileInfo.atime, destFileInfo.atime);
assertEquals(srcFileInfo.mtime, destFileInfo.mtime);
assertEquals(srcNestFileInfo.atime, destNestFileInfo.atime);
assertEquals(srcNestFileInfo.mtime, destNestFileInfo.mtime);
}
},
);
testCopySync(
"copySync() throws if src and dest are the same paths",
() => {
const srcFile = path.join(testdataDir, "copy_file_same_sync.txt");
assertThrows(
() => {
copySync(srcFile, srcFile);
},
Error,
"Source and destination cannot be the same",
);
},
);
testCopySync("copySync() copies file to new destination", (tempDir: string) => {
const srcFile = path.join(testdataDir, "copy_file.txt");
const destFile = path.join(tempDir, "copy_file_copy_sync.txt");
const srcContent = new TextDecoder().decode(Deno.readFileSync(srcFile));
assertEquals(existsSync(srcFile), true);
assertEquals(existsSync(destFile), false);
copySync(srcFile, destFile);
assertEquals(existsSync(srcFile), true);
assertEquals(existsSync(destFile), true);
const destContent = new TextDecoder().decode(Deno.readFileSync(destFile));
assertEquals(srcContent, destContent);
// Copy again without overwrite option and it should throw an error.
assertThrows(
() => {
copySync(srcFile, destFile);
},
Error,
`'${destFile}' already exists`,
);
// Modify destination file.
Deno.writeFileSync(destFile, new TextEncoder().encode("txt copy"));
assertEquals(
new TextDecoder().decode(Deno.readFileSync(destFile)),
"txt copy",
);
// Copy again with overwrite option.
copySync(srcFile, destFile, { overwrite: true });
// Make sure the file has been overwritten.
assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "txt");
});
testCopySync(
"copySync() throws if destination is its own subdirectory",
(tempDir: string) => {
const srcDir = path.join(tempDir, "parent");
const destDir = path.join(srcDir, "child");
ensureDirSync(srcDir);
assertThrows(
() => {
copySync(srcDir, destDir);
},
Error,
`Cannot copy '${srcDir}' to a subdirectory of itself: '${destDir}'`,
);
},
);
testCopySync(
"copySync() throws when copying a directory to an existent destination that is not a directory",
(tempDir: string) => {
const srcDir = path.join(tempDir, "parent_sync");
const destDir = path.join(tempDir, "child.txt");
ensureDirSync(srcDir);
ensureFileSync(destDir);
assertThrows(
() => {
copySync(srcDir, destDir);
},
Error,
`Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'`,
);
},
);
testCopySync("copySync() copies a directory", (tempDir: string) => {
const srcDir = path.join(testdataDir, "copy_dir");
const destDir = path.join(tempDir, "copy_dir_copy_sync");
const srcFile = path.join(srcDir, "0.txt");
const destFile = path.join(destDir, "0.txt");
const srcNestFile = path.join(srcDir, "nest", "0.txt");
const destNestFile = path.join(destDir, "nest", "0.txt");
copySync(srcDir, destDir);
assertEquals(existsSync(destFile), true);
assertEquals(existsSync(destNestFile), true);
// After copy. The source and destination should have the same content.
assertEquals(
new TextDecoder().decode(Deno.readFileSync(srcFile)),
new TextDecoder().decode(Deno.readFileSync(destFile)),
);
assertEquals(
new TextDecoder().decode(Deno.readFileSync(srcNestFile)),
new TextDecoder().decode(Deno.readFileSync(destNestFile)),
);
// Copy again without overwrite option and it should throw an error.
assertThrows(
() => {
copySync(srcDir, destDir);
},
Error,
`'${destDir}' already exists`,
);
// Modify the file in the destination directory.
Deno.writeFileSync(destNestFile, new TextEncoder().encode("nest copy"));
assertEquals(
new TextDecoder().decode(Deno.readFileSync(destNestFile)),
"nest copy",
);
// Copy again with overwrite option.
copySync(srcDir, destDir, { overwrite: true });
// Make sure the file has been overwritten.
assertEquals(
new TextDecoder().decode(Deno.readFileSync(destNestFile)),
"nest",
);
});
testCopySync(
"copySync() copies symlink file",
(tempDir: string) => {
const dir = path.join(testdataDir, "copy_dir_link_file");
const srcLink = path.join(dir, "0.txt");
const destLink = path.join(tempDir, "0_copy.txt");
assert(
Deno.lstatSync(srcLink).isSymlink,
`'${srcLink}' should be symlink type`,
);
copySync(srcLink, destLink);
const statInfo = Deno.lstatSync(destLink);
assert(statInfo.isSymlink, `'${destLink}' should be symlink type`);
},
);
testCopySync(
"copySync() copies symlink directory",
(tempDir: string) => {
const originDir = path.join(testdataDir, "copy_dir");
const srcLink = path.join(tempDir, "copy_dir_link");
const destLink = path.join(tempDir, "copy_dir_link_copy");
ensureSymlinkSync(originDir, srcLink);
assert(
Deno.lstatSync(srcLink).isSymlink,
`'${srcLink}' should be symlink type`,
);
copySync(srcLink, destLink);
const statInfo = Deno.lstatSync(destLink);
assert(statInfo.isSymlink);
},
);