fix(3597): make windows test cleanup retry on EBUSY + drop CRLF-broken local parseFrontmatter
Two windows-only failure clusters surfaced when the chunking fix in
52f23ac0 made Windows tests actually run:
1) Cleanup EBUSY race. On Windows, fs.rmSync without {maxRetries,retryDelay}
races against AV scanners / file-indexers / just-exited child processes
that still hold handles when teardown fires. Surfaces as
EBUSY: resource busy or locked, rmdir 'C:\Users\...\AppData\Local\Temp\gsd-...'
in bug-1736, bug-2248, bug-2698, bug-2838, and every test routing through
the shared tests/helpers.cjs cleanup() (≈170 consumers — bug-2774 et al).
Fix: add {maxRetries: 10, retryDelay: 100} to the shared helper plus the
four inline cleanup sites. On POSIX the retry loop is dead code (first
try succeeds), so no impact on ubuntu/macos.
2) bug-2839 had a local parseFrontmatter that anchored on /^---\n/ — on a
Windows checkout with autocrlf=true the file content is CRLF, the regex
never matches, the function returns null, and the before() hook asserts
"agent must have YAML frontmatter" → entire suite cancels. The shared
tests/helpers.cjs parseFrontmatter is already CRLF-aware (split /\r?\n/);
switch to it.
Validated: plex2 (ubuntu docker) 11224/0 pass post-patch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,7 @@ describe('#1736: local Claude install populates .claude/commands/gsd/', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test('local install creates .claude/commands/gsd/ directory', (t) => {
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('#2248: local Claude install does not clobber profile-level statusLine'
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
});
|
||||
|
||||
test('local install does not write statusLine to .claude/settings.json', (t) => {
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('#2698: CRLF stale gsd-update-check block is removed on Codex reinstall
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
});
|
||||
|
||||
// Helper: pre-populate .codex/config.toml with a GSD marker + stale hooks block
|
||||
|
||||
@@ -163,7 +163,7 @@ ${rescueBlock}
|
||||
}
|
||||
|
||||
function cleanup(tmp) {
|
||||
try { fs.rmSync(tmp, { recursive: true, force: true }); } catch (_) {}
|
||||
try { fs.rmSync(tmp, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } catch (_) {}
|
||||
}
|
||||
|
||||
describe('bug-2838: SUMMARY rescue handles gitignored .planning/', () => {
|
||||
|
||||
@@ -30,19 +30,9 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SENTINEL_NAME = '.review-fix-recovery-pending.json';
|
||||
const { parseFrontmatter } = require('./helpers.cjs');
|
||||
|
||||
function parseFrontmatter(content) {
|
||||
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!match) return null;
|
||||
const body = match[1];
|
||||
const out = {};
|
||||
for (const line of body.split('\n')) {
|
||||
const m = line.match(/^([a-zA-Z_]+):\s*(.*)$/);
|
||||
if (m) out[m[1]] = m[2].trim();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const SENTINEL_NAME = '.review-fix-recovery-pending.json';
|
||||
|
||||
function extractStep(content, stepName) {
|
||||
const re = new RegExp(`<step\\s+name="${stepName}">([\\s\\S]*?)</step>`);
|
||||
|
||||
@@ -104,7 +104,10 @@ function createTempGitProject(prefix = 'gsd-test-') {
|
||||
}
|
||||
|
||||
function cleanup(tmpDir) {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
// maxRetries/retryDelay absorbs transient Windows EBUSY where AV scanners,
|
||||
// file-indexers, or just-exited child processes still hold handles when
|
||||
// teardown runs. On POSIX the retry loop is a no-op (rmSync succeeds first try).
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user