Skip to content

Commit 8f5595f

Browse files
committed
test: record-change and http-conformance fixtures follow the engine's refusal; type a mock
The record-change org-probe pin asserts the absent organization object is quiet by construction; the conformance stack registers the authz resolver's read set (unprovisioned) that its stubbed auth service never did; the engine.test expand mock types its parameter (test-typecheck ledger). Claude-Session: https://claude.ai/code/session_01DDZNkDVwPQnevTFcYE47H3 Co-authored-by: Claude <noreply@anthropic.com>
1 parent ddcfe20 commit 8f5595f

3 files changed

Lines changed: 85 additions & 2 deletions

File tree

‎packages/objectql/src/engine.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2067,7 +2067,7 @@ describe('ObjectQL Engine', () => {
20672067
// (an in-process verb refuses a name it does not hold), so `task` is
20682068
// registered; the REFERENCED object is not, and expand leaves the
20692069
// raw id without a second driver read.
2070-
vi.mocked(SchemaRegistry.getObject).mockImplementation((name) => (name === 'task'
2070+
vi.mocked(SchemaRegistry.getObject).mockImplementation((name: string) => (name === 'task'
20712071
? { name: 'task', fields: { assignee: { type: 'lookup', reference: 'user' } } } as any
20722072
: undefined));
20732073

‎packages/qa/http-conformance/src/conformance.integration.test.ts‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,82 @@ const ADAPTERS: AdapterCase[] = [
4444
* parity) need the routes to exist. Pass false to exercise the
4545
* capability-absent posture: no mounts, shared 404.
4646
*/
47+
/**
48+
* [#21516] The objects core's authz resolver (`resolveUserAuthzGrants`) reads on
49+
* every request. A deployment's auth and security plugins register them; this
50+
* stack stubs the `auth` service instead, so they are spelled here with only the
51+
* columns that resolver reads (no dependency edge onto either package, the
52+
* precedent `trigger-record-change`'s integration fixture set) and registered
53+
* AFTER boot, so no schema sync provisions them: the resolver reads missing
54+
* tables and answers "no grants", exactly as it did when the engine still
55+
* handed an unregistered name to the driver (which it now refuses).
56+
*/
57+
const authzResolverObjects = [
58+
{
59+
owner: '@objectstack/plugin-auth',
60+
def: {
61+
name: 'sys_user',
62+
label: 'User',
63+
fields: {
64+
id: { name: 'id', type: 'text' as const, primaryKey: true },
65+
email: { name: 'email', type: 'text' as const },
66+
},
67+
},
68+
},
69+
{
70+
owner: '@objectstack/plugin-auth',
71+
def: {
72+
name: 'sys_member',
73+
label: 'Member',
74+
fields: {
75+
id: { name: 'id', type: 'text' as const, primaryKey: true },
76+
user_id: { name: 'user_id', type: 'text' as const },
77+
organization_id: { name: 'organization_id', type: 'text' as const },
78+
role: { name: 'role', type: 'text' as const },
79+
},
80+
},
81+
},
82+
{
83+
owner: '@objectstack/plugin-security',
84+
def: {
85+
name: 'sys_user_position',
86+
label: 'User Position',
87+
fields: {
88+
id: { name: 'id', type: 'text' as const, primaryKey: true },
89+
user_id: { name: 'user_id', type: 'text' as const },
90+
position: { name: 'position', type: 'text' as const },
91+
organization_id: { name: 'organization_id', type: 'text' as const },
92+
},
93+
},
94+
},
95+
{
96+
owner: '@objectstack/plugin-security',
97+
def: {
98+
name: 'sys_user_permission_set',
99+
label: 'User Permission Set',
100+
fields: {
101+
id: { name: 'id', type: 'text' as const, primaryKey: true },
102+
user_id: { name: 'user_id', type: 'text' as const },
103+
permission_set_id: { name: 'permission_set_id', type: 'text' as const },
104+
organization_id: { name: 'organization_id', type: 'text' as const },
105+
},
106+
},
107+
},
108+
{
109+
owner: '@objectstack/plugin-security',
110+
def: {
111+
name: 'sys_position',
112+
label: 'Position',
113+
fields: {
114+
id: { name: 'id', type: 'text' as const, primaryKey: true },
115+
name: { name: 'name', type: 'text' as const },
116+
active: { name: 'active', type: 'boolean' as const },
117+
organization_id: { name: 'organization_id', type: 'text' as const },
118+
},
119+
},
120+
},
121+
] as const;
122+
47123
async function bootStack(makePlugin: () => any, opts: { withAnalytics?: boolean } = {}) {
48124
const kernel = new LiteKernel();
49125
kernel.use(new ObjectQLPlugin());
@@ -97,6 +173,7 @@ async function bootStack(makePlugin: () => any, opts: { withAnalytics?: boolean
97173
// insert fails with `no such table`, which the REST error mapper turns into
98174
// a 404 OBJECT_NOT_FOUND — a routing-shaped symptom for a DDL-shaped cause.
99175
await ql.syncObjectSchema('task');
176+
for (const o of authzResolverObjects) ql.registry.registerObject(o.def as never, o.owner);
100177

101178
const httpServer = kernel.getService<IHttpServer>('http.server');
102179
return { kernel, base: `http://127.0.0.1:${httpServer.getPort!()}` };

‎packages/triggers/trigger-record-change/src/record-change-integration.test.ts‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
104104
* real console, and COUNTS what it withheld so `afterAll` can assert the
105105
* expected reads still happen. A capture nobody asserts is a mute.
106106
*/
107+
// [#21516] The engine now refuses a name its registry does not hold before any driver,
108+
// so the org probe asks the registry and never reads an unregistered organization
109+
// object: this read no longer happens, and the pin below asserts exactly that.
107110
const EXPECTED_ABSENT_PROBE_TABLES = ['sys_organization'] as const;
108111

109112
/**
@@ -206,7 +209,10 @@ const noise = captureExpectedReadRefusals([...EXPECTED_ABSENT_PROBE_TABLES]);
206209
* assertion exists to make loud.
207210
*/
208211
afterAll(() => {
209-
expect(noise.silentChannels()).toEqual([]);
212+
// [#21516] Quiet by construction now: the declared refusal no longer occurs. The
213+
// capture stays declared (a returning read is still withheld and counted) and
214+
// this asserts nothing was — so a read that starts happening again turns red.
215+
expect(noise.tablesSeen()).toEqual([]);
210216
});
211217

212218
/**

0 commit comments

Comments
 (0)