fix(mock): order NaN below -Infinity and exclude it from range and in filters - #127
Conversation
|
| if (typeof value === 'number' && Number.isNaN(value)) { | ||
| return false; | ||
| } | ||
| const diff = compare(value, where.value); | ||
| switch (where.op) { | ||
| case '<': |
There was a problem hiding this comment.
This change adds special NaN behavior for range, in, and not-in filters, while the related comparator change also alters NaN equality and ordering. The repository guide requires changes to mock semantics to be documented in packages/mock/README.md. That requirement must be satisfied before merging, including the relevant query-validation limitations.
Context Used: AGENTS.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
2945857 to
6ed4c50
Compare
Detail bug report: View on Detail
Closes #108
Bug
compareNumbersin the mock (packages/mock/src/lib/firestore/value.ts) useda < b ? -1 : a > b ? 1 : 0. ForNaNbothNaN < xandNaN > xarefalse, so it returned0for every operand. Sinceequalsiscompare(a,b) === 0, a storedNaNfield became equal to every number — matching any numeric==,!=,<=,>=, andin [..]query, tying with every number inorderBy, dedup-ing every number inarrayUnion/arrayRemove, and skipping NaN↔number transitions in live-stream change detection. Firestore actually normalizesNaN, orders it below-Infinity(equal only toNaN), and excludesNaNfield values from range andinfilters.Fix
value.ts—compareNumbersspecial-casesNaN(equal only toNaN, ordered before every other number, below-Infinity). One change fixes every consumer of the primitive:equals,orderBy,arrayUnion/arrayRemovededup,snapshotEqualsstream change-detection, andTimestamp/GeoPointsub-field ordering.query-filter.ts— a storedNaNfield value is excluded from range filters (<,<=,>,>=) andin, and always included innot-in. This matches the Firestore emulator's actual scan semantics and corrects the bug report's impact table, which assumed range/infilters follow the total order — they don't; onlyorderBydoes. Without this, the report's suggestedcompareNumbers-only fix would still diverge from Firestore for<,<=,in, andnot-in.==/!=(viaequals) andorderBy(viacompare) need no extra cases.Testing
compare/equalsNaN ordering,arrayUnion/arrayRemovededup, query filters across all operators plusorderBy, and a live-stream NaN→number change-detection case. None existed before, so the bug was silent.NaNnumeric field viafirebase-admin, and ran the same 22 queries — every result set matched the mock, including NaN exclusion from range/inand first-ascendingorderByordering.NaNquery operand (e.g.where v < NaN) withinvalid-argument; the mock returns an empty/ordered result instead because it has no query-operand validation path today. Happy-path queries (non-NaN operands, the bug's scenario) are faithful.Automatic Fixes PRs can be configured here.