Skip to content

Commit a28e4a1

Browse files
committed
test(plugin-security): hold the promotion-selection doubles to the shared engine-double contracts
The three doubles the new selection guard introduces are brought up to the contracts the repo's gates enforce, and the pinned ledger is grown so it actually protects this file: - update() routes through assertEngineUpdateDispatch, so a fixture drifting to a call shape ObjectQL.update would refuse fails loudly (check:engine-double-contract) - find() applies the caller's limit AFTER the filter and by presence (check:objectql-double-limit) - the WHERE matchers refuse a $-combinator by name instead of reading it as a field and answering false (check:where-matcher) scripts/engine-double-contract.pinned.json gains the new seams via `--write`; the shrink-only baseline is untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37
1 parent 9c1cc75 commit a28e4a1

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

packages/plugins/plugin-security/src/bootstrap-platform-admin-promotion-selection.test.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
*/
6161

6262
import { describe, it, expect, afterEach, beforeEach } from 'vitest';
63+
import { assertEngineUpdateDispatch } from '@objectstack/metadata-core';
6364
import { ObjectQL } from '@objectstack/objectql';
6465
import { SqlDriver } from '@objectstack/driver-sql';
6566
import { resetPlatformAdminEmailMemo } from '@objectstack/core';
@@ -177,7 +178,12 @@ function withNaturalOrder(engine: ObjectQL, order: NaturalOrder): any {
177178
if (id !== undefined) insertionRank.set(rankKey(object, id), nextRank++);
178179
return result;
179180
},
181+
// The shared engine-double contract (`check:engine-double-contract`): a
182+
// facade whose update() is looser than ObjectQL.update is how a dead code
183+
// path ships with its suite green. Asserted BEFORE delegating, so this
184+
// wrapper can never be the loose link.
180185
async update(object: string, data: any, options: any) {
186+
assertEngineUpdateDispatch(data, options);
181187
return (engine as any).update(object, data, options);
182188
},
183189
};
@@ -412,17 +418,28 @@ describe('#16682 — the promotion target is chosen, not sampled', () => {
412418
grants: () => tables.get('sys_user_permission_set')!,
413419
async find(object: string, q: any) {
414420
const where = q?.where ?? {};
415-
return (tables.get(object) ?? []).filter((r) =>
416-
Object.entries(where).every(([k, v]) => r[k] === v),
421+
const matched = (tables.get(object) ?? []).filter((r) =>
422+
Object.entries(where).every(([k, v]) => {
423+
// Refuse loudly rather than reading a combinator as a field name:
424+
// a matcher that silently answers `false` for `$or` is how a
425+
// double reports a filtered-out row as absent.
426+
if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`);
427+
return r[k] === v;
428+
}),
417429
);
430+
// The caller's bound, applied AFTER the filter and by PRESENCE.
431+
return typeof q?.limit === 'number' ? matched.slice(0, q.limit) : matched;
418432
},
419433
async insert(object: string, data: any) {
420434
(tables.get(object) ?? []).push({ ...data });
421435
return { id: data.id };
422436
},
423-
async update(object: string, data: any) {
424-
const row = (tables.get(object) ?? []).find((r) => r.id === data.id);
437+
async update(object: string, data: any, options?: any) {
438+
const dispatch = assertEngineUpdateDispatch(data, options);
439+
if (dispatch.kind !== 'by-id') return 0;
440+
const row = (tables.get(object) ?? []).find((r) => r.id === dispatch.id);
425441
if (row) Object.assign(row, data);
442+
return row ?? null;
426443
},
427444
};
428445
}
@@ -669,14 +686,28 @@ describe('#16682 — the promotion target is chosen, not sampled', () => {
669686
return {
670687
warns,
671688
async find(object: string, q: any) {
689+
// The caller's bound, applied AFTER the filter and by PRESENCE.
690+
const bound = (rows: any[]) =>
691+
typeof q?.limit === 'number' ? rows.slice(0, q.limit) : rows;
672692
if (object === 'sys_permission_set') {
673693
const where = q?.where ?? {};
674-
return permissionSets.filter((r) =>
675-
Object.entries(where).every(([k, v]) => r[k] === v),
694+
return bound(
695+
permissionSets.filter((r) =>
696+
Object.entries(where).every(([k, v]) => {
697+
// Refuse loudly rather than reading a combinator as a field
698+
// name — a matcher that answers `false` for `$or` reports a
699+
// row it never understood as absent.
700+
if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`);
701+
return r[k] === v;
702+
}),
703+
),
676704
);
677705
}
678706
if (object === 'sys_user') {
679707
const where = q?.where ?? {};
708+
for (const k of Object.keys(where)) {
709+
if (k.startsWith('$')) throw new Error(`fake driver: unsupported operator ${k}`);
710+
}
680711
if (Object.keys(where).length > 0) return [];
681712
const offset = q?.offset ?? 0;
682713
const limit = q?.limit ?? 100;
@@ -697,8 +728,9 @@ describe('#16682 — the promotion target is chosen, not sampled', () => {
697728
if (object === 'sys_permission_set') permissionSets.push({ ...data });
698729
return { id: data.id };
699730
},
700-
async update() {
701-
/* noop */
731+
async update(_object: string, data: any, options?: any) {
732+
assertEngineUpdateDispatch(data, options);
733+
return 0;
702734
},
703735
};
704736
}

scripts/engine-double-contract.pinned.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,6 +2611,11 @@
26112611
"verb": "update",
26122612
"pinned": 1
26132613
},
2614+
{
2615+
"file": "packages/plugins/plugin-security/src/bootstrap-platform-admin-promotion-selection.test.ts",
2616+
"verb": "update",
2617+
"pinned": 3
2618+
},
26142619
{
26152620
"file": "packages/plugins/plugin-security/src/bootstrap-platform-admin-walled-owner.test.ts",
26162621
"verb": "update",

0 commit comments

Comments
 (0)