test(cc-config): cover the env runClaudePlugin actually spawns with
`autoInstallSkillPlugins` takes `run` as a seam, so every existing test stops short of the real `spawnSync` — the line that decides whether CLAUDE_CONFIG_DIR is pinned was untested. Restoring the unconditional pin here (the bug that sent `.claude.json` to `~/.claude/.claude.json`) kept the whole suite green. Export `runClaudePlugin` and drive it against a fake `claude` on PATH that prints the variable back: one test for CLAUDE_HOME set, one for unset. Mutation-checked — the unconditional pin fails the second.
This commit is contained in:
@@ -238,3 +238,52 @@ describe("autoInstallSkillPlugins", () => {
|
||||
assert.deepEqual(run.calls, []);
|
||||
});
|
||||
});
|
||||
|
||||
describe("runClaudePlugin: the env it spawns with", () => {
|
||||
// Every other test in this file injects `run`, so nothing exercises the real
|
||||
// spawn. That gap is how a wrong CLAUDE_CONFIG_DIR pin lived here: the CLI
|
||||
// writes `.claude.json` INSIDE that dir, while cc-discovery reads it beside
|
||||
// ~/.claude by default, so pinning it unconditionally aimed writes at a file
|
||||
// nothing reads. cc-plugin-cli.js's runClaude has the mirror of this test.
|
||||
const PRINT_ENV = 'printf "%s\\n" "CLAUDE_CONFIG_DIR=${CLAUDE_CONFIG_DIR-<unset>}"';
|
||||
|
||||
function stubClaude(body) {
|
||||
const bin = fs.mkdtempSync(path.join(os.tmpdir(), "agenthub-skillplug-bin-"));
|
||||
const file = path.join(bin, "claude");
|
||||
fs.writeFileSync(file, `#!/usr/bin/env bash\n${body}\n`);
|
||||
fs.chmodSync(file, 0o755);
|
||||
const originalPath = process.env.PATH;
|
||||
process.env.PATH = `${bin}${path.delimiter}${originalPath}`;
|
||||
return () => {
|
||||
process.env.PATH = originalPath;
|
||||
fs.rmSync(bin, { recursive: true, force: true });
|
||||
};
|
||||
}
|
||||
|
||||
it("leaves CLAUDE_CONFIG_DIR unset when CLAUDE_HOME is not set", () => {
|
||||
const original = process.env.CLAUDE_HOME;
|
||||
delete process.env.CLAUDE_HOME;
|
||||
const restore = stubClaude(PRINT_ENV);
|
||||
try {
|
||||
const res = skillPlugins.runClaudePlugin(["plugin", "list"], 10_000);
|
||||
assert.equal(res.output.trim(), "CLAUDE_CONFIG_DIR=<unset>");
|
||||
} finally {
|
||||
restore();
|
||||
if (original !== undefined) process.env.CLAUDE_HOME = original;
|
||||
}
|
||||
});
|
||||
|
||||
it("pins CLAUDE_CONFIG_DIR to CLAUDE_HOME when the operator set it", () => {
|
||||
const original = process.env.CLAUDE_HOME;
|
||||
process.env.CLAUDE_HOME = "/tmp/some-other-claude-home";
|
||||
const restore = stubClaude(PRINT_ENV);
|
||||
try {
|
||||
const res = skillPlugins.runClaudePlugin(["plugin", "list"], 10_000);
|
||||
assert.equal(res.output.trim(), "CLAUDE_CONFIG_DIR=/tmp/some-other-claude-home");
|
||||
} finally {
|
||||
restore();
|
||||
if (original === undefined) delete process.env.CLAUDE_HOME;
|
||||
else process.env.CLAUDE_HOME = original;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -220,4 +220,8 @@ module.exports = {
|
||||
requiredSkillPlugins,
|
||||
missingSkillPlugins,
|
||||
autoInstallSkillPlugins,
|
||||
// Exported only so a test can pin the env it spawns with. Every other test
|
||||
// injects `run` and so never reaches the real spawn — which is how an
|
||||
// unconditional CLAUDE_CONFIG_DIR pin could be restored here unnoticed.
|
||||
runClaudePlugin,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user