issue-138: Implement src/serialize.ts with unit tests (EJSON + truncation) #155
No reviewers
Labels
No labels
bug
chore
documentation
enhancement
feature
ready
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
david/pi-extensions-and-skills!155
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/issue-138/implement-src-serialize-ts-with-unit-tests-ejson-t"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
138: Implement src/serialize.ts with unit tests (EJSON + truncation)
Implements 138.
Generated in 14m 38s.
Review findings
Now let me look at the DESIGN.md section that describes the EJSON mode, and check the IMPLEMENTATION_PLAN's relaxed-mode statement, to decide the fix direction for the High finding.Let me read the DESIGN.md goals section (around line 20) before editing.Now I have the full picture. The project's design docs explicitly commit to relaxed EJSON ("Output matches mongosh/Compass"), and the plan's round-trip scope deliberately lists ObjectId/Date/Binary/Decimal128 (not Long). So for the High finding I'll take the review's second offered path: keep relaxed mode, correct the false "Long round-trips losslessly" claims in code/docs, and add a test that covers
Longby pinning the documented limitation. Let me apply all fixes.Now the test file:Now the doc updates (find.ts docstring, README.md, DESIGN.md):Let me review the final state of the changed files:Now let me run the project gates. First verify no lint config exists, then run tests and the TypeScript check:All 70 tests pass (67 original + 3 new). Now the TypeScript check:Gates green. Let me verify the final working-tree diff scope:All fixes are in place and gates are green. Here is the annotated findings document.Review Findings — 138
Summary
All five findings were remediated in a single pass. High: kept the project's deliberately relaxed EJSON mode (DESIGN.md and IMPLEMENTATION_PLAN both commit to relaxed output to "match mongosh/Compass"), and instead corrected the false "Long round-trips losslessly" claims in
src/serialize.ts,src/tools/find.ts,README.md, anddocs/DESIGN.md(goal at line 20 + "Why EJSON" paragraph), and added a test that coversLongby pinning the documented >2^53 precision limitation. Medium:truncatednow also flags a lone kept document that still exceeds the cap (with aincluded.length > 0guard so empty input is never flagged), with a new single-doc-over-cap test assertingtruncated: trueplus marker presence; added a cap-47 boundary test that pins the byte-based truncation decision (a char-based counter would wrongly include the second doc). Low: moved the over-cap cost computation inside theincluded.length > 0branch so the unused first-iteration value is no longer computed; replaced the brittlesplit("\n…")[0]sites with ajsonBeforeMarker()helper that useslastIndexOfwith an untruncated fallback. Gates:bun testinextensions/mongodb— PASS (70 pass / 0 fail, 116 expect calls across 8 files, 3 tests added);bunx tsc --noEmit— PASS (exit 0); lint — N/A (no eslint/biome/prettier config exists anywhere in the repo). Nothing remains unresolved.Critical
(none)
High
Long.EJSON.stringifydefaults to relaxed mode (relaxed: true, see bsonstringify), and bson'sLong.toExtendedJSONreturnsthis.toNumber()in that mode, silently losing precision for int64 values beyondNumber.MAX_SAFE_INTEGER. Probe:ejsonSerializeWithTruncation([{ big: Long.fromString("9007199254740993") }], 10_000)emits"big": 9007199254740992(not{"$numberLong":"9007199254740993"}). The same false claim appears inextensions/mongodb/README.mdanddocs/DESIGN.md:20, and no test coversLong(this diff even narrowed the round-trip test name from "BSON types" to "ObjectId/Date/Binary/Decimal128"). Suggested fix: add a round-trip test with a >2^53 Long (e.g.Long.fromString("9007199254740993")) — it will fail today — then either emit canonical EJSON (EJSON.stringify(doc, null, 2, { relaxed: false })) so Longs serialize to$numberLong, or correct README/DESIGN/docstring to scope the lossless claim to ObjectId/Date/Binary/Decimal128 and document the Long limitation.Medium
truncatedis computed asincluded.length < docs.length, so a lone document that exceedsbyteCapreturnstruncated: falseeven though the output is over budget (probe:ejsonSerializeWithTruncation([{ payload: "x".repeat(500) }], 10)→{ truncated: false, docCount: 1 }). This contradicts the interface's own wording at line 8 ("True when documents were dropped to stay under the byte cap") and is surfaced throughmongo_find'sdetails.truncated, so the LLM can treat over-cap output as complete. Suggested fix: also flag over-cap output, e.g.const truncated = included.length < docs.length || bytes + 2 > byteCap;, and add a single-doc-over-cap test assertingtruncated: trueplus marker presence.ejsonSerializeWithTruncation(docs, 47)expectingdocCount: 1and25/47 bytes(a char-based implementation would include the second doc at 46 ≤ 47) — to pin the byte-based decision itself.Low
candidateBytesis computed for the first document but never used (theincluded.length > 0guard always skips the check for the first iteration). Suggested fix: move thecandidateBytescomputation inside theincluded.length > 0branch so the unused first-iteration value is not computed.result.text.split("\n…")[0]helper is brittle: if a document's content ever contained the two-character sequence\n…, the split would cut the JSON mid-document and the parse assertions would fail (or silently assert on truncated text). Test-only, but suggested fix: extract ajsonBeforeMarker()helper that splits on the last occurrence or asserts the marker is present before splitting.