Repository: denoland/deno
Pull request: fix(node/assert): deepStrictEqual now correctly handles Number objects

Fixes #31172

## Description

The `deepStrictEqual` function was using `asserts.equal()` which doesn't properly handle boxed primitives like Number objects. Changed it to use `isDeepStrictEqual()` from `comparisons.ts` which correctly handles Number, String, Boolean, BigInt, and Symbol objects.

## Changes

- Modified `ext/node/polyfills/assert.ts`:
  - Added import for `isDeepStrictEqual` from `ext:deno_node/internal/util/comparisons.ts`
  - Updated `deepStrictEqual` function to use `isDeepStrictEqual()` instead of `asserts.equal()`

- Added test cases in `tests/unit_node/assert_test.ts`:
  - Test that `deepStrictEqual` throws `AssertionError` for different Number objects
  - Test that `deepStrictEqual` passes for equal Number objects

## Testing

Added unit tests to verify:
- `deepStrictEqual` throws `AssertionError` for different Number objects (e.g., `new Number(1)` vs `new Number(2)`)
- `deepStrictEqual` passes for equal Number objects (e.g., `new Number(1)` vs `new Number(1)`)

## Related Issue

#31172 - `assert.deepStrictEqual` does not throw exception for Number objects

Changed files:
filename: ext/node/polyfills/assert.ts
patch: @@ -16,7 +16,10 @@ import {
   ERR_INVALID_RETURN_VALUE,
   ERR_MISSING_ARGS,
 } from "ext:deno_node/internal/errors.ts";
-import { isDeepEqual } from "ext:deno_node/internal/util/comparisons.ts";
+import {
+  isDeepEqual,
+  isDeepStrictEqual,
+} from "ext:deno_node/internal/util/comparisons.ts";
 import { primordials } from "ext:core/mod.js";
 import { CallTracker } from "ext:deno_node/internal/assert/calltracker.js";
 import { deprecate } from "node:util";
@@ -394,7 +397,7 @@ function deepStrictEqual(
     throw new ERR_MISSING_ARGS("actual", "expected");
   }
 
-  if (!asserts.equal(actual, expected)) {
+  if (!isDeepStrictEqual(actual, expected)) {
     throw new AssertionError({
       message,
       actual,

filename: ext/node/polyfills/internal/util/comparisons.ts
patch: @@ -27,6 +27,12 @@ import {
   ONLY_ENUMERABLE,
   SKIP_SYMBOLS,
 } from "ext:deno_node/internal_binding/util.ts";
+import { primordials } from "ext:core/mod.js";
+
+const {
+  ObjectPrototypeHasOwnProperty,
+  ObjectPrototypePropertyIsEnumerable,
+} = primordials;
 
 enum valueType {
   noIterator,
@@ -265,7 +271,7 @@ function keyCheck(
   // Cheap key test
   let i = 0;
   for (; i < aKeys.length; i++) {
-    if (!val2.propertyIsEnumerable(aKeys[i])) {
+    if (!ObjectPrototypePropertyIsEnumerable(val2, aKeys[i])) {
       return false;
     }
   }
@@ -276,14 +282,14 @@ function keyCheck(
       let count = 0;
       for (i = 0; i < symbolKeysA.length; i++) {
         const key = symbolKeysA[i];
-        if (val1.propertyIsEnumerable(key)) {
-          if (!val2.propertyIsEnumerable(key)) {
+        if (ObjectPrototypePropertyIsEnumerable(val1, key)) {
+          if (!ObjectPrototypePropertyIsEnumerable(val2, key)) {
             return false;
           }
           // added toString here
           aKeys.push(key.toString());
           count++;
-        } else if (val2.propertyIsEnumerable(key)) {
+        } else if (ObjectPrototypePropertyIsEnumerable(val2, key)) {
           return false;
         }
       }
@@ -436,7 +442,9 @@ function isEqualBoxedPrimitive(a: any, b: any): boolean {
 }
 
 function getEnumerables(val: any, keys: any) {
-  return keys.filter((key: string) => val.propertyIsEnumerable(key));
+  return keys.filter((key: string) =>
+    ObjectPrototypePropertyIsEnumerable(val, key)
+  );
 }
 
 function objEquiv(
@@ -459,21 +467,21 @@ function objEquiv(
     }
   } else if (iterationType === valueType.isArray) {
     for (; i < obj1.length; i++) {
-      if (obj1.hasOwnProperty(i)) {
+      if (ObjectPrototypeHasOwnProperty(obj1, i)) {
         if (
-          !obj2.hasOwnProperty(i) ||
+          !ObjectPrototypeHasOwnProperty(obj2, i) ||
           !innerDeepEqual(obj1[i], obj2[i], strict, memos)
         ) {
           return false;
         }
-      } else if (obj2.hasOwnProperty(i)) {
+      } else if (ObjectPrototypeHasOwnProperty(obj2, i)) {
         return false;
       } else {
         const keys1 = Object.keys(obj1);
         for (; i < keys1.length; i++) {
           const key = keys1[i];
           if (
-            !obj2.hasOwnProperty(key) ||
+            !ObjectPrototypeHasOwnProperty(obj2, key) ||
             !innerDeepEqual(obj1[key], obj2[key], strict, memos)
           ) {
             return false;

filename: tests/unit_node/assert_test.ts
patch: @@ -45,3 +45,20 @@ Deno.test("[node/assert] error message from strictEqual should be the same as As
     { message },
   );
 });
+
+Deno.test("[node/assert] deepStrictEqual throws for different Number objects", () => {
+  // Test case from issue #31172
+  assert.throws(
+    () => {
+      assert.deepStrictEqual(new Number(1), new Number(2));
+    },
+    assert.AssertionError,
+  );
+});
+
+Deno.test("[node/assert] deepStrictEqual passes for equal Number objects", () => {
+  // Equal Number objects should pass
+  assert.doesNotThrow(() => {
+    assert.deepStrictEqual(new Number(1), new Number(1));
+  });
+});