Skip to content

Commit ec6236e

Browse files
claude[bot]claude
andauthored
test(dogfood): give the declared-unresolvable CONTROL a subject the workspace can never supply (#16907)
* test(dogfood): make the enterprise-organizations control hermetic (#16539) WIP: fixture-only subject + premise legs. Verification pending. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8 * test(dogfood): cite ADR-0132 beside #16215 in the rewritten premises The landing that killed the old premise has a decision record; name it where the premise is restated, per Prime Directive #13. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015QE8qk46e5CHJxyQEUjbf8 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 96dfeaa commit ec6236e

2 files changed

Lines changed: 172 additions & 37 deletions

File tree

‎packages/qa/dogfood/test/enterprise-organizations.test.ts‎

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,49 @@
1212
* So these cases build real host roots on disk (real `node_modules`, a real
1313
* stand-in package, nothing mocked) and pin all three verdicts: available,
1414
* unavailable, and declared-but-missing.
15+
*
16+
* ── #16539 — why the subject below is a `@fixture/*` name ────────────────────
17+
*
18+
* Every verdict here is a statement about what a host root HAS and, just as
19+
* load-bearing, what it has NOT got. Until #16215 the second half came free:
20+
* `@objectstack/organizations` was cloud-private, so a temp host that declared
21+
* it and did not install it was unresolvable by construction. ADR-0132 (#16215)
22+
* brought the package into this workspace; pnpm's hoisted store carries it, and every
23+
* `pnpm exec`-launched runner (vitest's bin shim included) exports a `NODE_PATH`
24+
* that reaches that store. From then on the CONTROL's verdict was a function of
25+
* whether an unrelated package had been BUILT: green on CI, whose task graph
26+
* never builds it, red on any tree that had run a full `pnpm build`.
27+
*
28+
* The visible half of that is a false red. The half that matters is the quiet
29+
* one — a control whose subject is reachable is no longer controlling the thing
30+
* its name claims, and nothing says so. So the cases below drive a name this
31+
* workspace can never contain, and PROVE its absence instead of assuming it;
32+
* `ORGANIZATIONS_PKG` stays pinned as the probe's default subject by its own
33+
* case. #16723 made exactly this repair to `packages/types/src/node.test.ts`
34+
* for the same landing, and this file reuses its `@fixture/*` scope.
1535
*/
1636

1737
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
1838
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
1939
import { tmpdir } from 'node:os';
2040
import { join } from 'node:path';
41+
import { createHostRequire } from '@objectstack/types/node';
2142
import { probeOrganizations, MULTI_ORG_ENV, ORGANIZATIONS_PKG } from './enterprise-organizations.js';
2243

44+
/**
45+
* The package name every fixture host below is built around: modelled on the
46+
* real enterprise plugin (its export is the class `serve` / `bootStack`
47+
* construct), fixture-only in NAME.
48+
*
49+
* ⚠️ #16539 — it must stay a `@fixture/*` name. A name this workspace owns
50+
* cannot state "this host root does not have it", because the hoisted store and
51+
* the launcher's `NODE_PATH` answer that question instead of the fixture; and no
52+
* workspace name is safe from becoming one (`@objectstack/organizations` was
53+
* cloud-private when these cases were written). Only a name the workspace can
54+
* never contain is, and the PREMISE cases below prove this one still is not.
55+
*/
56+
const HOST_ONLY = '@fixture/enterprise-organizations';
57+
2358
let hostWithPkg: string;
2459
let hostWithoutPkg: string;
2560
/**
@@ -39,6 +74,9 @@ let hostDeclaredNoLoadableEntry: string;
3974
/**
4075
* The `declared-unresolvable` CONTROL: declared and NOT installed. #14270 left
4176
* this arm alone, so its wording must come out byte-identical.
77+
*
78+
* ⚠️ #16539 — "NOT installed" is a property of THIS directory, and only a
79+
* subject the workspace can never supply keeps it one. See {@link HOST_ONLY}.
4280
*/
4381
let hostDeclaredNotInstalled: string;
4482

@@ -55,12 +93,12 @@ function writeHost(
5593
name: 'dogfood-host-fixture',
5694
private: true,
5795
type: 'module',
58-
...(declare ? { dependencies: { [ORGANIZATIONS_PKG]: '*' } } : {}),
96+
...(declare ? { dependencies: { [HOST_ONLY]: '*' } } : {}),
5997
}),
6098
'utf8',
6199
);
62100
if (withPkg) {
63-
const pkgDir = join(dir, 'node_modules', ...ORGANIZATIONS_PKG.split('/'));
101+
const pkgDir = join(dir, 'node_modules', ...HOST_ONLY.split('/'));
64102
mkdirSync(pkgDir, { recursive: true });
65103
if (opts.typesOnly) {
66104
// No `require` condition (the CJS resolver throws) and no `import`
@@ -69,7 +107,7 @@ function writeHost(
69107
writeFileSync(
70108
join(pkgDir, 'package.json'),
71109
JSON.stringify({
72-
name: ORGANIZATIONS_PKG,
110+
name: HOST_ONLY,
73111
version: '0.0.0-fixture',
74112
type: 'module',
75113
exports: { '.': { types: './index.d.ts' } },
@@ -86,7 +124,7 @@ function writeHost(
86124
writeFileSync(
87125
join(pkgDir, 'package.json'),
88126
JSON.stringify({
89-
name: ORGANIZATIONS_PKG,
127+
name: HOST_ONLY,
90128
version: '0.0.0-fixture',
91129
type: 'module',
92130
main: 'index.js',
@@ -127,13 +165,13 @@ describe('enterprise multi-org probe (#4700)', () => {
127165
// The verdict the old probe could never reach, no matter what any app or CI
128166
// had installed. This is what makes `describe.skipIf(!organizationsAvailable)`
129167
// a real gate rather than an unconditional skip.
130-
const probe = await probeOrganizations(hostWithPkg, false);
168+
const probe = await probeOrganizations(hostWithPkg, false, HOST_ONLY);
131169
expect(probe.available).toBe(true);
132170
expect(probe.reason).toBeUndefined();
133171
});
134172

135173
it('reports UNAVAILABLE, with an actionable reason, when the app lacks it', async () => {
136-
const probe = await probeOrganizations(hostWithoutPkg, false);
174+
const probe = await probeOrganizations(hostWithoutPkg, false, HOST_ONLY);
137175
expect(probe.available).toBe(false);
138176
// The reason has to name the switch, or the skip stays folklore.
139177
expect(probe.reason).toContain(MULTI_ORG_ENV);
@@ -145,13 +183,13 @@ describe('enterprise multi-org probe (#4700)', () => {
145183
// failure a CI operator cannot miss (Prime Directive #10 / "absence must be
146184
// loud"). Without this, a cloud run that lost the package would look exactly
147185
// like a cloud run that has it.
148-
await expect(probeOrganizations(hostWithoutPkg, true)).rejects.toThrow(
186+
await expect(probeOrganizations(hostWithoutPkg, true, HOST_ONLY)).rejects.toThrow(
149187
new RegExp(`${MULTI_ORG_ENV}=1 declares`),
150188
);
151189
});
152190

153191
it('does not throw when the run declares the package AND it is there', async () => {
154-
await expect(probeOrganizations(hostWithPkg, true)).resolves.toEqual({ available: true });
192+
await expect(probeOrganizations(hostWithPkg, true, HOST_ONLY)).resolves.toEqual({ available: true });
155193
});
156194

157195
it('reports UNAVAILABLE when the package is merely PRESENT but not declared (#4719)', async () => {
@@ -161,26 +199,26 @@ describe('enterprise multi-org probe (#4700)', () => {
161199
// asked for the enterprise runtime. Worse, in a real pnpm workspace the
162200
// "present" half arrives via the bin shim's NODE_PATH, so the verdict moved
163201
// with the launcher.
164-
const probe = await probeOrganizations(hostInstalledButUndeclared, false);
202+
const probe = await probeOrganizations(hostInstalledButUndeclared, false, HOST_ONLY);
165203
expect(probe.available).toBe(false);
166204
expect(probe.reason).toContain("package.json");
167205
});
168206

169207
it('THROWS with a DECLARE-it remedy when the run declares it but the app does not (#4719)', async () => {
170208
// The remedy has to be the one that works. "Install it" is unfollowable
171209
// advice here — it is already installed; the missing act is declaring it.
172-
await expect(probeOrganizations(hostInstalledButUndeclared, true)).rejects.toThrow(
173-
new RegExp(`declare ${ORGANIZATIONS_PKG.replace('/', '\\/')} in .* package\\.json`),
210+
await expect(probeOrganizations(hostInstalledButUndeclared, true, HOST_ONLY)).rejects.toThrow(
211+
new RegExp(`declare ${HOST_ONLY.replace('/', '\\/')} in .* package\\.json`),
174212
);
175213
});
176214

177215
it('CONTROL — the `declared-unresolvable` remedy is unchanged: declared, not installed', async () => {
178216
// The arm #14270 did NOT touch. Pinned here so the three-way rewrite is a
179217
// measurement: this text has to be byte-identical either side of it.
180-
const probe = await probeOrganizations(hostDeclaredNotInstalled, false);
218+
const probe = await probeOrganizations(hostDeclaredNotInstalled, false, HOST_ONLY);
181219
expect(probe.available).toBe(false);
182220
expect(probe.reason).toContain(
183-
`${hostDeclaredNotInstalled} DECLARES ${ORGANIZATIONS_PKG}, so repair its INSTALL there `
221+
`${hostDeclaredNotInstalled} DECLARES ${HOST_ONLY}, so repair its INSTALL there `
184222
+ '(`pnpm install`, un-prune, rebuild its dist)',
185223
);
186224
});
@@ -190,15 +228,15 @@ describe('enterprise multi-org probe (#4700)', () => {
190228
// two, so `declared-no-loadable-entry` fell into the else leg and told an
191229
// operator whose app DECLARES the package and HAS it installed to declare
192230
// it and install it. No install action can change what a package publishes.
193-
const probe = await probeOrganizations(hostDeclaredNoLoadableEntry, false);
231+
const probe = await probeOrganizations(hostDeclaredNoLoadableEntry, false, HOST_ONLY);
194232
expect(probe.available).toBe(false);
195233
// Which arm fired: the deferral names the two things that are NOT the
196234
// problem and hands the remedy to the importer's message, which this
197235
// reason interpolates at the end.
198236
expect(probe.reason).toContain('and it IS installed, so neither is the problem');
199237
expect(probe.reason).toContain('publishes no entry Node can load');
200238
// ⛔ Neither of the other two arms — both are unfollowable for this kind.
201-
expect(probe.reason).not.toContain(`declare ${ORGANIZATIONS_PKG} in`);
239+
expect(probe.reason).not.toContain(`declare ${HOST_ONLY} in`);
202240
expect(probe.reason).not.toContain('repair its INSTALL');
203241
// The message deferred TO has to actually arrive.
204242
expect(probe.reason).toContain('publishes no entry that Node can load');
@@ -207,12 +245,67 @@ describe('enterprise multi-org probe (#4700)', () => {
207245
it('THROWS with that same deferral when the run declares the package (#14270)', async () => {
208246
// The loud half: MULTI_ORG=1 says the package is there, and it IS — it just
209247
// cannot be loaded. The refusal must still name the right remedy.
210-
const err = await probeOrganizations(hostDeclaredNoLoadableEntry, true).then(
248+
const err = await probeOrganizations(hostDeclaredNoLoadableEntry, true, HOST_ONLY).then(
211249
() => new Error('probeOrganizations resolved; MULTI_ORG=1 must make this a failure'),
212250
(e: unknown) => e as Error,
213251
);
214252
expect(err.message).toContain(MULTI_ORG_ENV);
215253
expect(err.message).toContain('and it IS installed, so neither is the problem');
216-
expect(err.message).not.toContain(`declare ${ORGANIZATIONS_PKG} in`);
254+
expect(err.message).not.toContain(`declare ${HOST_ONLY} in`);
255+
});
256+
});
257+
258+
/**
259+
* #16539 — the premises the cases above rest on, asserted instead of assumed.
260+
*
261+
* Both legs are load-bearing and they fail in opposite directions. Without leg 1
262+
* a name that exists nowhere at all satisfies leg 2 and every verdict above is
263+
* vacuous; without leg 2 the fixture stops deciding what the host root has, and
264+
* the CONTROL silently starts measuring the ambient workspace — which is exactly
265+
* how this file broke.
266+
*/
267+
describe('PREMISE — the fixture subject is host-only (#16539)', () => {
268+
it('resolves from a host app that installs it', () => {
269+
// Leg 1. The fixture host really can see it, so "unresolvable" elsewhere is
270+
// a statement about the resolver's anchor and not about a typo.
271+
const fromHost = createHostRequire(hostWithPkg).resolve(HOST_ONLY);
272+
expect(fromHost).toContain(hostWithPkg);
273+
});
274+
275+
it('is absent from every ambient store the runner exposes', () => {
276+
// Leg 2, and the guard that would have caught this card. Asserted on the
277+
// BARE SPECIFIER, ⛔ never on `/Cannot find module/` alone: a package the
278+
// runner CAN see whose entry file merely is not on disk throws
279+
// MODULE_NOT_FOUND too, naming `<store>/<pkg>/dist/index.js` instead of the
280+
// specifier. That second throw is what kept the CONTROL above green while
281+
// the property went unguarded — `@objectstack/organizations` was reachable
282+
// through the pnpm bin shim's NODE_PATH and simply unbuilt on CI's graph.
283+
// Read as a bare-specifier failure, the premise can no longer be satisfied
284+
// by an unbuilt workspace package, in either build state.
285+
expect(() => createHostRequire(hostDeclaredNotInstalled).resolve(HOST_ONLY)).toThrow(
286+
new RegExp(`Cannot find module '${HOST_ONLY}'`),
287+
);
288+
});
289+
290+
it('and the probe still binds the ENTERPRISE package as its default subject', () => {
291+
// What the fixture name must NOT quietly become: the subject. Production
292+
// callers pass no specifier, and the one they get is the real package.
293+
expect(ORGANIZATIONS_PKG).toBe('@objectstack/organizations');
294+
expect(HOST_ONLY).not.toBe(ORGANIZATIONS_PKG);
295+
});
296+
297+
it("and this package's own resolution still cannot see it", async () => {
298+
// The sentence that used to sit in `enterprise-organizations.ts` as prose —
299+
// "resolvable from nowhere in the framework workspace" — died with #16215
300+
// and nothing noticed. It is an assertion now, on the default subject, over
301+
// the arm that actually uses this module's ESM base.
302+
//
303+
// ⚠️ A red here is NOT a fixture problem: it means `@objectstack/dogfood`
304+
// can now resolve the enterprise package, so `organizationsAvailable` is
305+
// true in the framework repo and the multi-org gates have started running
306+
// here. Read the verdict, then decide — do not re-point this case.
307+
const probe = await probeOrganizations(hostWithoutPkg, false);
308+
expect(probe.available).toBe(false);
309+
expect(probe.reason).toContain(`declare ${ORGANIZATIONS_PKG} in`);
217310
});
218311
});

0 commit comments

Comments
 (0)