// @vitest-environment node import { describe, expect, test } from "vitest"; import { getHostRoutingRuntimeDecision } from "./host-routing-runtime"; function expectDispatchDecision(request: Request) { const decision = getHostRoutingRuntimeDecision(request); expect(decision.type).toBe("dispatch"); if (decision.type !== "dispatch") { throw new Error(`Expected dispatch decision, received ${decision.type}`); } return decision; } describe("getHostRoutingRuntimeDecision", () => { test("dispatches the canonical app host root to the tenant picker route", () => { const decision = expectDispatchDecision(new Request("http://app.localhost/?from=login#tenants")); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe("/"); expect(new URL(decision.request.url).search).toBe("?from=login"); expect(new URL(decision.request.url).hash).toBe("#tenants"); }); test("keeps app host requests on promoted tenant paths for web dispatch", () => { const decision = expectDispatchDecision(new Request("http://app.localhost/acme/dashboard?tab=activity#events")); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe("/acme/dashboard"); expect(new URL(decision.request.url).search).toBe("?tab=activity"); expect(new URL(decision.request.url).hash).toBe("#events"); }); test("dispatches public auth routes on the app host without tenant rewriting", () => { const decision = expectDispatchDecision(new Request("http://app.localhost/login?redirectTo=%2Facme%2Fdashboard")); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe("/login"); expect(new URL(decision.request.url).search).toBe("?redirectTo=%2Facme%2Fdashboard"); }); test("rejects removed public API paths instead of redirecting compatibility routes", () => { const decision = getHostRoutingRuntimeDecision(new Request("http://app.localhost/api/workflows/run/workflow_1?tenantId=tenant_1")); expect(decision).toEqual({ type: "reject", status: 404, }); }); test("redirects legacy api v1 paths before removed API path rejection", () => { expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/api/v1/health?check=deep#status"))).toEqual({ type: "redirect", status: 308, location: "http://api.localhost/v1/health?check=deep#status", }); }); test("lets internal API proxies reach private web API handlers without public redirects", () => { const decision = expectDispatchDecision( new Request("http://app.localhost/internal/api/workflows/run/workflow_1?tenantId=tenant_1", { method: "POST", headers: { "x-toolctrl-internal-web-api-proxy": "1", "x-toolctrl-internal-web-api-proxy-signature": "signed", "x-api-key": "tenant-api-key", }, body: JSON.stringify({ name: "Ada" }), }) ); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe("/internal/api/workflows/run/workflow_1"); expect(new URL(decision.request.url).search).toBe("?tenantId=tenant_1"); expect(decision.request.headers.get("x-toolctrl-internal-web-api-proxy")).toBe("1"); expect(decision.request.headers.get("x-toolctrl-internal-web-api-proxy-signature")).toBe("signed"); expect(decision.request.headers.get("x-api-key")).toBe("tenant-api-key"); }); test("dispatches canonical admin host requests for web dispatch", () => { const decision = expectDispatchDecision(new Request("http://admin.localhost/accounts/users?role=owner#active")); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe("/accounts/users"); expect(new URL(decision.request.url).search).toBe("?role=owner"); expect(new URL(decision.request.url).hash).toBe("#active"); }); test("keeps canonical admin host routes stable for web dispatch", () => { const settingsDecision = expectDispatchDecision(new Request("http://admin.localhost/settings/profile?tab=mfa#codes")); const metricsDataDecision = expectDispatchDecision(new Request("http://admin.localhost/metrics/summary.data?_routes=routes%2Fadmin%2Fmetrics%2Fsummary.__host")); expect(settingsDecision.target).toBe("web"); expect(new URL(settingsDecision.request.url).pathname).toBe("/settings/profile"); expect(new URL(settingsDecision.request.url).search).toBe("?tab=mfa"); expect(new URL(settingsDecision.request.url).hash).toBe("#codes"); expect(new URL(metricsDataDecision.request.url).pathname).toBe("/metrics/summary.data"); }); test.each([ ["/api?tab=overview", "/api"], ["/api/keys?filter=active#keys", "/api/keys"], ["/api/keys/new", "/api/keys/new"], ["/api/keys/api_key_1", "/api/keys/api_key_1"], ["/api/logs", "/api/logs"], ["/api/docs", "/api/docs"], ["/api/credits", "/api/credits"], ["/api/keys.data?_routes=routes%2Fadmin%2Fapi%2Fkeys.__host", "/api/keys.data"], ])("dispatches canonical admin API section route %s to the web runtime", (route, expectedPathname) => { const decision = expectDispatchDecision(new Request(`http://admin.localhost${route}`)); expect(decision.target).toBe("web"); expect(new URL(decision.request.url).pathname).toBe(expectedPathname); }); test("redirects API host unversioned aliases to v1 paths before dispatch", () => { expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/health?check=deep#status"))).toEqual({ type: "redirect", status: 308, location: "http://api.localhost/v1/health?check=deep#status", }); expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/accounts?cursor=next"))).toEqual({ type: "redirect", status: 308, location: "http://api.localhost/v1/accounts?cursor=next", }); }); test("redirects public web paths away from the API host", () => { const decision = getHostRoutingRuntimeDecision(new Request("http://api.localhost/login?redirectTo=%2Facme%2Fdashboard")); expect(decision).toEqual({ type: "redirect", status: 308, location: "http://app.localhost/login?redirectTo=%2Facme%2Fdashboard", }); }); test("does not double-prefix API internal paths", () => { const apiDecision = expectDispatchDecision(new Request("http://api.localhost/v1/health?check=deep#status")); expect(new URL(apiDecision.request.url).pathname).toBe("/v1/health"); }); test("rejects removed root app paths instead of redirecting compatibility routes", () => { const decision = getHostRoutingRuntimeDecision(new Request("http://localhost/app/acme/settings?tab=profile#billing")); expect(decision).toEqual({ type: "reject", status: 404, }); }); test("rejects removed admin paths on root and app hosts", () => { expect(getHostRoutingRuntimeDecision(new Request("http://localhost/admin/settings?tab=users#active"))).toEqual({ type: "reject", status: 404, }); expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/admin/settings?tab=users#active"))).toEqual({ type: "reject", status: 404, }); }); test("redirects canonical paths away from the wrong host before dispatch", () => { expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/dashboard?tab=activity#events"))).toEqual({ type: "redirect", status: 308, location: "http://admin.localhost/dashboard?tab=activity#events", }); expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/accounts/users?filter=active"))).toEqual({ type: "redirect", status: 308, location: "http://admin.localhost/accounts/users?filter=active", }); expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/acme/dashboard?tab=activity#events"))).toEqual({ type: "redirect", status: 308, location: "http://app.localhost/acme/dashboard?tab=activity#events", }); expect(getHostRoutingRuntimeDecision(new Request("http://admin.localhost/acme"))).toEqual({ type: "redirect", status: 308, location: "http://app.localhost/acme", }); expect(getHostRoutingRuntimeDecision(new Request("http://api.localhost/acme"))).toEqual({ type: "redirect", status: 308, location: "http://app.localhost/acme", }); expect(getHostRoutingRuntimeDecision(new Request("http://app.localhost/v1/health"))).toEqual({ type: "redirect", status: 308, location: "http://api.localhost/v1/health", }); }); test("dispatches API operational routes to the API runtime", () => { const healthDecision = expectDispatchDecision(new Request("http://api.localhost/healthz")); const readyDecision = expectDispatchDecision(new Request("http://api.localhost/readyz")); expect(healthDecision.target).toBe("api"); expect(new URL(healthDecision.request.url).pathname).toBe("/healthz"); expect(readyDecision.target).toBe("api"); expect(new URL(readyDecision.request.url).pathname).toBe("/readyz"); }); });