fix(windows): stabilize init/workflow tests across path and npm edge cases
This commit is contained in:
@@ -18,6 +18,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const { spawnSync } = require('node:child_process');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const HOOK_PATH = path.resolve(__dirname, '..', 'hooks', 'gsd-context-monitor.js');
|
||||
|
||||
@@ -99,10 +100,7 @@ describe('#1974 context exhaustion auto-record', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Windows: AV/file-indexer/not-yet-exited fire-and-forget subprocess may
|
||||
// still hold a handle on tmpDir at teardown. Match the helpers.cleanup()
|
||||
// retry budget (20 × 250ms = 5s) to absorb the deferred-handle window.
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 20, retryDelay: 250 });
|
||||
cleanup(tmpDir);
|
||||
// Clean up bridge files
|
||||
try {
|
||||
const warnPath = path.join(os.tmpdir(), `claude-ctx-${sessionId}-warned.json`);
|
||||
|
||||
@@ -16,7 +16,7 @@ const { describe, test, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { createTempProject, cleanup, runGsdTools } = require('./helpers.cjs');
|
||||
const { createTempProject, cleanup, runGsdTools, toPosixPath } = require('./helpers.cjs');
|
||||
|
||||
function setupMilestoneArchiveProject(tmpDir, options = {}) {
|
||||
const {
|
||||
@@ -151,6 +151,7 @@ describe('#3164 — validate consistency: milestone-archive layout', () => {
|
||||
|
||||
const out = JSON.parse(result.output);
|
||||
const warnings = out.warnings || [];
|
||||
const warningsPosix = warnings.map(w => toPosixPath(w));
|
||||
const phase64Warnings = warnings.filter(w => w.includes('Phase 64 exists on disk but not in ROADMAP.md'));
|
||||
assert.deepStrictEqual(
|
||||
phase64Warnings,
|
||||
@@ -158,12 +159,12 @@ describe('#3164 — validate consistency: milestone-archive layout', () => {
|
||||
`Old archived milestone phase 64 should not be treated as active:\n ${phase64Warnings.join('\n ')}`
|
||||
);
|
||||
assert.ok(
|
||||
warnings.some(w => w.includes('Gap in plan numbering in milestones/v1.7-phases/65-current')),
|
||||
warningsPosix.some(w => w.includes('Gap in plan numbering in milestones/v1.7-phases/65-current')),
|
||||
`Expected plan numbering warning from active archive root, got:\n ${warnings.join('\n ')}`
|
||||
);
|
||||
assert.ok(
|
||||
warnings.some(w => w.includes("milestones/v1.7-phases/65-current/65-01-PLAN.md: missing 'wave'"))
|
||||
|| warnings.some(w => w.includes("milestones/v1.7-phases/65-current/65-03-PLAN.md: missing 'wave'")),
|
||||
warningsPosix.some(w => w.includes("milestones/v1.7-phases/65-current/65-01-PLAN.md: missing 'wave'"))
|
||||
|| warningsPosix.some(w => w.includes("milestones/v1.7-phases/65-current/65-03-PLAN.md: missing 'wave'")),
|
||||
`Expected frontmatter warning from active archive plans, got:\n ${warnings.join('\n ')}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -32,23 +32,38 @@ function auditProductionVulns(cwd) {
|
||||
if (!fs.existsSync(path.join(cwd, 'node_modules'))) {
|
||||
return null; // signal "skip" to caller
|
||||
}
|
||||
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
||||
const isWindows = process.platform === 'win32';
|
||||
const npmCandidates = isWindows ? ['npm.cmd', 'npm'] : ['npm'];
|
||||
const args = ['audit', '--omit=dev', '--json'];
|
||||
let out;
|
||||
try {
|
||||
out = execFileSync(
|
||||
npmCmd,
|
||||
['audit', '--omit=dev', '--json'],
|
||||
{ cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 60_000 }
|
||||
);
|
||||
} catch (e) {
|
||||
// `npm audit` exits non-zero when advisories are present; the JSON is
|
||||
// still on stdout in that case. Recover and let the assertion classify.
|
||||
if (e && typeof e.stdout !== 'undefined') {
|
||||
out = Buffer.isBuffer(e.stdout) ? e.stdout.toString('utf-8') : String(e.stdout);
|
||||
} else {
|
||||
throw e;
|
||||
let lastErr = null;
|
||||
for (const npmCmd of npmCandidates) {
|
||||
try {
|
||||
out = execFileSync(
|
||||
npmCmd,
|
||||
args,
|
||||
{
|
||||
cwd,
|
||||
encoding: 'utf-8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
timeout: 60_000,
|
||||
shell: isWindows,
|
||||
}
|
||||
);
|
||||
lastErr = null;
|
||||
break;
|
||||
} catch (e) {
|
||||
// `npm audit` exits non-zero when advisories are present; the JSON is
|
||||
// still on stdout in that case. Recover and let the assertion classify.
|
||||
if (e && typeof e.stdout !== 'undefined' && e.stdout !== undefined && e.stdout !== null) {
|
||||
out = Buffer.isBuffer(e.stdout) ? e.stdout.toString('utf-8') : String(e.stdout);
|
||||
lastErr = null;
|
||||
break;
|
||||
}
|
||||
lastErr = e;
|
||||
}
|
||||
}
|
||||
if (lastErr) throw lastErr;
|
||||
const parsed = JSON.parse(out);
|
||||
// `null` is reserved for the "node_modules missing → skip" signal above.
|
||||
// Any other unexpected JSON shape is a real failure of the audit harness
|
||||
|
||||
Reference in New Issue
Block a user