Files
msd-core/tests/legacy-cleanup-config-dir.test.cjs
Tom Boucher f16ff7d1b3 enhance(#3545): widen no-source-grep with fold+hooks, migrate 76 sites (#4161)
* feat(#3545): widen no-source-grep with one-hop path-fold and hooks dir

Resolve a readFileSync() path argument that is a bare Identifier one hop
back to its VariableDeclarator initializer before classification, and
recognize `hooks` as a source directory alongside bin/lib/gsd-core/src.

Measured (epic #3464 phase 7): fold+hooks together newly flag 76
unsuppressed sites across 18 files that were previously invisible to
identifier-indirected or hooks/-rooted source reads. Neither widening
alone is sufficient — hooks-only surfaces 0 new sites, confirming #3520's
prior finding that the identifier-indirection gap must close first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(#3545): migrate 76 sites newly flagged by the fold+hooks widening

Per-site classification: rewrite behaviorally (require() the real module,
assert on its actual exported behavior) wherever the read was a proxy for
code behavior; add a site-scoped `// allow-test-rule: <reason> (#3545)`
marker only where the raw source text genuinely is the product under test
(codex-config.test.cjs's adapter-header-contract checks, install.js
structural-wiring guards with no exported symbol, AST-parse fixture
inputs, etc.) — each marker cites an existing repo-sanctioned category
from CONTRIBUTING.md's allow-test-rule exception table.

Also converts two try/finally test bodies (introduced during this same
migration) to the required t.after() cleanup pattern per CONTRIBUTING.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* chore(#3545): re-baseline effective-exemption ceiling to 81

The fold+hooks widening's own newly-detected sites are now suppressed by
site-scoped markers, moving them from invisible into the tightly-ratcheted
effective-exemption count. Ceiling rises from 10 to 81 (the exact measured
high-water mark, grace unchanged at 2) — a deliberate, measured re-baseline
per the widening working as intended, not an ordinary ceiling bump.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(#3545): use canonical allow-test-rule category tokens

4 markers added during migration cited an issue ref correctly but didn't
use one of CONTRIBUTING.md's seven recognized category tokens, unlike
every other marker in this change. Cosmetic only — same suppression
lines, same effective/live counts (81/81, 0 live).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(#3545): correct stale phase-artifact path in test comment

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-01 21:40:38 -04:00

141 lines
6.1 KiB
JavaScript

'use strict';
// ─────────────────────────────────────────────────────────────────────────────
// #3799 — legacy cleanup must honor the install's resolved destination.
//
// `install()` called `cleanupLegacyGsdCc({ dryRun: false })` with no
// arguments, so the scan always ran against `os.homedir()` +
// `_LEGACY_SCAN_SUBDIR_NAMES` — an install redirected with `--config-dir
// ~/.sandbox` planned removals of a LIVE legacy install under the DEFAULT
// home (the reporter's dry-run listed 60 default-home paths; a real install
// would have deleted 59 files). The cleanup now accepts an explicit
// `configDirs` scope (install() passes `[targetDir]` when --config-dir
// redirected the destination), and `--no-legacy-cleanup` skips the scan.
// ─────────────────────────────────────────────────────────────────────────────
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
process.env.GSD_TEST_MODE = '1';
const REPO_ROOT = path.join(__dirname, '..');
const INSTALL_BIN = path.join(REPO_ROOT, 'bin', 'install.js');
const { cleanupLegacyGsdCc } = require(INSTALL_BIN);
const { createTempDir, cleanup } = require('./helpers.cjs');
const { runNode } = require('./helpers/process-seam.cjs');
const LEGACY_PKG_SIGNAL = 'get-shit-done-cc';
function seedLegacySkill(configDir) {
// configDir is a CONFIG DIR root (like ~/.claude): artifacts live at
// <configDir>/skills/gsd-*/SKILL.md. The scan's content signal is a stale
// '/get-shit-done/' PATH reference (legacy-cleanup.cjs // gsd-allow-legacy-name
// LEGACY_SKILL_PATH_SIGNAL), not the bare package name.
const skillDir = path.join(configDir, 'skills', 'gsd-add-tests');
fs.mkdirSync(skillDir, { recursive: true });
fs.writeFileSync(
path.join(skillDir, 'SKILL.md'),
`<!-- installed via ${LEGACY_PKG_SIGNAL} -->\nSee ~/.claude/get-shit-done/skills/gsd-add-tests/SKILL.md\n`, // gsd-allow-legacy-name
);
return path.join(skillDir, 'SKILL.md');
}
describe('#3799: cleanupLegacyGsdCc honors an explicit configDirs scope', () => {
let tmpRoot;
let defaultHome;
let sandboxDir;
beforeEach(() => {
tmpRoot = createTempDir('gsd-3799-cleanup-');
defaultHome = path.join(tmpRoot, 'home');
sandboxDir = path.join(tmpRoot, 'pilot-sandbox');
fs.mkdirSync(defaultHome, { recursive: true });
fs.mkdirSync(sandboxDir, { recursive: true });
});
afterEach(() => {
cleanup(tmpRoot);
});
test('#3799: an explicit configDirs scope never plans removals outside it', () => {
// A LIVE legacy install under the default home (at <home>/.claude, one
// of _LEGACY_SCAN_SUBDIR_NAMES)…
const liveArtifact = seedLegacySkill(path.join(defaultHome, '.claude'));
// …and one stale artifact inside the redirected sandbox.
const sandboxArtifact = seedLegacySkill(sandboxDir);
const { plan } = cleanupLegacyGsdCc({
homeDir: defaultHome,
configDirs: [sandboxDir],
dryRun: true,
logger: { log: () => {} },
});
const paths = plan.map((p) => p.path);
assert.ok(
paths.every((p) => p.startsWith(sandboxDir)),
`#3799: every plan entry must live inside the explicit scope; got ${JSON.stringify(paths)}`,
);
assert.ok(paths.includes(sandboxArtifact), 'the sandbox artifact is in scope and planned');
assert.ok(
!paths.includes(liveArtifact),
'#3799: the default home\'s live legacy install must not be planned for removal',
);
assert.ok(
fs.existsSync(liveArtifact),
'dry-run leaves everything in place',
);
});
test('#3799 control: without the override, the home scan is unchanged', () => {
const liveArtifact = seedLegacySkill(path.join(defaultHome, '.claude'));
const { plan } = cleanupLegacyGsdCc({
homeDir: defaultHome,
dryRun: true,
logger: { log: () => {} },
});
assert.ok(
plan.map((p) => p.path).includes(liveArtifact),
'default behavior (no scope) still scans homeDir subdirs as before',
);
});
});
describe('#3799: --no-legacy-cleanup and --config-dir CLI flags', () => {
test('the --no-legacy-cleanup flag exists and is documented in --help', () => {
const result = runNode([INSTALL_BIN, '--help']);
assert.equal(result.outcome, 'exited', `install.js --help did not exit cleanly: ${result.stderr}`);
assert.ok(
result.stdout.includes('--no-legacy-cleanup'),
'the escape hatch must be documented in the installer --help output',
);
});
test('the install() call site threads the config-dir scope and the skip flag', () => {
// allow-test-rule: structural-implementation-guard (#3545) — structural assertion
// on install()'s internal wiring (that its cleanupLegacyGsdCc call site threads configDirs/
// skipNoLegacyCleanup) — install() is a ~2500-line, side-effect-heavy
// top-level installer routine, so exercising this specific plumbing
// behaviorally would require a full install() run; the source-slice
// check is the minimum-cost regression guard for the exact #3799 defect
// shape
const src = fs.readFileSync(INSTALL_BIN, 'utf-8');
// Slice the install() body up to its cleanup call so the conditional
// spread's braces cannot defeat a single-regex match.
const fnStart = src.indexOf('function install(isGlobal, runtime = DEFAULT_RUNTIME');
const callIdx = src.indexOf('cleanupLegacyGsdCc({ dryRun: false', fnStart);
assert.ok(fnStart > 0 && callIdx > fnStart, 'install() still calls cleanupLegacyGsdCc');
const callSite = src.slice(callIdx, callIdx + 240);
assert.ok(
/configDirs/.test(callSite),
'#3799: the call site must pass a configDirs scope',
);
assert.ok(
/skipNoLegacyCleanup/.test(src.slice(fnStart, callIdx + 400)),
'#3799: the call site must honor the --no-legacy-cleanup skip',
);
});
});