From dfaf15449c80d36327a6359d5b33b3207470d2ef Mon Sep 17 00:00:00 2001 From: garrytan Date: Sun, 4 Oct 2026 00:58:31 +0000 Subject: [PATCH] test(windows): spawn cmd.exe hooks verbatim, use a .invalid UNC host, and match CRLF fixtures and native separators windows-free-tests reds on 364f0f3, each verified on windows-latest (46 pass, 1 POSIX-only skip, 0 fail): - investigate-freeze-path cmd.exe cases: Bun MSVC-quoted the command line cmd.exe parses, so cmd saw \"bash and failed; Node's shell spawn (what Claude Code uses) passes it verbatim. - investigate-freeze-path UNC cases: a single-label host made each filesystem probe wait on network name resolution (cd 11 s, -L 2.8 s); a reserved .invalid host fails at once (55-80 ms). - paid-report-census: the junit fixture is CRLF in a Windows checkout. - codex-invocation-sites: rendered paths use backslashes on Windows. --- test/codex-invocation-sites.test.ts | 2 +- test/investigate-freeze-path.test.ts | 11 ++++++++--- test/paid-report-census.test.ts | 4 ++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/test/codex-invocation-sites.test.ts b/test/codex-invocation-sites.test.ts index 7faab3322..98c267739 100644 --- a/test/codex-invocation-sites.test.ts +++ b/test/codex-invocation-sites.test.ts @@ -53,7 +53,7 @@ function render(host: string): string[] { } describe('every rendered Codex invocation is classified by the shared validator', () => { - const rendered = Object.fromEntries(['claude', 'codex'].map(host => [host, render(host).flatMap(f => codexSites(fs.readFileSync(f, 'utf8'), path.relative(TMP, f)))])); + const rendered = Object.fromEntries(['claude', 'codex'].map(host => [host, render(host).flatMap(f => codexSites(fs.readFileSync(f, 'utf8'), path.relative(TMP, f).split(path.sep).join('/')))])); test('the Claude render has the expected invocation sites', () => { const files = new Set(rendered.claude!.map(s => s.file.replace(/^claude\//, ''))); diff --git a/test/investigate-freeze-path.test.ts b/test/investigate-freeze-path.test.ts index 393bbbf7c..fcd7f1b4a 100644 --- a/test/investigate-freeze-path.test.ts +++ b/test/investigate-freeze-path.test.ts @@ -80,9 +80,11 @@ describe('E1: freeze boundary and target share one platform-aware normalization ['C:\\dev\\proj\\src\\', 'C:\\dev\\proj\\src-old\\a.py', 'deny'], ['C:\\dev\\proj\\src\\', 'D:\\dev\\proj\\src\\a.py', 'deny'], ['C:\\dev\\proj\\src\\', 'C:\\dev\\proj\\other\\missing\\a.py', 'deny'], - ['\\\\server\\share\\proj\\', '\\\\server\\share\\proj\\a.py', 'allow'], - ['\\\\server\\share\\proj\\', '\\\\server\\other\\proj\\a.py', 'deny'], - ['\\\\server\\share\\proj\\', '/server/share/proj/a.py', 'deny'], + // A reserved .invalid host fails name resolution at once; a single-label + // host makes Windows probe the network for ~10 s per filesystem call. + ['\\\\server.invalid\\share\\proj\\', '\\\\server.invalid\\share\\proj\\a.py', 'allow'], + ['\\\\server.invalid\\share\\proj\\', '\\\\server.invalid\\other\\proj\\a.py', 'deny'], + ['\\\\server.invalid\\share\\proj\\', '/server.invalid/share/proj/a.py', 'deny'], ])('boundary %s, target %s → %s', (boundary, target, expected) => { expect(freezeDecision(boundary, target)).toBe(expected); }); @@ -196,10 +198,13 @@ describe('E1: registered hook commands run under cmd.exe and POSIX sh (#2354)', for (const [label, argv] of shells) { test(`${rel} hook #${i + 1} reaches a decision via ${label}`, () => { withInstalledHome((home) => withState(path.join(home, 'frozen'), (env) => { + // Claude Code hands cmd.exe its command line verbatim (Node's shell + // spawn); MSVC-quoting it would escape the inner quotes cmd.exe reads. const r = Bun.spawnSync(argv(home), { stdin: Buffer.from(JSON.stringify(input)), env: { ...env, HOME: home }, timeout: 15_000, + windowsVerbatimArguments: IS_WIN, }); expect(r.exitCode, r.stderr.toString()).toBe(0); expect(decision(r.stdout.toString())).toBe(expected); diff --git a/test/paid-report-census.test.ts b/test/paid-report-census.test.ts index b57a0ae74..8f693bc08 100644 --- a/test/paid-report-census.test.ts +++ b/test/paid-report-census.test.ts @@ -74,8 +74,8 @@ describe('census attribution from the 2026-10-03 artifacts', () => { const qa = junit(dir, 'skill-e2e-qa-workflow'); fs.writeFileSync(qa, fs.readFileSync(qa, 'utf8').replace(/(]*?) \/>/, '$1>')); const cso = junit(dir, 'skill-e2e-cso'); - fs.writeFileSync(cso, fs.readFileSync(cso, 'utf8').replace('\n', - '\n\n')); + fs.writeFileSync(cso, fs.readFileSync(cso, 'utf8').replace(/<\/testsuite>(\r?\n)<\/testsuites>/, + '$1$1')); }); expect(r.status, r.out).toBe(0); expect(r.out).toContain(' SKIPPED 1 · INFRA 0 · INCOMPLETE 0 · unattributed 1 · ACTION REQUIRED 0');