1// Flags: --expose-internals
2'use strict';
3const common = require('../common');
4const fixtures = require('../common/fixtures');
5const tmpdir = require('../common/tmpdir');
6const assert = require('assert');
7const fs = require('fs');
8const { internalBinding } = require('internal/test/binding');
9const {
10  UV_ENOENT,
11  UV_EEXIST
12} = internalBinding('uv');
13const path = require('path');
14const src = fixtures.path('a.js');
15const dest = path.join(tmpdir.path, 'copyfile.out');
16const {
17  COPYFILE_EXCL,
18  COPYFILE_FICLONE,
19  COPYFILE_FICLONE_FORCE,
20  UV_FS_COPYFILE_EXCL,
21  UV_FS_COPYFILE_FICLONE,
22  UV_FS_COPYFILE_FICLONE_FORCE
23} = fs.constants;
24
25function verify(src, dest) {
26  const srcData = fs.readFileSync(src, 'utf8');
27  const srcStat = fs.statSync(src);
28  const destData = fs.readFileSync(dest, 'utf8');
29  const destStat = fs.statSync(dest);
30
31  assert.strictEqual(srcData, destData);
32  assert.strictEqual(srcStat.mode, destStat.mode);
33  assert.strictEqual(srcStat.size, destStat.size);
34}
35
36tmpdir.refresh();
37
38// Verify that flags are defined.
39assert.strictEqual(typeof COPYFILE_EXCL, 'number');
40assert.strictEqual(typeof COPYFILE_FICLONE, 'number');
41assert.strictEqual(typeof COPYFILE_FICLONE_FORCE, 'number');
42assert.strictEqual(typeof UV_FS_COPYFILE_EXCL, 'number');
43assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE, 'number');
44assert.strictEqual(typeof UV_FS_COPYFILE_FICLONE_FORCE, 'number');
45assert.strictEqual(COPYFILE_EXCL, UV_FS_COPYFILE_EXCL);
46assert.strictEqual(COPYFILE_FICLONE, UV_FS_COPYFILE_FICLONE);
47assert.strictEqual(COPYFILE_FICLONE_FORCE, UV_FS_COPYFILE_FICLONE_FORCE);
48
49// Verify that files are overwritten when no flags are provided.
50fs.writeFileSync(dest, '', 'utf8');
51const result = fs.copyFileSync(src, dest);
52assert.strictEqual(result, undefined);
53verify(src, dest);
54
55// Verify that files are overwritten with default flags.
56fs.copyFileSync(src, dest, 0);
57verify(src, dest);
58
59// Verify that UV_FS_COPYFILE_FICLONE can be used.
60fs.unlinkSync(dest);
61fs.copyFileSync(src, dest, UV_FS_COPYFILE_FICLONE);
62verify(src, dest);
63
64// Verify that COPYFILE_FICLONE_FORCE can be used.
65try {
66  fs.unlinkSync(dest);
67  fs.copyFileSync(src, dest, COPYFILE_FICLONE_FORCE);
68  verify(src, dest);
69} catch (err) {
70  assert.strictEqual(err.syscall, 'copyfile');
71  assert(err.code === 'ENOTSUP' || err.code === 'ENOTTY' ||
72    err.code === 'ENOSYS' || err.code === 'EXDEV');
73  assert.strictEqual(err.path, src);
74  assert.strictEqual(err.dest, dest);
75}
76
77// Copies asynchronously.
78tmpdir.refresh(); // Don't use unlinkSync() since the last test may fail.
79fs.copyFile(src, dest, common.mustSucceed(() => {
80  verify(src, dest);
81
82  // Copy asynchronously with flags.
83  fs.copyFile(src, dest, COPYFILE_EXCL, common.mustCall((err) => {
84    if (err.code === 'ENOENT') {  // Could be ENOENT or EEXIST
85      assert.strictEqual(err.message,
86                         'ENOENT: no such file or directory, copyfile ' +
87                         `'${src}' -> '${dest}'`);
88      assert.strictEqual(err.errno, UV_ENOENT);
89      assert.strictEqual(err.code, 'ENOENT');
90      assert.strictEqual(err.syscall, 'copyfile');
91    } else {
92      assert.strictEqual(err.message,
93                         'EEXIST: file already exists, copyfile ' +
94                         `'${src}' -> '${dest}'`);
95      assert.strictEqual(err.errno, UV_EEXIST);
96      assert.strictEqual(err.code, 'EEXIST');
97      assert.strictEqual(err.syscall, 'copyfile');
98    }
99  }));
100}));
101
102// Throws if callback is not a function.
103assert.throws(() => {
104  fs.copyFile(src, dest, 0, 0);
105}, {
106  code: 'ERR_INVALID_ARG_TYPE',
107  name: 'TypeError'
108});
109
110// Throws if the source path is not a string.
111[false, 1, {}, [], null, undefined].forEach((i) => {
112  assert.throws(
113    () => fs.copyFile(i, dest, common.mustNotCall()),
114    {
115      code: 'ERR_INVALID_ARG_TYPE',
116      name: 'TypeError',
117      message: /src/
118    }
119  );
120  assert.throws(
121    () => fs.copyFile(src, i, common.mustNotCall()),
122    {
123      code: 'ERR_INVALID_ARG_TYPE',
124      name: 'TypeError',
125      message: /dest/
126    }
127  );
128  assert.throws(
129    () => fs.copyFileSync(i, dest),
130    {
131      code: 'ERR_INVALID_ARG_TYPE',
132      name: 'TypeError',
133      message: /src/
134    }
135  );
136  assert.throws(
137    () => fs.copyFileSync(src, i),
138    {
139      code: 'ERR_INVALID_ARG_TYPE',
140      name: 'TypeError',
141      message: /dest/
142    }
143  );
144});
145
146assert.throws(() => {
147  fs.copyFileSync(src, dest, 'r');
148}, {
149  code: 'ERR_INVALID_ARG_TYPE',
150  name: 'TypeError',
151  message: /mode/
152});
153
154assert.throws(() => {
155  fs.copyFileSync(src, dest, 8);
156}, {
157  code: 'ERR_OUT_OF_RANGE',
158  name: 'RangeError',
159});
160
161assert.throws(() => {
162  fs.copyFile(src, dest, 'r', common.mustNotCall());
163}, {
164  code: 'ERR_INVALID_ARG_TYPE',
165  name: 'TypeError',
166  message: /mode/
167});
168