From 5b164a64864a548192578eacd4bb149253d4be95 Mon Sep 17 00:00:00 2001 From: Tam Nhu Tran Date: Fri, 20 Feb 2026 23:23:32 +0700 Subject: [PATCH] fix(test): prevent child_process mock cross-test leakage - mock only Claude command spawns and pass through real child_process for others - add missing ChildProcess-compatible fields (pid/unref) on mock child - stabilizes cursor daemon and ccsd alias tests in CI parallel execution --- .../utils/claudecode-env-stripping.test.ts | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/tests/unit/utils/claudecode-env-stripping.test.ts b/tests/unit/utils/claudecode-env-stripping.test.ts index 5c6ba7bb..2cf7f97b 100644 --- a/tests/unit/utils/claudecode-env-stripping.test.ts +++ b/tests/unit/utils/claudecode-env-stripping.test.ts @@ -1,5 +1,6 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test'; import { EventEmitter } from 'events'; +import * as childProcess from 'child_process'; type SpawnCall = { command: string; @@ -18,6 +19,8 @@ function createMockChild(): EventEmitter & { stderr: EventEmitter; exitCode: number | null; killed: boolean; + pid: number; + unref: () => EventEmitter; kill: () => boolean; } { const child = new EventEmitter() as EventEmitter & { @@ -25,6 +28,8 @@ function createMockChild(): EventEmitter & { stderr: EventEmitter; exitCode: number | null; killed: boolean; + pid: number; + unref: () => EventEmitter; kill: () => boolean; }; @@ -32,6 +37,8 @@ function createMockChild(): EventEmitter & { child.stderr = new EventEmitter(); child.exitCode = null; child.killed = false; + child.pid = process.pid; + child.unref = () => child; child.kill = () => { child.killed = true; child.exitCode = 1; @@ -41,7 +48,13 @@ function createMockChild(): EventEmitter & { return child; } +function shouldMockCommand(command: string): boolean { + const normalized = command.toLowerCase(); + return normalized.includes('claude'); +} + mock.module('child_process', () => ({ + ...childProcess, spawn: (...spawnArgs: unknown[]) => { const command = String(spawnArgs[0] ?? ''); const maybeArgs = spawnArgs[1]; @@ -50,14 +63,39 @@ mock.module('child_process', () => ({ | Record | undefined; + if (!shouldMockCommand(command)) { + return childProcess.spawn( + command, + args, + options as Parameters[2] + ); + } + spawnCalls.push({ command, args, options }); const child = createMockChild(); setTimeout(() => child.emit('close', 0), 0); return child; }, - spawnSync: () => ({ status: 0 }), - execSync: () => '', + spawnSync: (...spawnArgs: unknown[]) => { + const command = String(spawnArgs[0] ?? ''); + const maybeArgs = spawnArgs[1]; + const args = Array.isArray(maybeArgs) ? (maybeArgs as string[]) : []; + const options = (Array.isArray(maybeArgs) ? spawnArgs[2] : spawnArgs[1]) as + | Record + | undefined; + + return childProcess.spawnSync( + command, + args, + options as Parameters[2] + ); + }, + execSync: (...execArgs: unknown[]) => + childProcess.execSync( + execArgs[0] as Parameters[0], + execArgs[1] as Parameters[1] + ), })); let execClaude: typeof import('../../../src/utils/shell-executor').execClaude;