Repository: denoland/deno
Pull request: fix(ext/node): fix validation of input in setMaxListeners

This PR updates the input validation of `setMaxListeners` API in `node:event`, enables `parallel/test-event-emitter-max-listeners.js` test case.

towards #29595 

Changed files:
filename: ext/node/polyfills/_events.mjs
patch: @@ -42,6 +42,7 @@ import {
   validateAbortSignal,
   validateBoolean,
   validateFunction,
+  validateNumber,
   validateString,
 } from "ext:deno_node/internal/validators.mjs";
 import { spliceOne } from "ext:deno_node/_utils.ts";
@@ -123,13 +124,7 @@ Object.defineProperty(EventEmitter, "defaultMaxListeners", {
     return defaultMaxListeners;
   },
   set: function (arg) {
-    if (typeof arg !== "number" || arg < 0 || Number.isNaN(arg)) {
-      throw new ERR_OUT_OF_RANGE(
-        "defaultMaxListeners",
-        "a non-negative number",
-        arg,
-      );
-    }
+    validateNumber(arg, "defaultMaxListeners", 0);
     defaultMaxListeners = arg;
   },
 });
@@ -159,9 +154,7 @@ export function setMaxListeners(
   n = defaultMaxListeners,
   ...eventTargets
 ) {
-  if (typeof n !== "number" || n < 0 || Number.isNaN(n)) {
-    throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n);
-  }
+  validateNumber(n, "setMaxListeners", 0);
   if (eventTargets.length === 0) {
     defaultMaxListeners = n;
   } else {
@@ -253,9 +246,7 @@ function emitUnhandledRejectionOrErr(ee, err, type, args) {
  * @returns {EventEmitter}
  */
 EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
-  if (typeof n !== "number" || n < 0 || Number.isNaN(n)) {
-    throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n);
-  }
+  validateNumber(n, "setMaxListeners", 0);
   this._maxListeners = n;
   return this;
 };

filename: tests/node_compat/config.jsonc
patch: @@ -72,7 +72,6 @@
       "test-dsa-fips-invalid-key.js",
       "test-error-aggregateTwoErrors.js",
       "test-error-prepare-stack-trace.js",
-      "test-event-emitter-max-listeners.js",
       "test-event-emitter-no-error-provided-to-error-event.js",
       "test-event-emitter-prepend.js",
       "test-events-on-async-iterator.js",

filename: tests/node_compat/test/parallel/test-event-emitter-max-listeners.js
patch: @@ -2,8 +2,8 @@
 // deno-lint-ignore-file
 
 // Copyright Joyent and Node contributors. All rights reserved. MIT license.
-// Taken from Node 16.13.0
-// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
+// Taken from Node 23.9.0
+// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually.
 
 // Copyright Joyent, Inc. and other Node contributors.
 //
@@ -30,24 +30,22 @@
 const common = require('../common');
 const assert = require('assert');
 const events = require('events');
-const { inspect } = require('util');
 const e = new events.EventEmitter();
 
 e.on('maxListeners', common.mustCall());
 
 // Should not corrupt the 'maxListeners' queue.
 e.setMaxListeners(42);
 
-const throwsObjs = [NaN, -1, 'and even this'];
+const rangeErrorObjs = [NaN, -1];
+const typeErrorObj = 'and even this';
 
-for (const obj of throwsObjs) {
+for (const obj of rangeErrorObjs) {
   assert.throws(
     () => e.setMaxListeners(obj),
     {
       code: 'ERR_OUT_OF_RANGE',
       name: 'RangeError',
-      message: 'The value of "n" is out of range. ' +
-               `It must be a non-negative number. Received ${inspect(obj)}`
     }
   );
 
@@ -56,25 +54,42 @@ for (const obj of throwsObjs) {
     {
       code: 'ERR_OUT_OF_RANGE',
       name: 'RangeError',
-      message: 'The value of "defaultMaxListeners" is out of range. ' +
-               `It must be a non-negative number. Received ${inspect(obj)}`
     }
   );
 }
 
+assert.throws(
+  () => e.setMaxListeners(typeErrorObj),
+  {
+    code: 'ERR_INVALID_ARG_TYPE',
+    name: 'TypeError',
+  }
+);
+
+assert.throws(
+  () => events.defaultMaxListeners = typeErrorObj,
+  {
+    code: 'ERR_INVALID_ARG_TYPE',
+    name: 'TypeError',
+  }
+);
+
 e.emit('maxListeners');
 
 {
   const { EventEmitter, defaultMaxListeners } = events;
-  for (const obj of throwsObjs) {
+  for (const obj of rangeErrorObjs) {
     assert.throws(() => EventEmitter.setMaxListeners(obj), {
       code: 'ERR_OUT_OF_RANGE',
     });
   }
 
-  // FIXME(bartlomieju):
-  // assert.throws(
-  //   () => EventEmitter.setMaxListeners(defaultMaxListeners, 'INVALID_EMITTER'),
-  //   { code: 'ERR_INVALID_ARG_TYPE' }
-  // );
+  assert.throws(() => EventEmitter.setMaxListeners(typeErrorObj), {
+    code: 'ERR_INVALID_ARG_TYPE',
+  });
+
+  assert.throws(
+    () => EventEmitter.setMaxListeners(defaultMaxListeners, 'INVALID_EMITTER'),
+    { code: 'ERR_INVALID_ARG_TYPE' }
+  );
 }