From bd966c9bfa2b4e535fca47093753e883f4221794 Mon Sep 17 00:00:00 2001 From: haelyra <49814733+haelyra@users.noreply.github.com> Date: Sun, 11 Oct 2026 02:57:28 -0400 Subject: [PATCH] test: reproduce commands-only ESM runtime and scope conflicts Actual installed skills-health --help fails with require undefined in ESM Claude project and home fixtures. RED evidence: command-runtime-evidence/red-final.log; bounded repair remains pending. --- tests/scripts/command-runtime.test.js | 157 ++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 tests/scripts/command-runtime.test.js diff --git a/tests/scripts/command-runtime.test.js b/tests/scripts/command-runtime.test.js new file mode 100644 index 000000000..765f971ba --- /dev/null +++ b/tests/scripts/command-runtime.test.js @@ -0,0 +1,157 @@ +// Actual commands-only install lifecycle and module-scope regression coverage. +'use strict'; +const assert = require('assert'); +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const { spawnSync } = require('child_process'); +const { pathToFileURL } = require('url'); +const { createManifestInstallPlan, applyInstallPlan, previewInstallPlan } = require('../../scripts/lib/install-executor'); +const { withHookConsent } = require('../../scripts/lib/install/hook-consent'); +const { repairInstalledStates } = require('../../scripts/lib/install-lifecycle'); +const repo = path.resolve(__dirname, '../..'); +let passed = 0; +let failed = 0; +function snapshot(root) { + if (!fs.existsSync(root)) return {}; + return Object.fromEntries(fs.readdirSync(root, { recursive: true, withFileTypes: true }) + .filter(entry => entry.isFile()) + .map(entry => { const file = path.join(entry.parentPath || entry.path, entry.name); + return [path.relative(root, file), require('crypto').createHash('sha256').update(fs.readFileSync(file)).digest('hex')]; }).sort()); +} +function test(name, fn, target = 'claude-project') { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-command-runtime-'))); + const home = path.join(dir, 'home'); + const project = path.join(dir, 'project'); + fs.mkdirSync(home); fs.mkdirSync(project); + const root = target === 'claude' ? path.join(home, '.claude') : path.join(project, '.claude'); + const scripts = path.join(root, 'scripts'); + const marker = path.join(scripts, 'package.json'); + const write = (file, bytes) => { fs.mkdirSync(path.dirname(file), { recursive: true }); fs.writeFileSync(file, bytes); }; + const env = { ...process.env, HOME: home, USERPROFILE: home, CLAUDE_CONFIG_DIR: path.join(home, '.claude') }; + const run = (file, args) => spawnSync(process.execPath, [file, ...args], { + cwd: project, env, encoding: 'utf8', timeout: 30000, maxBuffer: 8 * 1024 * 1024 }); + const install = () => run(path.join(repo, 'scripts/install-apply.js'), ['--target', target, '--modules', 'commands-core', '--no-hooks', '--json']); + const uninstall = () => run(path.join(repo, 'scripts/uninstall.js'), ['--target', target, '--json']); + const plan = () => withHookConsent(createManifestInstallPlan({ sourceRoot: repo, homeDir: home, + projectRoot: project, env: {}, target, moduleIds: ['commands-core'] }), 'declined'); + const statePath = path.join(root, 'ecc/install-state.json'); + const state = () => JSON.parse(fs.readFileSync(statePath, 'utf8')); + const ok = result => assert.strictEqual(result.status, 0, result.stderr || result.stdout); + const health = () => { + const file = path.join(scripts, 'skills-health.js'); + const help = run(file, ['--help']); ok(help); assert.match(help.stdout, /Usage:/); + const result = run(file, ['--json', '--home', home, '--skills-root', path.join(dir, 'none'), + '--learned-root', path.join(dir, 'none2'), '--imported-root', path.join(dir, 'none3')]); + ok(result); assert.deepStrictEqual(JSON.parse(result.stdout).skills, []); + }; + const probe = file => { + const result = run('--input-type=module', ['-e', `const m=await import(${JSON.stringify(pathToFileURL(file).href)}); console.log(m.default ?? m.value);`]); + ok(result); return result.stdout.trim(); + }; + const noPayload = () => { + for (const file of ['settings.json', '.mcp.json', 'hooks/hooks.json', 'scripts/hooks']) { + assert.ok(!fs.existsSync(path.join(root, file)), `Unexpected ${file}`); + } + assert.ok(!state().operations.some(op => ['hooks-runtime', 'mcp-configs'].includes(op.moduleId))); + }; + try { fn({ dir, home, project, root, scripts, marker, write, install, uninstall, plan, + state, statePath, ok, health, probe, noPayload, run, target }); passed++; console.log(`PASS ${name}`); } + catch (error) { failed++; console.error(`FAIL ${name}: ${error.stack}`); } + finally { fs.rmSync(dir, { recursive: true, force: true }); } +} +for (const target of ['claude-project', 'claude']) { + test(`${target}: fresh ESM commands-only CLI, receipt, reinstall, uninstall`, f => { + const projectBytes = '{ "type": "module", "user": "project" }\n'; + const rootBytes = '{ "type": "module", "user": "claude" }\n'; + f.write(path.join(f.project, 'package.json'), projectBytes); + f.write(path.join(f.root, 'package.json'), rootBytes); + f.ok(f.install()); f.health(); f.noPayload(); + assert.deepStrictEqual(JSON.parse(fs.readFileSync(f.marker, 'utf8')), { type: 'commonjs' }); + const owned = f.state().operations.filter(op => op.destinationPath === f.marker); + assert.strictEqual(owned.length, 1); assert.strictEqual(owned[0].ownership, 'managed'); + assert.strictEqual(owned[0].moduleId, 'commands-core'); + assert.ok(f.state().operations.some(op => op.moduleId === 'shared-script-utils' && op.destinationPath.endsWith('utils.js'))); + assert.ok(!fs.existsSync(path.join(f.scripts, 'lib/package.json'))); + f.ok(f.install()); f.health(); f.noPayload(); + assert.strictEqual(f.state().operations.filter(op => op.destinationPath === f.marker).length, 1); + f.ok(f.uninstall()); assert.ok(!fs.existsSync(f.marker)); assert.ok(!fs.existsSync(f.statePath)); + assert.strictEqual(fs.readFileSync(path.join(f.project, 'package.json'), 'utf8'), projectBytes); + assert.strictEqual(fs.readFileSync(path.join(f.root, 'package.json'), 'utf8'), rootBytes); + }, target); +} +for (const [scope, bytes] of [ + ['package.json', '{"type":"module"}\n'], ['package.json', '{broken'], + ['lib/package.json', '{"type":"module"}\n'], ['lib/package.json', '{broken'], + ['lib/skill-evolution/package.json', '{"type":"module"}\n'], + ['lib/astra-review/package.json', '{"type":"module"}\n'], + ['ci/package.json', '{"type":"module"}\n'], + ['lib/package.json', '[]'], ['lib/package.json', '{"type":"unexpected"}'], +]) { + test(`refuses incompatible ${scope} ${bytes} before writes`, f => { + f.write(path.join(f.project, 'package.json'), '{"type":"module"}\n'); + f.write(path.join(f.scripts, scope), bytes); + const before = snapshot(f.dir); + const result = f.install(); assert.notStrictEqual(result.status, 0, 'Install falsely reported success'); + assert.deepStrictEqual(snapshot(f.dir), before); + assert.throws(() => previewInstallPlan(f.plan())); assert.deepStrictEqual(snapshot(f.dir), before); + }); +} +for (const userFile of ['user.js', 'lib/user.js', 'lib/skill-evolution/user.js', 'ci/user.js', 'lib/utils.js']) { + test(`refuses unowned JS ${userFile} without scope mutation`, f => { + f.write(path.join(f.project, 'package.json'), '{"type":"module"}\n'); + const file = path.join(f.scripts, userFile); f.write(file, 'export const value = 42;\n'); + assert.strictEqual(f.probe(file), '42'); const before = snapshot(f.dir); + const result = f.install(); assert.notStrictEqual(result.status, 0); + assert.match(result.stderr + result.stdout, /unowned or edited JavaScript/); + assert.deepStrictEqual(snapshot(f.dir), before); assert.strictEqual(f.probe(file), '42'); + }); +} +for (const bytes of ['{ "type": "commonjs", "user": true }\n', '{"user":true}\n']) { + test('compatible scripts metadata remains unchanged and unowned', f => { + f.write(f.marker, bytes); const file = path.join(f.scripts, 'user.js'); f.write(file, 'module.exports = 43;\n'); + f.ok(f.install()); f.health(); + assert.ok(!f.state().operations.some(op => op.destinationPath === f.marker)); + f.ok(f.install()); f.health(); f.ok(f.uninstall()); + assert.strictEqual(fs.readFileSync(f.marker, 'utf8'), bytes); assert.strictEqual(f.probe(file), '43'); + }); +} +test('unrelated nested ESM package keeps interpretation and bytes', f => { + f.write(path.join(f.project, 'package.json'), '{"type":"module"}\n'); + const nested = path.join(f.scripts, 'user/package.json'); const bytes = '{"type":"module"}\n'; + const file = path.join(f.scripts, 'user/index.js'); f.write(nested, bytes); f.write(file, 'export const value = 44;\n'); + f.ok(f.install()); f.health(); assert.strictEqual(f.probe(file), '44'); + f.ok(f.uninstall()); assert.strictEqual(fs.readFileSync(nested, 'utf8'), bytes); assert.strictEqual(f.probe(file), '44'); +}); +test('compatible nested helper scope remains unchanged and unowned', f => { + const file = path.join(f.scripts, 'lib/package.json'); const bytes = '{"type":"commonjs","keep":true}\n'; + f.write(file, bytes); f.ok(f.install()); f.health(); f.ok(f.uninstall()); + assert.strictEqual(fs.readFileSync(file, 'utf8'), bytes); +}); +test('edited owned JS rejects missing-marker reinstall before any writes', f => { + f.ok(f.install()); fs.unlinkSync(f.marker); + f.write(path.join(f.scripts, 'lib/utils.js'), 'export const value = 45;\n'); + const before = snapshot(f.dir); const result = f.install(); assert.notStrictEqual(result.status, 0); + assert.match(result.stderr + result.stdout, /unowned or edited JavaScript/); assert.deepStrictEqual(snapshot(f.dir), before); +}); +test('missing owned marker restores safely on reinstall', f => { + f.ok(f.install()); fs.unlinkSync(f.marker); f.ok(f.install()); f.health(); f.ok(f.uninstall()); +}); +test('repair restores missing owned marker', f => { + f.ok(f.install()); fs.unlinkSync(f.marker); + const result = repairInstalledStates({ repoRoot: repo, homeDir: f.home, projectRoot: f.project, env: {}, targets: [f.target] }); + assert.strictEqual(result.summary.errorCount, 0); f.health(); f.ok(f.uninstall()); +}); +test('publication rechecks newly introduced unowned JS', f => { + assert.throws(() => applyInstallPlan(f.plan(), { beforeOperationWrite({ operation }) { + if (operation.destinationPath === f.marker) f.write(path.join(f.scripts, 'user.js'), 'export const value = 46;\n'); + } }), /unowned or edited JavaScript/); + assert.ok(!fs.existsSync(f.marker)); +}); +test('publication rechecks nested helper package conflict', f => { + assert.throws(() => applyInstallPlan(f.plan(), { beforeOperationWrite({ operation }) { + if (operation.destinationPath === f.marker) f.write(path.join(f.scripts, 'lib/package.json'), '{"type":"module"}\n'); + } }), /module boundary/); + assert.ok(!fs.existsSync(f.marker)); +}); +console.log(`Passed: ${passed}`); console.log(`Failed: ${failed}`); process.exitCode = failed ? 1 : 0;