test(testkit): cover the env-driven E2E ports instead of a fixed list

The ports test parsed a hard-coded array that no longer exists. It now
runs the script on a fake host whose fuser finds nothing, and checks the
default ports, the per-runner override, the rejection of a non-numeric
setting, and that the workflow picks the ports before freeing them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
This commit is contained in:
Claude 2026-10-02 16:03:19 +00:00
parent 2f05ab6a81
commit cd0aedafcd
No known key found for this signature in database

View File

@ -1,9 +1,12 @@
import { readFileSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { chmodSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
const repoRoot = path.resolve(import.meta.dirname, "../../..");
const scriptPath = path.join(repoRoot, "scripts/free-e2e-ports.sh");
const script = readFileSync(scriptPath, "utf8");
const playwrightWorkflow = readFileSync(
path.join(repoRoot, ".github/workflows/playwright.yml"),
"utf8",
@ -15,20 +18,62 @@ function portsFromShellArray(source: string, name: string): number[] {
return [...match![1].matchAll(/\b(\d+)\b/g)].map(([, port]) => Number(port));
}
/** Runs the script on a fake Linux host whose fuser never finds a listener, so nothing is killed. */
function runScript(env: Record<string, string>): { status: number; output: string } {
const fakeBin = mkdtempSync(path.join(tmpdir(), "evoo-e2e-ports-"));
for (const [name, body] of [
["uname", "echo Linux"],
["fuser", "exit 1"],
] as const) {
const file = path.join(fakeBin, name);
writeFileSync(file, `#!/usr/bin/env bash\n${body}\n`);
chmodSync(file, 0o755);
}
try {
const output = execFileSync("bash", [scriptPath], {
env: { PATH: `${fakeBin}:${process.env.PATH}`, ...env },
encoding: "utf8",
stdio: ["ignore", "pipe", "pipe"],
});
return { status: 0, output };
} catch (error) {
const failure = error as { status: number; stdout: string; stderr: string };
return { status: failure.status, output: `${failure.stdout}${failure.stderr}` };
}
}
function announcedPorts(output: string): number[] {
const match = output.match(/E2E ports \(([^)]*)\)/);
expect(match, "announced E2E ports").toBeTruthy();
return match![1].split(" ").map(Number);
}
describe("free-e2e-ports.sh", () => {
const script = readFileSync(scriptPath, "utf8");
it("declares dedicated E2E ports separate from live dev ports", () => {
it("keeps the live dev ports apart from the default E2E ports", () => {
const livePorts = portsFromShellArray(script, "LIVE_PORTS");
const e2ePorts = portsFromShellArray(script, "E2E_PORTS");
expect(livePorts).toEqual([3100, 5173]);
const run = runScript({});
expect(run.status).toBe(0);
const e2ePorts = announcedPorts(run.output);
expect(e2ePorts).toEqual([3110, 3111, 5180, 4321]);
for (const livePort of livePorts) {
expect(e2ePorts).not.toContain(livePort);
}
});
it("follows the harness port settings so runners on one host never touch each other", () => {
const run = runScript({ API_PORT: "3310", WEB_PORT: "5380", WWW_PORT: "4521" });
expect(run.status).toBe(0);
expect(announcedPorts(run.output)).toEqual([3310, 3311, 5380, 4521]);
});
it("refuses a port setting that is not a number", () => {
const run = runScript({ API_PORT: "abc" });
expect(run.status).toBe(1);
expect(run.output).toContain("API_PORT must be a port number");
});
it("uses fuser and skips non-Linux hosts", () => {
expect(script).toContain('fuser -k "$spec"');
expect(script).toContain('uname -s)" != "Linux"');
@ -44,4 +89,13 @@ describe("free-e2e-ports.sh", () => {
playwrightWorkflow.indexOf("Run marketing homepage E2E"),
);
});
it("gets its ports from the workflow step that picks them per runner", () => {
const pickStep = playwrightWorkflow.indexOf("Pick E2E ports for this runner");
expect(pickStep).toBeGreaterThan(-1);
expect(pickStep).toBeLessThan(playwrightWorkflow.indexOf("free-e2e-ports.sh"));
for (const setting of ["API_PORT=", "WEB_PORT=", "WWW_PORT="]) {
expect(playwrightWorkflow).toContain(setting);
}
});
});