Skip to content

Commit 54589ee

Browse files
os-zhuangclaude
andauthored
test(qa): assertArmed — a fixture proves the control under test is engaged before it measures anything (#8074) (#8407)
* test(qa): assertArmed — fixtures prove the control under test is engaged (#8074) * test(qa): retrofit assertArmed into the five patch-and-trust fixtures (#8074) --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent e9cb9ab commit 54589ee

6 files changed

Lines changed: 855 additions & 0 deletions
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
//
3+
// [#8074] The arming guard is SHOWN TO FIRE — in both directions, on real boots.
4+
//
5+
// This file exists because of what it guards. `assertArmed` is a mechanism for
6+
// catching assertions that cannot fail; a version of it that could not itself
7+
// fail would be the defect wearing the fix's clothes, and it would be invisible
8+
// in exactly the same way (green, quiet, reported as tested).
9+
//
10+
// So every probe is measured twice, and the disarmed halves are not synthetic:
11+
//
12+
// principalArmed DISARMED on an org-LESS boot — a live reproduction of
13+
// #8023, where a fresh sign-up holds `['everyone']` and the
14+
// `org_member`-gated write floor never applies
15+
// ARMED on `bootStack(..., { orgContext: true })`
16+
// authSettingArmed DISARMED on the default auth config — a live reproduction
17+
// of #8049, where `passwordHistoryCount` is 0/undefined and
18+
// the reuse control has nothing to reject against
19+
// ARMED after the `applyConfigPatch` the fixture uses
20+
// seededArmed DISARMED on a permission set that does not exist
21+
// ARMED on `member_default`, which does
22+
// armedWhen DISARMED / ARMED on both sides of its own predicate
23+
//
24+
// The `member_default` case is not decoration: it is the control that proves
25+
// the seeded-row probe CAN find something, so its negative reading is a real
26+
// negative rather than a probe that never matches anything.
27+
//
28+
// Two positive controls guard the "it throws" cases from the cheapest possible
29+
// fake — an `assertArmed` that always threw would satisfy every rejection
30+
// assertion here, so an all-armed declaration is asserted to RESOLVE.
31+
//
32+
// Boots two stacks, uses custom boot options and mutates auth config, so it
33+
// stays out of `SHARED_SHOWCASE` (see `vitest.config.ts`).
34+
35+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
36+
import showcaseStack from '@objectstack/example-showcase';
37+
import { bootStack, type VerifyStack } from '@objectstack/verify';
38+
import {
39+
assertArmed,
40+
armedWhen,
41+
authSettingArmed,
42+
principalArmed,
43+
resolveAuthzFor,
44+
seededArmed,
45+
type ArmingProbe,
46+
} from './armed.js';
47+
48+
/** The control #8023's fixture measures, named the way that card names it. */
49+
const WRITE_FLOOR = 'the wildcard row-level write floor (`owner_only_writes`)';
50+
const WRITE_FLOOR_DISARM =
51+
'an org-less boot: no organization ⇒ no `sys_member` row ⇒ the principal never holds ' +
52+
'`org_member` ⇒ the positions-gated floor never applies. Boot with `orgContext: true`.';
53+
54+
/** The control #8049's fixture measures. */
55+
const REUSE_CONTROL = "ADR-0069 D1's password-reuse rejection";
56+
const REUSE_DISARM =
57+
'`passwordHistoryCount` defaults to 0 (off): no history is recorded, so a reuse assertion ' +
58+
'has nothing to reject against. Arm it through `applyConfigPatch`.';
59+
60+
describe('[#8074] assertArmed: the guard against assertions that cannot fail', () => {
61+
/** The org-LESS stack — #8023's and #8049's disarmed shapes, booted for real. */
62+
let orgless: VerifyStack;
63+
/** The org-BOUND stack — the same probe, armed. */
64+
let orgbound: VerifyStack;
65+
66+
let orglessMember: string;
67+
let orgboundMember: string;
68+
69+
beforeAll(async () => {
70+
orgless = await bootStack(showcaseStack, {});
71+
orgbound = await bootStack(showcaseStack, { orgContext: true });
72+
73+
// The first user is the seeded dev admin; a fresh sign-up is the plain
74+
// member #8023's fixture measures.
75+
await orgless.signIn();
76+
await orgbound.signIn();
77+
orglessMember = await orgless.signUp('armed-orgless@verify.test');
78+
orgboundMember = await orgbound.signUp('armed-orgbound@verify.test');
79+
}, 300_000);
80+
81+
afterAll(async () => {
82+
await orgless?.stop?.();
83+
await orgbound?.stop?.();
84+
});
85+
86+
// ── the fixture's own integrity ───────────────────────────────────────────
87+
//
88+
// Every reading below is worthless if the two stacks are not actually the two
89+
// shapes they are named for, so that is asserted before anything is measured.
90+
91+
it('[integrity] the two stacks really are the org-less and org-bound shapes', async () => {
92+
const less = await resolveAuthzFor(orgless, orglessMember);
93+
const bound = await resolveAuthzFor(orgbound, orgboundMember);
94+
expect(less.userId, 'the org-less member resolved as a real principal').toBeTruthy();
95+
expect(bound.userId, 'the org-bound member resolved as a real principal').toBeTruthy();
96+
expect(less.positions, 'org-less: a fresh sign-up holds only the everyone anchor')
97+
.not.toContain('org_member');
98+
expect(bound.positions, 'org-bound: the membership reconciler bound the sign-up')
99+
.toContain('org_member');
100+
});
101+
102+
// ── principalArmed — instance 1, both directions ──────────────────────────
103+
104+
describe('principalArmed reproduces #8023 and reports it', () => {
105+
const probeOn = (stack: VerifyStack, token: string): ArmingProbe =>
106+
principalArmed({
107+
stack,
108+
token,
109+
who: 'the plain member',
110+
positions: ['org_member'],
111+
control: WRITE_FLOOR,
112+
disarmedBy: WRITE_FLOOR_DISARM,
113+
});
114+
115+
it('reports DISARMED on an org-less boot, and names what it saw', async () => {
116+
const verdict = await probeOn(orgless, orglessMember).read();
117+
expect(verdict.armed, 'org-less: the floor is outside this principal’s domain').toBe(false);
118+
expect(verdict.observed).toContain('missing positions');
119+
expect(verdict.observed).toContain('org_member');
120+
});
121+
122+
it('reports ARMED on `orgContext: true` — the same probe, the other direction', async () => {
123+
const verdict = await probeOn(orgbound, orgboundMember).read();
124+
expect(verdict.armed, 'org-bound: the principal is inside the floor’s domain').toBe(true);
125+
expect(verdict.observed).toContain('org_member');
126+
});
127+
128+
it('assertArmed REJECTS the disarmed stack, naming the control and the default', async () => {
129+
await expect(assertArmed([probeOn(orgless, orglessMember)])).rejects.toThrow(
130+
/this fixture is DISARMED/,
131+
);
132+
const err = await assertArmed([probeOn(orgless, orglessMember)]).catch((e: Error) => e);
133+
expect(String(err)).toContain(WRITE_FLOOR);
134+
// The remedy, not just the symptom — the sentence the next author needs.
135+
expect(String(err)).toContain('orgContext: true');
136+
});
137+
138+
it('[positive control] assertArmed RESOLVES on the armed stack', async () => {
139+
// Without this, every rejection assertion above would be satisfied by an
140+
// `assertArmed` that simply always threw.
141+
await expect(assertArmed([probeOn(orgbound, orgboundMember)])).resolves.toBeUndefined();
142+
});
143+
144+
it('a principal that resolves to nothing is DISARMED, not skipped', async () => {
145+
const verdict = await principalArmed({
146+
stack: orgbound,
147+
token: 'not-a-token-8074',
148+
who: 'a bogus credential',
149+
positions: ['org_member'],
150+
control: WRITE_FLOOR,
151+
disarmedBy: WRITE_FLOOR_DISARM,
152+
}).read();
153+
expect(verdict.armed).toBe(false);
154+
expect(verdict.observed).toContain('missing positions');
155+
});
156+
});
157+
158+
// ── authSettingArmed — instance 2, both directions ────────────────────────
159+
160+
describe('authSettingArmed reproduces #8049 and reports it', () => {
161+
const probe = (stack: VerifyStack): ArmingProbe =>
162+
authSettingArmed({
163+
stack,
164+
setting: 'passwordHistoryCount',
165+
armed: (v) => Number(v) >= 1,
166+
control: REUSE_CONTROL,
167+
disarmedBy: REUSE_DISARM,
168+
});
169+
170+
it('reads DISARMED by default and ARMED after the patch the fixture uses', async () => {
171+
// Read the default FIRST, then arm, then restore — so this case is
172+
// atomic and cannot depend on the order vitest runs the file in.
173+
const before = await probe(orgless).read();
174+
expect(before.armed, 'the platform default leaves the reuse control off').toBe(false);
175+
expect(before.observed).toContain('passwordHistoryCount=');
176+
177+
const auth = await orgless.kernel.getServiceAsync<any>('auth');
178+
try {
179+
auth.applyConfigPatch({ passwordHistoryCount: 3 });
180+
const after = await probe(orgless).read();
181+
expect(after.armed, 'the same seam the settings service writes arms it').toBe(true);
182+
expect(after.observed).toContain('passwordHistoryCount=3');
183+
await expect(assertArmed([probe(orgless)])).resolves.toBeUndefined();
184+
} finally {
185+
auth.applyConfigPatch({ passwordHistoryCount: undefined });
186+
}
187+
188+
const restored = await probe(orgless).read();
189+
expect(restored.armed, 'and the restore really disarmed it again').toBe(false);
190+
});
191+
192+
it('assertArmed REJECTS the default config, naming the 0 default', async () => {
193+
const err = await assertArmed([probe(orgless)]).catch((e: Error) => e);
194+
expect(String(err)).toContain(REUSE_CONTROL);
195+
expect(String(err)).toContain('defaults to 0');
196+
});
197+
198+
it('a stack with no readable auth config is DISARMED whatever the predicate says', async () => {
199+
// A predicate that would call `undefined` armed must not be able to turn
200+
// "the service is gone" into a pass.
201+
const verdict = await authSettingArmed({
202+
stack: { kernel: { getServiceAsync: async () => undefined } } as unknown as VerifyStack,
203+
setting: 'passwordHistoryCount',
204+
armed: (v) => v === undefined,
205+
control: REUSE_CONTROL,
206+
disarmedBy: REUSE_DISARM,
207+
}).read();
208+
expect(verdict.armed).toBe(false);
209+
expect(verdict.observed).toContain("no 'auth' service resolved");
210+
});
211+
});
212+
213+
// ── seededArmed — the row a control rides on ──────────────────────────────
214+
215+
describe('seededArmed', () => {
216+
it('[control] finds a set that really seeded — so its negative reading is a real negative', async () => {
217+
const verdict = await seededArmed({
218+
stack: orgless,
219+
object: 'sys_permission_set',
220+
where: { name: 'member_default' },
221+
control: 'the platform baseline permission set',
222+
disarmedBy: 'a permission set that failed to seed grants the principal nothing',
223+
}).read();
224+
expect(verdict.armed).toBe(true);
225+
expect(verdict.observed).toContain('is seeded');
226+
});
227+
228+
it('reports DISARMED for a set that does not exist', async () => {
229+
const verdict = await seededArmed({
230+
stack: orgless,
231+
object: 'sys_permission_set',
232+
where: { name: 'no_such_set_8074' },
233+
control: 'an app-declared permission set',
234+
disarmedBy: 'a set that failed to seed grants the principal nothing',
235+
}).read();
236+
expect(verdict.armed).toBe(false);
237+
expect(verdict.observed).toContain('never seeded');
238+
});
239+
240+
it('reports DISARMED when the row exists but fails its own predicate', async () => {
241+
const verdict = await seededArmed({
242+
stack: orgless,
243+
object: 'sys_permission_set',
244+
where: { name: 'member_default' },
245+
armed: () => false,
246+
control: 'a seeded row that must also carry something',
247+
disarmedBy: 'the row seeded but not the part the control rides on',
248+
}).read();
249+
expect(verdict.armed).toBe(false);
250+
expect(verdict.observed).toContain('fails its own arming predicate');
251+
});
252+
});
253+
254+
// ── armedWhen — the general probe, both directions ────────────────────────
255+
256+
describe('armedWhen', () => {
257+
const generic = (armed: (v: number) => boolean) =>
258+
armedWhen<number>({
259+
control: 'a generic control',
260+
disarmedBy: 'a generic default',
261+
observe: () => 7,
262+
armed,
263+
describe: (v) => `observed ${v}`,
264+
});
265+
266+
it('is ARMED when its predicate holds and DISARMED when it does not', async () => {
267+
expect((await generic((v) => v === 7).read()).armed).toBe(true);
268+
expect((await generic((v) => v === 8).read()).armed).toBe(false);
269+
});
270+
271+
it('treats a non-boolean predicate result as DISARMED', async () => {
272+
// "not proven armed" is the safe direction for an ambiguous reading.
273+
const sloppy = armedWhen<number>({
274+
control: 'a control judged by a truthy non-boolean',
275+
disarmedBy: 'a predicate that does not return a real boolean',
276+
observe: () => 7,
277+
armed: ((v: number) => v) as unknown as (v: number) => boolean,
278+
});
279+
expect((await sloppy.read()).armed).toBe(false);
280+
});
281+
});
282+
283+
// ── the guard's own vacuity refusals ──────────────────────────────────────
284+
285+
describe('the guard refuses its own vacuous spellings', () => {
286+
it('assertArmed([]) throws — an empty declaration would certify everything', async () => {
287+
await expect(assertArmed([])).rejects.toThrow(/arming declaration is EMPTY/);
288+
});
289+
290+
it('principalArmed with nothing required throws at CONSTRUCTION', () => {
291+
expect(() =>
292+
principalArmed({
293+
stack: orgless,
294+
token: orglessMember,
295+
who: 'nobody in particular',
296+
control: WRITE_FLOOR,
297+
disarmedBy: WRITE_FLOOR_DISARM,
298+
}),
299+
).toThrow(/can never fail/);
300+
});
301+
302+
it('seededArmed with an empty `where` throws at CONSTRUCTION', () => {
303+
expect(() =>
304+
seededArmed({
305+
stack: orgless,
306+
object: 'sys_permission_set',
307+
where: {},
308+
control: 'anything',
309+
disarmedBy: 'anything',
310+
}),
311+
).toThrow(/matches any row/);
312+
});
313+
314+
it('a probe whose read THROWS counts as disarmed, never as fine', async () => {
315+
const broken: ArmingProbe = {
316+
control: 'a control whose probe cannot read the stack',
317+
disarmedBy: 'an unreadable precondition',
318+
read: async () => {
319+
throw new Error('the service went away');
320+
},
321+
};
322+
const err = await assertArmed([broken]).catch((e: Error) => e);
323+
expect(String(err)).toContain('the arming probe itself threw');
324+
expect(String(err)).toContain('the service went away');
325+
});
326+
327+
it('names EVERY disarmed control, not just the first', async () => {
328+
const err = await assertArmed([
329+
principalArmed({
330+
stack: orgless,
331+
token: orglessMember,
332+
who: 'the plain member',
333+
positions: ['org_member'],
334+
control: WRITE_FLOOR,
335+
disarmedBy: WRITE_FLOOR_DISARM,
336+
}),
337+
authSettingArmed({
338+
stack: orgless,
339+
setting: 'passwordHistoryCount',
340+
armed: (v) => Number(v) >= 1,
341+
control: REUSE_CONTROL,
342+
disarmedBy: REUSE_DISARM,
343+
}),
344+
]).catch((e: Error) => e);
345+
expect(String(err)).toContain(WRITE_FLOOR);
346+
expect(String(err)).toContain(REUSE_CONTROL);
347+
expect(String(err)).toContain('2 of 2 control(s)');
348+
});
349+
350+
it('[positive control] a fully armed multi-probe declaration RESOLVES', async () => {
351+
// The counterweight to every rejection case above.
352+
await expect(
353+
assertArmed([
354+
principalArmed({
355+
stack: orgbound,
356+
token: orgboundMember,
357+
who: 'the org-bound member',
358+
positions: ['org_member'],
359+
control: WRITE_FLOOR,
360+
disarmedBy: WRITE_FLOOR_DISARM,
361+
}),
362+
seededArmed({
363+
stack: orgbound,
364+
object: 'sys_permission_set',
365+
where: { name: 'member_default' },
366+
control: 'the platform baseline permission set',
367+
disarmedBy: 'a set that failed to seed grants the principal nothing',
368+
}),
369+
]),
370+
).resolves.toBeUndefined();
371+
});
372+
});
373+
});

0 commit comments

Comments
 (0)