Skip to content

Commit cb29c5b

Browse files
committed
test(verify): pay the first boot load at collection and share one boot per configuration in two harness suites
harness.app-default-profile and harness.org-context booted a fresh stack in every case, and paid the boot's first load inside the first case's clock. Both now warm up at module scope (boot-warm-up.testkit.ts), and cases that read the same configuration under the same options read one boot from a beforeAll: app-default-profile 4 boots to 3, org-context 4 boots to 2, with every assertion unchanged in what it reads. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Bmz18vLaqY8KsiVaCXL5zS
1 parent a6e8f95 commit cb29c5b

2 files changed

Lines changed: 99 additions & 63 deletions

File tree

‎packages/verify/src/harness.app-default-profile.test.ts‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
// fixture object is denied unless the app's declared profile is actually wired.
2828
// A name-only assertion could not tell "wired" from "wired but inert".
2929

30-
import { describe, it, expect, afterAll } from 'vitest';
30+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
3131
import { defineStack } from '@objectstack/spec';
3232
import { ObjectSchema, Field } from '@objectstack/spec/data';
3333
import { PermissionSetSchema } from '@objectstack/spec/security';
@@ -48,10 +48,16 @@ import {
4848
// in `DEBT`, `TEST_DEBT` or `EXEMPT`. The layer is at zero with no ledger, so
4949
// an error a new file introduces is red on arrival.
5050
import { bootStack, type VerifyStack } from './harness.js';
51+
import { warmUpBoot } from './boot-warm-up.testkit.js';
5152

5253
// Booting the full in-process stack runs well past vitest's 5s default.
5354
const BOOT_TIMEOUT = 120_000;
5455

56+
// The boot's first load, paid during collection rather than in the first
57+
// boot's clock (see `boot-warm-up.testkit.ts`). Every boot below that is not
58+
// the opt-out case uses the default options.
59+
await warmUpBoot();
60+
5561
const APP_DEFAULT_SET = 'appdefault_member_default';
5662

5763
// `defineStack` enforces `${manifest.namespace}_${shortName}` on every object
@@ -142,10 +148,20 @@ const wiredBaseline = (stack: VerifyStack): Promise<string | null> =>
142148
stack.kernel.getServiceAsync<string | null>('security.fallbackPermissionSet');
143149

144150
describe('bootStack honours the app-declared default permission set (#7001)', () => {
151+
// The first two cases read ONE boot of the declared-default configuration
152+
// with the default options: the first reads the profile the boot wired, a
153+
// fixed answer of the boot, and the second signs a member up and reads as
154+
// them, which changes no wiring. Neither reads what the other writes, so
155+
// they hold in either order.
156+
let declaredDefault: VerifyStack;
157+
beforeAll(async () => {
158+
declaredDefault = await boot(buildAppWithDeclaredDefault());
159+
}, BOOT_TIMEOUT);
160+
145161
it(
146162
'wires the SAME profile `serve` wires for the same config',
147163
async () => {
148-
const stack = await boot(buildAppWithDeclaredDefault());
164+
const stack = declaredDefault;
149165

150166
// The exact expression `serve.ts` evaluates for its `fallbackPermissionSet`.
151167
const servesChoice = appDefaultPermissionSetName(appWithDeclaredDefault.permissions);
@@ -162,7 +178,7 @@ describe('bootStack honours the app-declared default permission set (#7001)', ()
162178
it(
163179
'and that profile is load-bearing: a fresh member holds the declared grants',
164180
async () => {
165-
const stack = await boot(buildAppWithDeclaredDefault());
181+
const stack = declaredDefault;
166182
await stack.signIn(); // first user is the seeded dev admin
167183
const memberToken = await stack.signUp('appdefault-member@verify.test');
168184

‎packages/verify/src/harness.org-context.test.ts‎

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@
2525
// lives in the dogfood suite, against a real app:
2626
// `packages/qa/dogfood/test/org-scoped-sharing-rule-listing.dogfood.test.ts`.
2727

28-
import { describe, it, expect, afterEach } from 'vitest';
28+
import { describe, it, expect, beforeAll, afterEach } from 'vitest';
2929
// `.js` extension deliberately: this package resolves NodeNext, so an
3030
// extensionless relative import is a TS2835 the type-check-debt ratchet counts
3131
// (the sibling test files predate the gate and carry theirs in the ledger — a
3232
// NEW one would raise it, which is not something a test file gets to do).
33-
import { bootStack, type VerifyStack } from './harness.js';
33+
import { bootStack, type BootOptions, type VerifyStack } from './harness.js';
34+
import { warmUpBoot } from './boot-warm-up.testkit.js';
3435

3536
const app = {
3637
manifest: {
@@ -56,6 +57,11 @@ const SYS = { isSystem: true } as const;
5657
// Booting the full in-process stack runs well past vitest's 5s default.
5758
const BOOT_TIMEOUT = 120_000;
5859

60+
// The boot's first load, paid during collection rather than in the first
61+
// boot's clock (see `boot-warm-up.testkit.ts`). The first boot below uses the
62+
// default options.
63+
await warmUpBoot();
64+
5965
afterEach(() => {
6066
delete process.env.OS_TENANCY_POSTURE;
6167
});
@@ -96,7 +102,58 @@ async function sessionOrgId(stack: VerifyStack): Promise<string | null> {
96102
return null;
97103
}
98104

105+
/** What one boot of `app` answered, read while it was live. */
106+
interface BootReading {
107+
posture: TenancyShape;
108+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
109+
orgs: any[];
110+
sessionOrgId: string | null;
111+
/** The roles of the admin's `sys_member` rows in the first organization. */
112+
adminRoles: unknown[];
113+
}
114+
115+
/**
116+
* Boot `app` with `opts`, read the posture, the organizations, the org id the
117+
* admin's session carries and the admin's membership roles, and stop. Each
118+
* read is one the cases below used to make on a boot of their own with the
119+
* same options.
120+
*/
121+
async function bootAndRead(opts: BootOptions): Promise<BootReading> {
122+
const stack = await bootStack(app, opts);
123+
try {
124+
const shape = await posture(stack);
125+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
126+
const ql = await stack.kernel.getServiceAsync<any>('objectql');
127+
const orgs = rowsOf(await ql.find('sys_organization', { limit: 10, context: SYS }));
128+
const orgId = await sessionOrgId(stack);
129+
const users = rowsOf(
130+
await ql.find('sys_user', { where: { email: 'admin@objectos.ai' }, limit: 1, context: SYS }),
131+
);
132+
const memberships = rowsOf(
133+
await ql.find('sys_member', {
134+
where: { user_id: users[0]?.id, organization_id: orgs[0]?.id },
135+
limit: 10,
136+
context: SYS,
137+
}),
138+
);
139+
return { posture: shape, orgs, sessionOrgId: orgId, adminRoles: memberships.map((m) => m.role) };
140+
} finally {
141+
await stack.stop();
142+
}
143+
}
144+
99145
describe('bootStack orgContext (#7762)', () => {
146+
// Two boots for the three cases that read one: the flag off (the default
147+
// boot) and on, in that order, each stopped before the next starts — the
148+
// order the ruling case always booted them in. Every case only reads what a
149+
// boot answered, so no case depends on another's run or on their order.
150+
let off: BootReading;
151+
let on: BootReading;
152+
beforeAll(async () => {
153+
off = await bootAndRead({});
154+
on = await bootAndRead({ orgContext: true });
155+
}, BOOT_TIMEOUT * 2);
156+
100157
it(
101158
'THE RULING: the tenancy posture and `degraded` are IDENTICAL with the flag off vs on',
102159
async () => {
@@ -106,21 +163,8 @@ describe('bootStack orgContext (#7762)', () => {
106163
// so binding the admin to an organization must not move it. If this ever
107164
// fails, `orgContext` is making the open core claim a wall it does not
108165
// have and the option must not ship in that shape.
109-
const off = await bootStack(app, {});
110-
let baseline: TenancyShape;
111-
try {
112-
baseline = await posture(off);
113-
} finally {
114-
await off.stop();
115-
}
116-
117-
const on = await bootStack(app, { orgContext: true });
118-
let withOrg: TenancyShape;
119-
try {
120-
withOrg = await posture(on);
121-
} finally {
122-
await on.stop();
123-
}
166+
const baseline = off.posture;
167+
const withOrg = on.posture;
124168

125169
expect(withOrg).toEqual(baseline);
126170
// Spelled out too, so a future refactor of `posture()` cannot make the
@@ -137,23 +181,16 @@ describe('bootStack orgContext (#7762)', () => {
137181
it(
138182
'binds the admin to a real organization, and their session carries it',
139183
async () => {
140-
const stack = await bootStack(app, { orgContext: true });
141-
try {
142-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
143-
const ql = await stack.kernel.getServiceAsync<any>('objectql');
144-
const orgs = rowsOf(await ql.find('sys_organization', { limit: 10, context: SYS }));
145-
expect(orgs.length, 'the default-org bootstrap minted an organization').toBeGreaterThan(0);
146-
147-
// `session.activeOrganizationId` is the ONE field `resolveAuthzContext`
148-
// reads into `tenantId` → `ExecutionContext`. Without it on the session
149-
// row, every org-scoped read downstream is org-less no matter how many
150-
// organizations exist in the table.
151-
const orgId = await sessionOrgId(stack);
152-
expect(orgId, "the admin's session carries an active organization").toBeTruthy();
153-
expect(orgs.map((o) => o.id)).toContain(orgId);
154-
} finally {
155-
await stack.stop();
156-
}
184+
const { orgs } = on;
185+
expect(orgs.length, 'the default-org bootstrap minted an organization').toBeGreaterThan(0);
186+
187+
// `session.activeOrganizationId` is the ONE field `resolveAuthzContext`
188+
// reads into `tenantId` → `ExecutionContext`. Without it on the session
189+
// row, every org-scoped read downstream is org-less no matter how many
190+
// organizations exist in the table.
191+
const orgId = on.sessionOrgId;
192+
expect(orgId, "the admin's session carries an active organization").toBeTruthy();
193+
expect(orgs.map((o) => o.id)).toContain(orgId);
157194
},
158195
BOOT_TIMEOUT,
159196
);
@@ -165,32 +202,15 @@ describe('bootStack orgContext (#7762)', () => {
165202
// Default Organization (a boot invariant), and the admin is bound to it
166203
// without asking — the shape `objectstack dev` / `serve` boot. There is
167204
// no org-less `single` boot left to keep a fixture on (ADR-0131 D11).
168-
const stack = await bootStack(app, {});
169-
try {
170-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
171-
const ql = await stack.kernel.getServiceAsync<any>('objectql');
172-
const orgs = rowsOf(await ql.find('sys_organization', { limit: 10, context: SYS }));
173-
expect(orgs.map((o) => o.slug)).toEqual(['default']);
174-
expect(await sessionOrgId(stack)).toBe(orgs[0].id);
175-
// The harness line this case pins is the owner bind (ADR-0093 D7), on
176-
// for every boot: the admin OWNS the Default Organization. The two
177-
// assertions above hold without it (the boot invariant creates the
178-
// organization and the membership reconciler binds the admin), but as
179-
// `member` — which is what an `objectstack dev` admin never is.
180-
const users = rowsOf(
181-
await ql.find('sys_user', { where: { email: 'admin@objectos.ai' }, limit: 1, context: SYS }),
182-
);
183-
const memberships = rowsOf(
184-
await ql.find('sys_member', {
185-
where: { user_id: users[0]?.id, organization_id: orgs[0].id },
186-
limit: 10,
187-
context: SYS,
188-
}),
189-
);
190-
expect(memberships.map((m) => m.role)).toEqual(['owner']);
191-
} finally {
192-
await stack.stop();
193-
}
205+
const { orgs } = off;
206+
expect(orgs.map((o) => o.slug)).toEqual(['default']);
207+
expect(off.sessionOrgId).toBe(orgs[0].id);
208+
// The harness line this case pins is the owner bind (ADR-0093 D7), on
209+
// for every boot: the admin OWNS the Default Organization. The two
210+
// assertions above hold without it (the boot invariant creates the
211+
// organization and the membership reconciler binds the admin), but as
212+
// `member` — which is what an `objectstack dev` admin never is.
213+
expect(off.adminRoles).toEqual(['owner']);
194214
},
195215
BOOT_TIMEOUT,
196216
);

0 commit comments

Comments
 (0)