Skip to content

Commit a11d87c

Browse files
committed
test(plugin-approvals): by-id parity of the two doors, the read a request's record page makes
Claude-Session: https://claude.ai/code/session_013j5gkUCpqQiti4GgPqqmnt Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0522d72 commit a11d87c

1 file changed

Lines changed: 32 additions & 1 deletion

File tree

‎packages/plugins/plugin-approvals/src/request-read-gate.integration.test.ts‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
* on a read that names the record, read-only;
2626
* - control: an administrator, and the approval engine's own reads as the
2727
* system, are unchanged;
28-
* - for every caller, the data door's ids equal the approvals door's.
28+
* - for every caller, the data door's ids equal the approvals door's, by
29+
* list and by id (the by-id read is what a request's record page makes).
2930
*
3031
* ## The rig
3132
*
@@ -201,6 +202,26 @@ async function byId(rig: Rig, context: any, id: string): Promise<{ record?: any;
201202
}
202203
}
203204

205+
/**
206+
* By id, the data door against the approvals door, for every caller and both
207+
* requests: the record read a `sys_approval_request` record page makes (its
208+
* highlights and details) is served exactly when `getRequest` serves the
209+
* request — and a refused one answers as a missing one.
210+
*/
211+
async function expectByIdParity(rig: Rig, callers: any[]): Promise<void> {
212+
for (const ctx of callers) {
213+
for (const id of [rig.ids.requestA, rig.ids.requestB]) {
214+
const served = (await rig.svc.getRequest(id, ctx)) !== null;
215+
const answer = await byId(rig, ctx, id);
216+
expect(answer.record?.id ?? null, `${ctx.userId} ${id}`).toBe(served ? id : null);
217+
if (!served) {
218+
expect({ code: answer.error?.code, status: answer.error?.status }, `${ctx.userId} ${id}`)
219+
.toEqual({ code: 'RECORD_NOT_FOUND', status: 404 });
220+
}
221+
}
222+
}
223+
}
224+
204225
describe('the record-reader tier OFF (the default)', () => {
205226
it('a non-participant who cannot read the record sees no request: list, a record\'s list, count, grouped count', async () => {
206227
const rig = await boot();
@@ -270,6 +291,11 @@ describe('the record-reader tier OFF (the default)', () => {
270291
}
271292
}
272293
});
294+
295+
it('one rule, two doors, by id: a request\'s record page reads it exactly when the approvals door serves it', async () => {
296+
const rig = await boot();
297+
await expectByIdParity(rig, [asUser(OUTSIDER), asUser(READER), asUser(SUBMITTER_A), asUser(SUBMITTER_B), asUser(APPROVER), ADMIN]);
298+
});
273299
});
274300

275301
describe('the record-reader tier ON for the object', () => {
@@ -317,6 +343,11 @@ describe('the record-reader tier ON for the object', () => {
317343
}
318344
}
319345
});
346+
347+
it('one rule, two doors, by id, with the tier on as well', async () => {
348+
const rig = await boot(ON);
349+
await expectByIdParity(rig, [asUser(OUTSIDER), asUser(READER), asUser(SUBMITTER_A), asUser(SUBMITTER_B), asUser(APPROVER), ADMIN]);
350+
});
320351
});
321352

322353
describe('fails closed', () => {

0 commit comments

Comments
 (0)