Files
diegosouzapw--omniroute/tests/unit/ninerouter-embed-port-6205.test.ts
2026-07-13 13:39:12 +08:00

101 lines
4.4 KiB
TypeScript

/**
* #6205 — 9Router embedded service: embed-panel 404 + pre-spawn EADDRINUSE.
*
* SUB-BUG A — the embed panel root (`/embed/`, zero segments after `embed/`)
* 404s because the proxy route was a REQUIRED catch-all `[...path]`, which does
* not match a segment-less path. Fix: OPTIONAL catch-all `[[...path]]` so the
* root matches, and `reverseProxy.toUpstreamPath([])` maps the empty segment
* array to `"/"`.
*
* SUB-BUG B — `ServiceSupervisor.start()` spawned the child with no pre-flight
* port/health probe, so an orphaned prior instance holding the port made the
* child die with a raw EADDRINUSE stack. Fix: `decidePreSpawn()` — adopt a
* healthy instance, surface a clear error for a held-but-unhealthy port.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
import path from "node:path";
import { toUpstreamPath } from "../../src/lib/services/embedPath.ts";
import { decidePreSpawn } from "../../src/lib/services/portProbe.ts";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const repoRoot = path.resolve(__dirname, "../..");
// ─── SUB-BUG A: optional catch-all route + empty-segment mapping ──────────────
describe("#6205 A — embed panel root no longer 404s", () => {
it("the proxy route folder is an OPTIONAL catch-all ([[...path]])", () => {
const optional = path.join(
repoRoot,
"src/app/(dashboard)/dashboard/providers/services/[name]/embed/[[...path]]/route.ts"
);
const required = path.join(
repoRoot,
"src/app/(dashboard)/dashboard/providers/services/[name]/embed/[...path]/route.ts"
);
assert.ok(existsSync(optional), "optional catch-all [[...path]]/route.ts must exist");
assert.ok(
!existsSync(required),
"required catch-all [...path]/route.ts must be gone (it cannot match /embed/)"
);
});
it("maps the segment-less embed root ([]) to upstream '/'", () => {
// The embed frame links to `/dashboard/providers/services/9router/embed/`,
// i.e. zero segments after `embed/`. With the optional catch-all matching,
// Next hands the route an empty segment array — which must map to "/".
assert.equal(toUpstreamPath([]), "/");
});
it("still maps nested segments to their upstream path", () => {
assert.equal(toUpstreamPath(["ui", "index.html"]), "/ui/index.html");
assert.equal(toUpstreamPath(["api", "models"]), "/api/models");
});
it("derives an empty segment array from the embed frame's constructed path", () => {
// Mirrors what Next's catch-all does: strip the prefix, split remaining.
const framePath = "/dashboard/providers/services/9router/embed/";
const prefix = "/dashboard/providers/services/9router/embed/";
const rest = framePath.slice(prefix.length); // ""
const segments = rest.split("/").filter(Boolean); // []
assert.deepEqual(segments, []);
assert.equal(toUpstreamPath(segments), "/");
});
});
// ─── SUB-BUG B: pre-spawn port/health decision ───────────────────────────────
describe("#6205 B — pre-spawn port probe avoids raw EADDRINUSE", () => {
it("adopts a healthy existing instance (no spawn)", () => {
const decision = decidePreSpawn({ healthy: true, portInUse: true }, 20130);
assert.equal(decision.action, "adopt");
});
it("returns a clear error object (not a throw) when the port is held but unhealthy", () => {
let decision;
assert.doesNotThrow(() => {
decision = decidePreSpawn({ healthy: false, portInUse: true }, 20130);
}, "decision must be returned, never thrown");
assert.equal(decision.action, "error");
assert.match(decision.message, /already in use/i);
assert.match(decision.message, /20130/, "error should name the port");
// The clear message must not be a raw EADDRINUSE stack trace.
assert.ok(!decision.message.includes("at /"), "must not leak a stack trace");
});
it("spawns when the port is free", () => {
const decision = decidePreSpawn({ healthy: false, portInUse: false }, 20130);
assert.equal(decision.action, "spawn");
});
it("adopts a healthy instance even if the TCP probe missed it", () => {
// Health is authoritative: a 2xx means a real instance is serving.
const decision = decidePreSpawn({ healthy: true, portInUse: false }, 20130);
assert.equal(decision.action, "adopt");
});
});