node/test/parallel/test-assert-async.js
Jacob Smith 5a18304cb2
errors: extract type detection & use in ERR_INVALID_RETURN_VALUE
PR-URL: https://github.com/nodejs/node/pull/43558
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2022-07-01 19:08:08 +01:00

238 lines
7.2 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
// Run all tests in parallel and check their outcome at the end.
const promises = [];
// Thenable object without `catch` method,
// shouldn't be considered as a valid Thenable
const invalidThenable = {
then: (fulfill, reject) => {
fulfill();
},
};
// Function that returns a Thenable function,
// a function with `catch` and `then` methods attached,
// shouldn't be considered as a valid Thenable.
const invalidThenableFunc = () => {
function f() {}
f.then = (fulfill, reject) => {
fulfill();
};
f.catch = () => {};
return f;
};
// Test assert.rejects() and assert.doesNotReject() by checking their
// expected output and by verifying that they do not work sync
// Check `assert.rejects`.
{
const rejectingFn = async () => assert.fail();
const errObj = {
code: 'ERR_ASSERTION',
name: 'AssertionError',
message: 'Failed'
};
// `assert.rejects` accepts a function or a promise
// or a thenable as first argument.
promises.push(assert.rejects(rejectingFn, errObj));
promises.push(assert.rejects(rejectingFn(), errObj));
const validRejectingThenable = {
then: (fulfill, reject) => {
reject({ code: 'FAIL' });
},
catch: () => {}
};
promises.push(assert.rejects(validRejectingThenable, { code: 'FAIL' }));
// `assert.rejects` should not accept thenables that
// use a function as `obj` and that have no `catch` handler.
promises.push(assert.rejects(
assert.rejects(invalidThenable, {}),
{
code: 'ERR_INVALID_ARG_TYPE'
})
);
promises.push(assert.rejects(
assert.rejects(invalidThenableFunc, {}),
{
code: 'ERR_INVALID_RETURN_VALUE'
})
);
const err = new Error('foobar');
const validate = () => { return 'baz'; };
promises.push(assert.rejects(
() => assert.rejects(Promise.reject(err), validate),
{
message: 'The "validate" validation function is expected to ' +
"return \"true\". Received 'baz'\n\nCaught error:\n\n" +
'Error: foobar',
code: 'ERR_ASSERTION',
actual: err,
expected: validate,
name: 'AssertionError',
operator: 'rejects',
}
));
}
{
const handler = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message,
'Missing expected rejection (mustNotCall).');
assert.strictEqual(err.operator, 'rejects');
assert.ok(!err.stack.includes('at Function.rejects'));
return true;
};
let promise = assert.rejects(async () => {}, common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler)));
promise = assert.rejects(() => {}, common.mustNotCall());
promises.push(assert.rejects(promise, {
name: 'TypeError',
code: 'ERR_INVALID_RETURN_VALUE',
// FIXME(JakobJingleheimer): This should match on key words, like /Promise/ and /undefined/.
message: 'Expected instance of Promise to be returned ' +
'from the "promiseFn" function but got undefined.'
}));
promise = assert.rejects(Promise.resolve(), common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler)));
}
{
const THROWN_ERROR = new Error();
promises.push(assert.rejects(() => {
throw THROWN_ERROR;
}, {}).catch(common.mustCall((err) => {
assert.strictEqual(err, THROWN_ERROR);
})));
}
promises.push(assert.rejects(
assert.rejects('fail', {}),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "promiseFn" argument must be of type function or an ' +
"instance of Promise. Received type string ('fail')"
}
));
{
const handler = (generated, actual, err) => {
assert.strictEqual(err.generatedMessage, generated);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.actual, actual);
assert.strictEqual(err.operator, 'rejects');
assert.match(err.stack, /rejects/);
return true;
};
const err = new Error();
promises.push(assert.rejects(
assert.rejects(Promise.reject(null), { code: 'FOO' }),
handler.bind(null, true, null)
));
promises.push(assert.rejects(
assert.rejects(Promise.reject(5), { code: 'FOO' }, 'AAAAA'),
handler.bind(null, false, 5)
));
promises.push(assert.rejects(
assert.rejects(Promise.reject(err), { code: 'FOO' }, 'AAAAA'),
handler.bind(null, false, err)
));
}
// Check `assert.doesNotReject`.
{
// `assert.doesNotReject` accepts a function or a promise
// or a thenable as first argument.
/* eslint-disable no-restricted-syntax */
let promise = assert.doesNotReject(() => new Map(), common.mustNotCall());
promises.push(assert.rejects(promise, {
message: 'Expected instance of Promise to be returned ' +
'from the "promiseFn" function but got an instance of Map.',
code: 'ERR_INVALID_RETURN_VALUE',
name: 'TypeError'
}));
promises.push(assert.doesNotReject(async () => {}));
promises.push(assert.doesNotReject(Promise.resolve()));
// `assert.doesNotReject` should not accept thenables that
// use a function as `obj` and that have no `catch` handler.
const validFulfillingThenable = {
then: (fulfill, reject) => {
fulfill();
},
catch: () => {}
};
promises.push(assert.doesNotReject(validFulfillingThenable));
promises.push(assert.rejects(
assert.doesNotReject(invalidThenable),
{
code: 'ERR_INVALID_ARG_TYPE'
})
);
promises.push(assert.rejects(
assert.doesNotReject(invalidThenableFunc),
{
code: 'ERR_INVALID_RETURN_VALUE'
})
);
const handler1 = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message, 'Failed');
return true;
};
const handler2 = (err) => {
assert(err instanceof assert.AssertionError,
`${err.name} is not instance of AssertionError`);
assert.strictEqual(err.code, 'ERR_ASSERTION');
assert.strictEqual(err.message,
'Got unwanted rejection.\nActual message: "Failed"');
assert.strictEqual(err.operator, 'doesNotReject');
assert.ok(err.stack);
assert.ok(!err.stack.includes('at Function.doesNotReject'));
return true;
};
const rejectingFn = async () => assert.fail();
promise = assert.doesNotReject(rejectingFn, common.mustCall(handler1));
promises.push(assert.rejects(promise, common.mustCall(handler2)));
promise = assert.doesNotReject(rejectingFn(), common.mustCall(handler1));
promises.push(assert.rejects(promise, common.mustCall(handler2)));
promise = assert.doesNotReject(() => assert.fail(), common.mustNotCall());
promises.push(assert.rejects(promise, common.mustCall(handler1)));
promises.push(assert.rejects(
assert.doesNotReject(123),
{
code: 'ERR_INVALID_ARG_TYPE',
message: 'The "promiseFn" argument must be of type ' +
'function or an instance of Promise. Received type number (123)'
}
));
/* eslint-enable no-restricted-syntax */
}
// Make sure all async code gets properly executed.
Promise.all(promises).then(common.mustCall());