Repository navigation
bun:test: compare an expected function by identity in toMatchObject and Bun.deepMatch - #38339
Conversation
Bun__deepMatch walked into any pair of objects, and functions are
objects with (usually) no enumerable properties, so a function in the
expected object matched any function in the received one:
expect({ p: () => 1 }).toMatchObject({ p: () => 2 }) passed, as did
Array vs Object and two distinct jest.fn() mocks. Jest compares
function leaves with Object.is.
A callable on the subset side is now compared with SameValue, both for
property values and at the root (Bun.deepMatch(a, b), toMatchObject,
snapshot property matchers, objectContaining). The check is made before
the seen-set bookkeeping so a function reused under several keys is
compared under each of them.
Comparing functions by identity exposed that the property walk included
JSC private symbols: Array.prototype's private builtin methods are
enumerable, so every array pattern would have compared those builtins
and rejected arrays from another realm (node:vm). Private symbols are
now excluded like everywhere else in deepEquals, and the check those
private names were accidentally providing, that an array pattern only
matches an array, is made explicit.
|
Warning Review limit reached
Next review available in: 2 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (6)
Comment |
|
Status: ready for review at a14502c. This PR is now the function step alone. Its base is the branch of #47702. Reproduced on a release build of #47702 (b0f58a7) with The stack, bottom to top: #47736, #44497, #47702, this PR. The step fails tests that pass today, so it waits for a maintainer's decision. The body lists them. CI (build 137119): the tests of this PR pass on each lane that ran. 28 test files are red, and none of them is in this diff. 27 are red on the builds of recently merged PRs too ( |
With FORCE_COLOR=1, which the CI runner sets, the word propertyMatchers in the message is wrapped in ANSI codes, so a substring spanning it does not match.
There was a problem hiding this comment.
Additional findings (outside current diff — PR may have been updated during review):
-
🔴
src/jsc/bindings/bindings.cpp:2069— This guard is correct, but two existing CI tests relied on the old any-function-matches behavior and neither is updated in this PR — Build #95364 is red on both. (1)test/js/node/net/server.spec.ts:83—expect(net.Server.prototype).toMatchObject(EventEmitter.prototype):EventEmitter.prototype.constructoris an own enumerable property (direct assignment at src/js/node/events.ts:100) whose value isEventEmitter, whilenet.Server.prototype.constructorisServer(set by$toClass), so the callable subset prop now falls through tosameValue(Server, EventEmitter)→ false; drop line 83 (line 82 already asserts the prototype chain) or rewrite withexpect.any(Function). (2)vendor/elysia/test/units/replace-schema-type.test.ts— elysia'st.Numeric()etc. embed freshDecode/Encodeclosures under the enumerable public symbol[TransformKind], so two independentt.Numeric()schemas now failtoMatchObject; Bun can't edit elysia's tests, so add a documentedskipTestsentry for this file totest/vendor.json(matching the four existing precedents there).Extended reasoning...
What breaks
The new
!subsetProp.isCallable()guard (and the equivalent root-levelsubsetValue.isCallable()check) is the correct, Jest-aligned behavior. But it changes what "match" means for any subset property whose value is a function: previouslyBun__deepMatchrecursed into the function as an object, found no enumerable properties, and vacuously returnedtrue; now it falls through tosameValueand requires reference identity. Two existing tests in Bun's CI gate depended on the old vacuous-walk behavior, and neither is touched in this PR's changed-files list. CI Build #95364 (robobun comment) shows both failing.REVIEW.md: "When changing output/defaults/messages, grep the suite for assertions on the old behavior and update them in the same PR." The PR body's scan of
test/fortoMatchObjectarguments containing function values missed (1) because the argument is a prototype object rather than a literal with a visible function value, and missed (2) because vendor tests aren't checked intotest/— they're fetched at CI time.(1)
test/js/node/net/server.spec.ts:83expect(net.Server.prototype).toMatchObject(EventEmitter.prototype);
Step-by-step:
src/js/node/events.ts:91setsEventEmitter.prototype = {}(a plain object), and line 100 doesEventEmitterPrototype.constructor = EventEmitter— a direct assignment on a plain object, which creates an own enumerable data property. So whenBun__deepMatchenumerates the subset (EventEmitter.prototype) withDontEnumPropertiesMode::Exclude,constructoris included.src/js/node/net.ts:3532calls$toClass(Server, "Server", EventEmitter), which givesnet.Server.prototypeits own (non-enumerable)constructor=Server, shadowing the inheritedEventEmitter.obj->getIfPropertyExists(globalObject, "constructor")therefore returnsServer.- At the property loop,
subsetProp=EventEmitterandprop=Server. With the new guardsubsetProp.isObject() && !subsetProp.isCallable() && prop.isObject(),!subsetProp.isCallable()isfalse(EventEmitteris callable), so control skips the recursive branch and reachessameValue(Server, EventEmitter)→false→toMatchObjectthrows. - Every other enumerable method on
EventEmitter.prototype(on,emit, …) is inherited by identity onnet.Server.prototypevia__proto__, so those keys stillsameValue-match;constructoris the one shadowed key that breaks. - Before this PR, both
ServerandEventEmitterare objects → recurse → neither has enumerable own properties → vacuously matched → test passed.
CI shows this file failing with code 1 on all 9 platforms, consistent with a deterministic behavior change rather than a flake.
Fix: the new behavior is correct (Jest agrees), so update the test in this PR — either delete line 83 (line 82 already asserts
net.Server.prototype.__proto__ === EventEmitter.prototype, which is the actual invariant), or replace it with an explicit shape check usingexpect.any(Function)for the methods.(2)
vendor/elysia/test/units/replace-schema-type.test.tstest/vendor.jsonpins elysia at1.4.28; the vendor lane fetches its tests at CI time and runs them under Bun. The failing file does e.g.:expect(replaceSchemaType(t.Object({ id: t.Number(), ... }), { from: t.Number(), to: () => t.Numeric() })) .toMatchObject(t.Object({ id: t.Numeric(), ... }))
Step-by-step:
- Elysia's
t.Numeric()(alsot.BooleanString(),t.ObjectString(),t.Date()) is built ast.Transform(...).Decode(fn).Encode(fn)(elysiasrc/type-system/index.ts). TypeBox's.Encode()returns{ ...schema, [TransformKind]: { Decode, Encode } }whereTransformKind = Symbol.for('TypeBox.Transform')— a public registered Symbol assigned via computed-key spread, so it's an own enumerable symbol property. Everyt.Numeric()call therefore produces freshDecode/Encodeclosures. Bun__deepMatchenumerates withPropertyNameMode::StringsAndSymbols, and the newPrivateSymbolMode::Excludedoes not hideTransformKind(it's a public JS Symbol, not a JSC private symbol). So it walks into[TransformKind]on both sides (both plain objects), then reachesDecodewheresubsetPropis a callable.- With the new guard,
Decodeno longer recurses; it falls through tosameValue(receivedDecode, expectedDecode)on two distinct closures →false. - Before this PR both closures were walked (no enumerable props) → vacuously matched → test passed. So this failure is new to this PR, not pre-existing.
Fix: Bun cannot edit elysia's tests. Following the four existing precedents in
test/vendor.json, add askipTestsentry:"units*replace-schema-type.test.ts": "TEMPORARY: elysia 1.4.28 relies on Bun's old any-function-matches-any-function toMatchObject behavior — t.Numeric()/t.BooleanString() embed fresh Decode/Encode closures under [TransformKind], so two independent schemas no longer deep-match now that Bun compares functions by identity (Jest-aligned, oven-sh/bun#<this PR>). Remove once elysia's tests use expect.any(Function) or a shared schema instance."
Why both, in this PR
These are two independent actionable items with the same root cause: (1) is a test-file edit; (2) is a
vendor.jsonaddition. Fixing one does not fix the other, and merging without both leaves CI red.
net/server.spec.ts matched Server.prototype against EventEmitter.prototype, whose enumerable own constructor is EventEmitter while Server.prototype's is Server; compare the inherited members instead. elysia's replace-schema-type tests toMatchObject independently created TypeBox transform schemas, whose Decode/Encode closures now compare by identity; skip that file until it is updated upstream.
|
CI turned up two existing tests that relied on the old behavior; both confirmed locally and handled in 29a7a0a:
The push dismissed the earlier approval; the PR description has been updated with the above. |
There was a problem hiding this comment.
I reviewed this PR and didn't find any bugs (the earlier FORCE_COLOR concern was addressed in d7b4007). Because this is a user-facing behavior change to toMatchObject/Bun.deepMatch semantics that breaks downstream tests — one in-tree test was rewritten and an elysia vendor file is now skipped — a human look would still be worthwhile to confirm the behavior change and the vendor skip are wanted.
What was reviewed:
- The
isCallable()guards at the root and property level fall through tosameValuecorrectly; asymmetric matchers are handled earlier and are not callable, soexpect.any(Function)is unaffected. isArraycalls now have exception checks (they can throw on revoked Proxy); the explicitsubsetIsArray && !objIsArrayrule preserves theexpect({}).toMatchObject([])failure that private symbols were previously enforcing accidentally.PrivateSymbolMode::Excludematches every enumeration inBun__deepEquals; verified the cross-realm array tests pin the behavior it preserves.- The
net/server.spec.tsrewrite still exercises what its name says: spread copies own-enumerable EventEmitter methods, andtoMatchObjectresolves each onServer.prototype's chain to the identical function.
Extended reasoning...
Overview
This PR changes Bun__deepMatch in src/jsc/bindings/bindings.cpp so that a callable value on the subset (pattern) side is compared by SameValue identity instead of being walked as an object with (typically) no enumerable properties. It also switches property enumeration to PrivateSymbolMode::Exclude, adds exception checks after the two isArray calls, and makes the array-subset-needs-array-received rule explicit (previously enforced only as a side effect of private-symbol enumeration). Tests are added to expect.test.js, deep-match.spec.ts, and bun-snapshots.test.ts; net/server.spec.ts is updated because it relied on any-function-matches-any-function; and an elysia vendor test file is skipped in test/vendor.json for the same reason.
Security risks
None. This is test-runner comparison logic; no auth, crypto, filesystem, or network paths are touched. isCallable() does not run user code (Proxy callability is fixed at construction), and the new isArray calls now correctly propagate exceptions from revoked Proxies where the old combined && expression did not.
Level of scrutiny
Medium-high. Bun__deepMatch backs toMatchObject, Bun.deepMatch, snapshot property matchers, and expect.objectContaining — a bug here would affect many test suites silently. The C++ change is small and mechanically correct (I traced the fall-through: when !subsetProp.isCallable() is false, control reaches the else branch which does JSC::sameValue, i.e. identity for two objects). The coupled PrivateSymbolMode and array-rule changes are each individually justified and pinned by cross-realm and {}-vs-[] tests.
Other factors
- This is a behavior change users can observe: two independently-created functions that used to match no longer do. The PR itself demonstrates real breakage (the in-tree
net/server.spec.tsand 8 elysia vendor tests). Aligning with Jest/Vitest is the right call, but shipping a change that requires a vendor-test skip is a decision a maintainer should sign off on rather than an automated approval. - My prior review flagged the FORCE_COLOR/ANSI issue in the snapshot test; the author confirmed and fixed it in d7b4007 by asserting only the uncolored substring. All comment-cop feedback on verbose C++ comments was addressed in 1df2009.
- Test coverage is thorough: identity/non-identity, nested, in-array, native constructors,
jest.fn(), functions with own props, reused-under-multiple-keys, root-level,objectContaining, snapshot property matchers, cross-realm arrays, and the array-like/typed-array negative cases. The PR description documents that the new tests fail on the released binary and pass underBUN_JSC_validateExceptionChecks=1. - The bug-hunting system found no issues on the current revision.
|
This PR stays open. #47702 does not replace it. The plan for the
The function rule stays a separate step because it has a known breakage. It fails 8 elysia tests, which this PR skips in |
…nd Bun.deepMatch Bun__deepMatch walks the enumerable keys of the expected value. A function has none that say which function it is, so each function matched each other function, and each object. The check for the kinds that the key walk cannot read (Date, Error, Set, Map) now runs before the keys are listed, and its last arm refuses an expected function that is not the received value. The keys of an expected function are not read. Under bun:test, toMatchObject gives the matcher error of Jest for a function as the whole expected value. Under the vitest import that value is compared by identity, as in Vitest.
…of toMatchObject, and mark the known differences Under `bun test --aot` an object literal has the type CompiledObject, so toMatchObject asked JSC whether each expected value is callable. It is now on the list of plain types beside FinalObject and Array. Tests: a todo for each pair that Jest and Vitest pass and Bun does not (a method that the expected object inherits, a function that an expected Set, Map or Date holds as a property, two callable Proxies in toEqual), and a comment on the symbol key row of the vitest import.
|
@dylan-conway I disabled the auto-merge that was armed on this PR on 2026-08-14.
Arm it again if this shape is what you want. |
Fixes #15674
Problem
toMatchObject,Bun.deepMatchand snapshot property matchers accept any function or object for an expected function:expect({ cb: f }).toMatchObject({ cb: g })passes. Jest, Vitest andtoEqualfail it.Bun__deepMatch(src/jsc/bindings/bindings.cpp) walks the enumerable keys of the expected value. A function has none that identify it.Fix
subsetSpecialObjectMatchesrefuses an expected function that is not the received value. It runs before the key listing, so those keys are not read.bun:test,toMatchObject(fn)gives Jest's matcher error. Undervitestit compares by identity.Background
subsetSpecialObjectMatchesis the check for values whose keys do not show their contents (Date,Error,Set,Map).isCallable()) and a skip for inherited methods (bun:test: compare only the expected value's own properties in toMatchObject and Bun.deepMatch #46171 owns those keys).Downsides
test/vendor.jsonskips their file.Symbol.iteratormethod, and aSet,MaporDatewith a function property.toMatchObjectcall.Notes
Order
Set,Map), which is on bun:test: compare an expected Date and Error by value in toMatchObject and Bun.deepMatch #44497 (Date,Error) and bun:test: leave private names out of the key walk of toMatchObject and Bun.deepMatch #47736 (private names). When those merge, the base becomesmain.[Function]), so a failure does not mark the function. If the pattern has all keys of the received object, the diff is empty (- Expected - 0/+ Received + 0), as it is fortoEqualon main. bun:test: say when both values print the same, and when printing stopped, instead of an empty diff #37764 puts a sentence there. If the received object has more keys, only those keys are marked. bun:test: diff only the properties the pattern mentions when toMatchObject fails #39109 is the PR for that.toEqual. If this step must ship in the same release, it can be cut on bun:test: leave private names out of the key walk of toMatchObject and Bun.deepMatch #47736 alone with a check of its own.The rule
Set, as a key or a value of aMap, and as the subset ofBun.deepMatch.jest.fn(), a built-in constructor and a callable Proxy.expect.addEqualityTestersstill decides first.{ cb: f }matches{ cb: {} }.toMatchObject: Jest's matcher error underbun:testand@jest/globals, also with.not. Undervitestit is compared by identity.toMatchSnapshot: a mismatch.Rows ("pass" means that
expect(received).toMatchObject(expected)passes. The base is #47702 at b0f58a7. The references areexpect30.5.2 and@vitest/expect4.1.12 on Node 26.3.0.){ cb: f }/{ cb: g }{ cb: {} }/{ cb: f }{ cb: class A {} }/{ cb: class B {} }{ cb: jest.fn() }/{ cb: jest.fn() }new TypeError("x")/{ constructor: RangeError }{ s: new Set([f]) }/{ s: new Set([g]) }handler = () => 1{}/fbun:test, fail undervitest{}/f, with.notbun:test, pass undervitest{ [symbol]: f }/{ [symbol]: g }Symbol.iteratormethodclass M extends Map { h = () => 1 }class E extends Error { h = () => 1 }{ a: 1 }/expect.objectContaining(f){ cb: f }withf.x = 1/{ cb: { x: 1 } }f/{}toEqualbun:test, 48 rows change. Agreement with Jest goes from 42 to 60 rows. 14 rows that agree with Jest on the base fail here.vitestimport, 46 rows change. Agreement with Vitest goes from 44 to 56 rows. 17 rows that agree with Vitest on the base fail here.What differs from the references after this PR
13 of the 91 rows pass on the base, in Jest and in Vitest, and fail here. They are the first three items.
Setof functions on the prototype. Two instances of one class share their methods and match. Methods of aclassdeclaration are not enumerable and are not compared. bun:test: compare only the expected value's own properties in toMatchObject and Bun.deepMatch #46171 changes the walk to own keys.Symbol.iteratormethod (2 rows). Jest and Vitest compare two iterables by what they yield (iterableEquality), before they read the keys. Bun has no rule for an iterable, so it compares the two methods. Bun'stoEqualfails the same pair on main. No issue or PR covers an iterable rule.Set,MaporDateholds as a property (4 rows). Jest and Vitest do not read the keys of those values. Bun reads them after the members, as on main, so it compares the two functions. The same holds for anErroragainst Jest (Vitest fails that pair): two equal Zod 3ZodErrorvalues are an example, because each holds its ownaddIssueclosure.vitestimport (4 rows). Vitest readsObject.keys(expected). Bun reads symbol keys under both imports, as bun:test: compare an expected Map and Set in toMatchObject and Bun.deepMatch #47702 lists. So two objects that each own a function under a symbol key fail under both imports: a TypeBox schema, or an object with its own[Symbol.asyncIterator]().vitest-flavor.test.tsmarks the row as a known difference.expect.objectContaining(fn)still matches each object. Jest and Vitest throwYou must provide an object to ObjectContaining, not 'function'.A function with properties as the sample is a working comparison in Bun today, so this PR does not change it.toMatchObjectand still passtoEqual,toStrictEqualand a key ofexpect.objectContaining.Bun__deepEqualshas its own list of the types of a function, and a callable Proxy is not on it. Make deepEquals and toEqual compare built-in constructors, mocks and callable Proxies by identity #38334 is the PR for that.expect.pollunder thevitestimport,toMatchObject(fn)gives the matcher error ofbun:test.is_vitest()is false inside an attempt (expect_deferred.rs,parent: None). On main,.not.toMatchObject()with a value that is not an object has the same answer there.toMatchSnapshot(fn)is a mismatch under both imports.expect.test.jshas atest.todofor items 1, 2, 3 and 6. Each passes on the two references.Tests that the rule changes
test/js/node/net/server.spec.ts:expect(net.Server.prototype).toMatchObject(EventEmitter.prototype)fails with the rule.constructoris enumerable onEventEmitter.prototypein Bun and not in Node.js, andnet.Server.prototypehas its own. The test now matches the methods withoutconstructor. The original assertion stays asit.failingwith a link to node: $toClass installs the prototype of built-in constructors; EventEmitter reads it with $getPrototypeOf #46197, which makesconstructornot enumerable. node: $toClass installs the prototype of built-in constructors; EventEmitter reads it with $getPrototypeOf #46197 conflicts with this branch in 12 files, so it is not stacked below.expect.test.js: the pin of bun:test: compare an expected Map and Set in toMatchObject and Bun.deepMatch #47702 for a function in aSetchanges to.not.deep-match.spec.ts: "does not work on functions" is the pin that the reporter ofBun.deepMatchalways returns true when comparing functions #15674 wrote. It is now "a function matches only itself" and holds the reproduction of the issue.test/units/replace-schema-type.test.ts: 39 pass on the base, 31 pass and 8 fail here. The 8 tests compare two TypeBox schemas.t.Numeric()and similar types make newDecodeandEncodeclosures on each call, under theSymbol.for("TypeBox.Transform")key. With Jest'sexpectas the library the same 8 tests fail. With@vitest/expectall 39 pass. The file is the same on elysia'smainbranch.expect.any(Function)in the expected schemas is the change that the upstream tests need.Measurements (release builds for linux x64 in one build directory. The base is the tree of #47702 at b0f58a7, and this PR is a14502c.)
llvm-size: text 140,296,431 to 140,296,687 (+256). data and bss do not change. The file grows by one page (140,482,848 to 140,486,944).llvm-nm -S:Bun__deepMatch<true>3,346 to 3,331 bytes,Bun__deepMatch<false>2,251 to 2,277,subsetSpecialObjectMatches6,256 to 6,380, thetoMatchObjectclosure 1,133 to 1,204. BothsubsetValueMatchescopies do not change (1,184 and 700).Android arm64: 132,896,904 bytes for the stripped binary on both builds, so +0 (
binary-sizestep of CI, build 137119 against build 137030 of bun:test: compare an expected Map and Set in toMatchObject and Bun.deepMatch #47702). The step passes. The Linux, FreeBSD and macOS x64 binaries do not change either. macOS arm64 grows by one page (16,512 bytes), Windows arm64 grows by 3,584 bytes and Windows x64 shrinks by 512 bytes.Instructions for each call (user space,
perf_event_open, JIT off, a loop of 20,000 calls minus an empty run, the lowest of three runs, without--aot):toMatchObject, 4 key object, 2 key patterntoMatchObject, 5 key object, the same 5 keystoMatchObject, 3 levels of plain objects and an arraytoMatchObject, array of 10 objectstoMatchObject, two instances of a classtoMatchObject, pattern with the same function under a keytoMatchObject, pattern with an equalDateunder a keyBun.deepMatch({}, {})Bun.deepMatch, 5 key object, the same 5 keysBun.deepMatch, 3 levels and an arrayexpect.objectContaining, 4 key objectBun.deepMatch({ cb: g }, { cb: f })Bun.deepMatch(g, f)Bun.deepMatch(new Date(2), new Date(1))Bun.deepMatch({ s: new Set([1, 2]) }, { s: new Set([1]) })The spread of the three runs is 3 or less, except for the array of 10 objects (43 on the base, 34 here).
The 6 to 8 instructions of a plain
toMatchObjectcall are the type test intoMatchObject.rs: 6 instructions with 2 conditional branches in the disassembly (llvm-objdump). It has three plain types:FinalObject,ArrayandCompiledObject, the type of an object literal underbun test --aot.Calls of
JSC__JSValue__isCallablefromtoMatchObject(gdb hit counts over 1,000 calls): 0 to 0 for a plain object or an array as the expected value, also under--aot. 0 to 1 for aDateor another value that is not a plain object.is_in_vitest_testruns only if the expected value is a function.Under
--aotthe instruction counts of one call differ by several hundred between runs, so there is no table for that mode. There, the check of bun:test: compare an expected Date and Error by value in toMatchObject and Bun.deepMatch #44497 already callssubsetSpecialObjectMatchesfor each object literal of the pattern on the base (1,000 calls for 1,000 patterns, gdb). This PR adds oneisCallable()to that call.Key lists (one 184 byte allocation each, gdb hit counts at the call site over 1,000 calls):
Bun.deepMatch(g, f)1 to 0 for each call, aDatepair that fails 1 to 0, a plain pair 1 to 1.Proxy traps on an expected callable Proxy, for one compare: 4 to 0 (
ownKeys, two ofgetOwnPropertyDescriptor,getPrototypeOf). This holds for the subset ofBun.deepMatch, under a key and as a member of aSet. A trap that throws no longer turns the mismatch into an error.Tests
expect.test.js6,deep-match.spec.ts10,bun-snapshots.test.ts1,server.spec.ts1,vitest-apis.test.ts1,vitest-flavor.test.ts1. A debug build and a release build of this PR pass all of them.toMatchObjecttests ofexpect.test.jsand all ofdeep-match.spec.tspass withBUN_JSC_validateExceptionChecks=1on the debug build.if (isBun), the todo tests too, pass onexpect30.5.2. Those thatexpect.test.jsdoes not skip for Vitest pass on@vitest/expect4.1.12.toMatchObject,Bun.deepMatchor snapshot property matchers. Each ran on a release build of the first commit of this step (d8bb63d). 292 ran tests and exit 0. 21 run no test on this machine. 33 exit non-zero, and each of those ran on the base build too: 31 have the same counts there (tests that need the network, a database or a WebKit checkout). The other two arefetch.test.ts(11 failures here, 14 on the base) andnapi.test.ts. Its one failure is "behaves like node on edge cases", a compare of two strings, and it also fails on the base build in 3 of 12 more runs. So no file outside this PR changes its result.What changed from the earlier shape of this PR (29a7a0a)
isCallable()checks in the walk (one for each call ofBun__deepMatch, one for each pair) are gone. The rule is one line in the cold arm.expect.objectContaining(). Now that sample keeps its answer..not.toMatchObject(fn)underbun:testis a matcher error. In the earlier shape it passed.test/vendor.jsonsays that Jest fails the elysia tests and Vitest passes them.Self-review
20 concerns raised, 15 addressed. What the review changed:
Set,MaporDateholds) and item 6 (callable Proxies) are new. Items 1, 2, 3 and 6 each have atest.todo.vitestimport, and the comment on its symbol key row.CompiledObjectis a plain type intoMatchObject.rs, so a plain pattern makes noisCallablecall under--aot.Not taken:
@vitest/expecton generated values. That is a new kind of test for this directory and needs its own PR. The todo tests hold the rows that differ.vitestimport. It changes the walk for each symbol key, so it is a step of its own.toEqualfor callable Proxies) is not refreshed here. It reachesBun.deepEqualsandnode:assert.