This commit is contained in:
2026-03-03 15:23:00 +00:00
parent 5e3726de39
commit 8e223bfbec
3689 changed files with 955330 additions and 1011 deletions

View File

@@ -1,41 +1,88 @@
import { describe, expect, it } from "bun:test";
import { mkdtempSync, rmSync } from "node:fs";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { createApp } from "../src/app";
import { reseedDatabase } from "../src/database";
import { openApiDocument } from "../src/openapi";
import { matchRoute, routes } from "../src/routes";
describe("route registry", () => {
it("matches concrete paths and extracts route params", () => {
const matched = matchRoute("GET", "/orgs/org_acme/tasks");
const swaggerAliasPaths = new Set(["/openapi.json", "/swagger.json"]);
expect(matched).not.toBeNull();
expect(matched?.route.operationId).toBe("listOrganizationTasks");
expect(matched?.params).toEqual({ orgId: "org_acme" });
function normalizeOpenApiPath(path: string) {
return path.replace(/:([^/]+)/g, "{$1}");
}
function getDocumentedOperations() {
return Object.entries(openApiDocument.paths).flatMap(([path, pathItem]) =>
Object.keys(pathItem as Record<string, unknown>).map(
(method) => `${method.toUpperCase()} ${normalizeOpenApiPath(path)}`,
),
);
}
function getRuntimeOperations() {
const tempDir = mkdtempSync(join(tmpdir(), "mock-task-api-routes-"));
try {
const db = reseedDatabase(join(tempDir, "test.sqlite"));
const app = createApp(db, { parseJson: false });
const stack = (app as { router: { stack: Array<{ route?: unknown }> } }).router.stack;
return stack.flatMap((layer) => {
if (!layer.route) {
return [];
}
const route = layer.route as { path: string; methods: Record<string, boolean> };
if (swaggerAliasPaths.has(route.path)) {
return [];
}
return Object.entries(route.methods)
.filter(([, enabled]) => enabled)
.map(([method]) => `${method.toUpperCase()} ${normalizeOpenApiPath(route.path)}`);
});
} finally {
rmSync(tempDir, { recursive: true, force: true });
}
}
describe("tsoa swagger document", () => {
it("matches all runtime business routes registered in express", () => {
const documentedOperations = getDocumentedOperations();
const runtimeOperations = getRuntimeOperations();
expect(runtimeOperations.sort()).toEqual(documentedOperations.sort());
});
it("returns null for unsupported methods and unknown paths", () => {
expect(matchRoute("DELETE", "/tasks/task_1001")).toBeNull();
expect(matchRoute("GET", "/does-not-exist")).toBeNull();
it("documents the expected business endpoints generated from controllers", () => {
const operations = getDocumentedOperations();
expect(operations.sort()).toEqual([
"GET /health",
"GET /orgs",
"GET /orgs/{orgId}",
"GET /orgs/{orgId}/projects",
"GET /orgs/{orgId}/tasks",
"GET /users",
"GET /projects",
"GET /tasks",
"POST /tasks",
"GET /tasks/{taskId}",
"PATCH /tasks/{taskId}",
].sort());
});
it("builds an openapi path entry for every registered route", () => {
const registeredOperations = routes.map(
(route) => `${route.method} ${route.path}:${route.operationId}`,
);
const documentedOperations = Object.entries(openApiDocument.paths).flatMap(
([path, pathItem]) =>
Object.entries(pathItem).map(
([method, operation]) =>
`${method.toUpperCase()} ${path}:${(operation as { operationId?: string }).operationId ?? ""}`,
),
);
expect(documentedOperations).toEqual(registeredOperations);
it("does not include swagger hosting aliases as business operations", () => {
expect(openApiDocument.paths["/openapi.json"]).toBeUndefined();
expect(openApiDocument.paths["/swagger.json"]).toBeUndefined();
expect(openApiDocument.paths["/api-docs"]).toBeUndefined();
});
it("keeps spec-serving routes explicit in the registry", () => {
const openApiAliases = routes
.filter((route) => route.servesOpenApiDocument)
.map((route) => route.path);
it("uses tsoa-generated schemas for the task mutation payloads", () => {
const schemas = openApiDocument.components.schemas as Record<string, Record<string, unknown>>;
expect(openApiAliases).toEqual(["/openapi.json", "/swagger.json", "/api-docs"]);
expect(schemas.CreateTaskRequest).toBeDefined();
expect(schemas.UpdateTaskRequest).toBeDefined();
});
});