File size: 2,216 Bytes
fc93158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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();
  });
});