Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
import { OpenAPIHono } from "@hono/zod-openapi";
import { createOpenAPIRegistry } from "@suaveplan/api-openapi";
import { createPluginContext } from "@suaveplan/api-plugin";
import { EventBus } from "@suaveplan/events";
import { afterEach, describe, expect, it } from "@suaveplan/testing/runner";
import type { Auth } from "better-auth";
import { AuthError } from "../error-codes/error-codes.js";
import type { AuthPluginConfig } from "../types/types.js";
import { __setBetterAuthFactory, createAuthPlugin } from "./plugin.js";
const noopLogger = {
debug: () => undefined,
info: () => undefined,
warn: () => undefined,
error: () => undefined,
};
function makeAuthStub(): Auth {
return {
handler: async () => new Response("ok", { status: 200 }),
api: { getSession: async () => null },
} as unknown as Auth;
}
afterEach(() => {
__setBetterAuthFactory(undefined);
});
describe("createAuthPlugin", () => {
it("createAuthPlugin returns valid PluginDescriptor", {
timeout: 5000,
}, () => {
const plugin = createAuthPlugin({ betterAuth: {} });
expect(plugin.name).toBe("auth");
expect(plugin.version).toBe("0.1.0");
expect(typeof plugin.onInit).toBe("function");
expect(typeof plugin.onClose).toBe("function");
});
it("invalid options raise AUTH_INVALID_INPUT", { timeout: 5000 }, () => {
let caught: AuthError | undefined;
try {
createAuthPlugin({} as never);
} catch (err) {
caught = err as AuthError;
}
expect(caught?.code).toBe("AUTH.INVALID_INPUT");
expect(caught?.cause).toBeDefined();
});
it("descriptor exposes resolved defaults", { timeout: 5000 }, () => {
const plugin = createAuthPlugin({
betterAuth: {},
routePrefix: "/v2/auth",
});
expect((plugin.defaults as AuthPluginConfig).routePrefix).toBe(
"/v2/auth"
);
expect((plugin.defaults as AuthPluginConfig).applySchemaOnInit).toBe(
true
);
});
it("onClose clears internal state without error", {
timeout: 5000,
}, async () => {
const plugin = createAuthPlugin({ betterAuth: {} });
const services = new Map<string, unknown>();
const ctrl = new AbortController();
const ctx = createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: ctrl.signal,
config: plugin.defaults as AuthPluginConfig,
services,
});
await plugin.onClose?.(ctx);
});
it("onInit raises AUTH_SERVICE_NOT_AVAILABLE when app missing", {
timeout: 10_000,
}, async () => {
__setBetterAuthFactory(() => makeAuthStub());
const plugin = createAuthPlugin({
betterAuth: {},
applySchemaOnInit: false,
});
const services = new Map<string, unknown>();
const ctrl = new AbortController();
const ctx = createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: ctrl.signal,
config: plugin.defaults as AuthPluginConfig,
services,
});
let caught: AuthError | undefined;
try {
await plugin.onInit?.(ctx);
} catch (err) {
caught = err as AuthError;
}
expect(caught?.code).toBe("AUTH.SERVICE_NOT_AVAILABLE");
});
it("onInit completes with applySchemaOnInit: false + stubbed auth", {
timeout: 15_000,
}, async () => {
__setBetterAuthFactory(() => makeAuthStub());
const plugin = createAuthPlugin({
betterAuth: {},
applySchemaOnInit: false,
});
const services = new Map<string, unknown>();
const app = new OpenAPIHono();
const registry = createOpenAPIRegistry({
info: { title: "t", version: "0.1.0" },
});
services.set("app", app);
services.set("openAPIRegistry", registry);
services.set("logger", noopLogger);
services.set("eventBus", new EventBus());
services.set("clock", () => Date.now());
const ctrl = new AbortController();
const ctx = createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: ctrl.signal,
config: plugin.defaults as AuthPluginConfig,
services,
});
await plugin.onInit?.(ctx);
expect(services.get("authService")).toBeDefined();
expect(registry.getRoutesByPlugin("auth")).toHaveLength(9);
});
it("default factory delegates to real betterAuth", {
timeout: 10_000,
}, async () => {
// Restore the default factory and exercise the real `betterAuth`
// import — invoked with deliberately bad input so the test
// surfaces the BETTER_AUTH_INIT_FAILED translation rather than
// standing up a real Better-Auth instance.
__setBetterAuthFactory(undefined);
const plugin = createAuthPlugin({
betterAuth: { __invalid__: Symbol("nope") as unknown as string },
applySchemaOnInit: false,
});
const services = new Map<string, unknown>();
services.set("app", new OpenAPIHono());
const ctx = createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: new AbortController().signal,
config: plugin.defaults as AuthPluginConfig,
services,
});
let caught: AuthError | undefined;
try {
await plugin.onInit?.(ctx);
} catch (err) {
caught = err as AuthError;
}
// Real betterAuth may succeed with default in-memory config
// depending on version; both outcomes count as covering the
// default factory path. We only assert NO exception other than
// an AuthError reaches here.
if (caught !== undefined) {
expect(caught).toBeInstanceOf(AuthError);
}
});
it("onInit raises AUTH_BETTER_AUTH_INIT_FAILED when betterAuth throws", {
timeout: 10_000,
}, async () => {
__setBetterAuthFactory(() => {
throw new Error("better-auth: bad config");
});
const plugin = createAuthPlugin({
betterAuth: {},
applySchemaOnInit: false,
});
const services = new Map<string, unknown>();
services.set("app", new OpenAPIHono());
const ctrl = new AbortController();
const ctx = createPluginContext<AuthPluginConfig>({
logger: noopLogger,
eventBus: new EventBus(),
signal: ctrl.signal,
config: plugin.defaults as AuthPluginConfig,
services,
});
let caught: AuthError | undefined;
try {
await plugin.onInit?.(ctx);
} catch (err) {
caught = err as AuthError;
}
expect(caught?.code).toBe("AUTH.BETTER_AUTH_INIT_FAILED");
});
});