Skip to content

Commit 58b68fe

Browse files
committed
test(runtime): the unpackaged-action pins' ledger double holds the caller's bound and is drivable by the double gates
The table double now lives in a module-scope factory holding only the store's find and insert, applies `limit` after the filter, and names its table as a literal, so check:objectql-double-limit grades it; it carries no update, so check:engine-double-contract's pinned ledger is unchanged. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VoSxBQujKLZKPwK2u5ehQ6
1 parent 157a4dc commit 58b68fe

1 file changed

Lines changed: 43 additions & 39 deletions

File tree

‎packages/runtime/src/domains/action-activation-unpackaged.test.ts‎

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@
3232
// already does that for the packaged case.
3333

3434
import { describe, it, expect } from 'vitest';
35-
import {
36-
ActionActivationProjection,
37-
ObjectStoreActionActivationStore,
38-
assertEngineUpdateDispatch,
39-
} from '@objectstack/objectql';
35+
import { ActionActivationProjection, ObjectStoreActionActivationStore } from '@objectstack/objectql';
4036

4137
import { HttpDispatcher } from '../http-dispatcher.js';
4238
import type { HttpProtocolContext } from '../http-dispatcher.js';
@@ -80,60 +76,69 @@ function matches(row: Row, where: Row | undefined): boolean {
8076
});
8177
}
8278

79+
/**
80+
* The in-memory `sys_metadata_activation` the durable store writes into: the
81+
* store's keyed `find` and its `insert`, and nothing else. At module scope, on
82+
* its own, so `pnpm check:objectql-double-limit` can lift and drive the `find`.
83+
*
84+
* `writeError` is thrown from every write — a store or record-validation
85+
* failure arriving through the door's own `catch`.
86+
*
87+
* ⛔ No `update`: every case here writes a FIRST row, which the store inserts.
88+
* The store's update leg for a package-less row is core's to pin
89+
* (`metadata-activation-store.test.ts`, "UPDATES its row with no package on the
90+
* way back on"); a second update double here would pin nothing that one does not.
91+
*/
92+
function makeLedgerTable(writeError?: Error) {
93+
const rows: Row[] = [];
94+
let nextId = 1;
95+
const engine = {
96+
async find(object: string, options?: any) {
97+
const found = object === 'sys_metadata_activation' ? rows.filter((r) => matches(r, options?.where)) : [];
98+
// The caller's bound, AFTER the filter, by presence: the store's
99+
// table probe reads `limit: 1`.
100+
return typeof options?.limit === 'number' ? found.slice(0, options.limit) : found;
101+
},
102+
async insert(object: string, data: Row) {
103+
if (writeError) throw writeError;
104+
const row = { id: `act_${nextId++}`, ...data };
105+
if (object === LEDGER) rows.push(row);
106+
return row;
107+
},
108+
};
109+
return { rows, engine };
110+
}
111+
83112
interface Harness {
84113
dispatcher: HttpDispatcher;
85114
rows: Row[];
86-
executeAction: () => Promise<unknown>;
87115
executed: () => number;
88116
}
89117

90118
/**
91119
* One deployment: the route object is a code artifact shipped by `crm`, the
92120
* engine carries the REAL action activation projection over the REAL durable
93-
* store, and that store writes into an in-memory `sys_metadata_activation`.
94-
*
95-
* `writeError` is thrown from every ledger write — a store or record-validation
96-
* failure arriving through the door's own `catch`.
121+
* store, and that store writes into {@link makeLedgerTable}.
97122
*/
98123
function boot(opts: { writeError?: Error } = {}): Harness {
99-
const rows: Row[] = [];
124+
const { rows, engine: ledger } = makeLedgerTable(opts.writeError);
100125
let executed = 0;
101-
let nextId = 1;
102126
const objectDef = {
103127
name: OBJECT, _packageId: PACKAGE, _provenance: 'package', actions: [PACKAGED, EMBEDDED_UNSTAMPED],
104128
};
105129
const projection = new ActionActivationProjection();
106-
const executeAction = async () => {
107-
executed++;
108-
return { ran: 'script' };
109-
};
110130
const ql: any = {
111-
executeAction,
131+
...ledger,
132+
executeAction: async () => {
133+
executed++;
134+
return { ran: 'script' };
135+
},
112136
getSchema: (name: string) => (name === OBJECT ? objectDef : undefined),
113137
registry: {
114138
getObject: (name: string) => (name === OBJECT ? objectDef : undefined),
115139
getItem: (type: string, name: string) =>
116140
(type === 'action' ? STANDALONE.find((a) => a.name === name) : undefined),
117141
},
118-
async find(object: string, options?: any) {
119-
return object === LEDGER ? rows.filter((r) => matches(r, options?.where)) : [];
120-
},
121-
async insert(object: string, data: Row) {
122-
if (opts.writeError) throw opts.writeError;
123-
const row = { id: `act_${nextId++}`, ...data };
124-
if (object === LEDGER) rows.push(row);
125-
return row;
126-
},
127-
async update(object: string, data: Row, options?: any) {
128-
// The real engine's dispatch: the store updates by id, and nothing
129-
// looser may pass here (`check:engine-double-contract`).
130-
const dispatch = assertEngineUpdateDispatch(data, options);
131-
if (dispatch.kind !== 'by-id') throw new Error('fake engine: only by-id updates are modelled');
132-
if (opts.writeError) throw opts.writeError;
133-
const row = rows.find((r) => r.id === dispatch.id);
134-
if (object === LEDGER && row) Object.assign(row, data);
135-
return row;
136-
},
137142
isActionEnabled: (name: string) => projection.isEnabled(name),
138143
describeDisabledAction: (name: string) => projection.describeDisabled(name),
139144
setActionActive: (row: any) => projection.setActive(row),
@@ -154,7 +159,7 @@ function boot(opts: { writeError?: Error } = {}): Harness {
154159
getServiceAsync: async (name: string) => resolve(name),
155160
context: { getService: resolve },
156161
};
157-
return { dispatcher: new HttpDispatcher(kernel as never), rows, executeAction, executed: () => executed };
162+
return { dispatcher: new HttpDispatcher(kernel as never), rows, executed: () => executed };
158163
}
159164

160165
/** A single-tenant administrator holding `manage_metadata`: every authority gate passes. */
@@ -189,10 +194,9 @@ describe('#22817 — an action no package ships is switched, and its row names n
189194
]);
190195
});
191196

192-
it('…and back on: the same row is UPDATED, still with no package', async () => {
197+
it('…and switching it ON first writes the same package-less row, armed', async () => {
193198
const h = boot();
194199

195-
await flip(h, 'global', ENV_GLOBAL.name, false);
196200
const res = await flip(h, 'global', ENV_GLOBAL.name, true);
197201

198202
expect(statusOf(res), bodyOf(res)).toBe(200);

0 commit comments

Comments
 (0)