import { OpenAPIHono, z } from "@hono/zod-openapi"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { definePluginRoutes } from "../../src/plugin-integration/plugin-integration.js"; import { createOpenAPIRegistry, DEFAULT_SPEC_PATH, } from "../../src/registry/registry.js"; import { GET, POST, successResponse, } from "../../src/route-factory/route-factory.js"; describe("full doc roundtrip — real OpenAPIHono + real registry + 3 plugins", () => { it("registers across 3 plugins and the served doc matches buildDocument()", { timeout: 30000, }, async () => { const app = new OpenAPIHono(); const registry = createOpenAPIRegistry({ info: { title: "All-in-One", version: "1.0.0" }, }); // Plugin: users const listUsers = GET("/users") .tags("users") .operationId("listUsers") .response(successResponse(z.array(z.object({ id: z.string() })))) .build(); const createUser = POST("/users") .tags("users") .operationId("createUser") .body(z.object({ email: z.string().email() })) .response( successResponse(z.object({ id: z.string(), email: z.string() })) ) .build(); definePluginRoutes( [ { route: listUsers, handler: (c) => c.json({ success: true as const, data: [] }, 200), }, { route: createUser, handler: (c) => c.json( { success: true as const, data: { id: "1", email: "u@e.com" }, }, 200 ), }, ], { app, registry, pluginName: "users" } ); // Plugin: projects const listProjects = GET("/projects") .tags("projects") .operationId("listProjects") .response(successResponse(z.array(z.object({ id: z.string() })))) .build(); definePluginRoutes( [ { route: listProjects, handler: (c) => c.json({ success: true as const, data: [] }, 200), }, ], { app, registry, pluginName: "projects" } ); // Plugin: health const health = GET("/health") .tags("ops") .operationId("getHealth") .response(successResponse(z.object({ ok: z.boolean() }))) .build(); definePluginRoutes( [ { route: health, handler: (c) => c.json( { success: true as const, data: { ok: true }, }, 200 ), }, ], { app, registry, pluginName: "health" } ); // Connect the registry to the app registry.applyTo(app); // Assert registry-built doc contains every route const built = registry.buildDocument(); expect(Object.keys(built.paths).sort()).toEqual([ "/health", "/projects", "/users", ]); expect(built.paths["/users"]?.get?.operationId).toBe("listUsers"); expect(built.paths["/users"]?.post?.operationId).toBe("createUser"); // Tag set carries through expect(built.tags?.map((t) => t.name).sort()).toEqual([ "ops", "projects", "users", ]); // Plugin-name introspection expect( registry .getRoutesByPlugin("users") .map((r) => r.path) .sort() ).toEqual(["/users", "/users"]); expect( registry.getRoutesByPlugin("projects").map((r) => r.path) ).toEqual(["/projects"]); // Served doc must equal buildDocument() byte-for-byte const res = await app.request(DEFAULT_SPEC_PATH); expect(res.status).toBe(200); const served = (await res.json()) as unknown; expect(served).toEqual(built); }); });