import { describe, expect, it } from "vitest" ;
import {
buildPortHints,
classifyPortListener,
formatPortDiagnostics,
formatPortListener,
isDualStackLoopbackGatewayListeners,
} from "./ports-format.js" ;
describe("ports-format" , () => {
it.each([
[{ commandLine: "ssh -N -L 18789:127.0.0.1:18789 user@host" }, "ssh" ],
[{ command: "ssh" }, "ssh" ],
[{ commandLine: "node /Users/me/Projects/openclaw/dist/entry.js gateway" }, "gateway" ],
[{ commandLine: "python -m http.server 18789" }, "unknown" ],
] as const )("classifies port listener %j" , (listener, expected) => {
expect(classifyPortListener(listener, 18789 )).toBe(expected);
});
it("builds ordered hints for mixed listener kinds and multiplicity" , () => {
expect(
buildPortHints(
[
{ commandLine: "node dist/index.js openclaw gateway" },
{ commandLine: "ssh -N -L 18789:127.0.0.1:18789" },
{ commandLine: "python -m http.server 18789" },
],
18789 ,
),
).toEqual([
expect.stringContaining("Gateway already running locally." ),
"SSH tunnel already bound to this port. Close the tunnel or use a different local port in -L." ,
"Another process is listening on this port." ,
expect.stringContaining("Multiple listeners detected" ),
]);
expect(buildPortHints([], 18789 )).toEqual([]);
});
it("treats single-process loopback dual-stack gateway listeners as benign" , () => {
const listeners = [
{ pid: 4242 , commandLine: "openclaw-gateway" , address: "127.0.0.1:18789" },
{ pid: 4242 , commandLine: "openclaw-gateway" , address: "[::1]:18789" },
];
expect(isDualStackLoopbackGatewayListeners(listeners, 18789 )).toBe(true );
expect(buildPortHints(listeners, 18789 )).toEqual([
expect.stringContaining("Gateway already running locally." ),
]);
});
it.each([
[
{ pid: 123 , user: "alice" , commandLine: "ssh -N" , address: "::1" },
"pid 123 alice: ssh -N (::1)" ,
],
[{ command: "ssh" , address: "127.0.0.1:18789" }, "pid ?: ssh (127.0.0.1:18789)" ],
[{}, "pid ?: unknown" ],
] as const )("formats port listener %j" , (listener, expected) => {
expect(formatPortListener(listener)).toBe(expected);
});
it("formats free and busy port diagnostics" , () => {
expect(
formatPortDiagnostics({
port: 18789 ,
status: "free" ,
listeners: [],
hints: [],
}),
).toEqual(["Port 18789 is free." ]);
const lines = formatPortDiagnostics({
port: 18789 ,
status: "busy" ,
listeners: [{ pid: 123 , user: "alice" , commandLine: "ssh -N -L 18789:127.0.0.1:18789" }],
hints: buildPortHints([{ pid: 123 , commandLine: "ssh -N -L 18789:127.0.0.1:18789" }], 18789 ),
});
expect(lines[0 ]).toContain("Port 18789 is already in use" );
expect(lines).toContain("- pid 123 alice: ssh -N -L 18789:127.0.0.1:18789" );
expect(lines.some((line) => line.includes("SSH tunnel" ))).toBe(true );
});
});
Messung V0.5 in Prozent C=100 H=100 G=100
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland