test(#2665): reversion guards for all three round-4 fixes
None of the round-4 fixes had a test that fails on reversion: re-shipping the test-instrumentation chain passes #2858 (everything ships, so every require resolves), dropping the strict env from test.yml demotes the guard to report-only with nothing red, and the skillsHome derivation tests are enumeration-relative over declarations that are all empty today. One guard each, every one negative-controlled against its reverted fix (fails pre-fix, passes post-fix): - packaging-shipped-scripts-require-only-shipped.test.cjs asserts the four chain files are absent from the npm pack file list (reuses the tarball set the #2858 gate already resolves — no second npm pack). - live-config-guard.test.cjs asserts all three test jobs wire GSD_STRICT_LIVE_CONFIG_GUARD, matching the WHOLE expression anchored — a prefix match accepted both a Windows-silently-strict tail and a malformed one. - helpers-process-isolation.test.cjs cold-requires helpers.cjs in a child with sentinel skillsHome env vars injected into both enumerations, so the walk itself is under test rather than today's empty declarations.
This commit is contained in:
@@ -235,3 +235,57 @@ describe('#2665: TEST_ENV_BASE config-location coverage', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#2665 round 4: the skillsHome walk is reversion-sensitive', () => {
|
||||||
|
// The coverage tests above are enumeration-relative, and skillsHome.env is
|
||||||
|
// empty everywhere today — so reverting the skillsHome rungs from the
|
||||||
|
// derivation leaves every one of them green (measured by this round's
|
||||||
|
// pre-push adversarial review). This test closes that: it cold-requires
|
||||||
|
// helpers.cjs in a child process after injecting sentinel skillsHome env
|
||||||
|
// vars into BOTH enumerations (registry and non-registry), so the walk
|
||||||
|
// itself is what is under test, not today's empty declarations.
|
||||||
|
test('sentinel skillsHome vars flow into TEST_ENV_BASE on both rungs', () => {
|
||||||
|
const { execFileSync } = require('node:child_process');
|
||||||
|
const regPath = require.resolve('../gsd-core/bin/lib/capability-registry.cjs');
|
||||||
|
const rhPath = require.resolve('../gsd-core/bin/lib/runtime-homes.cjs');
|
||||||
|
const helpersPath = require.resolve('./helpers.cjs');
|
||||||
|
|
||||||
|
const script = `
|
||||||
|
'use strict';
|
||||||
|
const reg = require(${JSON.stringify(regPath)});
|
||||||
|
const rh = require(${JSON.stringify(rhPath)});
|
||||||
|
// Rung 1 (registry): give one runtime a skillsHome env var. kilo already
|
||||||
|
// declares skillsHome (env: []); push a sentinel into whichever runtime
|
||||||
|
// declares it, or graft one onto the first runtime if none does.
|
||||||
|
const declaring = Object.values(reg.runtimes).find(
|
||||||
|
(r) => r?.runtime?.configHome?.skillsHome,
|
||||||
|
) ?? Object.values(reg.runtimes)[0];
|
||||||
|
if (!declaring.runtime.configHome.skillsHome) {
|
||||||
|
declaring.runtime.configHome.skillsHome = { kind: 'dot-home', name: '.x', env: [] };
|
||||||
|
}
|
||||||
|
declaring.runtime.configHome.skillsHome.env = ['GSD_TEST_SENTINEL_REGISTRY_SKILLS'];
|
||||||
|
// Rung 2 (non-registry): graft a skillsHome onto the first descriptor.
|
||||||
|
rh.NON_REGISTRY_CONFIG_HOME_DESCRIPTORS[0].skillsHome = {
|
||||||
|
kind: 'dot-home', name: '.x', env: ['GSD_TEST_SENTINEL_NONREG_SKILLS'],
|
||||||
|
};
|
||||||
|
const { TEST_ENV_BASE } = require(${JSON.stringify(helpersPath)});
|
||||||
|
const missing = [
|
||||||
|
'GSD_TEST_SENTINEL_REGISTRY_SKILLS',
|
||||||
|
'GSD_TEST_SENTINEL_NONREG_SKILLS',
|
||||||
|
].filter((k) => TEST_ENV_BASE[k] !== '');
|
||||||
|
if (missing.length > 0) {
|
||||||
|
console.error('skillsHome walk missed: ' + missing.join(', '));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
process.exit(0);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const out = execFileSync(process.execPath, ['-e', script], {
|
||||||
|
cwd: __dirname,
|
||||||
|
encoding: 'utf-8',
|
||||||
|
stdio: ['pipe', 'pipe', 'pipe'],
|
||||||
|
timeout: 30_000,
|
||||||
|
});
|
||||||
|
void out; // exit 0 is the assertion; execFileSync throws on nonzero
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -410,3 +410,40 @@ describe('#2665: scan-budget truncation', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#2665 round 4: CI wires the guard to strict mode', () => {
|
||||||
|
// The reversion this guards: dropping GSD_STRICT_LIVE_CONFIG_GUARD from
|
||||||
|
// test.yml silently demotes the guard back to report-only, and a future
|
||||||
|
// leak of exactly the class #2665 closes prints a warning and CI stays
|
||||||
|
// green. Windows lanes are deliberately report-only until the documented
|
||||||
|
// pre-existing USERPROFILE leak class is swept (SEVERITY note in
|
||||||
|
// scripts/live-config-guard.cjs) — so the assertion is per-OS, not global.
|
||||||
|
test('all three test jobs set GSD_STRICT_LIVE_CONFIG_GUARD (Windows carved out)', () => {
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
const wf = yaml.load(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.join(__dirname, '..', '.github', 'workflows', 'test.yml'),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const job of ['test', 'test-full']) {
|
||||||
|
const v = String(wf.jobs?.[job]?.env?.GSD_STRICT_LIVE_CONFIG_GUARD ?? '');
|
||||||
|
assert.match(
|
||||||
|
v,
|
||||||
|
// The WHOLE expression, anchored — a prefix match accepted both
|
||||||
|
// `&& '1' || '1'` (Windows silently strict) and a malformed tail
|
||||||
|
// (found by this round's pre-push adversarial review).
|
||||||
|
/^\$\{\{\s*matrix\.os\s*!=\s*'windows-latest'\s*&&\s*'1'\s*\|\|\s*''\s*\}\}$/,
|
||||||
|
`jobs.${job}.env.GSD_STRICT_LIVE_CONFIG_GUARD must be strict on ` +
|
||||||
|
`non-Windows lanes and empty on windows-latest; got: ${JSON.stringify(v)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
String(wf.jobs?.['test-inert']?.env?.GSD_STRICT_LIVE_CONFIG_GUARD ?? ''),
|
||||||
|
'1',
|
||||||
|
'jobs.test-inert (ubuntu-only) must set GSD_STRICT_LIVE_CONFIG_GUARD: 1',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -135,6 +135,27 @@ describe('#2858 — shipped scripts require only shipped paths', () => {
|
|||||||
.sort();
|
.sort();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('#2665: the test-instrumentation chain does not ship', () => {
|
||||||
|
// These four are one closed require chain of test instrumentation
|
||||||
|
// (run-tests -> live-config-guard, affected-tests-lib -> run-tests,
|
||||||
|
// run-affected-tests -> affected-tests-lib). Excluding a strict subset
|
||||||
|
// re-trips the shipped-requires-only-shipped gate above on whichever links
|
||||||
|
// still ship, so the exclusion set and this assertion cover the chain.
|
||||||
|
const TEST_INSTRUMENTATION = [
|
||||||
|
'scripts/live-config-guard.cjs',
|
||||||
|
'scripts/run-tests.cjs',
|
||||||
|
'scripts/affected-tests-lib.cjs',
|
||||||
|
'scripts/run-affected-tests.cjs',
|
||||||
|
];
|
||||||
|
for (const f of TEST_INSTRUMENTATION) {
|
||||||
|
assert.ok(
|
||||||
|
!shippedFiles.has(f),
|
||||||
|
`${f} is test instrumentation and must not ship — restore its ` +
|
||||||
|
"package.json files[] '!'-exclusion (and keep the whole chain excluded)",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
test('every shipped scripts/*.{cjs,js} is require-able from a shipped-only tree', () => {
|
test('every shipped scripts/*.{cjs,js} is require-able from a shipped-only tree', () => {
|
||||||
assert.ok(shippedScripts.length > 0, 'expected at least one shipped script');
|
assert.ok(shippedScripts.length > 0, 'expected at least one shipped script');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user