fix(#2665): defer the built-lib require so an unbuilt tree fails one test, not all
tests/helpers.cjs required gsd-core/bin/lib at module scope to derive the
config-location scrub set. That lib is BUILT, so on an unbuilt tree the require
threw inside `require('./helpers.cjs')` -- before a single test() had registered
-- turning one missing `npm run build:lib` into a whole-suite crash with no
message naming the remedy. This is the file ~370 test files import, so the blast
radius is the suite. `npm test` builds via its pretest hook; the shape that
reaches this is a direct `node --test` invocation, which is exactly what a
contributor reaches for when running one file.
The require is now memoized behind builtLib(), and the two derived exports
(TEST_ENV_BASE, CONFIG_LOCATION_ENV_KEYS) are enumerable lazy getters, so
destructuring and Object.keys() behave as before. Reading either is what forces
the build; a test file that needs neither now imports cleanly. When the build IS
missing, the error names `npm run build:lib` instead of surfacing a bare
MODULE_NOT_FOUND.
Verified by a cold-child probe rather than by inspection -- this process has
already loaded everything, so an in-process assertion would pass vacuously. The
probe checks require.cache before and after touching TEST_ENV_BASE, and fails
when the require is moved back to module scope.
Addresses review finding: Major 7.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
const {
|
||||
withIsolatedProcessState,
|
||||
@@ -9,6 +10,35 @@ const {
|
||||
scrubConfigLocationEnv,
|
||||
} = require('./helpers.cjs');
|
||||
|
||||
describe('#2665: the built-lib require is deferred', () => {
|
||||
// The scrub set derives from gsd-core/bin/lib, which is BUILT. Requiring it at
|
||||
// module scope made an unbuilt tree throw inside `require('./helpers.cjs')` —
|
||||
// before any test() registered — so one missing `npm run build:lib` became a
|
||||
// whole-suite crash in the file ~370 test files import. A cold child is the only
|
||||
// honest probe: this process has already loaded everything.
|
||||
const probe = (touch) => {
|
||||
const src = [
|
||||
"const path = require('node:path');",
|
||||
`require(${JSON.stringify(path.join(__dirname, 'helpers.cjs'))});`,
|
||||
touch,
|
||||
"const needle = path.join('gsd-core', 'bin', 'lib', 'capability-registry.cjs');",
|
||||
'process.stdout.write(String(Object.keys(require.cache).some((m) => m.endsWith(needle))));',
|
||||
].join('\n');
|
||||
const r = spawnSync(process.execPath, ['-e', src], { encoding: 'utf8' });
|
||||
assert.strictEqual(r.status, 0, `probe failed: ${r.stderr}`);
|
||||
return r.stdout === 'true';
|
||||
};
|
||||
|
||||
test('requiring helpers.cjs alone does NOT load the built runtime lib', () => {
|
||||
assert.strictEqual(probe(''), false, 'the built lib was loaded at import time');
|
||||
});
|
||||
|
||||
test('reading TEST_ENV_BASE is what loads it', () => {
|
||||
const touch = `require(${JSON.stringify(path.join(__dirname, 'helpers.cjs'))}).TEST_ENV_BASE;`;
|
||||
assert.strictEqual(probe(touch), true, 'reading the scrub set must resolve the built lib');
|
||||
});
|
||||
});
|
||||
|
||||
describe('withIsolatedProcessState', () => {
|
||||
test('restores env, cwd, and exitCode after callback', () => {
|
||||
const originalCwd = process.cwd();
|
||||
|
||||
Reference in New Issue
Block a user