From f510bd2e817d86c46c3ed7f11ed8510d0dcc89e3 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 4 Dec 2025 22:24:16 +0100 Subject: [PATCH] test: enforce better never-settling-promise detection Tests should be explicit regarding whether a promise is expected to settle, and the test should fail when the behavior does not meet expectations. --- .../callback-scope/test-resolve-async.js | 6 +- .../test-async-local-storage-errors.js | 1 + .../test-async-local-storage-promises.js | 4 +- .../test-async-local-storage-thenable.js | 2 +- test/async-hooks/test-destroy-not-blocked.js | 6 +- ...promise.chain-promise-before-init-hooks.js | 4 +- test/async-hooks/test-promise.js | 4 +- .../test-promise.promise-before-init-hooks.js | 6 +- .../test-http-proxy-request-max-sockets.mjs | 2 +- .../test-https-proxy-request-max-sockets.mjs | 2 +- .../test-esm-loader-stringify-text.mjs | 66 +++------- test/es-module/test-esm-tla.mjs | 5 +- .../test-require-module-tla-retry-import-2.js | 2 +- .../test-require-module-tla-retry-import.js | 2 +- test/es-module/test-wasm-simple.js | 4 +- test/internet/test-dns.js | 3 +- test/js-native-api/test_promise/test.js | 13 +- test/node-api/test_instance_data/test.js | 3 +- .../test-abortsignal-drop-settled-signals.mjs | 8 +- test/parallel/test-async-hooks-async-await.js | 4 +- test/parallel/test-async-hooks-fatal-error.js | 5 +- ...test-async-hooks-promise-enable-disable.js | 6 +- .../test-async-wrap-promise-after-enabled.js | 2 +- test/parallel/test-cli-node-options.js | 2 +- test/parallel/test-debugger-random-port.js | 3 +- .../test-debugger-run-after-quit-restart.js | 9 +- ...diagnostics-channel-module-import-error.js | 72 +++++----- .../test-diagnostics-channel-module-import.js | 1 - ...s-channel-tracing-channel-promise-error.js | 16 +-- ...nostics-channel-tracing-channel-promise.js | 1 - test/parallel/test-domain-promise.js | 2 +- test/parallel/test-eslint-must-call-assert.js | 41 +++++- .../test-eventtarget-memoryleakwarning.js | 2 +- test/parallel/test-fs-append-file.js | 11 +- test/parallel/test-fs-long-path.js | 2 +- ...est-fs-promises-file-handle-read-worker.js | 4 +- .../test-fs-promises-readfile-empty.js | 11 +- test/parallel/test-fs-readfile-eof.js | 4 +- .../parallel/test-fs-rmdir-recursive-error.js | 9 +- .../parallel/test-fs-write-optional-params.js | 2 +- .../test-heapdump-async-hooks-init-promise.js | 2 +- test/parallel/test-http-set-trailers.js | 6 +- .../test-inspector-connect-main-thread.js | 2 +- .../test-microtask-queue-integration.js | 1 + .../test-microtask-queue-run-immediate.js | 1 + test/parallel/test-microtask-queue-run.js | 1 + .../test-module-prototype-mutation.js | 2 +- ...-connections-close-makes-more-available.js | 7 +- .../test-next-tick-fixed-queue-regression.js | 2 +- .../test-performance-function-async.js | 2 +- test/parallel/test-promise-hook-exceptions.js | 4 +- test/parallel/test-promise-hook-on-after.js | 2 +- test/parallel/test-promise-hook-on-before.js | 1 + .../test-promises-unhandled-rejections.js | 123 ++++++++++-------- test/parallel/test-queue-microtask.js | 2 +- .../test-readable-from-iterator-closing.js | 2 +- test/parallel/test-readline-interface.js | 7 +- .../test-readline-promises-interface.js | 11 +- .../test-repl-eval-error-after-close.js | 1 + .../test-runner-mock-timers-scheduler.js | 2 +- test/parallel/test-runner-mock-timers.js | 8 +- test/parallel/test-stream-consumers.js | 24 ++-- test/parallel/test-stream-map.js | 8 +- .../test-stream-readable-strategy-option.js | 2 +- .../test-stream3-pipeline-async-iterator.js | 1 + .../test-timers-immediate-promisified.js | 2 +- .../test-timers-interval-promisified.js | 20 +-- ...st-timers-nan-duration-warning-promises.js | 2 +- .../test-timers-timeout-promisified.js | 2 +- .../test-tls-streamwrap-buffersize.js | 2 +- test/parallel/test-ttywrap-stack.js | 4 +- test/parallel/test-util-promisify.js | 7 +- test/parallel/test-vm-measure-memory-lazy.js | 8 +- test/parallel/test-vm-measure-memory.js | 6 +- .../parallel/test-vm-module-after-evaluate.js | 2 +- .../parallel/test-vm-script-after-evaluate.js | 2 +- .../test-webstreams-abort-controller.js | 2 +- test/parallel/test-webstreams-finished.js | 4 +- test/parallel/test-whatwg-readablestream.js | 12 +- .../test-worker-terminate-null-handler.js | 3 +- .../test-webcrypto-derivebits-pbkdf2.js | 62 ++++----- .../sequential/test-inspector-port-cluster.js | 2 +- tools/eslint-rules/must-call-assert.js | 87 ++++++++++++- 83 files changed, 451 insertions(+), 354 deletions(-) diff --git a/test/addons/callback-scope/test-resolve-async.js b/test/addons/callback-scope/test-resolve-async.js index 41c7470fabbef7..71d79b91d09c63 100644 --- a/test/addons/callback-scope/test-resolve-async.js +++ b/test/addons/callback-scope/test-resolve-async.js @@ -1,12 +1,8 @@ 'use strict'; const common = require('../../common'); -const assert = require('assert'); const { testResolveAsync } = require(`./build/${common.buildType}/binding`); // Checks that resolving promises from C++ works. -let called = false; -testResolveAsync().then(() => { called = true; }); - -process.on('beforeExit', common.mustCall(() => { assert(called); })); +testResolveAsync().then(common.mustCall()); diff --git a/test/async-hooks/test-async-local-storage-errors.js b/test/async-hooks/test-async-local-storage-errors.js index f10a953bc9709d..b6649ffd0419b7 100644 --- a/test/async-hooks/test-async-local-storage-errors.js +++ b/test/async-hooks/test-async-local-storage-errors.js @@ -106,6 +106,7 @@ function fireErr5() { const makeOrphan = vm.compileFunction(`(${String(() => { async function main() { await null; + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(() => { throw new Error('err5'); }); diff --git a/test/async-hooks/test-async-local-storage-promises.js b/test/async-hooks/test-async-local-storage-promises.js index 45efcef4096126..bfa77a4f89a7b2 100644 --- a/test/async-hooks/test-async-local-storage-promises.js +++ b/test/async-hooks/test-async-local-storage-promises.js @@ -11,11 +11,11 @@ async function main() { assert.strictEqual(asyncLocalStorage.getStore().get('a'), 1); throw err; }); - await assert.rejects(new Promise((resolve, reject) => { + await assert.rejects(new Promise((resolve) => { asyncLocalStorage.run(new Map(), () => { const store = asyncLocalStorage.getStore(); store.set('a', 1); - next().then(resolve, reject); + resolve(next()); }); }), err); assert.strictEqual(asyncLocalStorage.getStore(), undefined); diff --git a/test/async-hooks/test-async-local-storage-thenable.js b/test/async-hooks/test-async-local-storage-thenable.js index 33a8639fb02e8d..fbaf2cdf88f273 100644 --- a/test/async-hooks/test-async-local-storage-thenable.js +++ b/test/async-hooks/test-async-local-storage-thenable.js @@ -48,6 +48,6 @@ store.run(data, common.mustCall(() => { // Returning a thenable in a then handler store.run(data, common.mustCall(() => { assert.strictEqual(store.getStore(), data); - Promise.resolve().then(() => thenable()); + Promise.resolve().then(() => thenable()).then(common.mustCall()); assert.strictEqual(store.getStore(), data); })); diff --git a/test/async-hooks/test-destroy-not-blocked.js b/test/async-hooks/test-destroy-not-blocked.js index 509fb94b6b956c..2bbddf3047bb83 100644 --- a/test/async-hooks/test-destroy-not-blocked.js +++ b/test/async-hooks/test-destroy-not-blocked.js @@ -64,9 +64,9 @@ function testPromise() { assert.strictEqual(activeId, res.asyncId()); res.emitDestroy(); // Promise has higher prio than emit destroy - Promise.resolve().then(common.mustCall(() => - assert.strictEqual(activeId, res.asyncId())), - ); + Promise.resolve().then(common.mustCall(() => { + assert.strictEqual(activeId, res.asyncId()); + })); } async function testAwait() { diff --git a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js index c5e67b6f94ca68..f292c8286572ca 100644 --- a/test/async-hooks/test-promise.chain-promise-before-init-hooks.js +++ b/test/async-hooks/test-promise.chain-promise-before-init-hooks.js @@ -17,7 +17,7 @@ const p = new Promise(common.mustCall(function executor(resolve) { p.then(function afterResolution(val) { assert.strictEqual(val, 5); return val; -}); +}).then(common.mustCall()); // Init hooks after chained promise is created const hooks = initHooks(); @@ -34,7 +34,7 @@ process.on('exit', function onexit() { const as = hooks.activitiesOfTypes('PROMISE'); const unknown = hooks.activitiesOfTypes('Unknown'); assert.strictEqual(as.length, 0); - assert.strictEqual(unknown.length, 1); + assert.strictEqual(unknown.length, 2); const a0 = unknown[0]; assert.strictEqual(a0.type, 'Unknown'); diff --git a/test/async-hooks/test-promise.js b/test/async-hooks/test-promise.js index 554c3ae7dd711e..96be5e5efcb448 100644 --- a/test/async-hooks/test-promise.js +++ b/test/async-hooks/test-promise.js @@ -16,14 +16,14 @@ const hooks = initHooks(); hooks.enable(); const p = new Promise(common.mustCall(executor)); -p.then(function afterResolution(val) { +p.then(common.mustCall(function afterResolution(val) { assert.strictEqual(val, 5); const as = hooks.activitiesOfTypes('PROMISE'); assert.strictEqual(as.length, 2); checkInvocations(as[0], { init: 1 }, 'after resolution parent promise'); checkInvocations(as[1], { init: 1, before: 1 }, 'after resolution child promise'); -}); +})); function executor(resolve) { const as = hooks.activitiesOfTypes('PROMISE'); diff --git a/test/async-hooks/test-promise.promise-before-init-hooks.js b/test/async-hooks/test-promise.promise-before-init-hooks.js index 51787b8065a71b..b61a214f428937 100644 --- a/test/async-hooks/test-promise.promise-before-init-hooks.js +++ b/test/async-hooks/test-promise.promise-before-init-hooks.js @@ -16,18 +16,18 @@ hooks.enable(); p.then(function afterResolution(val) { assert.strictEqual(val, 5); const as = hooks.activitiesOfTypes('PROMISE'); - assert.strictEqual(as.length, 1); + assert.strictEqual(as.length, 2); checkInvocations(as[0], { init: 1, before: 1 }, 'after resolution child promise'); return val; -}); +}).then(common.mustCall()); process.on('exit', function onexit() { hooks.disable(); hooks.sanityCheck('PROMISE'); const as = hooks.activitiesOfTypes('PROMISE'); - assert.strictEqual(as.length, 1); + assert.strictEqual(as.length, 2); const a0 = as[0]; assert.strictEqual(a0.type, 'PROMISE'); diff --git a/test/client-proxy/test-http-proxy-request-max-sockets.mjs b/test/client-proxy/test-http-proxy-request-max-sockets.mjs index e01cf5bad91523..ab626c0467f90d 100644 --- a/test/client-proxy/test-http-proxy-request-max-sockets.mjs +++ b/test/client-proxy/test-http-proxy-request-max-sockets.mjs @@ -20,7 +20,7 @@ const server = http.createServer(common.mustCall((req, res) => { console.log('Responding to /first'); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Response for /first'); - }); + }).then(common.mustCall()); } else if (req.url === '/second') { // Respond immediately for the second request res.writeHead(200, { 'Content-Type': 'text/plain' }); diff --git a/test/client-proxy/test-https-proxy-request-max-sockets.mjs b/test/client-proxy/test-https-proxy-request-max-sockets.mjs index 64911c14f1bc4f..9c49c299c6f296 100644 --- a/test/client-proxy/test-https-proxy-request-max-sockets.mjs +++ b/test/client-proxy/test-https-proxy-request-max-sockets.mjs @@ -31,7 +31,7 @@ const server = https.createServer({ console.log('Responding to /first'); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Response for /first'); - }); + }).then(common.mustCall()); } else if (req.url === '/second') { // Respond immediately for the second request res.writeHead(200, { 'Content-Type': 'text/plain' }); diff --git a/test/es-module/test-esm-loader-stringify-text.mjs b/test/es-module/test-esm-loader-stringify-text.mjs index e66da9491b4cd2..8502375b31aa5a 100644 --- a/test/es-module/test-esm-loader-stringify-text.mjs +++ b/test/es-module/test-esm-loader-stringify-text.mjs @@ -1,90 +1,66 @@ // Flags: --experimental-loader ./test/fixtures/es-module-loaders/string-sources.mjs -import { mustCall, mustNotCall } from '../common/index.mjs'; +import { mustCall } from '../common/index.mjs'; import assert from 'assert'; -import('test:Array').then( - mustNotCall('Should not accept Arrays'), - mustCall((e) => { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); - }) -); +assert.rejects( + import('test:Array'), + { code: 'ERR_INVALID_RETURN_PROPERTY_VALUE' }, +).then(mustCall()); import('test:ArrayBuffer').then( mustCall(), - mustNotCall('Should accept ArrayBuffers'), ); import('test:BigInt64Array').then( mustCall(), - mustNotCall('Should accept BigInt64Array'), ); import('test:BigUint64Array').then( mustCall(), - mustNotCall('Should accept BigUint64Array'), ); import('test:Float32Array').then( mustCall(), - mustNotCall('Should accept Float32Array'), ); import('test:Float64Array').then( mustCall(), - mustNotCall('Should accept Float64Array'), ); import('test:Int8Array').then( mustCall(), - mustNotCall('Should accept Int8Array'), ); import('test:Int16Array').then( mustCall(), - mustNotCall('Should accept Int16Array'), ); import('test:Int32Array').then( mustCall(), - mustNotCall('Should accept Int32Array'), -); -import('test:null').then( - mustNotCall('Should not accept null'), - mustCall((e) => { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); - }) -); -import('test:Object').then( - mustNotCall('Should not stringify or valueOf Objects'), - mustCall((e) => { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); - }) ); +assert.rejects( + import('test:null'), + { code: 'ERR_INVALID_RETURN_PROPERTY_VALUE' }, +).then(mustCall()); +assert.rejects( + import('test:Object'), + { code: 'ERR_INVALID_RETURN_PROPERTY_VALUE' }, +).then(mustCall()); import('test:SharedArrayBuffer').then( mustCall(), - mustNotCall('Should accept SharedArrayBuffers'), ); import('test:string').then( mustCall(), - mustNotCall('Should accept strings'), -); -import('test:String').then( - mustNotCall('Should not accept wrapper Strings'), - mustCall((e) => { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); - }) ); +assert.rejects( + import('test:String'), + { code: 'ERR_INVALID_RETURN_PROPERTY_VALUE' }, +).then(mustCall()); import('test:Uint8ClampedArray').then( mustCall(), - mustNotCall('Should accept Uint8ClampedArray'), ); import('test:Uint16Array').then( mustCall(), - mustNotCall('Should accept Uint16Array'), ); import('test:Uint32Array').then( mustCall(), - mustNotCall('Should accept Uint32Array'), ); import('test:Uint8Array').then( mustCall(), - mustNotCall('Should accept Uint8Arrays'), -); -import('test:undefined').then( - mustNotCall('Should not accept undefined'), - mustCall((e) => { - assert.strictEqual(e.code, 'ERR_INVALID_RETURN_PROPERTY_VALUE'); - }) ); +assert.rejects( + import('test:undefined'), + { code: 'ERR_INVALID_RETURN_PROPERTY_VALUE' }, +).then(mustCall()); diff --git a/test/es-module/test-esm-tla.mjs b/test/es-module/test-esm-tla.mjs index 68a9d8954c5e2e..58e7f459927c30 100644 --- a/test/es-module/test-esm-tla.mjs +++ b/test/es-module/test-esm-tla.mjs @@ -1,4 +1,4 @@ -import '../common/index.mjs'; +import { mustCall } from '../common/index.mjs'; import fixtures from '../common/fixtures.js'; import assert from 'assert'; import { pathToFileURL } from 'url'; @@ -6,4 +6,5 @@ import { pathToFileURL } from 'url'; import(pathToFileURL(fixtures.path('/es-modules/tla/parent.mjs'))) .then(({ default: order }) => { assert.deepStrictEqual(order, ['order', 'b', 'c', 'd', 'a', 'parent']); - }); + }) + .then(mustCall()); diff --git a/test/es-module/test-require-module-tla-retry-import-2.js b/test/es-module/test-require-module-tla-retry-import-2.js index afbdf349047837..58461d915f731f 100644 --- a/test/es-module/test-require-module-tla-retry-import-2.js +++ b/test/es-module/test-require-module-tla-retry-import-2.js @@ -23,4 +23,4 @@ async function test() { } // Run the test twice to check consistency after caching. -test().then(common.mustCall(test)); +test().then(test).then(common.mustCall()); diff --git a/test/es-module/test-require-module-tla-retry-import.js b/test/es-module/test-require-module-tla-retry-import.js index e1d72e7ea6f80d..47796652d81cd9 100644 --- a/test/es-module/test-require-module-tla-retry-import.js +++ b/test/es-module/test-require-module-tla-retry-import.js @@ -22,4 +22,4 @@ async function test() { } // Run the test twice to check consistency after caching. -test().then(common.mustCall(test)); +test().then(test).then(common.mustCall()); diff --git a/test/es-module/test-wasm-simple.js b/test/es-module/test-wasm-simple.js index f769925be91e72..a666bd77950b8f 100644 --- a/test/es-module/test-wasm-simple.js +++ b/test/es-module/test-wasm-simple.js @@ -1,6 +1,6 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const fixtures = require('../common/fixtures'); @@ -14,4 +14,4 @@ WebAssembly.instantiate(buffer, {}).then((results) => { results.instance.exports.add(10, 20), 30 ); -}); +}).then(common.mustCall()); diff --git a/test/internet/test-dns.js b/test/internet/test-dns.js index a6534441636507..accc789f9e4d48 100644 --- a/test/internet/test-dns.js +++ b/test/internet/test-dns.js @@ -605,7 +605,8 @@ test(function test_lookup_ip_promise(done) { assert.strictEqual(family, 4); done(); - }); + }) + .then(common.mustCall()); }); diff --git a/test/js-native-api/test_promise/test.js b/test/js-native-api/test_promise/test.js index c53dbb5459ad10..3b43e6132ed4d9 100644 --- a/test/js-native-api/test_promise/test.js +++ b/test/js-native-api/test_promise/test.js @@ -12,10 +12,9 @@ const test_promise = require(`./build/${common.buildType}/test_promise`); const expected_result = 42; const promise = test_promise.createPromise(); promise.then( - common.mustCall(function(result) { + common.mustCall((result) => { assert.strictEqual(result, expected_result); - }), - common.mustNotCall()); + })); test_promise.concludeCurrentPromise(expected_result, true); } @@ -27,7 +26,8 @@ const test_promise = require(`./build/${common.buildType}/test_promise`); common.mustNotCall(), common.mustCall(function(result) { assert.strictEqual(result, expected_result); - })); + })) + .then(common.mustCall()); test_promise.concludeCurrentPromise(expected_result, false); } @@ -36,10 +36,9 @@ const test_promise = require(`./build/${common.buildType}/test_promise`); const expected_result = 'chained answer'; const promise = test_promise.createPromise(); promise.then( - common.mustCall(function(result) { + common.mustCall((result) => { assert.strictEqual(result, expected_result); - }), - common.mustNotCall()); + })); test_promise.concludeCurrentPromise(Promise.resolve('chained answer'), true); } diff --git a/test/node-api/test_instance_data/test.js b/test/node-api/test_instance_data/test.js index 1758bea28931d3..2c78f7fd5540bf 100644 --- a/test/node-api/test_instance_data/test.js +++ b/test/node-api/test_instance_data/test.js @@ -20,7 +20,8 @@ if (module !== require.main) { // Test that the thread-safe function can access the instance data. .then(() => new Promise((resolve) => test_instance_data.testThreadsafeFunction(common.mustCall(), - common.mustCall(resolve)))); + common.mustCall(resolve)))) + .then(common.mustCall()); } else { // When launched as a script, run tests in either a child process or in a // worker thread. diff --git a/test/parallel/test-abortsignal-drop-settled-signals.mjs b/test/parallel/test-abortsignal-drop-settled-signals.mjs index bb7448eacb1c34..d5d704d55c5243 100644 --- a/test/parallel/test-abortsignal-drop-settled-signals.mjs +++ b/test/parallel/test-abortsignal-drop-settled-signals.mjs @@ -1,6 +1,6 @@ // Flags: --expose_gc // -import '../common/index.mjs'; +import { mustCall } from '../common/index.mjs'; import { gcUntil } from '../common/gc.js'; import { describe, it } from 'node:test'; @@ -146,7 +146,7 @@ it('drops settled dependant signals when signal is composite', (t, done) => { t.assert.strictEqual(controllers[0].signal[kDependantSignals].size, 2); t.assert.strictEqual(controllers[1].signal[kDependantSignals].size, 1); - setImmediate(() => { + setImmediate(mustCall(() => { globalThis.gc({ execution: 'async' }).then(async () => { await gcUntil('all signals are GCed', () => { const totalDependantSignals = Math.max( @@ -158,8 +158,8 @@ it('drops settled dependant signals when signal is composite', (t, done) => { }); done(); - }); - }); + }).then(mustCall()); + })); }); it('drops settled signals even when there are listeners', (t, done) => { diff --git a/test/parallel/test-async-hooks-async-await.js b/test/parallel/test-async-hooks-async-await.js index 791adab75c8621..97b50f3f2685ec 100644 --- a/test/parallel/test-async-hooks-async-await.js +++ b/test/parallel/test-async-hooks-async-await.js @@ -2,7 +2,7 @@ // asyncIds & triggerAsyncId for async-await 'use strict'; -require('../common'); +const common = require('../common'); const async_hooks = require('async_hooks'); const assert = require('assert'); @@ -23,4 +23,4 @@ main().then(() => { assert.strictEqual(asyncIds[0][1], asyncIds[1][0]); assert.strictEqual(asyncIds[0][1], asyncIds[3][0]); assert.strictEqual(asyncIds[1][1], asyncIds[2][0]); -}); +}).then(common.mustCall()); diff --git a/test/parallel/test-async-hooks-fatal-error.js b/test/parallel/test-async-hooks-fatal-error.js index 40c2edf329df6e..30ead86132f6a6 100644 --- a/test/parallel/test-async-hooks-fatal-error.js +++ b/test/parallel/test-async-hooks-fatal-error.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const childProcess = require('child_process'); const os = require('os'); @@ -28,8 +28,7 @@ function child(type, valueType) { new Promise((resolve) => resolve()) // Trigger `after`/`destroy`. .then(() => fs.promises.readFile(__filename, 'utf8')) - // Make process exit with code 0 if no error caught. - .then(() => process.exit(0)); + .then(common.mustCall()); } function main() { diff --git a/test/parallel/test-async-hooks-promise-enable-disable.js b/test/parallel/test-async-hooks-promise-enable-disable.js index 47a259166ee182..92504ac121e578 100644 --- a/test/parallel/test-async-hooks-promise-enable-disable.js +++ b/test/parallel/test-async-hooks-promise-enable-disable.js @@ -31,14 +31,14 @@ const hook = async_hooks.createHook({ new Promise(common.mustCall((res) => { res(42); -})).then(common.mustCall((val) => { +})).then((val) => { hook.enable().enable(); const p = new Promise((res) => res(val)); hook.disable(); return p; -})).then(common.mustCall((val2) => { +}).then((val2) => { hook.enable(); const p = new Promise((res) => res(val2)); hook.disable(); return p; -})).catch((er) => p_er = er); +}).catch((er) => p_er = er).then(common.mustCall()); diff --git a/test/parallel/test-async-wrap-promise-after-enabled.js b/test/parallel/test-async-wrap-promise-after-enabled.js index afd049192bd57b..fafb732c2895df 100644 --- a/test/parallel/test-async-wrap-promise-after-enabled.js +++ b/test/parallel/test-async-wrap-promise-after-enabled.js @@ -15,7 +15,7 @@ const async_hooks = require('async_hooks'); const seenEvents = []; const p = new Promise((resolve) => resolve(1)); -p.then(() => seenEvents.push('then')); +p.then(() => seenEvents.push('then')).then(common.mustCall()); const hooks = async_hooks.createHook({ init: common.mustNotCall(), diff --git a/test/parallel/test-cli-node-options.js b/test/parallel/test-cli-node-options.js index 3eee6023025f85..0d17fc73dd4d02 100644 --- a/test/parallel/test-cli-node-options.js +++ b/test/parallel/test-cli-node-options.js @@ -157,6 +157,6 @@ function workerTest(opts, command, wantsError, test) { assert.strictEqual(code, wantsError ? 1 : 0); collectStream(worker.stdout).then((stdout) => { test(workerError, stdout); - }); + }).then(common.mustCall()); })); } diff --git a/test/parallel/test-debugger-random-port.js b/test/parallel/test-debugger-random-port.js index b6a0c98797423f..06f927b4db9d14 100644 --- a/test/parallel/test-debugger-random-port.js +++ b/test/parallel/test-debugger-random-port.js @@ -26,5 +26,6 @@ const assert = require('assert'); .then(() => cli.quit()) .then((code) => { assert.strictEqual(code, 0); - }); + }) + .then(common.mustCall()); } diff --git a/test/parallel/test-debugger-run-after-quit-restart.js b/test/parallel/test-debugger-run-after-quit-restart.js index 2da4cea6359c85..538798758601df 100644 --- a/test/parallel/test-debugger-run-after-quit-restart.js +++ b/test/parallel/test-debugger-run-after-quit-restart.js @@ -15,11 +15,6 @@ const path = require('path'); const script = path.relative(process.cwd(), scriptFullPath); const cli = startCLI([script]); - function onFatal(error) { - cli.quit(); - throw error; - } - cli.waitForInitialBreak() .then(() => cli.waitForPrompt()) .then(() => cli.stepCommand('n')) @@ -85,6 +80,6 @@ const path = require('path'); { filename: script, line: 1 }, ); }) - .then(() => cli.quit()) - .then(null, onFatal); + .finally(() => cli.quit()) + .then(common.mustCall()); } diff --git a/test/parallel/test-diagnostics-channel-module-import-error.js b/test/parallel/test-diagnostics-channel-module-import-error.js index bd27430226754a..f7fb65737a8f8e 100644 --- a/test/parallel/test-diagnostics-channel-module-import-error.js +++ b/test/parallel/test-diagnostics-channel-module-import-error.js @@ -28,40 +28,38 @@ trace.subscribe({ error: common.mustCall(track('error')), }); -import('does-not-exist').then( - common.mustNotCall(), - common.mustCall((error) => { - const expectedParentURL = pathToFileURL(module.filename).href; - // Verify order and contents of each event - assert.deepStrictEqual(events, [ - { - name: 'start', - parentURL: expectedParentURL, - url: 'does-not-exist', - }, - { - name: 'end', - parentURL: expectedParentURL, - url: 'does-not-exist', - }, - { - name: 'error', - parentURL: expectedParentURL, - url: 'does-not-exist', - error, - }, - { - name: 'asyncStart', - parentURL: expectedParentURL, - url: 'does-not-exist', - error, - }, - { - name: 'asyncEnd', - parentURL: expectedParentURL, - url: 'does-not-exist', - error, - }, - ]); - }) -); +assert.rejects(import('does-not-exist'), (error) => { + const expectedParentURL = pathToFileURL(module.filename).href; + // Verify order and contents of each event + assert.deepStrictEqual(events, [ + { + name: 'start', + parentURL: expectedParentURL, + url: 'does-not-exist', + }, + { + name: 'end', + parentURL: expectedParentURL, + url: 'does-not-exist', + }, + { + name: 'error', + parentURL: expectedParentURL, + url: 'does-not-exist', + error, + }, + { + name: 'asyncStart', + parentURL: expectedParentURL, + url: 'does-not-exist', + error, + }, + { + name: 'asyncEnd', + parentURL: expectedParentURL, + url: 'does-not-exist', + error, + }, + ]); + return true; +}).then(common.mustCall()); diff --git a/test/parallel/test-diagnostics-channel-module-import.js b/test/parallel/test-diagnostics-channel-module-import.js index c00e90a9b9d4db..f24be16a6e7bc1 100644 --- a/test/parallel/test-diagnostics-channel-module-import.js +++ b/test/parallel/test-diagnostics-channel-module-import.js @@ -57,5 +57,4 @@ import('http').then( }, ]); }), - common.mustNotCall(), ); diff --git a/test/parallel/test-diagnostics-channel-tracing-channel-promise-error.js b/test/parallel/test-diagnostics-channel-tracing-channel-promise-error.js index 02b00e684bf871..4a193b52a76443 100644 --- a/test/parallel/test-diagnostics-channel-tracing-channel-promise-error.js +++ b/test/parallel/test-diagnostics-channel-tracing-channel-promise-error.js @@ -27,12 +27,10 @@ const handlers = { channel.subscribe(handlers); -channel.tracePromise(common.mustCall(function(value) { - assert.deepStrictEqual(this, thisArg); - return Promise.reject(value); -}), input, thisArg, expectedError).then( - common.mustNotCall(), - common.mustCall((value) => { - assert.deepStrictEqual(value, expectedError); - }) -); +assert.rejects( + channel.tracePromise(common.mustCall(function(value) { + assert.deepStrictEqual(this, thisArg); + return Promise.reject(value); + }), input, thisArg, expectedError), + expectedError, +).then(common.mustCall()); diff --git a/test/parallel/test-diagnostics-channel-tracing-channel-promise.js b/test/parallel/test-diagnostics-channel-tracing-channel-promise.js index 5b8c0a172339af..1ac1a564962884 100644 --- a/test/parallel/test-diagnostics-channel-tracing-channel-promise.js +++ b/test/parallel/test-diagnostics-channel-tracing-channel-promise.js @@ -37,5 +37,4 @@ channel.tracePromise(common.mustCall(function(value) { common.mustCall((value) => { assert.deepStrictEqual(value, expectedResult); }), - common.mustNotCall() ); diff --git a/test/parallel/test-domain-promise.js b/test/parallel/test-domain-promise.js index d3b24eba9fb8ec..7b1796d84feab6 100644 --- a/test/parallel/test-domain-promise.js +++ b/test/parallel/test-domain-promise.js @@ -67,7 +67,7 @@ process.on('warning', common.mustNotCall()); d2.run(common.mustCall(() => { p.then(d1.bind(common.mustCall((v) => { assert.strictEqual(process.domain, d1); - }))); + }))).then(common.mustCall()); })); } diff --git a/test/parallel/test-eslint-must-call-assert.js b/test/parallel/test-eslint-must-call-assert.js index df0801ad929b3b..3992300fbed8d1 100644 --- a/test/parallel/test-eslint-must-call-assert.js +++ b/test/parallel/test-eslint-must-call-assert.js @@ -20,7 +20,7 @@ tester.run('must-call-assert', rule, { 'process.once("message", common.mustCall((code) => { (() => assert.strictEqual(code, 0))(); }));', 'someAsyncTask(common.mustSucceed((code) => { (() => assert.strictEqual(code, 0))(); }));', 'someAsyncTask(mustSucceed((code) => { (() => assert.strictEqual(code, 0))(); }));', - '(async () => {await assert.rejects(fun())})().then()', + '(async () => {await assert.rejects(fun())})().then(common.mustCall())', '[1, true].forEach((val) => assert.strictEqual(fun(val), 0));', 'const assert = require("node:assert")', 'const assert = require("assert")', @@ -113,6 +113,11 @@ tester.run('must-call-assert', rule, { 'eval("(" + function() {} + ")()")', // eslint-disable-next-line no-template-curly-in-string 'eval(`(${function() {}})()`)', + 'promise.then(mustCall())', + 'promise.then(mustNotCall("expected a never settling promise"))', + 'promise.then(common.mustCall((val) => { assert.ok(val); }))', + 'await promise.then((val) => { assert.ok(val); })', + 'checkSupportReusePort().then(test, () => common.skip("reuse port is not supported"))', ], invalid: [ { @@ -213,5 +218,39 @@ tester.run('must-call-assert', rule, { code: 'import "node:assert"', errors: [{ message: 'Only assign `node:assert` to `assert`' }], }, + { + code: 'promise.then(common.mustCall((val) => assert.ok(val)))', + errors: [{ message: 'Add an additional `.then(common.mustCall())` to detect if resulting promise settles' }], + }, + { + code: 'promise.then(common.mustCall(async (val) => { assert.ok(val); }))', + errors: [{ message: 'Add an additional `.then(common.mustCall())` to detect if resulting promise settles' }], + }, + { + code: 'promise.then(common.mustCall((val) => { assert.ok(val); return; }))', + errors: [{ message: 'Cannot mix `common.mustCall` and return statement inside a `.then` chain' }], + }, + { + code: 'promise.then((val) => val).then(common.mustCall((val) => { assert.ok(val); })).then(common.mustCall())', + errors: [{ message: 'in a `.then` chain, only use mustCall as the last node' }], + }, + { + code: 'promise.then((val) => val).then((val) => { assert.ok(val); })', + errors: [{ + message: 'Last item of `.then` list must use `mustCall()` or `mustNotCall("expected never settling promise")`', + }], + }, + { + code: 'promise.then((val) => { assert.ok(val); })', + errors: [{ + message: 'Last item of `.then` list must use `mustCall()` or `mustNotCall("expected never settling promise")`', + }], + }, + { + code: 'promise.then((val) => { assert.ok(val); }).then(common.mustCallAtLeast())', + errors: [{ + message: 'Last item of `.then` list must use `mustCall()` or `mustNotCall("expected never settling promise")`', + }], + }, ] }); diff --git a/test/parallel/test-eventtarget-memoryleakwarning.js b/test/parallel/test-eventtarget-memoryleakwarning.js index 2ec48720c8a8c5..38fc5efc9bd24a 100644 --- a/test/parallel/test-eventtarget-memoryleakwarning.js +++ b/test/parallel/test-eventtarget-memoryleakwarning.js @@ -105,5 +105,5 @@ common.expectWarning({ await setTimeout(0); globalThis.gc(); } - })().then(common.mustCall(), common.mustNotCall()); + })().then(common.mustCall()); } diff --git a/test/parallel/test-fs-append-file.js b/test/parallel/test-fs-append-file.js index 1e20625e5b9697..cdcc40b52feb6d 100644 --- a/test/parallel/test-fs-append-file.js +++ b/test/parallel/test-fs-append-file.js @@ -169,17 +169,16 @@ const throwNextTick = (e) => { process.nextTick(() => { throw e; }); }; let fd; fs.promises.open(filename, 'a+') - .then(common.mustCall((fileDescriptor) => { + .then((fileDescriptor) => { fd = fileDescriptor; return fs.promises.appendFile(fd, s); - })) - .then(common.mustCall(() => fd.close())) - .then(common.mustCall(() => fs.promises.readFile(filename))) + }) + .then(() => fd.close()) + .then(() => fs.promises.readFile(filename)) .then(common.mustCall((buffer) => { assert.strictEqual(Buffer.byteLength(s) + currentFileData.length, buffer.length); - })) - .catch(throwNextTick); + })); } assert.throws( diff --git a/test/parallel/test-fs-long-path.js b/test/parallel/test-fs-long-path.js index 11724a88dc4c29..e903eea2e53e2a 100644 --- a/test/parallel/test-fs-long-path.js +++ b/test/parallel/test-fs-long-path.js @@ -48,5 +48,5 @@ fs.writeFile(fullPath, 'ok', common.mustSucceed(() => { fs.realpath.native(fullPath, common.mustSucceed()); // Tests https://github.com/nodejs/node/issues/51031 - fs.promises.realpath(fullPath).then(common.mustCall(), common.mustNotCall()); + fs.promises.realpath(fullPath).then(common.mustCall()); })); diff --git a/test/parallel/test-fs-promises-file-handle-read-worker.js b/test/parallel/test-fs-promises-file-handle-read-worker.js index 7ae881801ad0b1..93669c279f83c8 100644 --- a/test/parallel/test-fs-promises-file-handle-read-worker.js +++ b/test/parallel/test-fs-promises-file-handle-read-worker.js @@ -17,7 +17,7 @@ if (isMainThread || !workerData) { workerData: { handle }, transferList: [handle] }); - }); + }).then(common.mustCall()); fs.promises.open(file, 'r').then(async (handle) => { try { fs.createReadStream(null, { fd: handle }); @@ -33,7 +33,7 @@ if (isMainThread || !workerData) { } finally { await handle.close(); } - }); + }).then(common.mustCall()); } else { let output = ''; diff --git a/test/parallel/test-fs-promises-readfile-empty.js b/test/parallel/test-fs-promises-readfile-empty.js index ef15a2681123c6..f71057a116e62f 100644 --- a/test/parallel/test-fs-promises-readfile-empty.js +++ b/test/parallel/test-fs-promises-readfile-empty.js @@ -1,5 +1,5 @@ 'use strict'; -require('../common'); +const common = require('../common'); const assert = require('assert'); const { promises: fs } = require('fs'); @@ -8,10 +8,13 @@ const fixtures = require('../common/fixtures'); const fn = fixtures.path('empty.txt'); fs.readFile(fn) - .then(assert.ok); + .then(assert.ok) + .then(common.mustCall()); fs.readFile(fn, 'utf8') - .then(assert.strictEqual.bind(this, '')); + .then(assert.strictEqual.bind(this, '')) + .then(common.mustCall()); fs.readFile(fn, { encoding: 'utf8' }) - .then(assert.strictEqual.bind(this, '')); + .then(assert.strictEqual.bind(this, '')) + .then(common.mustCall()); diff --git a/test/parallel/test-fs-readfile-eof.js b/test/parallel/test-fs-readfile-eof.js index d7f9e21c5bf158..5020db019497a5 100644 --- a/test/parallel/test-fs-readfile-eof.js +++ b/test/parallel/test-fs-readfile-eof.js @@ -11,12 +11,12 @@ const childType = ['child-encoding', 'child-non-encoding']; if (process.argv[2] === childType[0]) { fs.readFile('/dev/stdin', 'utf8').then((data) => { process.stdout.write(data); - }); + }).then(common.mustCall()); return; } else if (process.argv[2] === childType[1]) { fs.readFile('/dev/stdin').then((data) => { process.stdout.write(data); - }); + }).then(common.mustCall()); return; } diff --git a/test/parallel/test-fs-rmdir-recursive-error.js b/test/parallel/test-fs-rmdir-recursive-error.js index f76568f44e2a4c..dbfbdad40aa922 100644 --- a/test/parallel/test-fs-rmdir-recursive-error.js +++ b/test/parallel/test-fs-rmdir-recursive-error.js @@ -24,8 +24,7 @@ assert.throws(() => { code: 'ERR_INVALID_ARG_VALUE', }); -rmdirPromise('nonexistent', { - recursive: true, -}).then(common.mustNotCall(), common.mustCall((err) => { - assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE'); -})); +assert.rejects( + rmdirPromise('nonexistent', { recursive: true }), + { code: 'ERR_INVALID_ARG_VALUE' }, +).then(common.mustCall()); diff --git a/test/parallel/test-fs-write-optional-params.js b/test/parallel/test-fs-write-optional-params.js index eebc1cc88c95b1..3eda9141ee02da 100644 --- a/test/parallel/test-fs-write-optional-params.js +++ b/test/parallel/test-fs-write-optional-params.js @@ -108,5 +108,5 @@ async function runTests(fd) { } fs.open(destInvalid, 'w+', common.mustSucceed(async (fd) => { - runTests(fd).then(common.mustCall(() => fs.close(fd, common.mustSucceed()))); + runTests(fd).then(common.mustCall(() => { fs.close(fd, common.mustSucceed()); })); })); diff --git a/test/parallel/test-heapdump-async-hooks-init-promise.js b/test/parallel/test-heapdump-async-hooks-init-promise.js index 5de40cb966f946..80d6309deabd7f 100644 --- a/test/parallel/test-heapdump-async-hooks-init-promise.js +++ b/test/parallel/test-heapdump-async-hooks-init-promise.js @@ -42,5 +42,5 @@ async_hooks.createHook({ }).enable(); -Promise.resolve().then(() => {}); +Promise.resolve().then(common.mustCall()); setImmediate(globalThis.gc); diff --git a/test/parallel/test-http-set-trailers.js b/test/parallel/test-http-set-trailers.js index bfcdd5e2632bbb..ddf18541821c82 100644 --- a/test/parallel/test-http-set-trailers.js +++ b/test/parallel/test-http-set-trailers.js @@ -96,9 +96,9 @@ const server = http.createServer((req, res) => { res.addTrailers({ 'x-foo': 'bar' }); res.end('stuff\n'); }); -server.listen(0, () => { +server.listen(0, common.mustCall(() => { Promise.all([testHttp10, testHttp11, testClientTrailers] .map((f) => util.promisify(f)) .map((f) => f(server.address().port))) - .then(() => server.close()); -}); + .then(() => server.close()).then(common.mustCall()); +})); diff --git a/test/parallel/test-inspector-connect-main-thread.js b/test/parallel/test-inspector-connect-main-thread.js index 2281b5efcf3ed8..e0fd618abc721e 100644 --- a/test/parallel/test-inspector-connect-main-thread.js +++ b/test/parallel/test-inspector-connect-main-thread.js @@ -115,7 +115,7 @@ async function main() { const arrayBuffer = new Uint8Array(sharedBuffer); arrayBuffer[0] = 1; const worker = await startWorker(false, sharedBuffer); - worker.onConsoleRequest.then(doConsoleLog.bind(null, arrayBuffer)); + worker.onConsoleRequest.then(doConsoleLog.bind(null, arrayBuffer)).then(common.mustCall()); assert.strictEqual(toDebug(), 400); assert.deepStrictEqual(await worker.onMessagesSent, [ 'Debugger.enable', diff --git a/test/parallel/test-microtask-queue-integration.js b/test/parallel/test-microtask-queue-integration.js index 1c4812d360f6c1..79a161dfa7d07e 100644 --- a/test/parallel/test-microtask-queue-integration.js +++ b/test/parallel/test-microtask-queue-integration.js @@ -25,6 +25,7 @@ const assert = require('assert'); const implementations = [ function(fn) { + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(fn); }, ]; diff --git a/test/parallel/test-microtask-queue-run-immediate.js b/test/parallel/test-microtask-queue-run-immediate.js index 577391993bcc65..52f4cd5bd7e3ff 100644 --- a/test/parallel/test-microtask-queue-run-immediate.js +++ b/test/parallel/test-microtask-queue-run-immediate.js @@ -24,6 +24,7 @@ require('../common'); const assert = require('assert'); function enqueueMicrotask(fn) { + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(fn); } diff --git a/test/parallel/test-microtask-queue-run.js b/test/parallel/test-microtask-queue-run.js index 5281cb4f3c2ed2..4d3156affe960d 100644 --- a/test/parallel/test-microtask-queue-run.js +++ b/test/parallel/test-microtask-queue-run.js @@ -24,6 +24,7 @@ require('../common'); const assert = require('assert'); function enqueueMicrotask(fn) { + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(fn); } diff --git a/test/parallel/test-module-prototype-mutation.js b/test/parallel/test-module-prototype-mutation.js index d0149548600f0c..6d83533a5ed2e3 100644 --- a/test/parallel/test-module-prototype-mutation.js +++ b/test/parallel/test-module-prototype-mutation.js @@ -49,4 +49,4 @@ assert.strictEqual( ); import(fixtures.fileURL('es-module-specifiers', 'index.mjs')) - .then(common.mustCall((module) => assert.strictEqual(module.noMain, 'no main field'))); + .then(common.mustCall((module) => { assert.strictEqual(module.noMain, 'no main field'); })); diff --git a/test/parallel/test-net-server-max-connections-close-makes-more-available.js b/test/parallel/test-net-server-max-connections-close-makes-more-available.js index c889e41265e9fe..408fb4a01ee327 100644 --- a/test/parallel/test-net-server-max-connections-close-makes-more-available.js +++ b/test/parallel/test-net-server-max-connections-close-makes-more-available.js @@ -67,15 +67,16 @@ const server = net.createServer(function(socket) { server.maxConnections = 1; -server.listen(0, function() { +server.listen(0, common.mustCall(() => { createConnection(0) .then(createConnection.bind(null, 1)) .then(closeConnection.bind(null, 0)) .then(createConnection.bind(null, 2)) .then(createConnection.bind(null, 3)) .then(server.close.bind(server)) - .then(closeConnection.bind(null, 2)); -}); + .then(closeConnection.bind(null, 2)) + .then(common.mustCall()); +})); process.on('exit', function() { // Confirm that all connections tried to send data... diff --git a/test/parallel/test-next-tick-fixed-queue-regression.js b/test/parallel/test-next-tick-fixed-queue-regression.js index 1fe82d02b10907..82e04e371a7208 100644 --- a/test/parallel/test-next-tick-fixed-queue-regression.js +++ b/test/parallel/test-next-tick-fixed-queue-regression.js @@ -14,5 +14,5 @@ process.nextTick(() => { process.nextTick(common.mustCall()); const immediate = setImmediate(common.mustNotCall()); process.nextTick(common.mustCall(() => clearImmediate(immediate))); - }); + }).then(common.mustCall()); }); diff --git a/test/parallel/test-performance-function-async.js b/test/parallel/test-performance-function-async.js index 82ff6612c5bf86..c29a085f89b33d 100644 --- a/test/parallel/test-performance-function-async.js +++ b/test/parallel/test-performance-function-async.js @@ -36,4 +36,4 @@ const timerified = timerify(doIt); const res = timerified(); assert(res instanceof Promise); -res.then(common.mustCall(assert)); +res.then(assert).then(common.mustCall()); diff --git a/test/parallel/test-promise-hook-exceptions.js b/test/parallel/test-promise-hook-exceptions.js index 199ad2c52d2f35..87c72ecbaacc28 100644 --- a/test/parallel/test-promise-hook-exceptions.js +++ b/test/parallel/test-promise-hook-exceptions.js @@ -26,6 +26,6 @@ testHook('onSettled'); testHook('onBefore'); testHook('onAfter'); -const stop = promiseHooks.onInit(common.mustCall(2)); +const stop = promiseHooks.onInit(common.mustCall(3)); -Promise.resolve().then(stop); +Promise.resolve().then(stop).then(common.mustCall()); diff --git a/test/parallel/test-promise-hook-on-after.js b/test/parallel/test-promise-hook-on-after.js index 5785a8c40dc72a..d6b535969eeeff 100644 --- a/test/parallel/test-promise-hook-on-after.js +++ b/test/parallel/test-promise-hook-on-after.js @@ -24,6 +24,6 @@ const promise = Promise.resolve().then(() => { promise.then(() => { assert.strictEqual(seen, promise); stop(); -}); +}).then(common.mustCall()); assert.strictEqual(seen, undefined); diff --git a/test/parallel/test-promise-hook-on-before.js b/test/parallel/test-promise-hook-on-before.js index b732bc2494411a..e9a38fde375f00 100644 --- a/test/parallel/test-promise-hook-on-before.js +++ b/test/parallel/test-promise-hook-on-before.js @@ -22,6 +22,7 @@ const promise = Promise.resolve().then(() => { stop(); }); +// eslint-disable-next-line node-core/must-call-assert promise.then(); assert.strictEqual(seen, undefined); diff --git a/test/parallel/test-promises-unhandled-rejections.js b/test/parallel/test-promises-unhandled-rejections.js index 527840a91bdb0a..bfdd2cf449821e 100644 --- a/test/parallel/test-promises-unhandled-rejections.js +++ b/test/parallel/test-promises-unhandled-rejections.js @@ -165,6 +165,7 @@ test('Catching a promise rejection after setImmediate is not' + }); _reject(e); setImmediate(function() { + // eslint-disable-next-line node-core/must-call-assert promise.then(assert.fail, function() {}); }); }); @@ -241,6 +242,7 @@ test( function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert Promise.reject(e).then(assert.fail, function() {}); } ); @@ -251,6 +253,7 @@ test( function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert new Promise(function(_, reject) { reject(e); }).then(assert.fail, function() {}); @@ -263,6 +266,7 @@ test('Attaching a promise catch in a process.nextTick is soon enough to' + onUnhandledFail(done); const promise = Promise.reject(e); process.nextTick(function() { + // eslint-disable-next-line node-core/must-call-assert promise.then(assert.fail, function() {}); }); }); @@ -275,6 +279,7 @@ test('Attaching a promise catch in a process.nextTick is soon enough to' + reject(e); }); process.nextTick(function() { + // eslint-disable-next-line node-core/must-call-assert promise.then(assert.fail, function() {}); }); }); @@ -300,6 +305,7 @@ test('catching a promise which is asynchronously rejected (via ' + ' unhandledRejection', function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(function() { return new Promise(function(_, reject) { setTimeout(function() { @@ -315,6 +321,7 @@ test('Catching a rejected promise derived from throwing in a' + ' fulfillment handler prevents unhandledRejection', function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(function() { throw e; }).then(assert.fail, function(reason) { @@ -327,6 +334,7 @@ test('Catching a rejected promise derived from returning a' + ' prevents unhandledRejection', function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert Promise.resolve().then(function() { return Promise.reject(e); }).then(assert.fail, function(reason) { @@ -383,6 +391,7 @@ test('Catching the Promise.all() of a collection that includes a ' + 'rejected promise prevents unhandledRejection', function(done) { const e = new Error(); onUnhandledFail(done); + // eslint-disable-next-line node-core/must-call-assert Promise.all([Promise.reject(e)]).then(assert.fail, function() {}); }); @@ -399,6 +408,7 @@ test( }); p = Promise.all([p]); process.nextTick(function() { + // eslint-disable-next-line node-core/must-call-assert p.then(assert.fail, function() {}); }); } @@ -435,6 +445,7 @@ test('Waiting setTimeout(, 10) to catch a promise causes an' + throw e; }); setTimeout(function() { + // eslint-disable-next-line node-core/must-call-assert thePromise.then(assert.fail, function(reason) { assert.strictEqual(reason, e); }); @@ -450,13 +461,13 @@ test('Waiting for some combination of process.nextTick + promise' + const a = Promise.reject(e); process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { a.catch(function() {}); - }); + })); }); - }); + })); }); }); @@ -466,18 +477,18 @@ test('Waiting for some combination of process.nextTick + promise' + const e = new Error(); onUnhandledFail(done); - setImmediate(function() { + setImmediate(common.mustCall(() => { const a = Promise.reject(e); process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { a.catch(function() {}); - }); + })); }); - }); + })); }); - }); + })); }); test('Waiting for some combination of process.nextTick + promise ' + @@ -486,18 +497,18 @@ test('Waiting for some combination of process.nextTick + promise ' + const e = new Error(); onUnhandledFail(done); - setTimeout(function() { + setTimeout(common.mustCall(() => { const a = Promise.reject(e); process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { a.catch(function() {}); - }); + })); }); - }); + })); }); - }, 0); + }), 0); }); test('Waiting for some combination of promise microtasks + ' + @@ -508,15 +519,15 @@ test('Waiting for some combination of promise microtasks + ' + const a = Promise.reject(e); - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { a.catch(function() {}); }); - }); + })); }); - }); + })); }); test( @@ -527,18 +538,18 @@ test( const e = new Error(); onUnhandledFail(done); - setImmediate(function() { + setImmediate(common.mustCall(() => { const a = Promise.reject(e); - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { a.catch(function() {}); }); - }); + })); }); - }); - }); + })); + })); } ); @@ -548,18 +559,18 @@ test('Waiting for some combination of promise microtasks +' + const e = new Error(); onUnhandledFail(done); - setTimeout(function() { + setTimeout(common.mustCall(() => { const a = Promise.reject(e); - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { - Promise.resolve().then(function() { + Promise.resolve().then(common.mustCall(() => { process.nextTick(function() { a.catch(function() {}); }); - }); + })); }); - }); - }, 0); + })); + }), 0); }); test('setImmediate + promise microtasks is too late to attach a catch' + @@ -571,11 +582,11 @@ test('setImmediate + promise microtasks is too late to attach a catch' + assert.strictEqual(promise, p); })); const p = Promise.reject(e); - setImmediate(function() { - Promise.resolve().then(function() { + setImmediate(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { p.catch(function() {}); - }); - }); + })); + })); }); test('setImmediate + promise microtasks is too late to attach a catch' + @@ -585,17 +596,17 @@ test('setImmediate + promise microtasks is too late to attach a catch' + assert.strictEqual(reason, undefined); assert.strictEqual(promise, p); })); - setImmediate(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { + setImmediate(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { p.catch(function() {}); - }); - }); - }); - }); - }); + })); + })); + })); + })); + })); const p = Promise.reject(); }); @@ -607,17 +618,17 @@ test('setImmediate + promise microtasks is too late to attach a catch' + assert.strictEqual(promise, p); })); const p = Promise.reject(); - setImmediate(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { - Promise.resolve().then(function() { + setImmediate(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { + Promise.resolve().then(common.mustCall(() => { p.catch(function() {}); - }); - }); - }); - }); - }); + })); + })); + })); + })); + })); }); test('nextTick is immediately scheduled when called inside an event' + diff --git a/test/parallel/test-queue-microtask.js b/test/parallel/test-queue-microtask.js index f0eb4364258cc1..d25bfab9628736 100644 --- a/test/parallel/test-queue-microtask.js +++ b/test/parallel/test-queue-microtask.js @@ -32,7 +32,7 @@ queueMicrotask(common.mustCall(function() { { const q = []; - Promise.resolve().then(() => q.push('a')); + Promise.resolve().then(() => q.push('a')).then(common.mustCall()); queueMicrotask(common.mustCall(() => q.push('b'))); Promise.reject().catch(() => q.push('c')); diff --git a/test/parallel/test-readable-from-iterator-closing.js b/test/parallel/test-readable-from-iterator-closing.js index ff2746dc4601ea..ef7e54821aceb9 100644 --- a/test/parallel/test-readable-from-iterator-closing.js +++ b/test/parallel/test-readable-from-iterator-closing.js @@ -159,7 +159,7 @@ async function closeStreamWhileNextIsPending() { yielded.then(() => { stream.destroy(); resolveDestroy(); - }); + }).then(mustCall()); } async function closeAfterNullYielded() { diff --git a/test/parallel/test-readline-interface.js b/test/parallel/test-readline-interface.js index 1adee53bfe4f04..2fd4646c314c84 100644 --- a/test/parallel/test-readline-interface.js +++ b/test/parallel/test-readline-interface.js @@ -1192,11 +1192,12 @@ for (let i = 0; i < 12; i++) { question('What\'s your name?').then(common.mustCall((name) => { assert.strictEqual(name, 'Node.js'); rli.close(); - question('How are you?') - .then(common.mustNotCall(), common.expectsError({ + assert.rejects( + question('How are you?'), + { code: 'ERR_USE_AFTER_CLOSE', name: 'Error' - })); + }).then(common.mustCall()); assert.notStrictEqual(rli.getPrompt(), 'How are you?'); })); fi.emit('data', 'Node.js\n'); diff --git a/test/parallel/test-readline-promises-interface.js b/test/parallel/test-readline-promises-interface.js index 19c445572b9806..260431dd69cbc9 100644 --- a/test/parallel/test-readline-promises-interface.js +++ b/test/parallel/test-readline-promises-interface.js @@ -396,7 +396,7 @@ function assertCursorRowsAndCols(rli, rows, cols) { { const [rli] = getInterface({ terminal: true }); const expectedLines = ['foo']; - rli.question(expectedLines[0]).then(() => rli.close()); + rli.question(expectedLines[0]).then(() => rli.close()).then(common.mustNotCall('never settling promise')); assertCursorRowsAndCols(rli, 0, expectedLines[0].length); rli.close(); } @@ -405,7 +405,7 @@ function assertCursorRowsAndCols(rli, rows, cols) { { const [rli] = getInterface({ terminal: true }); const expectedLines = ['foo', 'bar']; - rli.question(expectedLines.join('\n')).then(() => rli.close()); + rli.question(expectedLines.join('\n')).then(() => rli.close()).then(common.mustNotCall('never settling promise')); assertCursorRowsAndCols( rli, expectedLines.length - 1, expectedLines.slice(-1)[0].length); rli.close(); @@ -997,11 +997,12 @@ for (let i = 0; i < 12; i++) { rli.question('What\'s your name?').then(common.mustCall((name) => { assert.strictEqual(name, 'Node.js'); rli.close(); - rli.question('How are you?') - .then(common.mustNotCall(), common.expectsError({ + assert.rejects( + rli.question('How are you?'), + { code: 'ERR_USE_AFTER_CLOSE', name: 'Error' - })); + }).then(common.mustCall()); assert.notStrictEqual(rli.getPrompt(), 'How are you?'); })); fi.emit('data', 'Node.js\n'); diff --git a/test/parallel/test-repl-eval-error-after-close.js b/test/parallel/test-repl-eval-error-after-close.js index 5fe330b49c236e..8c30a533efba15 100644 --- a/test/parallel/test-repl-eval-error-after-close.js +++ b/test/parallel/test-repl-eval-error-after-close.js @@ -14,6 +14,7 @@ const assert = require('node:assert'); const { replServer, output } = startNewREPLServer({ eval(_cmd, _context, _file, cb) { + // eslint-disable-next-line node-core/must-call-assert close$.promise.then(() => { cb(new Error('Error returned from the eval callback')); eval$.resolve(); diff --git a/test/parallel/test-runner-mock-timers-scheduler.js b/test/parallel/test-runner-mock-timers-scheduler.js index 6a83056e70eda2..72a69b5d675b39 100644 --- a/test/parallel/test-runner-mock-timers-scheduler.js +++ b/test/parallel/test-runner-mock-timers-scheduler.js @@ -30,7 +30,7 @@ describe('Mock Timers Scheduler Test Suite', () => { const fn = t.mock.fn(); - nodeTimersPromises.scheduler.wait(9999).then(fn); + nodeTimersPromises.scheduler.wait(9999).then(fn).then(common.mustCall()); t.mock.timers.tick(8999); assert.strictEqual(fn.mock.callCount(), 0); diff --git a/test/parallel/test-runner-mock-timers.js b/test/parallel/test-runner-mock-timers.js index fdf6d6416a6d44..17602e4eb90163 100644 --- a/test/parallel/test-runner-mock-timers.js +++ b/test/parallel/test-runner-mock-timers.js @@ -575,9 +575,9 @@ describe('Mock Timers Test Suite', () => { const ac = new AbortController(); // id 1 & pos 1 in priority queue - nodeTimersPromises.setTimeout(100, undefined, { signal: ac.signal }).then(f1, f1); + nodeTimersPromises.setTimeout(100, undefined, { signal: ac.signal }).then(f1, f1).then(common.mustCall()); // id 2 & pos 1 in priority queue (id 1 is moved to pos 2) - nodeTimersPromises.setTimeout(50).then(f2, f2); + nodeTimersPromises.setTimeout(50).then(f2, f2).then(common.mustCall()); ac.abort(); // BUG: will remove timer at pos 1 not timer with id 1! @@ -600,9 +600,9 @@ describe('Mock Timers Test Suite', () => { const ac = new AbortController(); // id 1 & pos 1 in priority queue - nodeTimersPromises.setTimeout(50, true, { signal: ac.signal }).then(f1, f1); + nodeTimersPromises.setTimeout(50, true, { signal: ac.signal }).then(f1, f1).then(common.mustCall()); // id 2 & pos 2 in priority queue - nodeTimersPromises.setTimeout(100).then(f2, f2); + nodeTimersPromises.setTimeout(100).then(f2, f2).then(common.mustCall()); // First setTimeout resolves t.mock.timers.tick(50); diff --git a/test/parallel/test-stream-consumers.js b/test/parallel/test-stream-consumers.js index 883d55dc6f02fb..8a5e783fc05369 100644 --- a/test/parallel/test-stream-consumers.js +++ b/test/parallel/test-stream-consumers.js @@ -28,10 +28,10 @@ const kArrayBuffer = { const passthrough = new PassThrough(); - blob(passthrough).then(common.mustCall(async (blob) => { + blob(passthrough).then(async (blob) => { assert.strictEqual(blob.size, 10); assert.deepStrictEqual(await blob.arrayBuffer(), kArrayBuffer); - })); + }).then(common.mustCall()); passthrough.write('hello'); setTimeout(() => passthrough.end('there'), 10); @@ -40,7 +40,7 @@ const kArrayBuffer = { const passthrough = new PassThrough(); - arrayBuffer(passthrough).then(common.mustCall(async (ab) => { + arrayBuffer(passthrough).then(common.mustCall((ab) => { assert.strictEqual(ab.byteLength, 10); assert.deepStrictEqual(ab, kArrayBuffer); })); @@ -52,7 +52,7 @@ const kArrayBuffer = { const passthrough = new PassThrough(); - buffer(passthrough).then(common.mustCall(async (buf) => { + buffer(passthrough).then(common.mustCall((buf) => { assert.strictEqual(buf.byteLength, 10); assert.deepStrictEqual(buf.buffer, kArrayBuffer); })); @@ -65,7 +65,7 @@ const kArrayBuffer = { const passthrough = new PassThrough(); - text(passthrough).then(common.mustCall(async (str) => { + text(passthrough).then(common.mustCall((str) => { assert.strictEqual(str.length, 10); assert.strictEqual(str, 'hellothere'); })); @@ -81,7 +81,7 @@ const kArrayBuffer = text(readable).then((data) => { assert.strictEqual(data, 'foo\ufffd\ufffd\ufffd'); - }); + }).then(common.mustCall()); readable.push(new Uint8Array([0x66, 0x6f, 0x6f, 0xed, 0xa0, 0x80])); readable.push(null); @@ -90,7 +90,7 @@ const kArrayBuffer = { const passthrough = new PassThrough(); - json(passthrough).then(common.mustCall(async (str) => { + json(passthrough).then(common.mustCall((str) => { assert.strictEqual(str.length, 10); assert.strictEqual(str, 'hellothere'); })); @@ -102,10 +102,10 @@ const kArrayBuffer = { const { writable, readable } = new TransformStream(); - blob(readable).then(common.mustCall(async (blob) => { + blob(readable).then(async (blob) => { assert.strictEqual(blob.size, 10); assert.deepStrictEqual(await blob.arrayBuffer(), kArrayBuffer); - })); + }).then(common.mustCall()); const writer = writable.getWriter(); writer.write('hello'); @@ -120,7 +120,7 @@ const kArrayBuffer = { const { writable, readable } = new TransformStream(); - arrayBuffer(readable).then(common.mustCall(async (ab) => { + arrayBuffer(readable).then(common.mustCall((ab) => { assert.strictEqual(ab.byteLength, 10); assert.deepStrictEqual(ab, kArrayBuffer); })); @@ -138,7 +138,7 @@ const kArrayBuffer = { const { writable, readable } = new TransformStream(); - text(readable).then(common.mustCall(async (str) => { + text(readable).then(common.mustCall((str) => { assert.strictEqual(str.length, 10); assert.strictEqual(str, 'hellothere'); })); @@ -156,7 +156,7 @@ const kArrayBuffer = { const { writable, readable } = new TransformStream(); - json(readable).then(common.mustCall(async (str) => { + json(readable).then(common.mustCall((str) => { assert.strictEqual(str.length, 10); assert.strictEqual(str, 'hellothere'); })); diff --git a/test/parallel/test-stream-map.js b/test/parallel/test-stream-map.js index 4a7a53c55960ea..212658313885f1 100644 --- a/test/parallel/test-stream-map.js +++ b/test/parallel/test-stream-map.js @@ -213,7 +213,7 @@ function createDependentPromises(n) { await stream.toArray(); assert.deepStrictEqual(finishOrder, [0, 1, 2, 3]); - })().then(common.mustCall(), common.mustNotCall()); + })().then(common.mustCall()); } { @@ -261,7 +261,7 @@ function createDependentPromises(n) { assert.deepStrictEqual(outputOrder, input); assert.deepStrictEqual(finishOrder, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); - })().then(common.mustCall(), common.mustNotCall()); + })().then(common.mustCall()); } { @@ -309,7 +309,7 @@ function createDependentPromises(n) { assert.deepStrictEqual(outputOrder, input); assert.deepStrictEqual(finishOrder, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]); - })().then(common.mustCall(), common.mustNotCall()); + })().then(common.mustCall()); } { @@ -338,7 +338,7 @@ function createDependentPromises(n) { (async () => { await stream.toArray(); - })().then(common.mustCall(), common.mustNotCall()); + })().then(common.mustCall()); } { diff --git a/test/parallel/test-stream-readable-strategy-option.js b/test/parallel/test-stream-readable-strategy-option.js index 255e570fdc4c60..b64cafd68985ac 100644 --- a/test/parallel/test-stream-readable-strategy-option.js +++ b/test/parallel/test-stream-readable-strategy-option.js @@ -70,5 +70,5 @@ const assert = require('assert'); reader.read().then(({ value, done }) => { assert.strictEqual(value, undefined); assert.strictEqual(done, true); - }); + }).then(common.mustCall()); } diff --git a/test/parallel/test-stream3-pipeline-async-iterator.js b/test/parallel/test-stream3-pipeline-async-iterator.js index fb505646c59f78..3ebe90e4b99163 100644 --- a/test/parallel/test-stream3-pipeline-async-iterator.js +++ b/test/parallel/test-stream3-pipeline-async-iterator.js @@ -24,5 +24,6 @@ const { pipeline } = require('node:stream/promises'); // eslint-disable-next-line node-core/must-call-assert require('assert').deepStrictEqual(messages, ['Hello', 'World']); })() + // eslint-disable-next-line node-core/must-call-assert .then(require('../common').mustCall()); } diff --git a/test/parallel/test-timers-immediate-promisified.js b/test/parallel/test-timers-immediate-promisified.js index c08b3735da4e3a..ec4d184fab014e 100644 --- a/test/parallel/test-timers-immediate-promisified.js +++ b/test/parallel/test-timers-immediate-promisified.js @@ -47,7 +47,7 @@ assert.strictEqual(setPromiseImmediate, timerPromises.setImmediate); const ac = new AbortController(); const signal = ac.signal; setPromiseImmediate(10, { signal }) - .then(common.mustCall(() => { ac.abort(); })) + .then(() => { ac.abort(); }) .then(common.mustCall()); } diff --git a/test/parallel/test-timers-interval-promisified.js b/test/parallel/test-timers-interval-promisified.js index 13ff05b6d76866..754531516c31f2 100644 --- a/test/parallel/test-timers-interval-promisified.js +++ b/test/parallel/test-timers-interval-promisified.js @@ -18,22 +18,22 @@ const { setInterval } = timerPromises; const iterable = setInterval(1, undefined); const iterator = iterable[Symbol.asyncIterator](); const promise = iterator.next(); - promise.then(common.mustCall((result) => { + promise.then((result) => { assert.ok(!result.done, 'iterator was wrongly marked as done'); assert.strictEqual(result.value, undefined); return iterator.return(); - })).then(common.mustCall()); + }).then(common.mustCall()); } { const iterable = setInterval(1, 'foobar'); const iterator = iterable[Symbol.asyncIterator](); const promise = iterator.next(); - promise.then(common.mustCall((result) => { + promise.then((result) => { assert.ok(!result.done, 'iterator was wronly marked as done'); assert.strictEqual(result.value, 'foobar'); return iterator.return(); - })).then(common.mustCall()); + }).then(common.mustCall()); } { @@ -41,16 +41,16 @@ const { setInterval } = timerPromises; const iterator = iterable[Symbol.asyncIterator](); const promise = iterator.next(); promise - .then(common.mustCall((result) => { + .then((result) => { assert.ok(!result.done, 'iterator was wronly marked as done'); assert.strictEqual(result.value, 'foobar'); return iterator.next(); - })) - .then(common.mustCall((result) => { + }) + .then((result) => { assert.ok(!result.done, 'iterator was wrongly marked as done'); assert.strictEqual(result.value, 'foobar'); return iterator.return(); - })) + }) .then(common.mustCall()); } @@ -230,14 +230,14 @@ const { setInterval } = timerPromises; const iterable = timerPromises.setInterval(time_unit * 2); const iterator = iterable[Symbol.asyncIterator](); - iterator.next().then(() => { + res(iterator.next().then(() => { assert.ok(pre, 'interval ran too early'); assert.ok(!post, 'interval ran too late'); return iterator.next(); }).then(() => { assert.ok(post, 'second interval ran too early'); return iterator.return(); - }).then(res); + })); }), setPromiseTimeout(time_unit * 3).then(() => post = true), ]).then(common.mustCall()); diff --git a/test/parallel/test-timers-nan-duration-warning-promises.js b/test/parallel/test-timers-nan-duration-warning-promises.js index 2a7cbeb2fb7d0e..ae2f8e1234b9e7 100644 --- a/test/parallel/test-timers-nan-duration-warning-promises.js +++ b/test/parallel/test-timers-nan-duration-warning-promises.js @@ -8,4 +8,4 @@ process.once('warning', common.mustCall((warning) => { assert.strictEqual(warning.name, 'TimeoutNaNWarning'); })); -setTimeout(NaN).then(common.mustCall(), common.mustNotCall()); +setTimeout(NaN).then(common.mustCall()); diff --git a/test/parallel/test-timers-timeout-promisified.js b/test/parallel/test-timers-timeout-promisified.js index 048588c0ae18cc..914c4915cab55b 100644 --- a/test/parallel/test-timers-timeout-promisified.js +++ b/test/parallel/test-timers-timeout-promisified.js @@ -47,7 +47,7 @@ assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout); const ac = new AbortController(); const signal = ac.signal; setPromiseTimeout(10, undefined, { signal }) - .then(common.mustCall(() => { ac.abort(); })) + .then(() => { ac.abort(); }) .then(common.mustCall()); } diff --git a/test/parallel/test-tls-streamwrap-buffersize.js b/test/parallel/test-tls-streamwrap-buffersize.js index 399c8b1c8cf272..4822b7eb3952e3 100644 --- a/test/parallel/test-tls-streamwrap-buffersize.js +++ b/test/parallel/test-tls-streamwrap-buffersize.js @@ -66,6 +66,6 @@ const net = require('net'); client.end(); })); - }); + }).then(common.mustCall()); })); } diff --git a/test/parallel/test-ttywrap-stack.js b/test/parallel/test-ttywrap-stack.js index 6fa85a338f1129..9b3a215bf64cce 100644 --- a/test/parallel/test-ttywrap-stack.js +++ b/test/parallel/test-ttywrap-stack.js @@ -5,8 +5,6 @@ const common = require('../common'); // will not crash the process if there // is not enough space on the V8 stack -const done = common.mustCall(); - async function test() { await test(); } @@ -17,4 +15,4 @@ async function test() { } catch (err) { console.log(err); } -})().then(done, done); +})().then(common.mustCall()); diff --git a/test/parallel/test-util-promisify.js b/test/parallel/test-util-promisify.js index e2318a4973b972..a77a3b3e1fd20b 100644 --- a/test/parallel/test-util-promisify.js +++ b/test/parallel/test-util-promisify.js @@ -27,7 +27,7 @@ promisify(async (callback) => { callback(); })().then(common.mustCall(() => { 'DeprecationWarning', 'Calling promisify on a function that returns a Promise is likely a mistake.', 'DEP0174'); - promisify(async () => {})().then(common.mustNotCall()); + promisify(async () => {})().then(common.mustNotCall('never settling promise expected')); })); const stat = promisify(fs.stat); @@ -166,7 +166,7 @@ const stat = promisify(fs.stat); o.fn = fn; - o.fn().then(common.mustCall((val) => assert(val))); + o.fn().then(common.mustCall((val) => { assert(val); })); } { @@ -201,7 +201,8 @@ const stat = promisify(fs.stat); }); thrower(1, 2, 3) .then(assert.fail) - .then(assert.fail, (e) => assert.strictEqual(e, errToThrow)); + .then(assert.fail, (e) => assert.strictEqual(e, errToThrow)) + .then(common.mustCall()); } { diff --git a/test/parallel/test-vm-measure-memory-lazy.js b/test/parallel/test-vm-measure-memory-lazy.js index 7f85f8d6ca9656..bca5520dfdbd8d 100644 --- a/test/parallel/test-vm-measure-memory-lazy.js +++ b/test/parallel/test-vm-measure-memory-lazy.js @@ -14,24 +14,24 @@ expectExperimentalWarning(); // or otherwise these may not resolve. { vm.measureMemory() - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); globalThis.gc(); } { vm.measureMemory({}) - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); globalThis.gc(); } { vm.measureMemory({ mode: 'summary' }) - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); globalThis.gc(); } { vm.measureMemory({ mode: 'detailed' }) - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); globalThis.gc(); } diff --git a/test/parallel/test-vm-measure-memory.js b/test/parallel/test-vm-measure-memory.js index 75625cb82f5f04..2aa060aa6e5be9 100644 --- a/test/parallel/test-vm-measure-memory.js +++ b/test/parallel/test-vm-measure-memory.js @@ -13,15 +13,15 @@ expectExperimentalWarning(); // Test eager memory measurement { vm.measureMemory({ execution: 'eager' }) - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); if (!common.isWindows) { vm.measureMemory({ mode: 'detailed', execution: 'eager' }) - .then(common.mustCall(assertSingleDetailedShape)); + .then(assertSingleDetailedShape).then(common.mustCall()); } vm.measureMemory({ mode: 'summary', execution: 'eager' }) - .then(common.mustCall(assertSummaryShape)); + .then(assertSummaryShape).then(common.mustCall()); assert.throws(() => vm.measureMemory(null), { code: 'ERR_INVALID_ARG_TYPE' diff --git a/test/parallel/test-vm-module-after-evaluate.js b/test/parallel/test-vm-module-after-evaluate.js index b3cde67a502370..acc5eaf874828b 100644 --- a/test/parallel/test-vm-module-after-evaluate.js +++ b/test/parallel/test-vm-module-after-evaluate.js @@ -38,7 +38,7 @@ const microtaskMode = 'afterEvaluate'; // onto the inner context microtask queue, but it will not be checkpointed, // therefore we never make progress. mustNotCall2(); -})().then(common.mustNotCall()); +})().then(common.mustNotCall('never settling promise expected')); (async () => { const mustNotCall1 = common.mustNotCall(); diff --git a/test/parallel/test-vm-script-after-evaluate.js b/test/parallel/test-vm-script-after-evaluate.js index 2d14619baea1f3..b5629e4978ffdc 100644 --- a/test/parallel/test-vm-script-after-evaluate.js +++ b/test/parallel/test-vm-script-after-evaluate.js @@ -23,7 +23,7 @@ const microtaskMode = 'afterEvaluate'; // the outer context results in the execution flow falling through, unless the // inner context microtask queue is manually drained, which we don't do here. mustNotCall1(); -})().then(common.mustNotCall()); +})().then(common.mustNotCall('never settling promise expected')); (async () => { const mustCall1 = common.mustCall(); diff --git a/test/parallel/test-webstreams-abort-controller.js b/test/parallel/test-webstreams-abort-controller.js index 1468e418c6cb4a..64d38af1683e40 100644 --- a/test/parallel/test-webstreams-abort-controller.js +++ b/test/parallel/test-webstreams-abort-controller.js @@ -135,7 +135,7 @@ function createTestWritableStream(values) { writer.write('a').then(() => { ac.abort(); - }); + }).then(common.mustCall()); } { diff --git a/test/parallel/test-webstreams-finished.js b/test/parallel/test-webstreams-finished.js index 15c08400faa6be..b7bdba329cbcc8 100644 --- a/test/parallel/test-webstreams-finished.js +++ b/test/parallel/test-webstreams-finished.js @@ -64,7 +64,7 @@ const { finished: finishedPromise } = require('stream/promises'); assert.deepStrictEqual(values, ['asd']); } - finishedPromise(rs).then(common.mustSucceed()); + finishedPromise(rs).then(common.mustCall()); test(); } @@ -193,7 +193,7 @@ const { finished: finishedPromise } = require('stream/promises'); } }); - finishedPromise(ws).then(common.mustSucceed(() => { + finishedPromise(ws).then(common.mustCall(() => { assert.strictEqual(str, 'asd'); })); diff --git a/test/parallel/test-whatwg-readablestream.js b/test/parallel/test-whatwg-readablestream.js index 840ce2bcf26825..2fd99d33929023 100644 --- a/test/parallel/test-whatwg-readablestream.js +++ b/test/parallel/test-whatwg-readablestream.js @@ -340,8 +340,8 @@ assert.throws(() => { const read1 = reader.read(); const read2 = reader.read(); - read1.then(common.mustNotCall(), common.mustCall()); - read2.then(common.mustNotCall(), common.mustCall()); + assert.rejects(read1, () => true).then(common.mustCall()); + assert.rejects(read2, () => true).then(common.mustCall()); assert.notStrictEqual(read1, read2); @@ -668,7 +668,7 @@ assert.throws(() => { reader.read().then(common.mustCall(({ value, done }) => { assert.deepStrictEqual(value, buf2); assert(!done); - reader.read().then(common.mustNotCall()); + reader.read().then(common.mustNotCall('never settling promise expected')); delay().then(common.mustCall()); })); })); @@ -1536,9 +1536,9 @@ class Source { }); const [r1, r2] = readableStreamTee(readable, true); r1.getReader().read().then( - common.mustCall(({ value }) => assert.strictEqual(value, 'hello'))); + common.mustCall(({ value }) => { assert.strictEqual(value, 'hello'); })); r2.getReader().read().then( - common.mustCall(({ value }) => assert.strictEqual(value, 'hello'))); + common.mustCall(({ value }) => { assert.strictEqual(value, 'hello'); })); } { @@ -1711,7 +1711,7 @@ class Source { const iterator = stream.values(); let microtaskCompleted = false; - Promise.resolve().then(() => { microtaskCompleted = true; }); + Promise.resolve().then(() => { microtaskCompleted = true; }).then(common.mustCall()); iterator.next().then(common.mustCall(({ done, value }) => { assert.strictEqual(done, false); diff --git a/test/parallel/test-worker-terminate-null-handler.js b/test/parallel/test-worker-terminate-null-handler.js index e546e662655e06..9111587eac454d 100644 --- a/test/parallel/test-worker-terminate-null-handler.js +++ b/test/parallel/test-worker-terminate-null-handler.js @@ -14,8 +14,7 @@ parentPort.postMessage({ hello: 'world' }); process.once('beforeExit', common.mustCall(() => worker.ref())); worker.on('exit', common.mustCall(() => { - worker.terminate().then((res) => assert.strictEqual(res, undefined)); - + worker.terminate().then((res) => assert.strictEqual(res, undefined)).then(common.mustCall()); })); worker.unref(); diff --git a/test/pummel/test-webcrypto-derivebits-pbkdf2.js b/test/pummel/test-webcrypto-derivebits-pbkdf2.js index 5db210b899efea..7cbcafd020edf7 100644 --- a/test/pummel/test-webcrypto-derivebits-pbkdf2.js +++ b/test/pummel/test-webcrypto-derivebits-pbkdf2.js @@ -362,50 +362,40 @@ const kDerivations = { }; async function setupBaseKeys() { - const promises = []; - const baseKeys = {}; const noBits = {}; const noKey = {}; let wrongKey = null; - Object.keys(kPasswords).forEach((size) => { - promises.push( - subtle.importKey( - 'raw', - Buffer.from(kPasswords[size], 'hex'), - { name: 'PBKDF2' }, - false, - ['deriveKey', 'deriveBits']) - .then((key) => baseKeys[size] = key)); - - promises.push( - subtle.importKey( - 'raw', - Buffer.from(kPasswords[size], 'hex'), - { name: 'PBKDF2' }, - false, - ['deriveBits']) - .then((key) => noKey[size] = key)); - - promises.push( - subtle.importKey( - 'raw', - Buffer.from(kPasswords[size], 'hex'), - { name: 'PBKDF2' }, - false, - ['deriveKey']) - .then((key) => noBits[size] = key)); - }); - - promises.push( - subtle.generateKey( - { name: 'ECDH', namedCurve: 'P-521' }, + await Promise.all(Object.keys(kPasswords).flatMap((size) => [ + subtle.importKey( + 'raw', + Buffer.from(kPasswords[size], 'hex'), + { name: 'PBKDF2' }, false, ['deriveKey', 'deriveBits']) - .then((key) => wrongKey = key.privateKey)); + .then((key) => baseKeys[size] = key), - await Promise.all(promises); + subtle.importKey( + 'raw', + Buffer.from(kPasswords[size], 'hex'), + { name: 'PBKDF2' }, + false, + ['deriveBits']) + .then((key) => noKey[size] = key), + + subtle.importKey( + 'raw', + Buffer.from(kPasswords[size], 'hex'), + { name: 'PBKDF2' }, + false, + ['deriveKey']) + .then((key) => noBits[size] = key), + ]).concat(subtle.generateKey( + { name: 'ECDH', namedCurve: 'P-521' }, + false, + ['deriveKey', 'deriveBits']) + .then((key) => wrongKey = key.privateKey))); return { baseKeys, noBits, noKey, wrongKey }; } diff --git a/test/sequential/test-inspector-port-cluster.js b/test/sequential/test-inspector-port-cluster.js index de5484e8c5e784..ae6ae7eb2576e7 100644 --- a/test/sequential/test-inspector-port-cluster.js +++ b/test/sequential/test-inspector-port-cluster.js @@ -201,7 +201,7 @@ function testRunnerMain() { { expectedInitialPort: port + 2 }, ], }); - }); + }).then(common.mustCall()); } function primaryProcessMain() { diff --git a/tools/eslint-rules/must-call-assert.js b/tools/eslint-rules/must-call-assert.js index 702525b28d6470..7a6f417770bae0 100644 --- a/tools/eslint-rules/must-call-assert.js +++ b/tools/eslint-rules/must-call-assert.js @@ -3,7 +3,6 @@ const message = 'Assertions must be wrapped into `common.mustSucceed`, `common.mustCall` or `common.mustCallAtLeast`'; - const requireCall = 'CallExpression[callee.name="require"]'; const assertModuleSpecifier = '/^(node:)?assert(.strict)?$/'; @@ -133,6 +132,92 @@ module.exports = { message: 'Only assign `node:assert` to `assert`', }); }, + + [`CallExpression[callee.property.name="then"][arguments.length=1]>CallExpression:matches(${[ + '[callee.name="mustCall"]', + '[callee.object.name="common"][callee.property.name="mustCall"]', + ].join(',')})[arguments.length=1]>:has(ReturnStatement)`]: (node) => { + context.report({ + node, + message: 'Cannot mix `common.mustCall` and return statement inside a `.then` chain', + }); + }, + + 'ExpressionStatement>CallExpression[callee.property.name="then"]': (node) => { + const { arguments: { length, 0: arg }, callee: { object } } = node; + + if (object.type === 'CallExpression' && + object.callee.type === 'Identifier' && + object.callee.name === 'checkSupportReusePort') { + return; + } + + const defaultMessage = 'Last item of `.then` list must use `mustCall()` ' + + 'or `mustNotCall("expected never settling promise")`'; + + if (length !== 1) { + context.report({ + node, + message: 'Expected exactly one argument for `.then`, consider using ' + + '`assert.rejects` or remove the second argument', + }); + } else if (arg.type !== 'CallExpression') { + context.report({ + node: arg, + message: defaultMessage, + }); + } else { + const message = { + mustCall(args) { + switch (args.length) { + case 0: return false; + case 1: { + const [arg] = args; + if (arg.async || arg.body.type !== 'BlockStatement') + return 'Add an additional `.then(common.mustCall())` to detect if resulting promise settles'; + return false; + } + default: return 'do not use more than one argument'; + } + }, + mustNotCall(args) { + if (args.length === 1 && args[0].type === 'Literal' && args[0].value.includes('never settling')) { + return false; + } + return 'Unexpected `common.mustNotCall`, either add a label mention ' + + '"never settling promise" or use `common.mustCall`'; + }, + }[arg.callee.type === 'MemberExpression' && arg.callee.object.name === 'common' ? + arg.callee.property.name : + arg.callee.name]?.(arg.arguments) ?? defaultMessage; + if (message) { + context.report({ node: arg, message }); + } + } + + node = node.callee.object; + // Walk down .then chain + while ( + node?.type === 'CallExpression' && + node.callee.type === 'MemberExpression' && + node.callee.property.name === 'then' + ) { + const { arguments: { 0: arg } } = node; + if (arg && + arg.type === 'CallExpression' && + (arg.callee.type === 'MemberExpression' && arg.callee.object.name === 'common' ? + arg.callee.property.name : + arg.callee.name + ) === 'mustCall') { + context.report({ + node, + message: 'in a `.then` chain, only use mustCall as the last node', + }); + } + // Go deeper: find next "left" call in chain + node = node.callee.object; + } + }, }; }, };