From f98020138ab7481c77402f29e086202924e3afec Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 24 Jul 2022 23:11:16 +0200 Subject: [PATCH] test_runner: validate `timeout` option PR-URL: https://github.com/nodejs/node/pull/43843 Reviewed-By: Feng Yu --- lib/internal/test_runner/test.js | 10 ++++++++-- lib/internal/validators.js | 11 ++++++++++- test/parallel/test-runner-option-validation.js | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-runner-option-validation.js diff --git a/lib/internal/test_runner/test.js b/lib/internal/test_runner/test.js index d2be83364a0f6d..fadba74d72da6e 100644 --- a/lib/internal/test_runner/test.js +++ b/lib/internal/test_runner/test.js @@ -31,8 +31,13 @@ const { kEmptyObject, } = require('internal/util'); const { isPromise } = require('internal/util/types'); -const { isUint32, validateAbortSignal } = require('internal/validators'); +const { + isUint32, + validateAbortSignal, + validateNumber, +} = require('internal/validators'); const { setTimeout } = require('timers/promises'); +const { TIMEOUT_MAX } = require('internal/timers'); const { cpus } = require('os'); const { bigint: hrtime } = process.hrtime; const kCallbackAndPromisePresent = 'callbackAndPromisePresent'; @@ -148,7 +153,8 @@ class Test extends AsyncResource { this.concurrency = concurrency; } - if (isUint32(timeout)) { + if (timeout != null && timeout !== Infinity) { + validateNumber(timeout, 'options.timeout', 0, TIMEOUT_MAX); this.timeout = timeout; } diff --git a/lib/internal/validators.js b/lib/internal/validators.js index 3bdd5285a39c31..09c836e7258276 100644 --- a/lib/internal/validators.js +++ b/lib/internal/validators.js @@ -6,6 +6,7 @@ const { ArrayPrototypeJoin, ArrayPrototypeMap, NumberIsInteger, + NumberIsNaN, NumberMAX_SAFE_INTEGER, NumberMIN_SAFE_INTEGER, NumberParseInt, @@ -115,9 +116,17 @@ function validateString(value, name) { throw new ERR_INVALID_ARG_TYPE(name, 'string', value); } -function validateNumber(value, name) { +function validateNumber(value, name, min = undefined, max) { if (typeof value !== 'number') throw new ERR_INVALID_ARG_TYPE(name, 'number', value); + + if ((min != null && value < min) || (max != null && value > max) || + ((min != null || max != null) && NumberIsNaN(value))) { + throw new ERR_OUT_OF_RANGE( + name, + `${min != null ? `>= ${min}` : ''}${min != null && max != null ? ' && ' : ''}${max != null ? `<= ${max}` : ''}`, + value); + } } const validateOneOf = hideStackFrames((value, name, oneOf) => { diff --git a/test/parallel/test-runner-option-validation.js b/test/parallel/test-runner-option-validation.js new file mode 100644 index 00000000000000..a6b7cb1826b166 --- /dev/null +++ b/test/parallel/test-runner-option-validation.js @@ -0,0 +1,15 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const test = require('node:test'); + +[Symbol(), {}, [], () => {}, 1n, true, '1'].forEach((timeout) => { + assert.throws(() => test({ timeout }), { code: 'ERR_INVALID_ARG_TYPE' }); +}); +[-1, -Infinity, NaN, 2 ** 33, Number.MAX_SAFE_INTEGER].forEach((timeout) => { + assert.throws(() => test({ timeout }), { code: 'ERR_OUT_OF_RANGE' }); +}); +[null, undefined, Infinity, 0, 1, 1.1].forEach((timeout) => { + // Valid values should not throw. + test({ timeout }); +});