/** * Smoke: AUTH_REQUIRED gate via Fastify inject. * Run: AUTH_REQUIRED=true AUTH_JWT_SECRET=test-secret-at-least-8 tsx src/plugins/auth.smoke.test.ts */ import assert from "node:assert/strict" import { SignJWT } from "jose" process.env.AUTH_REQUIRED = "true" process.env.AUTH_JWT_SECRET = "test-secret-at-least-8" process.env.AUTH_ISSUER = "https://auth.test.local" process.env.AUTH_PORTAL_URL = "http://localhost:5175" process.env.CORS_ORIGIN = "http://localhost:3000" process.env.NODE_ENV = "test" // Dynamic import after env is set const { buildApp } = await import("../index.js") const secret = new TextEncoder().encode("test-secret-at-least-8") async function mint(payload: Record): Promise { return new SignJWT(payload) .setProtectedHeader({ alg: "HS256" }) .setIssuer("https://auth.test.local") .setExpirationTime("1h") .sign(secret) } const app = await buildApp({ logger: false, startScheduler: false }) const health = await app.inject({ method: "GET", url: "/health" }) assert.equal(health.statusCode, 200) const cfg = await app.inject({ method: "GET", url: "/api/auth/config" }) assert.equal(cfg.statusCode, 200) assert.equal(cfg.json().required, true) const noToken = await app.inject({ method: "GET", url: "/api/sidebar-counts" }) assert.equal(noToken.statusCode, 401) const badApp = await mint({ sub: "u1", email: "a@b.c", name: "A", apps: ["cdn"], permissions: ["cdn:dashboard:read"], }) const forbiddenApp = await app.inject({ method: "GET", url: "/api/sidebar-counts", headers: { authorization: `Bearer ${badApp}` }, }) assert.equal(forbiddenApp.statusCode, 403) const okToken = await mint({ sub: "u1", email: "a@b.c", name: "A", apps: ["mm"], permissions: ["mm:dashboard:read"], }) const ok = await app.inject({ method: "GET", url: "/api/sidebar-counts", headers: { authorization: `Bearer ${okToken}` }, }) // May be 200 or 500 if DB missing — must not be 401/403 assert.notEqual(ok.statusCode, 401) assert.notEqual(ok.statusCode, 403) await app.close() console.log("auth.smoke.test.ts: ok")