|
| 1 | +--- |
| 2 | +"@objectstack/objectql": patch |
| 3 | +--- |
| 4 | + |
| 5 | +test(objectql): six in-memory driver doubles conjoin `$or`/`$and` with their sibling filters instead of short-circuiting (part of #7620) |
| 6 | + |
| 7 | +Six test files in `packages/objectql/src` build an in-memory driver whose `WHERE` |
| 8 | +matcher **returned early** on `$and`/`$or`, discarding every sibling equality key |
| 9 | +in the same object: |
| 10 | + |
| 11 | +```ts |
| 12 | +if (Array.isArray(where.$and)) return where.$and.every((w) => matchesWhere(row, w)); |
| 13 | +if (Array.isArray(where.$or)) return where.$or.some((w) => matchesWhere(row, w)); |
| 14 | +for (const [k, v] of Object.entries(where)) { /* siblings, never reached */ } |
| 15 | +``` |
| 16 | + |
| 17 | +A real driver ANDs them. So a query shaped like `SysMetadataRepository.listDrafts`'s — |
| 18 | +`{ state:'draft', package_id:'app.x', $or:[{organization_id:ORG},{organization_id:null}] }` |
| 19 | +— would have been answered on the `$or` alone, handing back rows matching neither |
| 20 | +`state` nor `package_id`. That is not a stricter or looser edge case; it is a |
| 21 | +different query, and the suite stays **green** while testing it. |
| 22 | + |
| 23 | +The corrected form is the one `protocol-revert-org-scope.test.ts` already carries |
| 24 | +from #7619: fold `$and`/`$or` into the entries loop so they compose with their |
| 25 | +siblings rather than replacing them. |
| 26 | + |
| 27 | +Files corrected: `protocol-recorded-by-null.test.ts`, |
| 28 | +`save-meta-response-conformance.test.ts`, `plugin.authoring-channel.test.ts`, |
| 29 | +`publish-meta-response-conformance.test.ts`, |
| 30 | +`protocol-save-meta-repo-path-real-engine.test.ts`, |
| 31 | +`protocol-registry-shadow.test.ts`. |
| 32 | + |
| 33 | +**All six are dormant today — measured, not assumed.** A probe installed in each |
| 34 | +matcher, logging every `where` it was handed across the six suites, recorded |
| 35 | +**132 matcher calls and not one `$or` or `$and`** (44 / 60 / 21 / 7 plain-equality |
| 36 | +calls in four of the files; the matchers in `save-meta-response-conformance` and |
| 37 | +`plugin.authoring-channel` were never invoked at all — those suites drive writes, |
| 38 | +not reads). The control that makes that silence evidence rather than a dead probe |
| 39 | +is the 132 plain calls it did record through the same instrumentation. So no |
| 40 | +existing test outcome changes, and none should: `packages/objectql` is |
| 41 | +**185 files / 3274 tests, all passing**, before and after. |
| 42 | + |
| 43 | +Dormant is not harmless, which is the point of closing it: nothing distinguished |
| 44 | +"this double is faithful here" from "this double quietly changed the fixture", |
| 45 | +and the next test to add an `$or` would have inherited a matcher that lies. |
| 46 | + |
| 47 | +No product code changed, and no test assertion changed. Each matcher keeps |
| 48 | +exactly the operator surface it already had — `$eq` unwrapping, the |
| 49 | +`undefined`→`null` comparison normalisation, and the skip for any other |
| 50 | +`$`-prefixed key — and a non-array `$and`/`$or` still falls through to that skip, |
| 51 | +as before. **Deliberately not extracted into a shared helper**: the six are |
| 52 | +identical, but `publish-meta-response-conformance.test.ts` carries the repo's own |
| 53 | +rationale for keeping these harnesses self-contained ("a gate that imports its own |
| 54 | +substrate from another gate's file couples two tripwires that must be able to fail |
| 55 | +independently"), #7619's reference correction is inline for the same reason, and an |
| 56 | +objectql-local helper could not serve the ten remaining files in `plugin-sharing`, |
| 57 | +`plugin-security` and `runtime` anyway — it would add a second convention rather |
| 58 | +than consolidate to one. |
0 commit comments