Files
2026-05-27 23:29:53 +01:00

144 lines
4.9 KiB
TypeScript

import { OpenAPIHono } from "@hono/zod-openapi";
import { createOpenAPIRegistry } from "@suaveplan/api-openapi";
import { createPluginContext } from "@suaveplan/api-plugin";
import { betterAuthSurrealAdapter } from "@suaveplan/better-auth-surrealdb";
import { EventBus } from "@suaveplan/events";
import { SurrealAdapter } from "@suaveplan/surrealdb";
import {
afterAll,
beforeAll,
describe,
expect,
it,
} from "@suaveplan/testing/runner";
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
import { createAuthPlugin } from "../../src/plugin/plugin.js";
import type { AuthPluginConfig } from "../../src/types/types.js";
const noopLogger = {
debug: () => undefined,
info: () => undefined,
warn: () => undefined,
error: () => undefined,
};
let fixture: SurrealDBFixture | null = null;
let client: SurrealAdapter | null = null;
beforeAll(async () => {
fixture = new SurrealDBFixture();
const cfg = await fixture.start();
client = new SurrealAdapter({
url: cfg.url,
namespace: cfg.namespace,
database: cfg.database,
username: cfg.username,
password: cfg.password,
});
await client.connect();
await client.execute("DEFINE TABLE OVERWRITE user SCHEMAFULL;");
await client.execute(
"DEFINE FIELD OVERWRITE email ON user TYPE option<string>;"
);
await client.execute(
"DEFINE FIELD OVERWRITE phoneNumber ON user TYPE option<string>;"
);
await client.execute(
"DEFINE FIELD OVERWRITE createdAt ON user TYPE option<datetime> DEFAULT time::now() VALUE $before OR time::now();"
);
await client.execute(
"DEFINE FIELD OVERWRITE updatedAt ON user TYPE option<datetime> DEFAULT time::now() VALUE time::now();"
);
}, 180_000);
afterAll(async () => {
if (client) {
await client.disconnect();
client = null;
}
if (fixture) {
await fixture.stop();
fixture = null;
}
}, 60_000);
describe("openapi-doc", () => {
it("served /openapi.json includes auth routes with Zod-typed schemas", {
timeout: 120_000,
}, async () => {
if (client === null) throw new Error("client not ready");
const app = new OpenAPIHono();
const registry = createOpenAPIRegistry({
info: { title: "auth-test", version: "0.1.0" },
});
const plugin = createAuthPlugin({
betterAuth: {
database: betterAuthSurrealAdapter({ client }),
secret: "test-secret-at-least-32-chars-long-for-better-auth",
baseURL: "http://localhost:3000/auth",
emailAndPassword: { enabled: true },
},
});
const services = new Map<string, unknown>();
services.set("app", app);
services.set("openAPIRegistry", registry);
services.set("db", client);
await plugin.onInit?.(
createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: new AbortController().signal,
config: plugin.defaults as AuthPluginConfig,
services,
})
);
registry.applyTo(app, "/openapi.json");
const res = await app.request("/openapi.json");
const doc = (await res.json()) as {
paths: Record<string, Record<string, unknown>>;
};
for (const path of [
"/auth/sign-up",
"/auth/sign-in",
"/auth/sign-out",
"/auth/session",
"/auth/forgot-password",
"/auth/reset-password",
"/auth/verify-email",
]) {
expect(doc.paths[path]).toBeDefined();
}
});
it("served doc lists all 9 auth routes", { timeout: 120_000 }, async () => {
if (client === null) throw new Error("client not ready");
const app = new OpenAPIHono();
const registry = createOpenAPIRegistry({
info: { title: "auth-test", version: "0.1.0" },
});
const plugin = createAuthPlugin({
betterAuth: {
database: betterAuthSurrealAdapter({ client }),
secret: "test-secret-at-least-32-chars-long-for-better-auth",
baseURL: "http://localhost:3000/auth",
emailAndPassword: { enabled: true },
},
});
const services = new Map<string, unknown>();
services.set("app", app);
services.set("openAPIRegistry", registry);
services.set("db", client);
await plugin.onInit?.(
createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: new AbortController().signal,
config: plugin.defaults as AuthPluginConfig,
services,
})
);
const routes = registry.getRoutesByPlugin("auth");
expect(routes.length).toBe(9);
});
});