M3's gate as named -- "remaining crud coverage; e2e3/e2e4 green" -- cannot
see this work. e2e3 and e2e4 contain zero positional paths, and the pinned
crud corpus covers `$[<identifier>]` only: no `$[]` case, no bare `$` case
anywhere in it. Both stayed green through a bug that replaced an array with
`{"$[i]": {...}}` and answered ok: 1. So M3 brings its own corpus, built the
way M2.5's was: inputs authored in `sources/`, every expectation recorded
from mongod 8.3.7, run through the shared runner with `--suite-dir`.
51 cases across the three spellings. It stands at 15 pass / 36 fail against
the refusal, which is the intended shape -- `expressions.json` was recorded
at 1 pass / 26 fail before the evaluator and is green now. The 15 that pass
are refusals where this server's code already matches; of the 36, 31 are the
constructs answering "not implemented" and 5 are refusals whose code
differs, four of them arrayFilters validation this server cannot do because
it never parses the option.
Every case records its `outcome`, refusals included. That is deliberate and
it is the whole point of the file: a refusal that left the document mangled
is indistinguishable from a clean one in `expectError` alone, and a mangled
document is what this corpus exists to catch.
What recording it settled, none of it guessable, the first contradicting
what the design review assumed:
- `y.$[i].c.$[i].d`, one identifier reused at two levels, is **accepted**
-- not a duplicate-identifier error
- `$[]` over an empty array is a no-op with modifiedCount 0
- `$[]` over an array with a non-document element is error 28, where every
other path failure here is 2
- a positional segment never creates: a missing or non-array path is an
error, where `$set: {'a.b': 1}` would construct one
- an upsert gets no special case -- it fails for the same reason
- `$` writes only the first matching element, and is refused when the query
never touched the array
- any of the three in first position is refused, as is `$` twice in a path
- `arrayFilters` alongside a replacement is ignored rather than refused
A case needing its own documents reseeds through operations rather than
`initialData`, because the format's `initialData` is per file and the runner
seeds it once per test. That keeps one file per operator instead of one file
per document shape.
crud scorecard unchanged at 204/87, aggregation corpus still 70/0.
119 lines
3.4 KiB
JSON
119 lines
3.4 KiB
JSON
{
|
|
"_comment": [
|
|
"Inputs only. Expectations are measured -- see record.js.",
|
|
"`$[]`, the all-positional operator. The pinned crud corpus contains no",
|
|
"`$[]` case at all, which is half of why this directory exists: before",
|
|
"the refusal landed, `$[]` overwrote the array it was meant to walk and",
|
|
"nothing external would have noticed."
|
|
],
|
|
"documents": [
|
|
{ "_id": 1, "y": [{ "b": 3 }, { "b": 1 }] },
|
|
{ "_id": 2, "y": [] },
|
|
{ "_id": 3, "y": [{ "c": [{ "d": 1 }, { "d": 2 }] }, { "c": [{ "d": 3 }] }] }
|
|
],
|
|
"cases": [
|
|
{
|
|
"description": "every element of the array is written",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 1 },
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "nested $[] is a cross-product over both levels",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 3 },
|
|
"update": { "$set": { "y.$[].c.$[].d": 0 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] over an empty array",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 2 },
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$inc through $[] adds to every element",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 1 },
|
|
"update": { "$inc": { "y.$[].b": 10 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] as the leaf over an array of scalars",
|
|
"documents": [{ "_id": 1, "y": [1, 2, 3] }],
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": {},
|
|
"update": { "$set": { "y.$[]": 0 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$unset through $[] removes a field from every element",
|
|
"documents": [{ "_id": 1, "y": [{ "b": 1, "e": 1 }, { "b": 2, "e": 2 }] }],
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": {},
|
|
"update": { "$unset": { "y.$[].e": "" } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] where an element is not a document",
|
|
"documents": [{ "_id": 1, "y": [{ "b": 1 }, 7] }],
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": {},
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "updateMany with $[] across every document",
|
|
"operation": "updateMany",
|
|
"arguments": {
|
|
"filter": { "_id": { "$in": [1, 2] } },
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] where the path does not exist",
|
|
"documents": [{ "_id": 1, "z": 1 }],
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": {},
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] where the path is not an array",
|
|
"documents": [{ "_id": 1, "y": 5 }],
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": {},
|
|
"update": { "$set": { "y.$[].b": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] in first position is refused",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 1 },
|
|
"update": { "$set": { "$[]": 9 } }
|
|
}
|
|
},
|
|
{
|
|
"description": "$[] combined with an identifier in one path",
|
|
"operation": "updateOne",
|
|
"arguments": {
|
|
"filter": { "_id": 3 },
|
|
"update": { "$set": { "y.$[].c.$[j].d": 0 } },
|
|
"arrayFilters": [{ "j.d": 2 }]
|
|
}
|
|
}
|
|
]
|
|
}
|