Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions test/common/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use strict';

const { mustNotMutateObjectDeep } = require('.');
const { readdirSync } = require('node:fs');
const { join } = require('node:path');
const assert = require('node:assert');
const tmpdir = require('./tmpdir.js');

let dirc = 0;
function nextdir(dirname) {
return tmpdir.resolve(dirname || `copy_%${++dirc}`);
}

function assertDirEquivalent(dir1, dir2) {
const dir1Entries = [];
collectEntries(dir1, dir1Entries);
const dir2Entries = [];
collectEntries(dir2, dir2Entries);
assert.strictEqual(dir1Entries.length, dir2Entries.length);
for (const entry1 of dir1Entries) {
const entry2 = dir2Entries.find((entry) => {
return entry.name === entry1.name;
});
assert(entry2, `entry ${entry2.name} not copied`);
if (entry1.isFile()) {
assert(entry2.isFile(), `${entry2.name} was not file`);
} else if (entry1.isDirectory()) {
assert(entry2.isDirectory(), `${entry2.name} was not directory`);
} else if (entry1.isSymbolicLink()) {
assert(entry2.isSymbolicLink(), `${entry2.name} was not symlink`);
}
}
}

function collectEntries(dir, dirEntries) {
const newEntries = readdirSync(dir, mustNotMutateObjectDeep({ withFileTypes: true }));
for (const entry of newEntries) {
if (entry.isDirectory()) {
collectEntries(join(dir, entry.name), dirEntries);
}
}
dirEntries.push(...newEntries);
}

module.exports = {
nextdir,
assertDirEquivalent,
collectEntries,
};
10 changes: 8 additions & 2 deletions test/parallel/parallel.status
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ test-snapshot-incompatible: SKIP
test-async-context-frame: PASS, FLAKY
# https://github.com/nodejs/node/issues/54534
test-runner-run-watch: PASS, FLAKY
# https://github.com/nodejs/node/issues/56794
test-fs-cp: PASS, FLAKY
# https://github.com/nodejs/node/pull/59408#issuecomment-3170650933
test-fs-cp-sync-error-on-exist: PASS, FLAKY
test-fs-cp-sync-copy-symlink-not-pointing-to-folder: PASS, FLAKY
test-fs-cp-sync-symlink-points-to-dest-error: PASS, FLAKY
test-fs-cp-sync-resolve-relative-symlinks-false: PASS, FLAKY
test-fs-cp-async-symlink-points-to-dest: PASS, FLAKY
test-fs-cp-sync-unicode-folder-names: PASS, FLAKY
test-fs-cp-sync-resolve-relative-symlinks-default: PASS, FLAKY

# https://github.com/nodejs/node/issues/56751
test-without-async-context-frame: PASS, FLAKY
Expand Down
32 changes: 32 additions & 0 deletions test/parallel/test-fs-cp-async-async-filter-function.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// This tests that cp() supports async filter function.

import { mustCall } from '../common/index.mjs';
import { nextdir, collectEntries } from '../common/fs.js';
import assert from 'node:assert';
import { cp, statSync } from 'node:fs';
import { setTimeout } from 'node:timers/promises';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';
tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink');
const dest = nextdir();
cp(src, dest, {
filter: async (path) => {
await setTimeout(5, 'done');
const pathStat = statSync(path);
return pathStat.isDirectory() || path.endsWith('.js');
},
dereference: true,
recursive: true,
}, mustCall((err) => {
assert.strictEqual(err, null);
const destEntries = [];
collectEntries(dest, destEntries);
for (const entry of destEntries) {
assert.strictEqual(
entry.isDirectory() || entry.name.endsWith('.js'),
true
);
}
}));
22 changes: 22 additions & 0 deletions test/parallel/test-fs-cp-async-copy-non-directory-symlink.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This tests that cp() copies link if it does not point to folder in src.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync, readlinkSync, symlinkSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

const src = nextdir();
mkdirSync(join(src, 'a', 'b'), mustNotMutateObjectDeep({ recursive: true }));
symlinkSync(src, join(src, 'a', 'c'));
const dest = nextdir();
mkdirSync(join(dest, 'a'), mustNotMutateObjectDeep({ recursive: true }));
symlinkSync(dest, join(dest, 'a', 'c'));
cp(src, dest, mustNotMutateObjectDeep({ recursive: true }), mustCall((err) => {
assert.strictEqual(err, null);
const link = readlinkSync(join(dest, 'a', 'c'));
assert.strictEqual(link, src);
}));
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This tests that it does not fail if the same directory is copied to dest
// twice, when dereference is true, and force is false (fails silently).

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import assert from 'node:assert';
import { cp, cpSync, lstatSync } from 'node:fs';
import { join } from 'node:path';
import { nextdir } from '../common/fs.js';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink');
const dest = nextdir();
const destFile = join(dest, 'a/b/README2.md');
cpSync(src, dest, mustNotMutateObjectDeep({ dereference: true, recursive: true }));
cp(src, dest, {
dereference: true,
recursive: true
}, mustCall((err) => {
assert.strictEqual(err, null);
const stat = lstatSync(destFile);
assert(stat.isFile());
}));
27 changes: 27 additions & 0 deletions test/parallel/test-fs-cp-async-dereference-symlink.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// This tests that cp() copies file itself, rather than symlink, when dereference is true.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, lstatSync, mkdirSync, symlinkSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

const src = nextdir();
mkdirSync(src, mustNotMutateObjectDeep({ recursive: true }));
writeFileSync(join(src, 'foo.js'), 'foo', 'utf8');
symlinkSync(join(src, 'foo.js'), join(src, 'bar.js'));

const dest = nextdir();
mkdirSync(dest, mustNotMutateObjectDeep({ recursive: true }));
const destFile = join(dest, 'foo.js');

cp(join(src, 'bar.js'), destFile, mustNotMutateObjectDeep({ dereference: true }),
mustCall((err) => {
assert.strictEqual(err, null);
const stat = lstatSync(destFile);
assert(stat.isFile());
})
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This tests that cp() returns error if parent directory of symlink in dest points to src.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync, symlinkSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

const src = nextdir();
mkdirSync(join(src, 'a'), mustNotMutateObjectDeep({ recursive: true }));
const dest = nextdir();
// Create symlink in dest pointing to src.
const destLink = join(dest, 'b');
mkdirSync(dest, mustNotMutateObjectDeep({ recursive: true }));
symlinkSync(src, destLink);
cp(src, join(dest, 'b', 'c'), mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_EINVAL');
}));
17 changes: 17 additions & 0 deletions test/parallel/test-fs-cp-async-dir-to-file.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This tests that cp() returns error if attempt is made to copy directory to file.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync } from 'node:fs';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = nextdir();
mkdirSync(src, mustNotMutateObjectDeep({ recursive: true }));
const dest = fixtures.path('copy/kitchen-sink/README.md');
cp(src, dest, mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_DIR_TO_NON_DIR');
}));
22 changes: 22 additions & 0 deletions test/parallel/test-fs-cp-async-error-on-exist.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This tests that cp() returns error if errorOnExist is true, force is false, and file or folder copied over.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, cpSync } from 'node:fs';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink');
const dest = nextdir();
cpSync(src, dest, mustNotMutateObjectDeep({ recursive: true }));
cp(src, dest, {
dereference: true,
errorOnExist: true,
force: false,
recursive: true,
}, mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_EEXIST');
}));
17 changes: 17 additions & 0 deletions test/parallel/test-fs-cp-async-file-to-dir.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// This tests that cp() returns error if attempt is made to copy file to directory.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, mkdirSync } from 'node:fs';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink/README.md');
const dest = nextdir();
mkdirSync(dest, mustNotMutateObjectDeep({ recursive: true }));
cp(src, dest, mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_NON_DIR_TO_DIR');
}));
19 changes: 19 additions & 0 deletions test/parallel/test-fs-cp-async-file-to-file.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// This tests that cp() allows file to be copied to a file path.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, lstatSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const srcFile = fixtures.path('copy/kitchen-sink/README.md');
const destFile = join(nextdir(), 'index.js');
cp(srcFile, destFile, mustNotMutateObjectDeep({ dereference: true }), mustCall((err) => {
assert.strictEqual(err, null);
const stat = lstatSync(destFile);
assert(stat.isFile());
}));
18 changes: 18 additions & 0 deletions test/parallel/test-fs-cp-async-file-url.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This tests that it accepts file URL as src and dest.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import assert from 'node:assert';
import { cp } from 'node:fs';
import { pathToFileURL } from 'node:url';
import tmpdir from '../common/tmpdir.js';
import { assertDirEquivalent, nextdir } from '../common/fs.js';

tmpdir.refresh();

const src = './test/fixtures/copy/kitchen-sink';
const dest = nextdir();
cp(pathToFileURL(src), pathToFileURL(dest), mustNotMutateObjectDeep({ recursive: true }),
mustCall((err) => {
assert.strictEqual(err, null);
assertDirEquivalent(src, dest);
}));
26 changes: 26 additions & 0 deletions test/parallel/test-fs-cp-async-filter-child-folder.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// This tests that cp() should not throw exception if child folder is filtered out.

import { mustCall, mustNotMutateObjectDeep } from '../common/index.mjs';
import { nextdir } from '../common/fs.js';
import assert from 'node:assert';
import { cp, cpSync, mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

const src = nextdir();
mkdirSync(join(src, 'test-cp'), mustNotMutateObjectDeep({ recursive: true }));

const dest = nextdir();
mkdirSync(dest, mustNotMutateObjectDeep({ recursive: true }));
writeFileSync(join(dest, 'test-cp'), 'test-content', mustNotMutateObjectDeep({ mode: 0o444 }));

const opts = {
filter: (path) => !path.includes('test-cp'),
recursive: true,
};
cp(src, dest, opts, mustCall((err) => {
assert.strictEqual(err, null);
}));
cpSync(src, dest, opts);
31 changes: 31 additions & 0 deletions test/parallel/test-fs-cp-async-filter-function.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// This tests that cp() applies filter function.

import { mustCall } from '../common/index.mjs';
import { nextdir, collectEntries } from '../common/fs.js';
import assert from 'node:assert';
import { cp, statSync } from 'node:fs';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink');
const dest = nextdir();
cp(src, dest, {
filter: (path) => {
const pathStat = statSync(path);
return pathStat.isDirectory() || path.endsWith('.js');
},
dereference: true,
recursive: true,
}, mustCall((err) => {
assert.strictEqual(err, null);
const destEntries = [];
collectEntries(dest, destEntries);
for (const entry of destEntries) {
assert.strictEqual(
entry.isDirectory() || entry.name.endsWith('.js'),
true
);
}
}));
14 changes: 14 additions & 0 deletions test/parallel/test-fs-cp-async-identical-src-dest.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// This tests that cp() returns error when src and dest are identical.

import { mustCall } from '../common/index.mjs';
import assert from 'node:assert';
import { cp } from 'node:fs';
import tmpdir from '../common/tmpdir.js';
import fixtures from '../common/fixtures.js';

tmpdir.refresh();

const src = fixtures.path('copy/kitchen-sink');
cp(src, src, mustCall((err) => {
assert.strictEqual(err.code, 'ERR_FS_CP_EINVAL');
}));
13 changes: 13 additions & 0 deletions test/parallel/test-fs-cp-async-invalid-mode-range.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This tests that cp() throws if mode is out of range.

import '../common/index.mjs';
import assert from 'node:assert';
import { cp } from 'node:fs';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

assert.throws(
() => cp('a', 'b', { mode: -1 }, () => {}),
{ code: 'ERR_OUT_OF_RANGE' }
);
13 changes: 13 additions & 0 deletions test/parallel/test-fs-cp-async-invalid-options-type.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This tests that cp() throws if options is not object.

import '../common/index.mjs';
import assert from 'node:assert';
import { cp } from 'node:fs';
import tmpdir from '../common/tmpdir.js';

tmpdir.refresh();

assert.throws(
() => cp('a', 'b', 'hello', () => {}),
{ code: 'ERR_INVALID_ARG_TYPE' }
);
Loading
Loading