mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-13 08:15:17 +00:00
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
This commit is contained in:
1 parent
b3177179b1
commit
5b164a6486
1 file changed
+40
-2
@@ -1,5 +1,6 @@
|
|||||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
import * as childProcess from 'child_process';
|
||||||
|
|
||||||
type SpawnCall = {
|
type SpawnCall = {
|
||||||
command: string;
|
command: string;
|
||||||
@@ -18,6 +19,8 @@ function createMockChild(): EventEmitter & {
|
|||||||
stderr: EventEmitter;
|
stderr: EventEmitter;
|
||||||
exitCode: number | null;
|
exitCode: number | null;
|
||||||
killed: boolean;
|
killed: boolean;
|
||||||
|
pid: number;
|
||||||
|
unref: () => EventEmitter;
|
||||||
kill: () => boolean;
|
kill: () => boolean;
|
||||||
} {
|
} {
|
||||||
const child = new EventEmitter() as EventEmitter & {
|
const child = new EventEmitter() as EventEmitter & {
|
||||||
@@ -25,6 +28,8 @@ function createMockChild(): EventEmitter & {
|
|||||||
stderr: EventEmitter;
|
stderr: EventEmitter;
|
||||||
exitCode: number | null;
|
exitCode: number | null;
|
||||||
killed: boolean;
|
killed: boolean;
|
||||||
|
pid: number;
|
||||||
|
unref: () => EventEmitter;
|
||||||
kill: () => boolean;
|
kill: () => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +37,8 @@ function createMockChild(): EventEmitter & {
|
|||||||
child.stderr = new EventEmitter();
|
child.stderr = new EventEmitter();
|
||||||
child.exitCode = null;
|
child.exitCode = null;
|
||||||
child.killed = false;
|
child.killed = false;
|
||||||
|
child.pid = process.pid;
|
||||||
|
child.unref = () => child;
|
||||||
child.kill = () => {
|
child.kill = () => {
|
||||||
child.killed = true;
|
child.killed = true;
|
||||||
child.exitCode = 1;
|
child.exitCode = 1;
|
||||||
@@ -41,7 +48,13 @@ function createMockChild(): EventEmitter & {
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function shouldMockCommand(command: string): boolean {
|
||||||
|
const normalized = command.toLowerCase();
|
||||||
|
return normalized.includes('claude');
|
||||||
|
}
|
||||||
|
|
||||||
mock.module('child_process', () => ({
|
mock.module('child_process', () => ({
|
||||||
|
...childProcess,
|
||||||
spawn: (...spawnArgs: unknown[]) => {
|
spawn: (...spawnArgs: unknown[]) => {
|
||||||
const command = String(spawnArgs[0] ?? '');
|
const command = String(spawnArgs[0] ?? '');
|
||||||
const maybeArgs = spawnArgs[1];
|
const maybeArgs = spawnArgs[1];
|
||||||
@@ -50,14 +63,39 @@ mock.module('child_process', () => ({
|
|||||||
| Record<string, unknown>
|
| Record<string, unknown>
|
||||||
| undefined;
|
| undefined;
|
||||||
|
|
||||||
|
if (!shouldMockCommand(command)) {
|
||||||
|
return childProcess.spawn(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
options as Parameters<typeof childProcess.spawn>[2]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
spawnCalls.push({ command, args, options });
|
spawnCalls.push({ command, args, options });
|
||||||
|
|
||||||
const child = createMockChild();
|
const child = createMockChild();
|
||||||
setTimeout(() => child.emit('close', 0), 0);
|
setTimeout(() => child.emit('close', 0), 0);
|
||||||
return child;
|
return child;
|
||||||
},
|
},
|
||||||
spawnSync: () => ({ status: 0 }),
|
spawnSync: (...spawnArgs: unknown[]) => {
|
||||||
execSync: () => '',
|
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<string, unknown>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
return childProcess.spawnSync(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
options as Parameters<typeof childProcess.spawnSync>[2]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
execSync: (...execArgs: unknown[]) =>
|
||||||
|
childProcess.execSync(
|
||||||
|
execArgs[0] as Parameters<typeof childProcess.execSync>[0],
|
||||||
|
execArgs[1] as Parameters<typeof childProcess.execSync>[1]
|
||||||
|
),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let execClaude: typeof import('../../../src/utils/shell-executor').execClaude;
|
let execClaude: typeof import('../../../src/utils/shell-executor').execClaude;
|
||||||
|
|||||||
Reference in new issue
Block a user