From 6d5c860e11a79ae4873ca20e0d58d4ae9c1b9ce8 Mon Sep 17 00:00:00 2001 From: "A.Shakhmatov" Date: Sun, 9 Aug 2026 13:25:28 +0300 Subject: [PATCH] tests/spec: an event's command is compared in the shape it was sent A command-monitoring event hands over the command as the driver holds it in memory, and that is not always the shape it puts on the wire: a sort is a JS `Map` (driver lib/sort.js). `Object.keys` on a Map is empty, so the matcher reported every key of an expected sort as missing from a command that in fact carried it -- five cases, all of them the runner's fault and none the engine's. This one is worth the paragraph because of how well it hides. EJSON serializes a Map exactly like a document, so `MFDB_DUMP_EVENTS` prints `"sort":{"_id":1}` next to a failure that says `sort._id` is missing, and the dump -- the tool built for exactly this triage in the commit that added the buffers -- reads as evidence that the matcher is wrong about something else. It took `Object.keys(formatSort({_id: 1}))` returning `[]` to see it. Converted for the comparison only, and at every depth, since a sort also appears inside `updates[i]`. `match` stays a plain reading of the spec's Evaluating Matches with no driver knowledge in it. Mutation-checked: pass the event's own value through and findOne.json "FindOne with filter, sort, and skip" goes red again with the original message. 189/102/196 becomes 194/97/196. --- tests/spec/run.js | 25 +++++++++++++++++++++++-- tests/spec/scorecard.txt | 17 ++++++----------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/tests/spec/run.js b/tests/spec/run.js index b54fa26..406267a 100644 --- a/tests/spec/run.js +++ b/tests/spec/run.js @@ -669,6 +669,27 @@ function verifyEvents(expectEvents, entities) { } } +// A monitored event hands over the command as the driver holds it in memory, +// which is not always the shape it puts on the wire: a sort is a JS `Map` +// (driver lib/sort.js). `Object.keys` on a Map is empty, so the matcher +// reported every key of an expected sort as missing from a command that in +// fact carried it. EJSON serializes a Map exactly like a document, which is +// why a dump of the event looks perfectly correct and this had to be measured +// rather than read. Converted only for the comparison, and at every depth, +// since a sort also appears inside `updates[i]`. +function wireShape(v) { + if (v instanceof Map) { + const out = {}; + for (const [k, x] of v) out[k] = wireShape(x); + return out; + } + if (Array.isArray(v)) return v.map(wireShape); + if (!isPlainDoc(v)) return v; + const out = {}; + for (const [k, x] of Object.entries(v)) out[k] = wireShape(x); + return out; +} + function matchEvent(expected, actual, entities, pathStr) { const [name, body] = Object.entries(expected)[0]; if (!(name in COMMAND_EVENTS)) throw new Unsupported(`event ${name}`); @@ -690,10 +711,10 @@ function matchEvent(expected, actual, entities, pathStr) { if (isPlainDoc(v) && Object.prototype.hasOwnProperty.call(v, 'maxTimeMS')) { throw new Unsupported('maxTimeMS in an expected command (CSOT rewrites it)'); } - match(v, actual.ev[k], entities, `${pathStr}.${k}`, true); + match(v, wireShape(actual.ev[k]), entities, `${pathStr}.${k}`, true); break; case 'reply': - match(v, actual.ev[k], entities, `${pathStr}.${k}`, true); + match(v, wireShape(actual.ev[k]), entities, `${pathStr}.${k}`, true); break; case 'commandName': case 'databaseName': diff --git a/tests/spec/scorecard.txt b/tests/spec/scorecard.txt index 6d1e8a6..e690acb 100644 --- a/tests/spec/scorecard.txt +++ b/tests/spec/scorecard.txt @@ -18,7 +18,7 @@ # hasServerConnectionId, and maxTimeMS in an expected command (CSOT rewrites # it -- the only assertion this runner declines to make). -total 189 pass 102 fail 196 skip 175 files 0 errored +total 194 pass 97 fail 196 skip 175 files 0 errored # per-file: name pass fail skip aggregate-allowdiskuse.json 3 0 0 @@ -48,7 +48,7 @@ bulkWrite-replaceOne-dots_and_dollars.json 2 1 1 bulkWrite-replaceOne-hint-unacknowledged.json 2 0 0 bulkWrite-replaceOne-let.json 0 1 1 bulkWrite-replaceOne-rawdata.json 1 0 1 -bulkWrite-replaceOne-sort.json 0 1 1 +bulkWrite-replaceOne-sort.json 1 0 1 bulkWrite-update-hint.json 3 0 0 bulkWrite-update-validation.json 3 0 0 bulkWrite-updateMany-dots_and_dollars.json 0 0 4 @@ -61,7 +61,7 @@ bulkWrite-updateOne-hint-unacknowledged.json 2 0 0 bulkWrite-updateOne-let.json 0 1 1 bulkWrite-updateOne-pipeline.json 0 1 0 bulkWrite-updateOne-rawdata.json 0 1 1 -bulkWrite-updateOne-sort.json 0 1 1 +bulkWrite-updateOne-sort.json 1 0 1 bulkWrite.json 10 0 0 bypassDocumentValidation.json 4 5 0 client-bulkWrite-delete-options.json 0 0 2 @@ -122,7 +122,7 @@ find-comment.json 1 2 2 find-let.json 0 1 1 find-rawdata.json 1 0 1 find.json 5 0 0 -findOne.json 1 1 0 +findOne.json 2 0 0 findOneAndDelete-collation.json 0 1 0 findOneAndDelete-comment.json 2 0 1 findOneAndDelete-hint-serverError.json 0 0 2 @@ -169,7 +169,7 @@ replaceOne-hint-unacknowledged.json 2 0 0 replaceOne-hint.json 2 0 0 replaceOne-let.json 0 1 1 replaceOne-rawdata.json 1 0 1 -replaceOne-sort.json 0 1 1 +replaceOne-sort.json 1 0 1 replaceOne-validation.json 1 0 0 replaceOne.json 5 0 0 updateMany-arrayFilters.json 0 3 0 @@ -193,7 +193,7 @@ updateOne-hint.json 2 0 0 updateOne-let.json 0 1 1 updateOne-pipeline.json 0 1 0 updateOne-rawdata.json 1 0 1 -updateOne-sort.json 0 1 1 +updateOne-sort.json 1 0 1 updateOne-validation.json 1 0 0 updateOne.json 4 0 0 @@ -242,7 +242,6 @@ bulkWrite-replaceOne-let.json SKIP BulkWrite replaceOne with let option needs se bulkWrite-replaceOne-let.json FAIL BulkWrite replaceOne with let option unsupported (server-side error) bulkWrite: expected an error, the operation succeeded bulkWrite-replaceOne-rawdata.json SKIP BulkWrite replaceOne with rawData option needs server >= 8.2.0 bulkWrite-replaceOne-sort.json SKIP BulkWrite replaceOne with sort option needs server >= 8.0 -bulkWrite-replaceOne-sort.json FAIL BulkWrite replaceOne with sort option unsupported (server-side error) events client0[0].command.updates[0].sort._id: missing from actual bulkWrite-updateMany-dots_and_dollars.json SKIP Updating document to set top-level dollar-prefixed key on 5.0+ server needs server >= 5.0 bulkWrite-updateMany-dots_and_dollars.json SKIP Updating document to set top-level dotted key on 5.0+ server needs server >= 5.0 bulkWrite-updateMany-dots_and_dollars.json SKIP Updating document to set dollar-prefixed key in embedded doc on 5.0+ server needs server >= 5.0 @@ -262,7 +261,6 @@ bulkWrite-updateOne-pipeline.json FAIL UpdateOne in bulk write using pipelines M bulkWrite-updateOne-rawdata.json SKIP BulkWrite updateOne with rawData option needs server >= 8.2.0 bulkWrite-updateOne-rawdata.json FAIL BulkWrite updateOne with rawData option on less than 8.2.0 - ignore argument MongoBulkWriteError: update spec requires u bulkWrite-updateOne-sort.json SKIP BulkWrite updateOne with sort option needs server >= 8.0 -bulkWrite-updateOne-sort.json FAIL BulkWrite updateOne with sort option unsupported (server-side error) events client0[0].command.updates[0].sort._id: missing from actual bypassDocumentValidation.json FAIL Aggregate with $out passes bypassDocumentValidation: false MongoServerError: Unrecognized pipeline stage name: '$out' bypassDocumentValidation.json FAIL BulkWrite passes bypassDocumentValidation: false events client0[0].command.bypassDocumentValidation: missing from actual bypassDocumentValidation.json FAIL FindOneAndReplace passes bypassDocumentValidation: false events client0[0].command.bypassDocumentValidation: missing from actual @@ -351,7 +349,6 @@ find-comment.json SKIP find with comment does not set comment on getMore - pre 4 find-let.json SKIP Find with let option needs server >= 5.0 find-let.json FAIL Find with let option unsupported (server-side error) find: expected an error, the operation succeeded find-rawdata.json SKIP Find with rawData option needs server >= 8.2.0 -findOne.json FAIL FindOne with filter, sort, and skip events client0[0].command.sort._id: missing from actual findOneAndDelete-collation.json FAIL FindOneAndDelete when one document matches with collation findOneAndDelete: expected a document, got null findOneAndDelete-comment.json SKIP findOneAndDelete with comment - pre 4.4 needs server <= 4.2.99 findOneAndDelete-hint-serverError.json SKIP * needs server <= 4.3.3 @@ -416,7 +413,6 @@ replaceOne-let.json SKIP ReplaceOne with let option needs server >= 5.0 replaceOne-let.json FAIL ReplaceOne with let option unsupported (server-side error) replaceOne: expected an error, the operation succeeded replaceOne-rawdata.json SKIP ReplaceOne with rawData option needs server >= 8.2.0 replaceOne-sort.json SKIP ReplaceOne with sort option needs server >= 8.0 -replaceOne-sort.json FAIL replaceOne with sort option unsupported (server-side error) events client0[0].command.updates[0].sort._id: missing from actual updateMany-arrayFilters.json FAIL UpdateMany when no documents match arrayFilters updateMany.modifiedCount: expected 0, got 2 updateMany-arrayFilters.json FAIL UpdateMany when one document matches arrayFilters updateMany.modifiedCount: expected 1, got 2 updateMany-arrayFilters.json FAIL UpdateMany when multiple documents match arrayFilters outcome crud-v1.coll[0].y: expected an array, got {"$[i]":{"b":2}} @@ -447,4 +443,3 @@ updateOne-let.json FAIL UpdateOne with let option unsupported (server-side error updateOne-pipeline.json FAIL UpdateOne using pipelines MongoServerError: update spec requires u updateOne-rawdata.json SKIP UpdateOne with rawData option needs server >= 8.2.0 updateOne-sort.json SKIP UpdateOne with sort option needs server >= 8.0 -updateOne-sort.json FAIL updateOne with sort option unsupported (server-side error) events client0[0].command.updates[0].sort._id: missing from actual