File size: 2,119 Bytes
5cb63c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
// Supervisor adapter test support builds mock process handles for adapter tests.
import fs from "node:fs";
import { expect, vi } from "vitest";

/**
 * Shared supervisor adapter assertions for the SIGTERM -> SIGKILL fallback
 * contract. Kept outside individual adapter tests so child and pty backends
 * prove the same timer semantics.
 */
type WaitResult = {
  code: number | null;
  signal: number | NodeJS.Signals | null;
};

/** Keep Linux adapter wiring tests deterministic on hosts without `/bin/sh`. */
export function mockLinuxOomWrapperShell(): () => void {
  const statSync = fs.statSync.bind(fs);
  const fileStats = statSync(process.execPath);
  const statSyncMock = vi
    .spyOn(fs, "statSync")
    .mockImplementation((target, options) =>
      String(target) === "/bin/sh" ? fileStats : statSync(target, options as never),
    );
  return () => {
    statSyncMock.mockRestore();
    // The production helper memoizes shell availability; clear it for later tests.
    vi.resetModules();
  };
}

/** Assert fallback SIGKILL resolves only after the grace timer expires. */
export async function expectWaitStaysPendingUntilSigkillFallback(
  waitPromise: Promise<WaitResult>,
  triggerKill: () => void,
): Promise<void> {
  const settled = vi.fn();
  void waitPromise.then(() => settled());

  triggerKill();

  await Promise.resolve();
  expect(settled).not.toHaveBeenCalled();

  await vi.advanceTimersByTimeAsync(3999);
  expect(settled).not.toHaveBeenCalled();

  await vi.advanceTimersByTimeAsync(1);
  await expect(waitPromise).resolves.toEqual({ code: null, signal: "SIGKILL" });
}

/** Assert a real process exit beats the fallback timer and stays idempotent. */
export async function expectRealExitWinsOverSigkillFallback(params: {
  waitPromise: Promise<WaitResult>;
  triggerKill: () => void;
  emitExit: () => void;
  expected: WaitResult;
}): Promise<void> {
  params.triggerKill();
  params.emitExit();

  await expect(params.waitPromise).resolves.toEqual(params.expected);

  await vi.advanceTimersByTimeAsync(4_001);
  await expect(params.waitPromise).resolves.toEqual(params.expected);
}