Files
ECC/tests/lib/helpers/windows-test-env.js
T
montjeffrey 20cd70e06d test: stop Windows developer tooling from leaking into setup, tar, and audit tests
On a Windows machine with Claude Code installed natively, Git for Windows,
and the ECC plugin installed, `node tests/run-all.js` on main fails 44 tests
in 7 files. Linux CI never sees them. Three of the four causes are test
isolation gaps, fixed here (43 tests):

- claude-plugin-setup, claude-scope-migration, setup (38): fixtures put a
  fake claude.cmd first on PATH, but on Windows spawning a bare `claude`
  resolves only .com/.exe, so a native ~/.local/bin/claude.exe ran instead
  of the fake. Fixture PATHs now drop entries holding claude.exe/claude.com.
- ecc-universal-bin, eval-harness-package (4): tar received an absolute
  C:\ archive path, which Git Bash's GNU tar reads as a host:path remote
  spec ("Cannot connect to C: resolve failed"). Extract from inside the
  destination with a relative, forward-slash archive name, which both GNU
  tar and bsdtar accept.
- harness-audit (1): the cache-layout test overrode HOME only, while
  findPluginInstall also reads USERPROFILE and os.homedir(), so the real
  installed plugin won over the fixture. Override USERPROFILE too.

The two helpers live in tests/lib/helpers/windows-test-env.js with a
platform-independent test, so Linux CI holds both invariants too.

The 44th failure, plugin-hook-bootstrap's no-bash test, is a production
bug rather than a test leak: System32\bash.exe launches an installed WSL
distro, which passes the bash probe. It gets a separate fix.
2026-10-09 14:32:52 -04:00

39 lines
1.4 KiB
JavaScript

/**
* Keep tests independent of tools installed on a Windows developer machine.
*
* Linux CI never sees these leaks, so they surface only as local failures.
*/
'use strict';
const fs = require('fs');
const path = require('path');
// On Windows, spawning a bare `claude` resolves only .com/.exe on PATH, never
// .cmd. A natively installed claude.exe therefore wins over a test's fake
// claude.cmd even when the fake's directory comes first on PATH.
const SHADOWING_CLAUDE_BINARIES = ['claude.com', 'claude.exe'];
/**
* Drop PATH entries holding a real Claude binary that would shadow a fake
* `claude.cmd` launcher. A no-op off Windows, where PATH order is honoured.
*/
function withoutShadowingClaude(pathValue, platform = process.platform) {
if (platform !== 'win32') return pathValue || '';
return String(pathValue || '')
.split(path.win32.delimiter)
.filter(dir => dir && !SHADOWING_CLAUDE_BINARIES.some(name => fs.existsSync(path.join(dir, name))))
.join(path.win32.delimiter);
}
/**
* Name an archive relative to the directory tar runs in, with forward slashes.
* GNU tar, which Git for Windows puts first on PATH, reads an archive argument
* containing a colon (C:\...) as a host:path remote spec; bsdtar does not.
*/
function toRelativeTarPath(cwd, archivePath) {
return path.relative(cwd, archivePath).split(path.sep).join('/');
}
module.exports = { withoutShadowingClaude, toRelativeTarPath };