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:
parent
2f05ab6a81
commit
cd0aedafcd
@ -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 path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
const repoRoot = path.resolve(import.meta.dirname, "../../..");
|
||||||
const scriptPath = path.join(repoRoot, "scripts/free-e2e-ports.sh");
|
const scriptPath = path.join(repoRoot, "scripts/free-e2e-ports.sh");
|
||||||
|
const script = readFileSync(scriptPath, "utf8");
|
||||||
const playwrightWorkflow = readFileSync(
|
const playwrightWorkflow = readFileSync(
|
||||||
path.join(repoRoot, ".github/workflows/playwright.yml"),
|
path.join(repoRoot, ".github/workflows/playwright.yml"),
|
||||||
"utf8",
|
"utf8",
|
||||||
@ -15,20 +18,62 @@ function portsFromShellArray(source: string, name: string): number[] {
|
|||||||
return [...match![1].matchAll(/\b(\d+)\b/g)].map(([, port]) => Number(port));
|
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", () => {
|
describe("free-e2e-ports.sh", () => {
|
||||||
const script = readFileSync(scriptPath, "utf8");
|
it("keeps the live dev ports apart from the default E2E ports", () => {
|
||||||
|
|
||||||
it("declares dedicated E2E ports separate from live dev ports", () => {
|
|
||||||
const livePorts = portsFromShellArray(script, "LIVE_PORTS");
|
const livePorts = portsFromShellArray(script, "LIVE_PORTS");
|
||||||
const e2ePorts = portsFromShellArray(script, "E2E_PORTS");
|
|
||||||
|
|
||||||
expect(livePorts).toEqual([3100, 5173]);
|
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]);
|
expect(e2ePorts).toEqual([3110, 3111, 5180, 4321]);
|
||||||
for (const livePort of livePorts) {
|
for (const livePort of livePorts) {
|
||||||
expect(e2ePorts).not.toContain(livePort);
|
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", () => {
|
it("uses fuser and skips non-Linux hosts", () => {
|
||||||
expect(script).toContain('fuser -k "$spec"');
|
expect(script).toContain('fuser -k "$spec"');
|
||||||
expect(script).toContain('uname -s)" != "Linux"');
|
expect(script).toContain('uname -s)" != "Linux"');
|
||||||
@ -44,4 +89,13 @@ describe("free-e2e-ports.sh", () => {
|
|||||||
playwrightWorkflow.indexOf("Run marketing homepage E2E"),
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user