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