Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

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

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

/**
* The package name every fixture host below is built around: modelled on the
* real enterprise plugin (its export is the class `serve` / `bootStack`
* construct), fixture-only in NAME.
*
* ⚠️ #16539 — it must stay a `@fixture/*` name. A name this workspace owns
* cannot state "this host root does not have it", because the hoisted store and
* the launcher's `NODE_PATH` answer that question instead of the fixture; and no
* workspace name is safe from becoming one (`@objectstack/organizations` was
* cloud-private when these cases were written). Only a name the workspace can
* never contain is, and the PREMISE cases below prove this one still is not.
*/
const HOST_ONLY = '@fixture/enterprise-organizations';

let hostWithPkg: string;
let hostWithoutPkg: string;
/**
Expand All @@ -39,6 +74,9 @@ let hostDeclaredNoLoadableEntry: string;
/**
* The `declared-unresolvable` CONTROL: declared and NOT installed. #14270 left
* this arm alone, so its wording must come out byte-identical.
*
* ⚠️ #16539 — "NOT installed" is a property of THIS directory, and only a
* subject the workspace can never supply keeps it one. See {@link HOST_ONLY}.
*/
let hostDeclaredNotInstalled: string;

Expand All @@ -55,12 +93,12 @@ function writeHost(
name: 'dogfood-host-fixture',
private: true,
type: 'module',
...(declare ? { dependencies: { [ORGANIZATIONS_PKG]: '*' } } : {}),
...(declare ? { dependencies: { [HOST_ONLY]: '*' } } : {}),
}),
'utf8',
);
if (withPkg) {
const pkgDir = join(dir, 'node_modules', ...ORGANIZATIONS_PKG.split('/'));
const pkgDir = join(dir, 'node_modules', ...HOST_ONLY.split('/'));
mkdirSync(pkgDir, { recursive: true });
if (opts.typesOnly) {
// No `require` condition (the CJS resolver throws) and no `import`
Expand All @@ -69,7 +107,7 @@ function writeHost(
writeFileSync(
join(pkgDir, 'package.json'),
JSON.stringify({
name: ORGANIZATIONS_PKG,
name: HOST_ONLY,
version: '0.0.0-fixture',
type: 'module',
exports: { '.': { types: './index.d.ts' } },
Expand All @@ -86,7 +124,7 @@ function writeHost(
writeFileSync(
join(pkgDir, 'package.json'),
JSON.stringify({
name: ORGANIZATIONS_PKG,
name: HOST_ONLY,
version: '0.0.0-fixture',
type: 'module',
main: 'index.js',
Expand Down Expand Up @@ -127,13 +165,13 @@ describe('enterprise multi-org probe (#4700)', () => {
// The verdict the old probe could never reach, no matter what any app or CI
// had installed. This is what makes `describe.skipIf(!organizationsAvailable)`
// a real gate rather than an unconditional skip.
const probe = await probeOrganizations(hostWithPkg, false);
const probe = await probeOrganizations(hostWithPkg, false, HOST_ONLY);
expect(probe.available).toBe(true);
expect(probe.reason).toBeUndefined();
});

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

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

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

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

it('CONTROL — the `declared-unresolvable` remedy is unchanged: declared, not installed', async () => {
// The arm #14270 did NOT touch. Pinned here so the three-way rewrite is a
// measurement: this text has to be byte-identical either side of it.
const probe = await probeOrganizations(hostDeclaredNotInstalled, false);
const probe = await probeOrganizations(hostDeclaredNotInstalled, false, HOST_ONLY);
expect(probe.available).toBe(false);
expect(probe.reason).toContain(
`${hostDeclaredNotInstalled} DECLARES ${ORGANIZATIONS_PKG}, so repair its INSTALL there `
`${hostDeclaredNotInstalled} DECLARES ${HOST_ONLY}, so repair its INSTALL there `
+ '(`pnpm install`, un-prune, rebuild its dist)',
);
});
Expand All @@ -190,15 +228,15 @@ describe('enterprise multi-org probe (#4700)', () => {
// two, so `declared-no-loadable-entry` fell into the else leg and told an
// operator whose app DECLARES the package and HAS it installed to declare
// it and install it. No install action can change what a package publishes.
const probe = await probeOrganizations(hostDeclaredNoLoadableEntry, false);
const probe = await probeOrganizations(hostDeclaredNoLoadableEntry, false, HOST_ONLY);
expect(probe.available).toBe(false);
// Which arm fired: the deferral names the two things that are NOT the
// problem and hands the remedy to the importer's message, which this
// reason interpolates at the end.
expect(probe.reason).toContain('and it IS installed, so neither is the problem');
expect(probe.reason).toContain('publishes no entry Node can load');
// ⛔ Neither of the other two arms — both are unfollowable for this kind.
expect(probe.reason).not.toContain(`declare ${ORGANIZATIONS_PKG} in`);
expect(probe.reason).not.toContain(`declare ${HOST_ONLY} in`);
expect(probe.reason).not.toContain('repair its INSTALL');
// The message deferred TO has to actually arrive.
expect(probe.reason).toContain('publishes no entry that Node can load');
Expand All @@ -207,12 +245,67 @@ describe('enterprise multi-org probe (#4700)', () => {
it('THROWS with that same deferral when the run declares the package (#14270)', async () => {
// The loud half: MULTI_ORG=1 says the package is there, and it IS — it just
// cannot be loaded. The refusal must still name the right remedy.
const err = await probeOrganizations(hostDeclaredNoLoadableEntry, true).then(
const err = await probeOrganizations(hostDeclaredNoLoadableEntry, true, HOST_ONLY).then(
() => new Error('probeOrganizations resolved; MULTI_ORG=1 must make this a failure'),
(e: unknown) => e as Error,
);
expect(err.message).toContain(MULTI_ORG_ENV);
expect(err.message).toContain('and it IS installed, so neither is the problem');
expect(err.message).not.toContain(`declare ${ORGANIZATIONS_PKG} in`);
expect(err.message).not.toContain(`declare ${HOST_ONLY} in`);
});
});

/**
* #16539 — the premises the cases above rest on, asserted instead of assumed.
*
* Both legs are load-bearing and they fail in opposite directions. Without leg 1
* a name that exists nowhere at all satisfies leg 2 and every verdict above is
* vacuous; without leg 2 the fixture stops deciding what the host root has, and
* the CONTROL silently starts measuring the ambient workspace — which is exactly
* how this file broke.
*/
describe('PREMISE — the fixture subject is host-only (#16539)', () => {
it('resolves from a host app that installs it', () => {
// Leg 1. The fixture host really can see it, so "unresolvable" elsewhere is
// a statement about the resolver's anchor and not about a typo.
const fromHost = createHostRequire(hostWithPkg).resolve(HOST_ONLY);
expect(fromHost).toContain(hostWithPkg);
});

it('is absent from every ambient store the runner exposes', () => {
// Leg 2, and the guard that would have caught this card. Asserted on the
// BARE SPECIFIER, ⛔ never on `/Cannot find module/` alone: a package the
// runner CAN see whose entry file merely is not on disk throws
// MODULE_NOT_FOUND too, naming `<store>/<pkg>/dist/index.js` instead of the
// specifier. That second throw is what kept the CONTROL above green while
// the property went unguarded — `@objectstack/organizations` was reachable
// through the pnpm bin shim's NODE_PATH and simply unbuilt on CI's graph.
// Read as a bare-specifier failure, the premise can no longer be satisfied
// by an unbuilt workspace package, in either build state.
expect(() => createHostRequire(hostDeclaredNotInstalled).resolve(HOST_ONLY)).toThrow(
new RegExp(`Cannot find module '${HOST_ONLY}'`),
);
});

it('and the probe still binds the ENTERPRISE package as its default subject', () => {
// What the fixture name must NOT quietly become: the subject. Production
// callers pass no specifier, and the one they get is the real package.
expect(ORGANIZATIONS_PKG).toBe('@objectstack/organizations');
expect(HOST_ONLY).not.toBe(ORGANIZATIONS_PKG);
});

it("and this package's own resolution still cannot see it", async () => {
// The sentence that used to sit in `enterprise-organizations.ts` as prose —
// "resolvable from nowhere in the framework workspace" — died with #16215
// and nothing noticed. It is an assertion now, on the default subject, over
// the arm that actually uses this module's ESM base.
//
// ⚠️ A red here is NOT a fixture problem: it means `@objectstack/dogfood`
// can now resolve the enterprise package, so `organizationsAvailable` is
// true in the framework repo and the multi-org gates have started running
// here. Read the verdict, then decide — do not re-point this case.
const probe = await probeOrganizations(hostWithoutPkg, false);
expect(probe.available).toBe(false);
expect(probe.reason).toContain(`declare ${ORGANIZATIONS_PKG} in`);
});
});
Loading
Loading