Repository navigation
Description
Summary
claude-settings.test.ts runs installStatusLine against the real home directory: one test resets the ccstatusline config path to the actual ~/.config/ccstatusline/settings.json, and another test deletes CLAUDE_CONFIG_DIR without restoring it, so the rest of the file (including every install test) operates on the real ~/.claude/settings.json. When config validation is broken — e.g. under a mutation-testing run, or any regression in the settings parser — these tests silently destroy a developer's real configs.
I just lost both of mine to this while running Stryker over config.ts / claude-settings.ts (details below).
The code (v2.2.32, src/utils/__tests__/claude-settings.test.ts)
- Line ~189: this test deletes
process.env.CLAUDE_CONFIG_DIRand theafterEachnever restores it (restoreAllMocks,initConfigPath(),rmSync— no env restore):
it('should resolve .claude.json beside the default Claude config dir when CLAUDE_CONFIG_DIR is unset', () => {
delete process.env.CLAUDE_CONFIG_DIR;
...
});- Line ~203: this test calls
config.initConfigPath()with no argument — that resets the ccstatusline config path to the real~/.config/ccstatusline/settings.json— and then runs a real install:
it('should use base command when no custom config path', async () => {
config.initConfigPath();
await installStatusLine({ commandMode: 'auto-npx' });
expect(readInstalledCommand()).toBe(CCSTATUSLINE_COMMANDS.NPM);
});installStatusLine at this point runs with the real ~/.claude/settings.json (see 1) and the real ccstatusline config path (see 2). The same pattern repeats at lines ~333 and ~339.
What actually happened on my machine
Running Stryker with --mutate src/utils/config.ts,src/utils/claude-settings.ts,src/utils/powerline.ts:
- A mutant in config validation made
loadSettingsreject my real (rich, heavily customized)~/.config/ccstatusline/settings.json→ the code path materialised default settings over it. The file had been maintained through the TUI for weeks; no backup survived, because the.bakwritten by a later test in the same run had already captured the destroyed state (each install test backs up whatever the previous one left). - Another install test wrote
statusLine.command = sh <stryker sandbox>/client/ccstatusline-ipcinto my real~/.claude/settings.json. After the run Stryker deletes.stryker-tmp/, so the referenced file was gone — my Claude Code status line rendered empty until I noticed and reinstalled.
Both files are user-owned state holding real data (hooks, env, apiKeyHelper, a hand-tuned status line). The tests pass green while doing this; nothing in the suite notices.
Why plain bun test is at risk too
On clean code the config is valid, so loadSettings succeeds and the ccstatusline file is "only" read. But the ~/.claude/settings.json overwrite happens on every run: any developer (or CI job) with a personal statusLine, hooks or env in that file has it replaced by a bare {"statusLine": …} written from the test's own CLAUDE_CONFIG_DIR-less state — the .bak/.orig backups then contain whatever earlier tests in the same process already wrote.
Suggested fix
Isolate the whole file the way #653 did for the config tests:
- in
beforeEach, point bothHOMEandCLAUDE_CONFIG_DIRat a fresh temp directory (and restore both inafterEach, including after thedeletein the path-resolution test); - never call
config.initConfigPath()without a temp path in a test that executes installs; - optionally assert at the end of the suite that nothing outside the temp dir was touched.
For anyone running mutation testing over these files in the meantime, a global-setup that redirects HOME/CLAUDE_CONFIG_DIR for the whole sandbox run (like Stryker's globalSetup) is a working band-aid.
Activity
test(usage): reproduce upstream settings-privacy and usage-network fi…
test(claude-settings): isolate install tests from the real home direc…
fix(ipc): harden the visible-degradation fallback (#65)
fix(ipc): degrade visibly instead of an empty status line (#65) (#66)