import { expect, test } from "bun:test"; import { createPluginRunner } from "@wrnexus/plugin"; import { authPlugin } from "../src/plugin.ts"; import { AUTH_ROUTE_DEFINITIONS } from "../src/routes/definitions.ts"; import { readFileSync } from "node:fs"; test("plugin contributes components, runtime, styles, migration, and toolbar", async () => { const metadata = new Map(); const runner = createPluginRunner( authPlugin({ includeRoutes: true, includeMigrations: true, includeMiddleware: true }), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }, ); const contributions = await runner.contributions(); expect(contributions.componentDirs).toHaveLength(1); expect(contributions.clientRuntimes[0]).toMatchObject({ id: "auth", singleton: true }); expect(contributions.styles[0]?.id).toBe("auth-components"); expect(contributions.migrations.map((migration) => migration.id)).toEqual([ "wrnexus-auth-001", "wrnexus-auth-002-otp-purpose", ]); expect(contributions.routes.length).toBeGreaterThanOrEqual(30); expect(contributions.middleware).toHaveLength(1); }); test("unconfigured automatic discovery fails closed for routes, middleware, and migrations", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin(), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }); const contributions = await runner.contributions(); expect(contributions.routes).toHaveLength(0); expect(contributions.middleware).toHaveLength(0); expect(contributions.migrations).toHaveLength(0); expect(contributions.componentDirs).toHaveLength(1); expect(contributions.clientRuntimes).toHaveLength(1); }); test("config.auth controls route groups and migrations without explicit plugin options", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin(), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }); await runner.configure({ auth: { engine: {} as never, migrations: false, routes: { enabled: true, registration: false, passkeys: false }, }, }); const contributions = await runner.contributions(); expect(contributions.migrations).toHaveLength(0); expect(contributions.routes.some((route) => route.path === "/api/auth/register")).toBe(false); expect(contributions.routes.some((route) => route.path.includes("/passkeys/"))).toBe(false); expect(contributions.routes.some((route) => route.path === "/api/auth/login")).toBe(true); }); test("auth runtime contains built-in browser schemas", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin({ includeMigrations: false }), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }); const contributions = await runner.contributions(); expect(contributions.clientRuntimes[0]?.source).toContain("auth-password-request"); expect(contributions.clientRuntimes[0]?.source).toContain("auth-register"); }); test("each package auth route uses a route-specific entry module", async () => { const metadata = new Map(); const runner = createPluginRunner( authPlugin({ includeRoutes: true, includeMigrations: false, includeMiddleware: false, }), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }, ); const contributions = await runner.contributions(); const entries = contributions.routes.map((route) => route.entry); const passwordRequest = contributions.routes.find( (route) => route.path === "/api/auth/password/request", ); expect(new Set(entries).size).toBe(entries.length); expect( passwordRequest?.entry.replace(/\\/g, "/").endsWith("/src/routes/api/password-request.ts"), ).toBe(true); for (const definition of AUTH_ROUTE_DEFINITIONS) { const route = contributions.routes.find((item) => item.path === definition.path); expect(route).toBeDefined(); const source = readFileSync(route!.entry, "utf8"); expect(source).toContain(`invokeAuthHandler("${definition.handler}"`); expect(source.includes("dispatchAuthRoute")).toBe(false); for (const method of definition.methods) { expect(source).toContain(`export function ${method}`); } } }); test("config.auth registers package routes", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin(), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }); await runner.configure({ auth: { engine: {} as never, routes: true, middleware: true, migrations: false, }, }); const contributions = await runner.contributions(); expect(contributions.routes.some((route) => route.path === "/api/auth/password/request")).toBe( true, ); expect(contributions.routes.some((route) => route.path === "/api/auth/register")).toBe(true); expect(contributions.routes.some((route) => route.path === "/api/auth/login")).toBe(true); expect(contributions.middleware).toHaveLength(1); expect(contributions.migrations).toHaveLength(0); }); test("config.auth contributes a reusable forward-auth verification route", async () => { const runner = createPluginRunner(authPlugin(), { root: process.cwd(), mode: "development", command: "dev", metadata: new Map(), warn() {}, }); await runner.configure({ auth: { engine: {} as never, routes: true, }, }); const contributions = await runner.contributions(); expect(contributions.routes.some((route) => route.path === "/api/auth/session/verify")).toBe( true, ); }); test("config.auth can disable route groups", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin(), { root: process.cwd(), mode: "development", command: "dev", metadata, warn() {}, }); await runner.configure({ auth: { engine: {} as never, migrations: false, routes: { enabled: true, password: true, passkeys: false, }, }, }); const contributions = await runner.contributions(); expect(contributions.routes.some((route) => route.path === "/api/auth/password/request")).toBe( true, ); expect(contributions.routes.some((route) => route.path.includes("/passkeys/"))).toBe(false); });