• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const common = require('../common');
4
5// This test ensures that filehandle.write accepts "named parameters" object
6// and doesn't interpret objects as strings
7
8const assert = require('assert');
9const fsPromises = require('fs').promises;
10const path = require('path');
11const tmpdir = require('../common/tmpdir');
12
13tmpdir.refresh();
14
15const dest = path.resolve(tmpdir.path, 'tmp.txt');
16const buffer = Buffer.from('zyx');
17
18async function testInvalid(dest, expectedCode, ...params) {
19  if (params.length >= 2) {
20    params[1] = common.mustNotMutateObjectDeep(params[1]);
21  }
22  let fh;
23  try {
24    fh = await fsPromises.open(dest, 'w+');
25    await assert.rejects(
26      fh.write(...params),
27      { code: expectedCode });
28  } finally {
29    await fh?.close();
30  }
31}
32
33async function testValid(dest, buffer, options) {
34  const length = options?.length;
35  const offset = options?.offset;
36  let fh, writeResult, writeBufCopy, readResult, readBufCopy;
37
38  try {
39    fh = await fsPromises.open(dest, 'w');
40    writeResult = await fh.write(buffer, options);
41    writeBufCopy = Uint8Array.prototype.slice.call(writeResult.buffer);
42  } finally {
43    await fh?.close();
44  }
45
46  try {
47    fh = await fsPromises.open(dest, 'r');
48    readResult = await fh.read(buffer, options);
49    readBufCopy = Uint8Array.prototype.slice.call(readResult.buffer);
50  } finally {
51    await fh?.close();
52  }
53
54  assert.ok(writeResult.bytesWritten >= readResult.bytesRead);
55  if (length !== undefined && length !== null) {
56    assert.strictEqual(writeResult.bytesWritten, length);
57    assert.strictEqual(readResult.bytesRead, length);
58  }
59  if (offset === undefined || offset === 0) {
60    assert.deepStrictEqual(writeBufCopy, readBufCopy);
61  }
62  assert.deepStrictEqual(writeResult.buffer, readResult.buffer);
63}
64
65(async () => {
66  // Test if first argument is not wrongly interpreted as ArrayBufferView|string
67  for (const badBuffer of [
68    undefined, null, true, 42, 42n, Symbol('42'), NaN, [], () => {},
69    common.mustNotCall(),
70    common.mustNotMutateObjectDeep({}),
71    Promise.resolve(new Uint8Array(1)),
72    {},
73    { buffer: 'amNotParam' },
74    { string: 'amNotParam' },
75    { buffer: new Uint8Array(1).buffer },
76    new Date(),
77    new String('notPrimitive'),
78    { toString() { return 'amObject'; } },
79    { [Symbol.toPrimitive]: (hint) => 'amObject' },
80  ]) {
81    await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', common.mustNotMutateObjectDeep(badBuffer), {});
82  }
83
84  // First argument (buffer or string) is mandatory
85  await testInvalid(dest, 'ERR_INVALID_ARG_TYPE');
86
87  // Various invalid options
88  await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 5 });
89  await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: 5 });
90  await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: 1, offset: 3 });
91  await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { length: -1 });
92  await testInvalid(dest, 'ERR_OUT_OF_RANGE', buffer, { offset: -1 });
93  await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', buffer, { offset: false });
94  await testInvalid(dest, 'ERR_INVALID_ARG_TYPE', buffer, { offset: true });
95
96  // Test compatibility with filehandle.read counterpart
97  for (const options of [
98    undefined,
99    null,
100    {},
101    { length: 1 },
102    { position: 5 },
103    { length: 1, position: 5 },
104    { length: 1, position: -1, offset: 2 },
105    { length: null },
106    { position: null },
107    { offset: 1 },
108  ]) {
109    await testValid(dest, buffer, common.mustNotMutateObjectDeep(options));
110  }
111})().then(common.mustCall());
112