98 lines
5.1 KiB
TypeScript
98 lines
5.1 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
DEFAULT_HEADROOM_URL,
|
|
isLoopbackHeadroomUrl,
|
|
parsePortFromHeadroomUrl,
|
|
buildHeadroomStatus,
|
|
} from "../../src/lib/headroom/detect";
|
|
import { getManagedPid, isPidAlive } from "../../src/lib/headroom/process";
|
|
import { isLocalOnlyPath } from "../../src/server/authz/routeGuard";
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// 1. Route guard: /api/headroom/{start,stop} MUST be classified LOCAL_ONLY
|
|
// so a tunneled JWT cannot trigger child-process spawning (Hard Rule #15).
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
test("isLocalOnlyPath gates /api/headroom/start and /stop", () => {
|
|
assert.equal(isLocalOnlyPath("/api/headroom/start"), true);
|
|
assert.equal(isLocalOnlyPath("/api/headroom/stop"), true);
|
|
// status is a read-only probe — keep it accessible (matches upstream b55cf36d).
|
|
assert.equal(isLocalOnlyPath("/api/headroom/status"), false);
|
|
});
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// 2. URL helpers — pure functions, no side effects.
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
test("DEFAULT_HEADROOM_URL falls back to localhost:8787", () => {
|
|
assert.ok(DEFAULT_HEADROOM_URL.startsWith("http"));
|
|
assert.ok(/:8787$/.test(DEFAULT_HEADROOM_URL) || /HEADROOM_URL/.test(DEFAULT_HEADROOM_URL));
|
|
});
|
|
|
|
test("isLoopbackHeadroomUrl recognizes loopback hosts", () => {
|
|
assert.equal(isLoopbackHeadroomUrl("http://localhost:8787"), true);
|
|
assert.equal(isLoopbackHeadroomUrl("http://127.0.0.1:8787"), true);
|
|
assert.equal(isLoopbackHeadroomUrl("http://[::1]:8787"), true);
|
|
assert.equal(isLoopbackHeadroomUrl("http://headroom:8787"), false);
|
|
assert.equal(isLoopbackHeadroomUrl("http://10.0.0.5:8787"), false);
|
|
assert.equal(isLoopbackHeadroomUrl("not-a-url"), false);
|
|
});
|
|
|
|
test("parsePortFromHeadroomUrl extracts valid port or returns null", () => {
|
|
assert.equal(parsePortFromHeadroomUrl("http://localhost:8787"), 8787);
|
|
assert.equal(parsePortFromHeadroomUrl("http://localhost"), null);
|
|
assert.equal(parsePortFromHeadroomUrl("bogus"), null);
|
|
// Pair-port 50ed79fe: Docker sidecar with non-standard port.
|
|
assert.equal(parsePortFromHeadroomUrl("http://headroom:9090"), 9090);
|
|
});
|
|
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
// 3. buildHeadroomStatus — pure shape assembly (mockable inputs).
|
|
// Pair commit 50ed79fe: a reachable external proxy must report
|
|
// running=true and canStart=false even without a local CLI.
|
|
// ──────────────────────────────────────────────────────────────────────────────
|
|
test("buildHeadroomStatus: local CLI present + proxy running → canStart=true", () => {
|
|
const status = buildHeadroomStatus({
|
|
url: "http://localhost:8787",
|
|
binaryPath: "/usr/local/bin/headroom",
|
|
python: "python3.12",
|
|
proxyReachable: true,
|
|
});
|
|
assert.equal(status.installed, true);
|
|
assert.equal(status.running, true);
|
|
assert.equal(status.localUrl, true);
|
|
assert.equal(status.canStart, true);
|
|
assert.equal(status.python, "python3.12");
|
|
});
|
|
|
|
test("buildHeadroomStatus: external proxy reachable, no local CLI → running=true, canStart=false (Docker sidecar)", () => {
|
|
const status = buildHeadroomStatus({
|
|
url: "http://headroom:8787",
|
|
binaryPath: null,
|
|
python: null,
|
|
proxyReachable: true,
|
|
});
|
|
assert.equal(status.installed, false);
|
|
assert.equal(status.running, true);
|
|
assert.equal(status.localUrl, false);
|
|
assert.equal(status.canStart, false);
|
|
});
|
|
|
|
test("buildHeadroomStatus: nothing installed and proxy unreachable → all false", () => {
|
|
const status = buildHeadroomStatus({
|
|
url: "http://localhost:8787",
|
|
binaryPath: null,
|
|
python: null,
|
|
proxyReachable: false,
|
|
});
|
|
assert.equal(status.installed, false);
|
|
assert.equal(status.running, false);
|
|
assert.equal(status.localUrl, true);
|
|
assert.equal(status.canStart, false);
|
|
});
|
|
|
|
test("process status helpers are safe for absent managed processes", () => {
|
|
assert.equal(isPidAlive(null), false);
|
|
assert.equal(isPidAlive(undefined), false);
|
|
assert.equal(getManagedPid(), null);
|
|
});
|