import { afterEach, expect, test } from "bun:test"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { tmpdir } from "node:os"; import { buildRouter } from "@wrnexus/router"; import { v } from "@wrnexus/validation"; import { createHandlers, type RuntimeDeps } from "../src/runtime.ts"; const roots: string[] = []; afterEach(() => roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }))); const server = { upgrade: () => false }; test("server actions validate, enforce CSRF, invalidate, and progressively enhance forms", async () => { const root = mkdtempSync(join(tmpdir(), "wrnexus-action-runtime-")); roots.push(root); const app = join(root, "app"); mkdirSync(join(app, "pages"), { recursive: true }); writeFileSync(join(app, "pages", "users.ts"), "export default () => '';"); const router = buildRouter(app); const schema = v.object({ name: v.string().min(2) }); const security: Record = {}; let authenticated = false; let granted = false; const handlers = createHandlers({ mode: "production", hmr: false, router, loadModule: async () => ({ default: () => `
`, __wrnexusActions: { createUser: { schema, run: (input: { name: string }, ctx: { locals: Record }) => { ctx.locals.__wrnexusInvalidatedTags = ["users", "users"]; return { id: `user-${input.name}` }; }, }, }, __wrnexusSecurity: security, }), getMiddleware: async () => [ (ctx, next) => { if (authenticated) ctx.user = { id: "operator" }; ctx.locals.permissions = granted ? ["users.create"] : []; return next(); }, ], assets: { serve: async () => null }, } satisfies RuntimeDeps); const page = await handlers.fetch(new Request("https://example.test/users"), server); const html = await page!.text(); expect(html).toContain('name="_csrf"'); expect(html).toContain("/__wrnexus/actions.js"); const cookie = page!.headers.get("set-cookie")!; const token = /wire-csrf=([^;]+)/.exec(cookie)?.[1]; if (!token) throw new Error("expected CSRF cookie"); const invalid = await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { "content-type": "application/json", accept: "application/json", cookie, "x-wrnexus-action": "createUser", "x-csrf-token": token, }, body: JSON.stringify({ name: "x" }), }), server, ); expect(invalid?.status).toBe(422); expect(await invalid?.json()).toMatchObject({ errors: { name: expect.any(String) } }); const noCsrf = await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { "content-type": "application/json", accept: "application/json", cookie, "x-wrnexus-action": "createUser", }, body: JSON.stringify({ name: "Ada" }), }), server, ); expect(noCsrf?.status).toBe(403); const success = await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { "content-type": "application/json", accept: "application/json", cookie, "x-wrnexus-action": "createUser", "x-csrf-token": token, }, body: JSON.stringify({ name: "Ada" }), }), server, ); expect(await success?.json()).toEqual({ ok: true, data: { id: "user-Ada" }, invalidated: ["users"], }); security.auth = "required"; expect( ( await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { "content-type": "application/json", accept: "application/json", cookie, "x-wrnexus-action": "createUser", "x-csrf-token": token, }, body: JSON.stringify({ name: "Ada" }), }), server, ) )?.status, ).toBe(401); authenticated = true; security.permission = "users.create"; expect( ( await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { "content-type": "application/json", accept: "application/json", cookie, "x-wrnexus-action": "createUser", "x-csrf-token": token, }, body: JSON.stringify({ name: "Ada" }), }), server, ) )?.status, ).toBe(403); granted = true; const form = new FormData(); form.set("_wrnexus_action", "createUser"); form.set("_csrf", token); form.set("name", "Grace"); const progressive = await handlers.fetch( new Request("https://example.test/users", { method: "POST", headers: { cookie, origin: "https://example.test" }, body: form, }), server, ); expect(progressive?.status).toBe(303); expect(progressive?.headers.get("location")).toBe("/users"); });