import { afterAll, beforeAll, describe, expect, it } from "vitest"; import request from "supertest"; import type { FastifyInstance } from "fastify"; import { PrismaClient } from "@prisma/client"; import argon2 from "argon2"; import { buildApp } from "../src/server"; const prisma = new PrismaClient(); let app: FastifyInstance; function readCookieValue(setCookie: string[] | undefined, cookieName: string): string | null { if (!setCookie) return null; for (const raw of setCookie) { const firstPart = raw.split(";")[0] ?? ""; const [name, value] = firstPart.split("="); if (name?.trim() === cookieName && value) { return value.trim(); } } return null; } beforeAll(async () => { app = await buildApp({ AUTH_DISABLED: false, SEED_DEFAULT_BUDGET: true }); await app.ready(); }); afterAll(async () => { if (app) { await app.close(); } await prisma.$disconnect(); }); describe("Auth routes", () => { it("rejects protected routes without a session", async () => { const res = await request(app.server).get("/dashboard"); expect(res.status).toBe(401); expect(res.body.code).toBe("UNAUTHENTICATED"); }); it("rejects spoofed x-user-id when auth is enabled", async () => { const res = await request(app.server) .get("/dashboard") .set("x-user-id", "spoofed-user-id"); expect(res.status).toBe(401); expect(res.body.code).toBe("UNAUTHENTICATED"); }); it("rejects weak passwords on registration", async () => { const agent = request.agent(app.server); const email = `weak-${Date.now()}@test.dev`; const password = "weakpass123"; const register = await agent.post("/auth/register").send({ email, password }); expect(register.status).toBe(400); expect(register.body.ok).toBe(false); const user = await prisma.user.findUnique({ where: { email } }); expect(user).toBeNull(); }); it("registers a user and grants access via cookie session", async () => { const agent = request.agent(app.server); const email = `reg-${Date.now()}@test.dev`; const password = "SupersAFE123!"; const register = await agent.post("/auth/register").send({ email, password }); expect(register.status).toBe(200); expect(register.body.needsVerification).toBe(true); const dash = await agent.get("/dashboard"); expect(dash.status).toBe(401); const created = await prisma.user.findUniqueOrThrow({ where: { email } }); const [catCount, planCount] = await Promise.all([ prisma.variableCategory.count({ where: { userId: created.id } }), prisma.fixedPlan.count({ where: { userId: created.id } }), ]); expect(catCount).toBeGreaterThan(0); expect(planCount).toBeGreaterThan(0); await prisma.user.deleteMany({ where: { email } }); }); it("logs in existing user and accesses dashboard", async () => { const agent = request.agent(app.server); const email = `login-${Date.now()}@test.dev`; const password = "SupersAFE123!"; await agent.post("/auth/register").send({ email, password }); await prisma.user.update({ where: { email }, data: { emailVerified: true }, }); const login = await agent.post("/auth/login").send({ email, password }); expect(login.status).toBe(200); const dash = await agent.get("/dashboard"); expect(dash.status).toBe(200); await prisma.user.deleteMany({ where: { email } }); }); it("reports session info and handles logout", async () => { const agent = request.agent(app.server); const email = `session-${Date.now()}@test.dev`; const password = "SupersAFE123!"; await agent.post("/auth/register").send({ email, password }); await prisma.user.update({ where: { email }, data: { emailVerified: true }, }); const login = await agent.post("/auth/login").send({ email, password }); expect(login.status).toBe(200); const csrf = readCookieValue(login.headers["set-cookie"], "csrf"); expect(csrf).toBeTruthy(); const session = await agent.get("/auth/session"); expect(session.status).toBe(200); expect(session.body.userId).toBeDefined(); await agent .post("/auth/logout") .set("x-csrf-token", csrf as string); const afterLogout = await agent.get("/dashboard"); expect(afterLogout.status).toBe(401); await prisma.user.deleteMany({ where: { email } }); }); it("locks login after repeated failed attempts", async () => { const agent = request.agent(app.server); const email = `locked-${Date.now()}@test.dev`; const password = "SupersAFE123!"; await prisma.user.create({ data: { email, passwordHash: await argon2.hash(password), emailVerified: true, }, }); for (let attempt = 1; attempt <= 4; attempt++) { const res = await agent.post("/auth/login").send({ email, password: "WrongPass123!" }); expect(res.status).toBe(401); } const locked = await agent.post("/auth/login").send({ email, password: "WrongPass123!" }); expect(locked.status).toBe(429); expect(locked.body.code).toBe("LOGIN_LOCKED"); expect(locked.headers["retry-after"]).toBeTruthy(); const blockedValid = await agent.post("/auth/login").send({ email, password }); expect(blockedValid.status).toBe(429); expect(blockedValid.body.code).toBe("LOGIN_LOCKED"); await prisma.user.deleteMany({ where: { email } }); }); });