Repository: denoland/deno
Pull request: fix(ext/node): fix multiple deepEqual comparison issues

## Summary

Fixes `test-assert-deep.js` compat test from #32706.

- **Invalid dates**: Fix `NaN !== NaN` comparison by using `NumberIsNaN` for `Date.getTime()` results, so two invalid dates with the same properties are considered deeply equal
- **Date type check order**: Check `isDate(val2)` before calling `DatePrototypeGetTime(val2)` to prevent `TypeError` on non-Date objects with `[Symbol.toStringTag] = 'Date'`
- **Loose array comparison**: Allow `null` and `undefined` to be loosely equal in arrays by deferring to `innerDeepEqual` in loose mode instead of short-circuiting
- **Set comparison**: Fix `setObjectEquiv` to properly reject non-matching primitives in strict mode (was incorrectly continuing instead of returning false)
- **Map comparison**: Fix `mapObjectEquiv` with same pattern — check `b.has(key1)` before skipping primitive keys
- **Promise comparison**: Two different Promise instances are now only equal by reference, not structurally

### Enabled tests
- `test-assert-deep.js` (all 52 subtests pass)

## Test plan
- [x] `test-assert-deep.js` passes via `./x test-compat`
- [ ] CI passes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Changed files:
filename: ext/node/polyfills/internal/util/comparisons.ts
patch: @@ -16,6 +16,7 @@ import {
   isKeyObject,
   isMap,
   isNumberObject,
+  isPromise,
   isRegExp,
   isSet,
   isStringObject,
@@ -59,6 +60,7 @@ const {
   Int8Array,
   Map,
   Number,
+  NumberIsNaN,
   NumberPrototypeValueOf,
   Object,
   ObjectGetOwnPropertyDescriptor,
@@ -343,10 +345,12 @@ function objectComparisonStart(
   } else if (val1Tag === "[object Object]") {
     return keyCheck(val1, val2, mode, memos, valueType.noIterator);
   } else if (isDate(val1)) {
-    if (
-      !isDate(val2) ||
-      DatePrototypeGetTime(val1) !== DatePrototypeGetTime(val2)
-    ) {
+    if (!isDate(val2)) {
+      return false;
+    }
+    const time1 = DatePrototypeGetTime(val1);
+    const time2 = DatePrototypeGetTime(val2);
+    if (time1 !== time2 && !(NumberIsNaN(time1) && NumberIsNaN(time2))) {
       return false;
     }
   } else if (isRegExp(val1)) {
@@ -472,6 +476,9 @@ function objectComparisonStart(
     }
   } else if (isWeakMap(val1) || isWeakSet(val1)) {
     return false;
+  } else if (isPromise(val1) && isPromise(val2)) {
+    // Native Promises can only be equal by reference.
+    return false;
   }
 
   return keyCheck(val1, val2, mode, memos, valueType.noIterator);
@@ -810,8 +817,10 @@ function setObjectEquiv(
         if (b.has(val1)) {
           continue;
         }
-      } else if (mode !== kLoose || b.has(val1)) {
+      } else if (b.has(val1)) {
         continue;
+      } else if (mode !== kLoose) {
+        return false;
       }
     }
 
@@ -1030,13 +1039,12 @@ function mapObjectEquiv(
   const extraChecks = mode === kLoose || array.length !== a.size;
 
   for (const { 0: key1, 1: item1 } of a) {
-    if (
-      extraChecks &&
-      (typeof key1 !== "object" || key1 === null) &&
-      (mode !== kLoose ||
-        (b.has(key1) && innerDeepEqual(item1, b.get(key1), mode, memo)))
-    ) { // Mixed mode
-      continue;
+    if (extraChecks && (typeof key1 !== "object" || key1 === null)) {
+      if (b.has(key1) && innerDeepEqual(item1, b.get(key1), mode, memo)) {
+        continue;
+      } else if (mode !== kLoose) {
+        return false;
+      }
     }
 
     let innerStart = start;
@@ -1272,11 +1280,16 @@ function objEquiv(
         if (!hasOwn(b, i)) {
           return sparseArrayEquiv(a, b, mode, memos, i);
         }
-        if (a[i] !== undefined || !hasOwn(a, i)) {
+        if (mode !== kLoose && (a[i] !== undefined || !hasOwn(a, i))) {
+          return false;
+        } else if (
+          mode === kLoose && !innerDeepEqual(a[i], b[i], mode, memos)
+        ) {
           return false;
         }
       } else if (
-        a[i] === undefined || !innerDeepEqual(a[i], b[i], mode, memos)
+        (mode !== kLoose && a[i] === undefined) ||
+        !innerDeepEqual(a[i], b[i], mode, memos)
       ) {
         return false;
       }

filename: tests/node_compat/config.jsonc
patch: @@ -86,7 +86,7 @@
     "parallel/test-assert-class-destructuring.js": {},
     "parallel/test-assert-class.js": {},
     "parallel/test-assert-deep-with-error.js": {},
-    // "parallel/test-assert-deep.js": {}, // https://github.com/denoland/deno/issues/32706
+    "parallel/test-assert-deep.js": {},
     "parallel/test-assert-esm-cjs-message-verify.js": {},
     "parallel/test-assert-fail-deprecation.js": {},
     "parallel/test-assert-fail.js": {},