11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ciconst common = require('../common');
31cb0ef41Sopenharmony_ci
41cb0ef41Sopenharmony_ci// This test checks that the semantics of `util.callbackify` are as described in
51cb0ef41Sopenharmony_ci// the API docs
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst assert = require('assert');
81cb0ef41Sopenharmony_ciconst { callbackify } = require('util');
91cb0ef41Sopenharmony_ciconst { execFile } = require('child_process');
101cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures');
111cb0ef41Sopenharmony_ci
121cb0ef41Sopenharmony_ciconst values = [
131cb0ef41Sopenharmony_ci  'hello world',
141cb0ef41Sopenharmony_ci  null,
151cb0ef41Sopenharmony_ci  undefined,
161cb0ef41Sopenharmony_ci  false,
171cb0ef41Sopenharmony_ci  0,
181cb0ef41Sopenharmony_ci  {},
191cb0ef41Sopenharmony_ci  { key: 'value' },
201cb0ef41Sopenharmony_ci  Symbol('I am a symbol'),
211cb0ef41Sopenharmony_ci  function ok() {},
221cb0ef41Sopenharmony_ci  ['array', 'with', 4, 'values'],
231cb0ef41Sopenharmony_ci  new Error('boo'),
241cb0ef41Sopenharmony_ci];
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci{
271cb0ef41Sopenharmony_ci  // Test that the resolution value is passed as second argument to callback
281cb0ef41Sopenharmony_ci  for (const value of values) {
291cb0ef41Sopenharmony_ci    // Test and `async function`
301cb0ef41Sopenharmony_ci    async function asyncFn() {
311cb0ef41Sopenharmony_ci      return value;
321cb0ef41Sopenharmony_ci    }
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci    const cbAsyncFn = callbackify(asyncFn);
351cb0ef41Sopenharmony_ci    cbAsyncFn(common.mustSucceed((ret) => {
361cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
371cb0ef41Sopenharmony_ci    }));
381cb0ef41Sopenharmony_ci
391cb0ef41Sopenharmony_ci    // Test Promise factory
401cb0ef41Sopenharmony_ci    function promiseFn() {
411cb0ef41Sopenharmony_ci      return Promise.resolve(value);
421cb0ef41Sopenharmony_ci    }
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci    const cbPromiseFn = callbackify(promiseFn);
451cb0ef41Sopenharmony_ci    cbPromiseFn(common.mustSucceed((ret) => {
461cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
471cb0ef41Sopenharmony_ci    }));
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci    // Test Thenable
501cb0ef41Sopenharmony_ci    function thenableFn() {
511cb0ef41Sopenharmony_ci      return {
521cb0ef41Sopenharmony_ci        then(onRes, onRej) {
531cb0ef41Sopenharmony_ci          onRes(value);
541cb0ef41Sopenharmony_ci        }
551cb0ef41Sopenharmony_ci      };
561cb0ef41Sopenharmony_ci    }
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci    const cbThenableFn = callbackify(thenableFn);
591cb0ef41Sopenharmony_ci    cbThenableFn(common.mustSucceed((ret) => {
601cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
611cb0ef41Sopenharmony_ci    }));
621cb0ef41Sopenharmony_ci  }
631cb0ef41Sopenharmony_ci}
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci{
661cb0ef41Sopenharmony_ci  // Test that rejection reason is passed as first argument to callback
671cb0ef41Sopenharmony_ci  for (const value of values) {
681cb0ef41Sopenharmony_ci    // Test an `async function`
691cb0ef41Sopenharmony_ci    async function asyncFn() {
701cb0ef41Sopenharmony_ci      return Promise.reject(value);
711cb0ef41Sopenharmony_ci    }
721cb0ef41Sopenharmony_ci
731cb0ef41Sopenharmony_ci    const cbAsyncFn = callbackify(asyncFn);
741cb0ef41Sopenharmony_ci    assert.strictEqual(cbAsyncFn.length, 1);
751cb0ef41Sopenharmony_ci    assert.strictEqual(cbAsyncFn.name, 'asyncFnCallbackified');
761cb0ef41Sopenharmony_ci    cbAsyncFn(common.mustCall((err, ret) => {
771cb0ef41Sopenharmony_ci      assert.strictEqual(ret, undefined);
781cb0ef41Sopenharmony_ci      if (err instanceof Error) {
791cb0ef41Sopenharmony_ci        if ('reason' in err) {
801cb0ef41Sopenharmony_ci          assert(!value);
811cb0ef41Sopenharmony_ci          assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
821cb0ef41Sopenharmony_ci          assert.strictEqual(err.reason, value);
831cb0ef41Sopenharmony_ci        } else {
841cb0ef41Sopenharmony_ci          assert.strictEqual(String(value).endsWith(err.message), true);
851cb0ef41Sopenharmony_ci        }
861cb0ef41Sopenharmony_ci      } else {
871cb0ef41Sopenharmony_ci        assert.strictEqual(err, value);
881cb0ef41Sopenharmony_ci      }
891cb0ef41Sopenharmony_ci    }));
901cb0ef41Sopenharmony_ci
911cb0ef41Sopenharmony_ci    // Test a Promise factory
921cb0ef41Sopenharmony_ci    function promiseFn() {
931cb0ef41Sopenharmony_ci      return Promise.reject(value);
941cb0ef41Sopenharmony_ci    }
951cb0ef41Sopenharmony_ci    const obj = {};
961cb0ef41Sopenharmony_ci    Object.defineProperty(promiseFn, 'name', {
971cb0ef41Sopenharmony_ci      value: obj,
981cb0ef41Sopenharmony_ci      writable: false,
991cb0ef41Sopenharmony_ci      enumerable: false,
1001cb0ef41Sopenharmony_ci      configurable: true
1011cb0ef41Sopenharmony_ci    });
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci    const cbPromiseFn = callbackify(promiseFn);
1041cb0ef41Sopenharmony_ci    assert.strictEqual(promiseFn.name, obj);
1051cb0ef41Sopenharmony_ci    cbPromiseFn(common.mustCall((err, ret) => {
1061cb0ef41Sopenharmony_ci      assert.strictEqual(ret, undefined);
1071cb0ef41Sopenharmony_ci      if (err instanceof Error) {
1081cb0ef41Sopenharmony_ci        if ('reason' in err) {
1091cb0ef41Sopenharmony_ci          assert(!value);
1101cb0ef41Sopenharmony_ci          assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
1111cb0ef41Sopenharmony_ci          assert.strictEqual(err.reason, value);
1121cb0ef41Sopenharmony_ci        } else {
1131cb0ef41Sopenharmony_ci          assert.strictEqual(String(value).endsWith(err.message), true);
1141cb0ef41Sopenharmony_ci        }
1151cb0ef41Sopenharmony_ci      } else {
1161cb0ef41Sopenharmony_ci        assert.strictEqual(err, value);
1171cb0ef41Sopenharmony_ci      }
1181cb0ef41Sopenharmony_ci    }));
1191cb0ef41Sopenharmony_ci
1201cb0ef41Sopenharmony_ci    // Test Thenable
1211cb0ef41Sopenharmony_ci    function thenableFn() {
1221cb0ef41Sopenharmony_ci      return {
1231cb0ef41Sopenharmony_ci        then(onRes, onRej) {
1241cb0ef41Sopenharmony_ci          onRej(value);
1251cb0ef41Sopenharmony_ci        }
1261cb0ef41Sopenharmony_ci      };
1271cb0ef41Sopenharmony_ci    }
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci    const cbThenableFn = callbackify(thenableFn);
1301cb0ef41Sopenharmony_ci    cbThenableFn(common.mustCall((err, ret) => {
1311cb0ef41Sopenharmony_ci      assert.strictEqual(ret, undefined);
1321cb0ef41Sopenharmony_ci      if (err instanceof Error) {
1331cb0ef41Sopenharmony_ci        if ('reason' in err) {
1341cb0ef41Sopenharmony_ci          assert(!value);
1351cb0ef41Sopenharmony_ci          assert.strictEqual(err.code, 'ERR_FALSY_VALUE_REJECTION');
1361cb0ef41Sopenharmony_ci          assert.strictEqual(err.reason, value);
1371cb0ef41Sopenharmony_ci        } else {
1381cb0ef41Sopenharmony_ci          assert.strictEqual(String(value).endsWith(err.message), true);
1391cb0ef41Sopenharmony_ci        }
1401cb0ef41Sopenharmony_ci      } else {
1411cb0ef41Sopenharmony_ci        assert.strictEqual(err, value);
1421cb0ef41Sopenharmony_ci      }
1431cb0ef41Sopenharmony_ci    }));
1441cb0ef41Sopenharmony_ci  }
1451cb0ef41Sopenharmony_ci}
1461cb0ef41Sopenharmony_ci
1471cb0ef41Sopenharmony_ci{
1481cb0ef41Sopenharmony_ci  // Test that arguments passed to callbackified function are passed to original
1491cb0ef41Sopenharmony_ci  for (const value of values) {
1501cb0ef41Sopenharmony_ci    async function asyncFn(arg) {
1511cb0ef41Sopenharmony_ci      assert.strictEqual(arg, value);
1521cb0ef41Sopenharmony_ci      return arg;
1531cb0ef41Sopenharmony_ci    }
1541cb0ef41Sopenharmony_ci
1551cb0ef41Sopenharmony_ci    const cbAsyncFn = callbackify(asyncFn);
1561cb0ef41Sopenharmony_ci    assert.strictEqual(cbAsyncFn.length, 2);
1571cb0ef41Sopenharmony_ci    assert.notStrictEqual(
1581cb0ef41Sopenharmony_ci      Object.getPrototypeOf(cbAsyncFn),
1591cb0ef41Sopenharmony_ci      Object.getPrototypeOf(asyncFn)
1601cb0ef41Sopenharmony_ci    );
1611cb0ef41Sopenharmony_ci    assert.strictEqual(Object.getPrototypeOf(cbAsyncFn), Function.prototype);
1621cb0ef41Sopenharmony_ci    cbAsyncFn(value, common.mustSucceed((ret) => {
1631cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
1641cb0ef41Sopenharmony_ci    }));
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ci    function promiseFn(arg) {
1671cb0ef41Sopenharmony_ci      assert.strictEqual(arg, value);
1681cb0ef41Sopenharmony_ci      return Promise.resolve(arg);
1691cb0ef41Sopenharmony_ci    }
1701cb0ef41Sopenharmony_ci    const obj = {};
1711cb0ef41Sopenharmony_ci    Object.defineProperty(promiseFn, 'length', {
1721cb0ef41Sopenharmony_ci      value: obj,
1731cb0ef41Sopenharmony_ci      writable: false,
1741cb0ef41Sopenharmony_ci      enumerable: false,
1751cb0ef41Sopenharmony_ci      configurable: true
1761cb0ef41Sopenharmony_ci    });
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci    const cbPromiseFn = callbackify(promiseFn);
1791cb0ef41Sopenharmony_ci    assert.strictEqual(promiseFn.length, obj);
1801cb0ef41Sopenharmony_ci    cbPromiseFn(value, common.mustSucceed((ret) => {
1811cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
1821cb0ef41Sopenharmony_ci    }));
1831cb0ef41Sopenharmony_ci  }
1841cb0ef41Sopenharmony_ci}
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci{
1871cb0ef41Sopenharmony_ci  // Test that `this` binding is the same for callbackified and original
1881cb0ef41Sopenharmony_ci  for (const value of values) {
1891cb0ef41Sopenharmony_ci    const iAmThis = {
1901cb0ef41Sopenharmony_ci      fn(arg) {
1911cb0ef41Sopenharmony_ci        assert.strictEqual(this, iAmThis);
1921cb0ef41Sopenharmony_ci        return Promise.resolve(arg);
1931cb0ef41Sopenharmony_ci      },
1941cb0ef41Sopenharmony_ci    };
1951cb0ef41Sopenharmony_ci    iAmThis.cbFn = callbackify(iAmThis.fn);
1961cb0ef41Sopenharmony_ci    iAmThis.cbFn(value, common.mustSucceed(function(ret) {
1971cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
1981cb0ef41Sopenharmony_ci      assert.strictEqual(this, iAmThis);
1991cb0ef41Sopenharmony_ci    }));
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci    const iAmThat = {
2021cb0ef41Sopenharmony_ci      async fn(arg) {
2031cb0ef41Sopenharmony_ci        assert.strictEqual(this, iAmThat);
2041cb0ef41Sopenharmony_ci        return arg;
2051cb0ef41Sopenharmony_ci      },
2061cb0ef41Sopenharmony_ci    };
2071cb0ef41Sopenharmony_ci    iAmThat.cbFn = callbackify(iAmThat.fn);
2081cb0ef41Sopenharmony_ci    iAmThat.cbFn(value, common.mustSucceed(function(ret) {
2091cb0ef41Sopenharmony_ci      assert.strictEqual(ret, value);
2101cb0ef41Sopenharmony_ci      assert.strictEqual(this, iAmThat);
2111cb0ef41Sopenharmony_ci    }));
2121cb0ef41Sopenharmony_ci  }
2131cb0ef41Sopenharmony_ci}
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci{
2161cb0ef41Sopenharmony_ci  // Test that callback that throws emits an `uncaughtException` event
2171cb0ef41Sopenharmony_ci  const fixture = fixtures.path('uncaught-exceptions', 'callbackify1.js');
2181cb0ef41Sopenharmony_ci  execFile(
2191cb0ef41Sopenharmony_ci    process.execPath,
2201cb0ef41Sopenharmony_ci    [fixture],
2211cb0ef41Sopenharmony_ci    common.mustCall((err, stdout, stderr) => {
2221cb0ef41Sopenharmony_ci      assert.strictEqual(err.code, 1);
2231cb0ef41Sopenharmony_ci      assert.strictEqual(Object.getPrototypeOf(err).name, 'Error');
2241cb0ef41Sopenharmony_ci      assert.strictEqual(stdout, '');
2251cb0ef41Sopenharmony_ci      const errLines = stderr.trim().split(/[\r\n]+/);
2261cb0ef41Sopenharmony_ci      const errLine = errLines.find((l) => /^Error/.exec(l));
2271cb0ef41Sopenharmony_ci      assert.strictEqual(errLine, `Error: ${fixture}`);
2281cb0ef41Sopenharmony_ci    })
2291cb0ef41Sopenharmony_ci  );
2301cb0ef41Sopenharmony_ci}
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci{
2331cb0ef41Sopenharmony_ci  // Test that handled `uncaughtException` works and passes rejection reason
2341cb0ef41Sopenharmony_ci  const fixture = fixtures.path('uncaught-exceptions', 'callbackify2.js');
2351cb0ef41Sopenharmony_ci  execFile(
2361cb0ef41Sopenharmony_ci    process.execPath,
2371cb0ef41Sopenharmony_ci    [fixture],
2381cb0ef41Sopenharmony_ci    common.mustSucceed((stdout, stderr) => {
2391cb0ef41Sopenharmony_ci      assert.strictEqual(
2401cb0ef41Sopenharmony_ci        stdout.trim(),
2411cb0ef41Sopenharmony_ci        `ifError got unwanted exception: ${fixture}`);
2421cb0ef41Sopenharmony_ci      assert.strictEqual(stderr, '');
2431cb0ef41Sopenharmony_ci    })
2441cb0ef41Sopenharmony_ci  );
2451cb0ef41Sopenharmony_ci}
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_ci{
2481cb0ef41Sopenharmony_ci  // Verify that non-function inputs throw.
2491cb0ef41Sopenharmony_ci  ['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
2501cb0ef41Sopenharmony_ci    assert.throws(() => {
2511cb0ef41Sopenharmony_ci      callbackify(value);
2521cb0ef41Sopenharmony_ci    }, {
2531cb0ef41Sopenharmony_ci      code: 'ERR_INVALID_ARG_TYPE',
2541cb0ef41Sopenharmony_ci      name: 'TypeError',
2551cb0ef41Sopenharmony_ci      message: 'The "original" argument must be of type function.' +
2561cb0ef41Sopenharmony_ci               common.invalidArgTypeHelper(value)
2571cb0ef41Sopenharmony_ci    });
2581cb0ef41Sopenharmony_ci  });
2591cb0ef41Sopenharmony_ci}
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci{
2621cb0ef41Sopenharmony_ci  async function asyncFn() {
2631cb0ef41Sopenharmony_ci    return 42;
2641cb0ef41Sopenharmony_ci  }
2651cb0ef41Sopenharmony_ci
2661cb0ef41Sopenharmony_ci  const cb = callbackify(asyncFn);
2671cb0ef41Sopenharmony_ci  const args = [];
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci  // Verify that the last argument to the callbackified function is a function.
2701cb0ef41Sopenharmony_ci  ['foo', null, undefined, false, 0, {}, Symbol(), []].forEach((value) => {
2711cb0ef41Sopenharmony_ci    args.push(value);
2721cb0ef41Sopenharmony_ci    assert.throws(() => {
2731cb0ef41Sopenharmony_ci      cb(...args);
2741cb0ef41Sopenharmony_ci    }, {
2751cb0ef41Sopenharmony_ci      code: 'ERR_INVALID_ARG_TYPE',
2761cb0ef41Sopenharmony_ci      name: 'TypeError',
2771cb0ef41Sopenharmony_ci      message: 'The last argument must be of type function.' +
2781cb0ef41Sopenharmony_ci               common.invalidArgTypeHelper(value)
2791cb0ef41Sopenharmony_ci    });
2801cb0ef41Sopenharmony_ci  });
2811cb0ef41Sopenharmony_ci}
282