| import test from "node:test"; | |
| import assert from "node:assert/strict"; | |
| import { classifyRoute } from "../../../src/server/authz/classify.ts"; | |
| import type { RouteClass } from "../../../src/server/authz/types.ts"; | |
| interface Case { | |
| name: string; | |
| path: string; | |
| method?: string; | |
| expectedClass: RouteClass; | |
| expectedNormalized?: string; | |
| } | |
| const cases: Case[] = [ | |
| { name: "root /", path: "/", expectedClass: "MANAGEMENT", expectedNormalized: "/" }, | |
| { name: "dashboard root", path: "/dashboard", expectedClass: "MANAGEMENT" }, | |
| { name: "dashboard nested", path: "/dashboard/settings", expectedClass: "MANAGEMENT" }, | |
| { name: "dashboard onboarding", path: "/dashboard/onboarding", expectedClass: "PUBLIC" }, | |
| { | |
| name: "/api/v1 base", | |
| path: "/api/v1", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1", | |
| }, | |
| { | |
| name: "/api/v1/chat/completions", | |
| path: "/api/v1/chat/completions", | |
| expectedClass: "CLIENT_API", | |
| }, | |
| { name: "/api/v1/responses", path: "/api/v1/responses", expectedClass: "CLIENT_API" }, | |
| { name: "/api/v1/models", path: "/api/v1/models", expectedClass: "CLIENT_API" }, | |
| { name: "/api/v1/embeddings", path: "/api/v1/embeddings", expectedClass: "CLIENT_API" }, | |
| { name: "/api/v1/files", path: "/api/v1/files", expectedClass: "CLIENT_API" }, | |
| { name: "/api/v1/batches", path: "/api/v1/batches", expectedClass: "CLIENT_API" }, | |
| { name: "/api/v1/ws", path: "/api/v1/ws", expectedClass: "CLIENT_API" }, | |
| { name: "/api/mcp/* stays management", path: "/api/mcp/status", expectedClass: "MANAGEMENT" }, | |
| { | |
| name: "/v1 alias", | |
| path: "/v1", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1", | |
| }, | |
| { | |
| name: "/v1/chat/completions alias", | |
| path: "/v1/chat/completions", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/chat/completions", | |
| }, | |
| { | |
| name: "/v1/v1 double-prefix", | |
| path: "/v1/v1", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1", | |
| }, | |
| { | |
| name: "/v1/v1/embeddings double-prefix", | |
| path: "/v1/v1/embeddings", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/embeddings", | |
| }, | |
| { | |
| name: "/chat/completions alias", | |
| path: "/chat/completions", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/chat/completions", | |
| }, | |
| { | |
| name: "/responses alias", | |
| path: "/responses", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/responses", | |
| }, | |
| { | |
| name: "/responses/abc alias", | |
| path: "/responses/abc", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/responses/abc", | |
| }, | |
| { | |
| name: "/codex/* alias collapses to /api/v1/responses", | |
| path: "/codex/anything", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/responses", | |
| }, | |
| { | |
| name: "/models alias", | |
| path: "/models", | |
| expectedClass: "CLIENT_API", | |
| expectedNormalized: "/api/v1/models", | |
| }, | |
| { | |
| name: "/api/auth/login is PUBLIC", | |
| path: "/api/auth/login", | |
| method: "POST", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/auth/logout is PUBLIC", | |
| path: "/api/auth/logout", | |
| method: "POST", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/auth/status is PUBLIC", | |
| path: "/api/auth/status", | |
| method: "GET", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { name: "/api/init is PUBLIC", path: "/api/init", method: "POST", expectedClass: "PUBLIC" }, | |
| { | |
| name: "/api/monitoring/health is PUBLIC", | |
| path: "/api/monitoring/health", | |
| method: "GET", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/cloud/* is PUBLIC", | |
| path: "/api/cloud/something", | |
| method: "GET", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/oauth/* is PUBLIC", | |
| path: "/api/oauth/callback", | |
| method: "GET", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/sync/bundle is PUBLIC", | |
| path: "/api/sync/bundle", | |
| method: "POST", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/settings/require-login GET is PUBLIC readonly", | |
| path: "/api/settings/require-login", | |
| method: "GET", | |
| expectedClass: "PUBLIC", | |
| }, | |
| { | |
| name: "/api/settings/require-login POST is MANAGEMENT", | |
| path: "/api/settings/require-login", | |
| method: "POST", | |
| expectedClass: "MANAGEMENT", | |
| }, | |
| { | |
| name: "/api/providers/* MUST stay MANAGEMENT", | |
| path: "/api/providers/openai", | |
| expectedClass: "MANAGEMENT", | |
| }, | |
| { name: "/api/keys MANAGEMENT", path: "/api/keys", expectedClass: "MANAGEMENT" }, | |
| { name: "/api/db/health MANAGEMENT", path: "/api/db/health", expectedClass: "MANAGEMENT" }, | |
| { name: "/api/settings MANAGEMENT", path: "/api/settings", expectedClass: "MANAGEMENT" }, | |
| { name: "/api/audit MANAGEMENT", path: "/api/audit", expectedClass: "MANAGEMENT" }, | |
| { | |
| name: "Unknown top-level path defaults MANAGEMENT (fail-closed)", | |
| path: "/totally-unknown", | |
| expectedClass: "MANAGEMENT", | |
| }, | |
| ]; | |
| for (const c of cases) { | |
| test(`classifyRoute: ${c.name}`, () => { | |
| const r = classifyRoute(c.path, c.method ?? "GET"); | |
| assert.equal(r.routeClass, c.expectedClass, `routeClass for ${c.path}`); | |
| if (c.expectedNormalized) { | |
| assert.equal(r.normalizedPath, c.expectedNormalized, `normalizedPath for ${c.path}`); | |
| } | |
| }); | |
| } | |
| test("classifyRoute returns deterministic result for trailing slash", () => { | |
| const a = classifyRoute("/api/v1/chat/completions", "POST"); | |
| const b = classifyRoute("/api/v1/chat/completions/", "POST"); | |
| assert.equal(a.routeClass, b.routeClass); | |
| assert.equal(a.normalizedPath, b.normalizedPath); | |
| }); | |
| test("classifyRoute strips trailing slash on root only when not '/'", () => { | |
| assert.equal(classifyRoute("/").normalizedPath, "/"); | |
| }); | |
| test("classifyRoute treats /api/v1 prefix exactly", () => { | |
| assert.equal(classifyRoute("/api/v1abc").routeClass, "MANAGEMENT"); | |
| assert.equal(classifyRoute("/api/v1/x").routeClass, "CLIENT_API"); | |
| }); | |