import type { execFile as execFileType } from "node:child_process" ;
import { EventEmitter } from "node:events" ;
import fs from "node:fs" ;
import path from "node:path" ;
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest" ;
const spawnMock = vi.hoisted(() => vi.fn());
const execFileMock = vi.hoisted(() =>
Object.assign(vi.fn(), {
__promisify__: vi.fn(),
}),
);
vi.mock("node:child_process" , async () => {
const { mockNodeBuiltinModule } = await import ("../../test/helpers/node-builtin-mocks.js" );
return mockNodeBuiltinModule(
() => vi.importActual<typeof import ("node:child_process" )>("node:child_process" ),
{
spawn: spawnMock,
execFile: execFileMock as unknown as typeof execFileType,
},
);
});
let runCommandWithTimeout: typeof import ("./exec.js" ).runCommandWithTimeout;
let runExec: typeof import ("./exec.js" ).runExec;
type MockChild = EventEmitter & {
exitCode?: number | null ;
signalCode?: NodeJS.Signals | null ;
stdout: EventEmitter;
stderr: EventEmitter;
stdin: { write: ReturnType<typeof vi.fn>; end: ReturnType<typeof vi.fn> };
kill: ReturnType<typeof vi.fn>;
pid?: number;
killed?: boolean ;
};
function createMockChild(params?: {
closeCode?: number | null ;
closeSignal?: NodeJS.Signals | null ;
exitCode?: number | null ;
exitCodeAfterClose?: number | null ;
exitCodeAfterCloseDelayMs?: number;
signal?: NodeJS.Signals | null ;
}): MockChild {
const child = new EventEmitter() as MockChild;
child.stdout = new EventEmitter();
child.stderr = new EventEmitter();
child.exitCode = params?.exitCode ?? params?.closeCode ?? 0 ;
child.signalCode = params?.signal ?? null ;
child.stdin = {
write: vi.fn(),
end: vi.fn(),
};
child.kill = vi.fn(() => true );
child.pid = 1234 ;
child.killed = false ;
queueMicrotask(() => {
child.emit("close" , params?.closeCode ?? 0 , params?.closeSignal ?? params?.signal ?? null );
if (params?.exitCodeAfterClose !== undefined) {
setTimeout(() => {
child.exitCode = params.exitCodeAfterClose ?? null ;
}, params.exitCodeAfterCloseDelayMs ?? 0 );
}
});
return child;
}
type SpawnCall = [string, string[], Record<string, unknown>];
type ExecCall = [
string,
string[],
Record<string, unknown>,
(err: Error | null , stdout: string, stderr: string) => void ,
];
function expectCmdWrappedInvocation(params: {
captured: SpawnCall | ExecCall | undefined;
expectedComSpec: string;
}) {
if (!params.captured) {
throw new Error("expected command wrapper to be called" );
}
expect(params.captured[0 ]).toBe(params.expectedComSpec);
expect(params.captured[1 ].slice(0 , 3 )).toEqual(["/d" , "/s" , "/c" ]);
expect(params.captured[1 ][3 ]).toContain("pnpm.cmd --version" );
expect(params.captured[2 ].windowsHide).toBe(true );
expect(params.captured[2 ].windowsVerbatimArguments).toBe(true );
}
async function expectShimmedWindowsCommandWithoutExitCodeSucceeds(params?: { killed?: boolean }) {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const child = createMockChild({
closeCode: null ,
exitCode: null ,
});
child.killed = params?.killed ?? false ;
spawnMock.mockImplementation(() => child);
try {
const result = await runCommandWithTimeout(["npm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
expect(result.signal).toBeNull();
expect(result.termination).toBe("exit" );
} finally {
platformSpy.mockRestore();
}
}
describe("windows command wrapper behavior" , () => {
beforeAll(async () => {
({ runCommandWithTimeout, runExec } = await import ("./exec.js" ));
});
beforeEach(() => {
spawnMock.mockReset();
execFileMock.mockReset();
});
afterEach(() => {
vi.restoreAllMocks();
});
it("wraps .cmd commands via cmd.exe in runCommandWithTimeout" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const expectedComSpec = process.env.ComSpec ?? "cmd.exe" ;
spawnMock.mockImplementation(
(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
);
try {
const result = await runCommandWithTimeout(["pnpm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
const captured = spawnMock.mock.calls[0 ] as SpawnCall | undefined;
expectCmdWrappedInvocation({ captured, expectedComSpec });
} finally {
platformSpy.mockRestore();
}
});
it("wraps corepack.cmd via cmd.exe in runCommandWithTimeout" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const expectedComSpec = process.env.ComSpec ?? "cmd.exe" ;
spawnMock.mockImplementation(
(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
);
try {
const result = await runCommandWithTimeout(["corepack" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
const captured = spawnMock.mock.calls[0 ] as SpawnCall | undefined;
if (!captured) {
throw new Error("expected corepack shim spawn" );
}
expect(captured[0 ]).toBe(expectedComSpec);
expect(captured[1 ].slice(0 , 3 )).toEqual(["/d" , "/s" , "/c" ]);
expect(captured[1 ][3 ]).toContain("corepack.cmd --version" );
expect(captured[2 ].windowsHide).toBe(true );
expect(captured[2 ].windowsVerbatimArguments).toBe(true );
} finally {
platformSpy.mockRestore();
}
});
it("keeps child exitCode when close reports null on Windows npm shims" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const child = createMockChild({ closeCode: null , exitCode: 0 });
spawnMock.mockImplementation(() => child);
try {
const result = await runCommandWithTimeout(["npm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
} finally {
platformSpy.mockRestore();
}
});
it("spawns node + npm-cli.js for npm argv to avoid direct .cmd execution" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const existsSpy = vi.spyOn(fs, "existsSync" ).mockReturnValue(true );
const child = createMockChild({ closeCode: 0 , exitCode: 0 });
spawnMock.mockImplementation(() => child);
try {
const result = await runCommandWithTimeout(["npm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
const captured = spawnMock.mock.calls[0 ] as SpawnCall | undefined;
if (!captured) {
throw new Error("expected npm shim spawn" );
}
expect(captured[0 ]).toBe(process.execPath);
expect(captured[1 ][0 ]).toBe(
path.join(path.dirname(process.execPath), "node_modules" , "npm" , "bin" , "npm-cli.js" ),
);
expect(captured[1 ][1 ]).toBe("--version" );
expect(captured[2 ].windowsHide).toBe(true );
expect(captured[2 ].windowsVerbatimArguments).toBeUndefined();
expect(captured[2 ].stdio).toEqual(["inherit" , "pipe" , "pipe" ]);
} finally {
existsSpy.mockRestore();
platformSpy.mockRestore();
}
});
it("falls back to npm.cmd when npm-cli.js is unavailable" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const existsSpy = vi.spyOn(fs, "existsSync" ).mockReturnValue(false );
const expectedComSpec = process.env.ComSpec ?? "cmd.exe" ;
spawnMock.mockImplementation(
(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
);
try {
const result = await runCommandWithTimeout(["npm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
const captured = spawnMock.mock.calls[0 ] as SpawnCall | undefined;
if (!captured) {
throw new Error("expected npm.cmd fallback spawn" );
}
expect(captured[0 ]).toBe(expectedComSpec);
expect(captured[1 ].slice(0 , 3 )).toEqual(["/d" , "/s" , "/c" ]);
expect(captured[1 ][3 ]).toContain("npm.cmd --version" );
expect(captured[2 ].windowsHide).toBe(true );
expect(captured[2 ].windowsVerbatimArguments).toBe(true );
expect(captured[2 ].stdio).toEqual(["inherit" , "pipe" , "pipe" ]);
} finally {
existsSpy.mockRestore();
platformSpy.mockRestore();
}
});
it("waits for Windows exitCode settlement after close reports null" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const child = createMockChild({
closeCode: null ,
exitCode: null ,
exitCodeAfterClose: 0 ,
exitCodeAfterCloseDelayMs: 50 ,
});
spawnMock.mockImplementation(() => child);
try {
const result = await runCommandWithTimeout(["npm" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
} finally {
platformSpy.mockRestore();
}
});
it("treats shimmed Windows commands without a reported exit code as success when they close cleanly" , async () => {
await expectShimmedWindowsCommandWithoutExitCodeSucceeds();
});
it("treats shimmed Windows commands without a reported exit code as success even when child.killed is true" , async () => {
await expectShimmedWindowsCommandWithoutExitCodeSucceeds({ killed: true });
});
it("uses cmd.exe wrapper with windowsVerbatimArguments in runExec for .cmd shims" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
const expectedComSpec = process.env.ComSpec ?? "cmd.exe" ;
execFileMock.mockImplementation(
(
_command: string,
_args: string[],
_options: Record<string, unknown>,
cb: (err: Error | null , stdout: string, stderr: string) => void ,
) => {
cb(null , "ok" , "" );
},
);
try {
await runExec("pnpm" , ["--version" ], 1000 );
const captured = execFileMock.mock.calls[0 ] as ExecCall | undefined;
expectCmdWrappedInvocation({ captured, expectedComSpec });
} finally {
platformSpy.mockRestore();
}
});
it("sets windowsHide on direct runExec invocations too" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
execFileMock.mockImplementation(
(
_command: string,
_args: string[],
_options: Record<string, unknown>,
cb: (err: Error | null , stdout: string, stderr: string) => void ,
) => {
cb(null , "ok" , "" );
},
);
try {
await runExec("node" , ["--version" ], 1000 );
const captured = execFileMock.mock.calls[0 ] as ExecCall | undefined;
if (!captured) {
throw new Error("expected direct execFile invocation" );
}
expect(captured[0 ]).toBe("node" );
expect(captured[1 ]).toEqual(["--version" ]);
expect(captured[2 ].windowsHide).toBe(true );
} finally {
platformSpy.mockRestore();
}
});
it("sets windowsHide on direct runCommandWithTimeout invocations too" , async () => {
const platformSpy = vi.spyOn(process, "platform" , "get" ).mockReturnValue("win32" );
spawnMock.mockImplementation(
(_command: string, _args: string[], _options: Record<string, unknown>) => createMockChild(),
);
try {
const result = await runCommandWithTimeout(["node" , "--version" ], { timeoutMs: 1000 });
expect(result.code).toBe(0 );
const captured = spawnMock.mock.calls[0 ] as SpawnCall | undefined;
if (!captured) {
throw new Error("expected direct spawn invocation" );
}
expect(captured[0 ]).toBe("node" );
expect(captured[1 ]).toEqual(["--version" ]);
expect(captured[2 ].windowsHide).toBe(true );
expect(captured[2 ].windowsVerbatimArguments).toBeUndefined();
} finally {
platformSpy.mockRestore();
}
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-09)
¤
*© Formatika GbR, Deutschland