fix(test): preserve survivor probe stderr after process exit (#155220)

This commit is contained in:
Peter Steinberger 2026-09-21 15:05:12 -07:00 • committed by GitHub
parent a7c9689871
commit f56c75f2a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,11 +1,11 @@
// Upgrade Survivor Probe Gateway tests cover upgrade survivor probe gateway script behavior.
import { spawn } from "node:child_process";
import childProcess from "node:child_process";
import fs from "node:fs";
import { createServer as createHttpServer } from "node:http";
import { createServer as createTcpServer, type Server, type Socket } from "node:net";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createBoundedChildOutput } from "../helpers/bounded-child-output.js";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
@ -35,7 +35,7 @@ function runProbe(
nodeArgs: string[] = [],
): Promise<ProbeResult> {
return new Promise((resolve) => {
const child = spawn(process.execPath, [...nodeArgs, probePath, ...args], {
const child = childProcess.spawn(process.execPath, [...nodeArgs, probePath, ...args], {
env: { ...process.env, ...env },
stdio: ["ignore", "pipe", "pipe"],
});
@ -58,7 +58,7 @@ function runProbe(
clearTimeout(timer);
resolve({ error, signal: null, status: null, stderr: stderr.text(), stdout: stdout.text() });
});
child.on("exit", (status, signal) => {
child.on("close", (status, signal) => {
clearTimeout(timer);
resolve({
error: timedOut ? new Error(`probe timed out after ${timeout}ms`) : undefined,
@ -87,6 +87,33 @@ async function listen(server: Server): Promise<string> {
}
describe("scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs", () => {
it("drains stderr after process exit before publishing the probe result", async () => {
const spawn = childProcess.spawn;
let exitObserved:
| { status: number | null; signal: NodeJS.Signals | null; stderrPaused: boolean }
| undefined;
const spawnSpy = vi
.spyOn(childProcess, "spawn")
.mockImplementationOnce((command, args, options) => {
const child = spawn(command, args, options);
child.stderr?.pause();
child.once("exit", (status, signal) => {
exitObserved = { status, signal, stderrPaused: child.stderr?.isPaused() === true };
child.stderr?.resume();
});
return child;
});
try {
const result = await runProbe(["--timeout-ms", "1e3"]);
expect(exitObserved).toEqual({ status: 1, signal: null, stderrPaused: true });
expect(result).toMatchObject({ status: 1, signal: null, error: undefined });
expect(result.stderr).toContain("invalid --timeout-ms: 1e3");
} finally {
spawnSpy.mockRestore();
}
});
it("does not hard-code degraded ready allowlists into Docker survivor probes", () => {
const script = fs.readFileSync(dockerSurvivorPath, "utf8");
@ -108,7 +135,7 @@ describe("scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs", () => {
"1e3",
]);
expect(timeoutResult.status).not.toBe(0);
expect(timeoutResult).toMatchObject({ status: 1, signal: null, error: undefined });
expect(timeoutResult.stderr).toContain("invalid --timeout-ms: 1e3");
const bodyLimitResult = await runProbe(
@ -119,7 +146,7 @@ describe("scripts/e2e/lib/upgrade-survivor/probe-gateway.mjs", () => {
},
);
expect(bodyLimitResult.status).not.toBe(0);
expect(bodyLimitResult).toMatchObject({ status: 1, signal: null, error: undefined });
expect(bodyLimitResult.stderr).toContain(
"invalid OPENCLAW_UPGRADE_SURVIVOR_PROBE_MAX_BODY_BYTES: 64bytes",
);