mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 04:47:03 -06:00
fix(testing): bind QA docker port probes to loopback
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
// Qa Lab tests cover docker up plugin behavior.
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { createServer } from "node:net";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
@@ -8,31 +7,6 @@ import { runQaDockerUp } from "./docker-up.runtime.js";
|
||||
|
||||
type QaDockerUpDeps = NonNullable<Parameters<typeof runQaDockerUp>[1]>;
|
||||
|
||||
async function occupyPortOrAcceptExisting(port: number): Promise<{ close: () => Promise<void> }> {
|
||||
const server = createServer();
|
||||
const listening = await new Promise<boolean>((resolve, reject) => {
|
||||
server.once("error", (error: NodeJS.ErrnoException) => {
|
||||
if (error.code === "EADDRINUSE") {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
reject(error);
|
||||
});
|
||||
server.listen(port, "127.0.0.1", () => resolve(true));
|
||||
});
|
||||
|
||||
return {
|
||||
close: async () => {
|
||||
if (!listening) {
|
||||
return;
|
||||
}
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createHealthyDockerDeps(calls: string[]): QaDockerUpDeps {
|
||||
return {
|
||||
async runCommand(command, args, cwd) {
|
||||
@@ -163,7 +137,8 @@ describe("runQaDockerUp", () => {
|
||||
const outputDir = await mkdtemp(path.join(os.tmpdir(), "qa-docker-up-"));
|
||||
const gatewayPort = 18789;
|
||||
const qaLabPort = 43124;
|
||||
const resolveHostPort = vi.fn(async (preferredPort: number) => {
|
||||
const resolveHostPort = vi.fn(async (preferredPort: number, pinned: boolean) => {
|
||||
expect(pinned).toBe(false);
|
||||
if (preferredPort === gatewayPort) {
|
||||
return 28001;
|
||||
}
|
||||
@@ -172,16 +147,12 @@ describe("runQaDockerUp", () => {
|
||||
}
|
||||
return preferredPort;
|
||||
});
|
||||
const gatewayPortReservation = await occupyPortOrAcceptExisting(18789);
|
||||
const qaLabPortReservation = await occupyPortOrAcceptExisting(43124);
|
||||
|
||||
try {
|
||||
const result = await runQaDockerUp(
|
||||
{
|
||||
repoRoot: "/repo/openclaw",
|
||||
outputDir,
|
||||
gatewayPort,
|
||||
qaLabPort,
|
||||
skipUiBuild: true,
|
||||
usePrebuiltImage: true,
|
||||
},
|
||||
@@ -202,9 +173,9 @@ describe("runQaDockerUp", () => {
|
||||
expect(result.qaLabUrl).not.toBe(`http://127.0.0.1:${qaLabPort}`);
|
||||
expect(result.gatewayUrl).toBe("http://127.0.0.1:28001/");
|
||||
expect(result.qaLabUrl).toBe("http://127.0.0.1:28002");
|
||||
expect(resolveHostPort).toHaveBeenCalledWith(gatewayPort, false);
|
||||
expect(resolveHostPort).toHaveBeenCalledWith(qaLabPort, false);
|
||||
} finally {
|
||||
await gatewayPortReservation.close();
|
||||
await qaLabPortReservation.close();
|
||||
await rm(outputDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Tests QA runtime command loading and private CLI gating.
|
||||
*/
|
||||
import { Command } from "commander";
|
||||
import { createServer } from "node:net";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import {
|
||||
cleanupTempDirs,
|
||||
@@ -36,6 +37,29 @@ describe("plugin-sdk qa-runtime", () => {
|
||||
restorePrivateQaCliEnv(originalPrivateQaCli);
|
||||
});
|
||||
|
||||
async function occupyLoopbackPort(): Promise<{ close: () => Promise<void>; port: number }> {
|
||||
const server = createServer();
|
||||
const port = await new Promise<number>((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(0, "127.0.0.1", () => {
|
||||
const address = server.address();
|
||||
if (!address || typeof address === "string") {
|
||||
reject(new Error("test server address unavailable"));
|
||||
return;
|
||||
}
|
||||
resolve(address.port);
|
||||
});
|
||||
});
|
||||
return {
|
||||
port,
|
||||
close: async () => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
server.close((error) => (error ? reject(error) : resolve()));
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
it("stays cold until the runtime seam is used", async () => {
|
||||
const module = await import("./qa-runtime.js");
|
||||
|
||||
@@ -273,4 +297,19 @@ describe("plugin-sdk qa-runtime", () => {
|
||||
expect(runCommand).toHaveBeenCalledTimes(2);
|
||||
expect(sleepImpl).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("resolves an unpinned QA Docker host port away from an occupied loopback default", async () => {
|
||||
const module = await import("./qa-runtime.js");
|
||||
const reservation = await occupyLoopbackPort();
|
||||
try {
|
||||
await expect(module.resolveQaDockerHostPort(reservation.port, true)).resolves.toBe(
|
||||
reservation.port,
|
||||
);
|
||||
const fallbackPort = await module.resolveQaDockerHostPort(reservation.port, false);
|
||||
expect(fallbackPort).toBeGreaterThan(0);
|
||||
expect(fallbackPort).not.toBe(reservation.port);
|
||||
} finally {
|
||||
await reservation.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -395,7 +395,7 @@ async function findFreeQaDockerPort() {
|
||||
return await new Promise<number>((resolve, reject) => {
|
||||
const server = createServer();
|
||||
server.once("error", reject);
|
||||
server.listen(0, () => {
|
||||
server.listen(0, "127.0.0.1", () => {
|
||||
const address = server.address();
|
||||
if (!address || typeof address === "string") {
|
||||
server.close();
|
||||
|
||||
Reference in New Issue
Block a user