test(auth): restore plugin engine-hook coverage via invokeAuthHandler
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,45 @@
|
|||||||
import { expect, test } from "bun:test";
|
import { expect, test } from "bun:test";
|
||||||
|
import type { Context } from "@wrnexus/core";
|
||||||
import { createPluginRunner } from "@wrnexus/plugin";
|
import { createPluginRunner } from "@wrnexus/plugin";
|
||||||
import { authPlugin } from "../src/plugin.ts";
|
import { authPlugin } from "../src/plugin.ts";
|
||||||
import { AUTH_ROUTE_DEFINITIONS } from "../src/routes/definitions.ts";
|
import { AUTH_ROUTE_DEFINITIONS } from "../src/routes/definitions.ts";
|
||||||
import { readFileSync } from "node:fs";
|
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<string, unknown>();
|
||||||
|
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: <T>(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 () => {
|
test("plugin contributes components, runtime, styles, migration, and toolbar", async () => {
|
||||||
const metadata = new Map<string, unknown>();
|
const metadata = new Map<string, unknown>();
|
||||||
@@ -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);
|
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 () => {
|
test("auth runtime contains built-in browser schemas", async () => {
|
||||||
const metadata = new Map<string, unknown>();
|
const metadata = new Map<string, unknown>();
|
||||||
const runner = createPluginRunner(authPlugin({ includeMigrations: false }), {
|
const runner = createPluginRunner(authPlugin({ includeMigrations: false }), {
|
||||||
|
|||||||
Reference in New Issue
Block a user