Files
2026-07-13 13:39:12 +08:00

257 lines
7.6 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import * as classifyPublicApi from "../../../src/server/authz/classify.ts";
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: "/v1beta alias",
path: "/v1beta",
expectedClass: "CLIENT_API",
expectedNormalized: "/api/v1beta",
},
{
name: "/v1beta generateContent alias",
path: "/v1beta/models/gemini-pro:generateContent",
expectedClass: "CLIENT_API",
expectedNormalized: "/api/v1beta/models/gemini-pro:generateContent",
},
{
name: "/api/v1beta generateContent",
path: "/api/v1beta/models/gemini-pro:generateContent",
expectedClass: "CLIENT_API",
},
{
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/auth POST is PUBLIC",
path: "/api/cloud/auth",
method: "POST",
expectedClass: "PUBLIC",
},
{
name: "/api/cloud/model/resolve POST is PUBLIC",
path: "/api/cloud/model/resolve",
method: "POST",
expectedClass: "PUBLIC",
},
{
name: "/api/cloud/models/alias GET is PUBLIC",
path: "/api/cloud/models/alias",
method: "GET",
expectedClass: "PUBLIC",
},
{
name: "/api/cloud/credentials/update PUT is MANAGEMENT",
path: "/api/cloud/credentials/update",
method: "PUT",
expectedClass: "MANAGEMENT",
},
{
name: "/api/cloud/models/alias PUT is MANAGEMENT",
path: "/api/cloud/models/alias",
method: "PUT",
expectedClass: "MANAGEMENT",
},
{
name: "/api/cloud/unknown GET is MANAGEMENT",
path: "/api/cloud/unknown",
method: "GET",
expectedClass: "MANAGEMENT",
},
{
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: "/api/usage/om-usage is PUBLIC (handler enforces its own API key auth)",
path: "/api/usage/om-usage",
method: "GET",
expectedClass: "PUBLIC",
},
{
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");
assert.equal(classifyRoute("/api/v1betamax").routeClass, "MANAGEMENT");
assert.equal(classifyRoute("/api/v1beta/models").routeClass, "CLIENT_API");
});
test("classify module public surface only exposes route classification", () => {
assert.equal("classifyRoute" in classifyPublicApi, true);
assert.equal("isClientApi" in classifyPublicApi, false);
assert.equal("isManagement" in classifyPublicApi, false);
assert.equal("isPublic" in classifyPublicApi, false);
});