mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-29 09:41:08 +00:00
test(process): replace no-output timer subprocess with spawn mock
This commit is contained in:
73
src/process/exec.no-output-timer.test.ts
Normal file
73
src/process/exec.no-output-timer.test.ts
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import type { ChildProcess } from "node:child_process";
|
||||||
|
import { EventEmitter } from "node:events";
|
||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
const spawnMock = vi.hoisted(() => vi.fn());
|
||||||
|
|
||||||
|
vi.mock("node:child_process", async () => {
|
||||||
|
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
spawn: spawnMock,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
import { runCommandWithTimeout } from "./exec.js";
|
||||||
|
|
||||||
|
function createFakeSpawnedChild() {
|
||||||
|
const child = new EventEmitter() as EventEmitter & ChildProcess;
|
||||||
|
const stdout = new EventEmitter();
|
||||||
|
const stderr = new EventEmitter();
|
||||||
|
let killed = false;
|
||||||
|
const kill = vi.fn<(signal?: NodeJS.Signals) => boolean>(() => {
|
||||||
|
killed = true;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
Object.defineProperty(child, "killed", {
|
||||||
|
get: () => killed,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
Object.defineProperty(child, "pid", {
|
||||||
|
value: 12345,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
child.stdout = stdout as ChildProcess["stdout"];
|
||||||
|
child.stderr = stderr as ChildProcess["stderr"];
|
||||||
|
child.stdin = null;
|
||||||
|
child.kill = kill as ChildProcess["kill"];
|
||||||
|
return { child, stdout, stderr, kill };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("runCommandWithTimeout no-output timer", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.useRealTimers();
|
||||||
|
vi.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resets no-output timeout when spawned child keeps emitting stdout", async () => {
|
||||||
|
vi.useFakeTimers();
|
||||||
|
const fake = createFakeSpawnedChild();
|
||||||
|
spawnMock.mockReturnValue(fake.child);
|
||||||
|
|
||||||
|
const runPromise = runCommandWithTimeout(["node", "-e", "ignored"], {
|
||||||
|
timeoutMs: 1_000,
|
||||||
|
noOutputTimeoutMs: 80,
|
||||||
|
});
|
||||||
|
|
||||||
|
fake.stdout.emit("data", Buffer.from("."));
|
||||||
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
|
fake.stdout.emit("data", Buffer.from("."));
|
||||||
|
await vi.advanceTimersByTimeAsync(40);
|
||||||
|
fake.stdout.emit("data", Buffer.from("."));
|
||||||
|
await vi.advanceTimersByTimeAsync(20);
|
||||||
|
|
||||||
|
fake.child.emit("close", 0, null);
|
||||||
|
const result = await runPromise;
|
||||||
|
|
||||||
|
expect(result.code ?? 0).toBe(0);
|
||||||
|
expect(result.termination).toBe("exit");
|
||||||
|
expect(result.noOutputTimedOut).toBe(false);
|
||||||
|
expect(result.stdout).toBe("...");
|
||||||
|
expect(fake.kill).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -56,38 +56,6 @@ describe("runCommandWithTimeout", () => {
|
|||||||
expect(result.code).not.toBe(0);
|
expect(result.code).not.toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("resets no output timer when command keeps emitting output", async () => {
|
|
||||||
const result = await runCommandWithTimeout(
|
|
||||||
[
|
|
||||||
process.execPath,
|
|
||||||
"-e",
|
|
||||||
[
|
|
||||||
"let count = 0;",
|
|
||||||
"const emit = () => {",
|
|
||||||
'process.stdout.write(".");',
|
|
||||||
"count += 1;",
|
|
||||||
"if (count >= 4) {",
|
|
||||||
"process.exit(0);",
|
|
||||||
"return;",
|
|
||||||
"}",
|
|
||||||
"setTimeout(emit, 40);",
|
|
||||||
"};",
|
|
||||||
"emit();",
|
|
||||||
].join(" "),
|
|
||||||
],
|
|
||||||
{
|
|
||||||
timeoutMs: 2_000,
|
|
||||||
// Keep a healthy margin above the emit interval for loaded CI runners.
|
|
||||||
noOutputTimeoutMs: 400,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.code ?? 0).toBe(0);
|
|
||||||
expect(result.termination).toBe("exit");
|
|
||||||
expect(result.noOutputTimedOut).toBe(false);
|
|
||||||
expect(result.stdout.length).toBeGreaterThanOrEqual(3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("reports global timeout termination when overall timeout elapses", async () => {
|
it("reports global timeout termination when overall timeout elapses", async () => {
|
||||||
const result = await runCommandWithTimeout(
|
const result = await runCommandWithTimeout(
|
||||||
[process.execPath, "-e", "setTimeout(() => {}, 10)"],
|
[process.execPath, "-e", "setTimeout(() => {}, 10)"],
|
||||||
|
|||||||
Reference in New Issue
Block a user