From 5720f93db17d2d6c0f1e04ffb74b8210c3425499 Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Thu, 20 Aug 2026 01:43:38 +0530 Subject: [PATCH] test(auth): restore plugin engine-hook coverage via invokeAuthHandler Co-Authored-By: Claude Opus 5 --- packages/auth/test/plugin.test.ts | 102 ++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/packages/auth/test/plugin.test.ts b/packages/auth/test/plugin.test.ts index ce9cef88..ce0c81a7 100644 --- a/packages/auth/test/plugin.test.ts +++ b/packages/auth/test/plugin.test.ts @@ -1,8 +1,45 @@ import { expect, test } from "bun:test"; +import type { Context } from "@wrnexus/core"; 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"; +import { createAuthEngine } from "../src/engine.ts"; +import { MemoryAuthStore } from "../src/stores/memory.ts"; +import { invokeAuthHandler } from "../src/routes/api.ts"; + +function routeContext(request: Request): Context { + const values = new Map(); + return { + req: request, + url: new URL(request.url), + params: {}, + locals: {}, + lang: "en", + t: (key: string) => key, + ip: "127.0.0.1", + user: null, + cookies: { + get: (name: string) => (name === "wrn-csrf" ? "plugin-test-csrf-token" : undefined), + } as Context["cookies"], + localStorage: {} as Context["localStorage"], + session: { + id: () => "plugin-test-session", + get: (key: string) => values.get(key) as T | undefined, + getAll: () => Object.fromEntries(values), + set: (key: string, value: unknown) => { + values.set(key, value); + }, + delete: (key: string) => { + values.delete(key); + }, + regenerate: () => {}, + clear: () => { + values.clear(); + }, + }, + } as Context; +} test("plugin contributes components, runtime, styles, migration, and toolbar", async () => { const metadata = new Map(); @@ -69,6 +106,71 @@ test("config.auth controls route groups and migrations without explicit plugin o expect(contributions.routes.some((route) => route.path === "/api/auth/login")).toBe(true); }); +test("a hook set on config.auth.engine fires when a request goes through the route layer", async () => { + const calls: string[] = []; + const engine = createAuthEngine({ + store: new MemoryAuthStore(), + secret: "plugin-navigation-hooks-secret-longer-than-thirty-two-characters", + onSignedIn(ctx, returnTo) { + calls.push(`in:${returnTo}`); + return Response.redirect(new URL(returnTo ?? "/account", ctx.url), 303); + }, + onSignedOut(ctx) { + calls.push("out"); + return Response.redirect(new URL("/sign-in", ctx.url), 303); + }, + }); + await engine.register({ + email: "plugin-navigation@example.com", + password: "StrongPassword123", + }); + + const runner = createPluginRunner(authPlugin(), { + root: process.cwd(), + mode: "development", + command: "dev", + metadata: new Map(), + warn() {}, + }); + + await runner.configure({ + auth: { engine, routes: true }, + }); + + const loginResponse = await invokeAuthHandler( + "login", + routeContext( + new Request("https://example.test/api/auth/login", { + method: "POST", + headers: { + "content-type": "application/json", + "x-csrf-token": "plugin-test-csrf-token", + }, + body: JSON.stringify({ + identifier: "plugin-navigation@example.com", + password: "StrongPassword123", + returnTo: "/dashboard", + }), + }), + ), + ); + expect(loginResponse.status).toBe(303); + expect(loginResponse.headers.get("location")).toBe("https://example.test/dashboard"); + + const logoutResponse = await invokeAuthHandler( + "logout", + routeContext( + new Request("https://example.test/api/auth/logout", { + method: "POST", + headers: { "x-csrf-token": "plugin-test-csrf-token" }, + }), + ), + ); + expect(logoutResponse.status).toBe(303); + expect(logoutResponse.headers.get("location")).toBe("https://example.test/sign-in"); + expect(calls).toEqual(["in:/dashboard", "out"]); +}); + test("auth runtime contains built-in browser schemas", async () => { const metadata = new Map(); const runner = createPluginRunner(authPlugin({ includeMigrations: false }), {