import { describe, it, expect, beforeAll, afterAll, beforeEach } from "vitest"; import type { FastifyInstance } from "fastify"; import { createTestApp, cleanDb, requestSignupOtpCode, signupUser } from "./helpers.js"; let app: FastifyInstance; beforeAll(async () => { app = await createTestApp(); }); afterAll(async () => { await app.close(); }); beforeEach(async () => { await cleanDb(); }); describe("POST /signup", () => { it("sends OTP for a new email", async () => { const res = await app.inject({ method: "POST", url: "/signup", payload: { email: "new@example.com" }, }); expect(res.statusCode).toBe(200); expect(res.json()).toEqual({ message: "OTP sent to your email" }); }); it("returns 409 if user already exists", async () => { await signupUser(app, "existing@example.com", "Org"); const res = await app.inject({ method: "POST", url: "/signup", payload: { email: "existing@example.com" }, }); expect(res.statusCode).toBe(409); }); it("returns 400 if email is missing", async () => { const res = await app.inject({ method: "POST", url: "/signup", payload: {}, }); expect(res.statusCode).toBe(400); }); }); describe("POST /signup/verify", () => { it("creates user, account, and returns tokens", async () => { const res = await signupUser(app, "new@example.com", "My Org", "newuser"); const body = res.json(); expect(res.statusCode).toBe(201); expect(body.accessToken).toBeDefined(); expect(body.refreshToken).toBeDefined(); expect(body.user.email).toBe("new@example.com"); expect(body.user.name).toBe("newuser"); expect(body.accounts).toHaveLength(1); expect(body.accounts[0].name).toBe("My Org"); expect(body.accounts[0].role).toBe("owner"); }); it("returns 409 if user already exists", async () => { await signupUser(app, "existing@example.com", "Org 1"); const code = await requestSignupOtpCode(app, "new2@example.com"); const res = await app.inject({ method: "POST", url: "/signup/verify", payload: { email: "existing@example.com", code, accountName: "Org 2", username: "test" }, }); // OTP was for a different email, so it will fail with 400 expect(res.statusCode).toBe(400); }); it("returns 400 with invalid OTP code", async () => { await requestSignupOtpCode(app, "test@example.com"); const res = await app.inject({ method: "POST", url: "/signup/verify", payload: { email: "test@example.com", code: "000000", accountName: "Org", username: "test" }, }); expect(res.statusCode).toBe(400); }); it("returns 400 if required fields are missing", async () => { const code = await requestSignupOtpCode(app, "test@example.com"); const res = await app.inject({ method: "POST", url: "/signup/verify", payload: { email: "test@example.com", code }, }); expect(res.statusCode).toBe(400); }); });