From 238cc7c73cb3693feb4b8a01e4d53d8ef75cc0ce Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 25 Aug 2026 14:49:22 +0000 Subject: [PATCH] test(plugin-auth): declare IDataEngine on the member-role migration double so its update pin ratchets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `makeMemoryEngine` in `member-role-canonical.test.ts` already bound its `update` to ObjectQL's own dispatch predicate by hand, but spelled only one engine sibling (`find`) — below `check:engine-double-contract`'s inference threshold. So the pin was real protection that no ledger row named: drop the `assertEngineUpdateDispatch` call, or the `update` member itself, and nothing reddens. Declaring the contract admits it through #11626's declaration route. The intersection (`IDataEngine & MemoryEngineHandles`) is what keeps this honest rather than padded — the contract is asserted and `rows`/`calls` stay declared as what they are, test handles read by the assertions. No member was added to satisfy discovery, and the double's behaviour is unchanged: 35/35 tests pass identically before and after. The ledger row is written by the gate's own `--write` (391 rows, 1 added, 0 lost). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6 --- .../src/member-role-canonical.test.ts | 23 ++++++++++++++++--- scripts/engine-double-contract.pinned.json | 5 ++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/plugins/plugin-auth/src/member-role-canonical.test.ts b/packages/plugins/plugin-auth/src/member-role-canonical.test.ts index 736bb7d75e..2d181d8c1d 100644 --- a/packages/plugins/plugin-auth/src/member-role-canonical.test.ts +++ b/packages/plugins/plugin-auth/src/member-role-canonical.test.ts @@ -52,6 +52,7 @@ import { callerCarriesCreatorRole, } from './remove-member-permission-guard.js'; import { BUILTIN_MEMBERSHIP_ROLES } from '@objectstack/spec/identity'; +import type { IDataEngine } from '@objectstack/spec/contracts'; // --------------------------------------------------------------------------- // The vendor's three owner-tests, extracted from the installed package @@ -383,13 +384,29 @@ describe('#8317 — write-path canonicalisation hooks', () => { // The one-off pass // --------------------------------------------------------------------------- +/** The test-only handles this double hangs off the contract it implements. */ +type MemoryEngineHandles = { + rows: Array>; + calls: Array<{ object: string; patch: any; options: any }>; +}; + /** * Memory engine for the migration. `update` is pinned to ObjectQL's own * dispatch predicate (#4550/#5480): a fake looser than the real engine turns a * green suite into no suite at all on exactly the write this pass performs. + * + * It DECLARES `IDataEngine` so `check:engine-double-contract` can see that pin + * and ratchet it (#11626's declaration route). The double spells one engine + * sibling (`find`), which is below the inference threshold, so before the + * declaration the `assertEngineUpdateDispatch` call above was real protection + * that no ledger row named — drop it tomorrow and nothing reddens. The + * intersection is what keeps this honest rather than padded: the contract is + * asserted, and `rows`/`calls` stay declared as what they are, test handles. */ -function makeMemoryEngine(rows: Array>) { - const calls: Array<{ object: string; patch: any; options: any }> = []; +function makeMemoryEngine( + rows: MemoryEngineHandles['rows'], +): IDataEngine & MemoryEngineHandles { + const calls: MemoryEngineHandles['calls'] = []; return { rows, calls, @@ -406,7 +423,7 @@ function makeMemoryEngine(rows: Array>) { Object.assign(row, patch); return { ...row }; }, - }; + } as unknown as IDataEngine & MemoryEngineHandles; } describe('#8317 — the one-off convergent pass', () => { diff --git a/scripts/engine-double-contract.pinned.json b/scripts/engine-double-contract.pinned.json index 1f9541c944..29ddde784e 100644 --- a/scripts/engine-double-contract.pinned.json +++ b/scripts/engine-double-contract.pinned.json @@ -1326,6 +1326,11 @@ "verb": "update", "pinned": 1 }, + { + "file": "packages/plugins/plugin-auth/src/member-role-canonical.test.ts", + "verb": "update", + "pinned": 1 + }, { "file": "packages/plugins/plugin-auth/src/org-create-posture-gate.test.ts", "verb": "delete",