node/test/parallel/test-util-callbackify.js
James M Snell 7535a94c8a test: begin normalizing fixtures use
Adds a new `../common/fixtures' module to begin normalizing
`test/fixtures` use. Our test code is a bit inconsistent with
regards to use of the fixtures directory. Some code uses
`path.join()`, some code uses string concats, some other
code uses template strings, etc. In mnay cases, significant
duplication of code is seen when accessing fixture files, etc.

This updates many (but by no means all) of the tests in the
test suite to use the new consistent API. There are still
many more to update, which would make an excelent Code-n-Learn
exercise.

PR-URL: https://github.com/nodejs/node/pull/14332
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gibson Fahnestock <gibfahn@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
2017-08-07 18:00:57 -07:00

262 lines
6.7 KiB
JavaScript

'use strict';
const common = require('../common');
// This test checks that the semantics of `util.callbackify` are as described in
// the API docs
const assert = require('assert');
const { callbackify } = require('util');
const { execFile } = require('child_process');
const fixtures = require('../common/fixtures');
const values = [
'hello world',
null,
undefined,
false,
0,
{},
{ key: 'value' },
Symbol('I am a symbol'),
function ok() {},
['array', 'with', 4, 'values'],
new Error('boo')
];
{
// Test that the resolution value is passed as second argument to callback
for (const value of values) {
// Test and `async function`
async function asyncFn() {
return await Promise.resolve(value);
}
const cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(common.mustCall((err, ret) => {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
// Test Promise factory
function promiseFn() {
return Promise.resolve(value);
}
const cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(common.mustCall((err, ret) => {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
// Test Thenable
function thenableFn() {
return {
then(onRes, onRej) {
onRes(value);
}
};
}
const cbThenableFn = callbackify(thenableFn);
cbThenableFn(common.mustCall((err, ret) => {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
}
}
{
// Test that rejection reason is passed as first argument to callback
for (const value of values) {
// Test an `async function`
async function asyncFn() {
return await Promise.reject(value);
}
const cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(common.mustCall((err, ret) => {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
// test a Promise factory
function promiseFn() {
return Promise.reject(value);
}
const cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(common.mustCall((err, ret) => {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
// Test Thenable
function thenableFn() {
return {
then(onRes, onRej) {
onRej(value);
}
};
}
const cbThenableFn = callbackify(thenableFn);
cbThenableFn(common.mustCall((err, ret) => {
assert.strictEqual(ret, undefined);
if (err instanceof Error) {
if ('reason' in err) {
assert(!value);
assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
assert.strictEqual(err.reason, value);
} else {
assert.strictEqual(String(value).endsWith(err.message), true);
}
} else {
assert.strictEqual(err, value);
}
}));
}
}
{
// Test that arguments passed to callbackified function are passed to original
for (const value of values) {
async function asyncFn(arg) {
assert.strictEqual(arg, value);
return await Promise.resolve(arg);
}
const cbAsyncFn = callbackify(asyncFn);
cbAsyncFn(value, common.mustCall((err, ret) => {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
function promiseFn(arg) {
assert.strictEqual(arg, value);
return Promise.resolve(arg);
}
const cbPromiseFn = callbackify(promiseFn);
cbPromiseFn(value, common.mustCall((err, ret) => {
assert.ifError(err);
assert.strictEqual(ret, value);
}));
}
}
{
// Test that `this` binding is the same for callbackified and original
for (const value of values) {
const iAmThis = {
fn(arg) {
assert.strictEqual(this, iAmThis);
return Promise.resolve(arg);
},
};
iAmThis.cbFn = callbackify(iAmThis.fn);
iAmThis.cbFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
assert.strictEqual(this, iAmThis);
}));
const iAmThat = {
async fn(arg) {
assert.strictEqual(this, iAmThat);
return await Promise.resolve(arg);
},
};
iAmThat.cbFn = callbackify(iAmThat.fn);
iAmThat.cbFn(value, common.mustCall(function(err, ret) {
assert.ifError(err);
assert.strictEqual(ret, value);
assert.strictEqual(this, iAmThat);
}));
}
}
{
// Test that callback that throws emits an `uncaughtException` event
const fixture = fixtures.path('uncaught-exceptions', 'callbackify1.js');
execFile(
process.execPath,
[fixture],
common.mustCall((err, stdout, stderr) => {
assert.strictEqual(err.code, 1);
assert.strictEqual(Object.getPrototypeOf(err).name, 'Error');
assert.strictEqual(stdout, '');
const errLines = stderr.trim().split(/[\r\n]+/);
const errLine = errLines.find((l) => /^Error/.exec(l));
assert.strictEqual(errLine, `Error: ${fixture}`);
})
);
}
{
// Test that handled `uncaughtException` works and passes rejection reason
const fixture = fixtures.path('uncaught-exceptions', 'callbackify2.js');
execFile(
process.execPath,
[fixture],
common.mustCall((err, stdout, stderr) => {
assert.ifError(err);
assert.strictEqual(stdout.trim(), fixture);
assert.strictEqual(stderr, '');
})
);
}
{
// Verify that non-function inputs throw.
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
assert.throws(() => {
callbackify(value);
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The "original" argument must be of type function'
}));
});
}
{
async function asyncFn() {
return await Promise.resolve(42);
}
const cb = callbackify(asyncFn);
const args = [];
// Verify that the last argument to the callbackified function is a function.
['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
args.push(value);
assert.throws(() => {
cb(...args);
}, common.expectsError({
code: 'ERR_INVALID_ARG_TYPE',
type: TypeError,
message: 'The last argument must be of type function'
}));
});
}