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.
This commit is contained in:
@@ -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':
|
||||
|
||||
Reference in New Issue
Block a user