File size: 2,542 Bytes
3144483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// No-output timer tests cover idle command timeout and output reset behavior.
import { describe, expect, it } from "vitest";
import { runCommandWithTimeout } from "./exec.js";

describe("runCommandWithTimeout no-output timer", () => {
  it("resets no-output timeout while the child emits stdout", async () => {
    const script = [
      "let count = 0",
      "let timer",
      "const emit = () => {",
      "  process.stdout.write('.')",
      "  if (++count === 21) { clearInterval(timer); process.exit(0) }",
      "}",
      "emit()",
      "timer = setInterval(emit, 100)",
    ].join(";");
    const result = await runCommandWithTimeout([process.execPath, "-e", script], {
      timeoutMs: 10_000,
      // Leave ample process-startup margin while keeping total runtime above
      // this threshold, so only output-driven resets let the child finish.
      noOutputTimeoutMs: 1_500,
    });

    expect(result).toMatchObject({
      code: 0,
      noOutputTimedOut: false,
      stdout: ".".repeat(21),
      termination: "exit",
    });
  });

  it("bounds captured stdout and stderr while keeping the newest output", async () => {
    const script = ["process.stdout.write('abcdefgh')", "process.stderr.write('1234567')"].join(
      ";",
    );
    const result = await runCommandWithTimeout([process.execPath, "-e", script], {
      // Output capture is independent from watchdog timing; Vitest owns the
      // test deadline so a loaded worker cannot race the child's exit event.
      maxOutputBytes: 5,
    });

    expect(result.stdout).toBe("defgh");
    expect(result.stderr).toBe("34567");
    expect(result.stdoutTruncatedBytes).toBe(3);
    expect(result.stderrTruncatedBytes).toBe(2);
    expect(result.termination).toBe("exit");
  });

  it("marks no-output timeout when the child goes silent", async () => {
    const result = await runCommandWithTimeout(
      [process.execPath, "-e", "setInterval(() => {}, 1_000)"],
      {
        timeoutMs: 2_000,
        noOutputTimeoutMs: 100,
      },
    );

    expect(result.termination).toBe("no-output-timeout");
    expect(result.noOutputTimedOut).toBe(true);
    expect(result.code).toBe(124);
  });

  it("marks global timeout when the overall timeout elapses", async () => {
    const result = await runCommandWithTimeout(
      [process.execPath, "-e", "setInterval(() => {}, 1_000)"],
      { timeoutMs: 100 },
    );

    expect(result.termination).toBe("timeout");
    expect(result.noOutputTimedOut).toBe(false);
    expect(result.code).toBe(124);
  });
});