fix: harden windows CI tests and markdown lint

This commit is contained in:
Affaan Mustafa
2026-03-20 03:03:57 -07:00
parent cbccb7fdc0
commit e70c43bcd4
6 changed files with 51 additions and 20 deletions

View File

@@ -28,6 +28,13 @@ function makeTempDir() {
return fs.mkdtempSync(path.join(os.tmpdir(), 'cost-tracker-test-'));
}
function withTempHome(homeDir) {
return {
HOME: homeDir,
USERPROFILE: homeDir,
};
}
function runScript(input, envOverrides = {}) {
const inputStr = typeof input === 'string' ? input : JSON.stringify(input);
const result = spawnSync('node', [script], {
@@ -64,7 +71,7 @@ function runTests() {
model: 'claude-sonnet-4-20250514',
usage: { input_tokens: 1000, output_tokens: 500 },
};
const result = runScript(input, { HOME: tmpHome });
const result = runScript(input, withTempHome(tmpHome));
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
const metricsFile = path.join(tmpHome, '.claude', 'metrics', 'costs.jsonl');
@@ -84,7 +91,7 @@ function runTests() {
// 3. Handles empty input gracefully
(test('handles empty input gracefully', () => {
const tmpHome = makeTempDir();
const result = runScript('', { HOME: tmpHome });
const result = runScript('', withTempHome(tmpHome));
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
// stdout should be empty since input was empty
assert.strictEqual(result.stdout, '', 'Expected empty stdout for empty input');
@@ -96,7 +103,7 @@ function runTests() {
(test('handles invalid JSON gracefully', () => {
const tmpHome = makeTempDir();
const invalidInput = 'not valid json {{{';
const result = runScript(invalidInput, { HOME: tmpHome });
const result = runScript(invalidInput, withTempHome(tmpHome));
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
// Should still pass through the raw input on stdout
assert.strictEqual(result.stdout, invalidInput, 'Expected stdout to contain original invalid input');
@@ -109,7 +116,7 @@ function runTests() {
const tmpHome = makeTempDir();
const input = { model: 'claude-sonnet-4-20250514' };
const inputStr = JSON.stringify(input);
const result = runScript(input, { HOME: tmpHome });
const result = runScript(input, withTempHome(tmpHome));
assert.strictEqual(result.code, 0, `Expected exit code 0, got ${result.code}`);
assert.strictEqual(result.stdout, inputStr, 'Expected stdout to match original input');

View File

@@ -41,6 +41,20 @@ function cleanupDir(dir) {
}
}
function toBashPath(filePath) {
if (process.platform !== 'win32') {
return filePath;
}
return String(filePath)
.replace(/^([A-Za-z]):/, (_, driveLetter) => `/${driveLetter.toLowerCase()}`)
.replace(/\\/g, '/');
}
function runBash(command, options = {}) {
return execSync(`bash -lc '${command.replace(/'/g, "'\\''")}'`, options).toString().trim();
}
const repoRoot = path.resolve(__dirname, '..', '..');
const detectProjectPath = path.join(
repoRoot,
@@ -98,7 +112,7 @@ test('[ -d ] returns true for .git directory', () => {
const dir = path.join(behaviorDir, 'test-d-dir');
fs.mkdirSync(dir, { recursive: true });
fs.mkdirSync(path.join(dir, '.git'));
const result = execSync(`bash -c '[ -d "${dir}/.git" ] && echo yes || echo no'`).toString().trim();
const result = runBash(`[ -d "${toBashPath(path.join(dir, '.git'))}" ] && echo yes || echo no`);
assert.strictEqual(result, 'yes');
});
@@ -106,7 +120,7 @@ test('[ -d ] returns false for .git file', () => {
const dir = path.join(behaviorDir, 'test-d-file');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, '.git'), 'gitdir: /some/path\n');
const result = execSync(`bash -c '[ -d "${dir}/.git" ] && echo yes || echo no'`).toString().trim();
const result = runBash(`[ -d "${toBashPath(path.join(dir, '.git'))}" ] && echo yes || echo no`);
assert.strictEqual(result, 'no');
});
@@ -114,7 +128,7 @@ test('[ -e ] returns true for .git directory', () => {
const dir = path.join(behaviorDir, 'test-e-dir');
fs.mkdirSync(dir, { recursive: true });
fs.mkdirSync(path.join(dir, '.git'));
const result = execSync(`bash -c '[ -e "${dir}/.git" ] && echo yes || echo no'`).toString().trim();
const result = runBash(`[ -e "${toBashPath(path.join(dir, '.git'))}" ] && echo yes || echo no`);
assert.strictEqual(result, 'yes');
});
@@ -122,14 +136,14 @@ test('[ -e ] returns true for .git file', () => {
const dir = path.join(behaviorDir, 'test-e-file');
fs.mkdirSync(dir, { recursive: true });
fs.writeFileSync(path.join(dir, '.git'), 'gitdir: /some/path\n');
const result = execSync(`bash -c '[ -e "${dir}/.git" ] && echo yes || echo no'`).toString().trim();
const result = runBash(`[ -e "${toBashPath(path.join(dir, '.git'))}" ] && echo yes || echo no`);
assert.strictEqual(result, 'yes');
});
test('[ -e ] returns false when .git does not exist', () => {
const dir = path.join(behaviorDir, 'test-e-none');
fs.mkdirSync(dir, { recursive: true });
const result = execSync(`bash -c '[ -e "${dir}/.git" ] && echo yes || echo no'`).toString().trim();
const result = runBash(`[ -e "${toBashPath(path.join(dir, '.git'))}" ] && echo yes || echo no`);
assert.strictEqual(result, 'no');
});
@@ -188,20 +202,21 @@ test('detect-project.sh sets PROJECT_NAME and non-global PROJECT_ID for worktree
// Source detect-project.sh from the worktree directory and capture results
const script = `
export CLAUDE_PROJECT_DIR="${worktreeDir}"
export HOME="${testDir}"
source "${detectProjectPath}"
export CLAUDE_PROJECT_DIR="${toBashPath(worktreeDir)}"
export HOME="${toBashPath(testDir)}"
source "${toBashPath(detectProjectPath)}"
echo "PROJECT_NAME=\${PROJECT_NAME}"
echo "PROJECT_ID=\${PROJECT_ID}"
`;
const result = execSync(`bash -c '${script.replace(/'/g, "'\\''")}'`, {
const result = execSync(`bash -lc '${script.replace(/'/g, "'\\''")}'`, {
cwd: worktreeDir,
timeout: 10000,
env: {
...process.env,
HOME: testDir,
CLAUDE_PROJECT_DIR: worktreeDir
HOME: toBashPath(testDir),
USERPROFILE: testDir,
CLAUDE_PROJECT_DIR: toBashPath(worktreeDir)
}
}).toString();

View File

@@ -16,7 +16,7 @@ function toBashPath(filePath) {
}
return String(filePath)
.replace(/^([A-Za-z]):/, (_, driveLetter) => `/mnt/${driveLetter.toLowerCase()}`)
.replace(/^([A-Za-z]):/, (_, driveLetter) => `/${driveLetter.toLowerCase()}`)
.replace(/\\/g, '/');
}