import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest" import type { PrismaClient } from "@/generated/prisma/client" import { createTestUser } from "../helpers/factories" import { resetIntegrationTestDatabase, startIntegrationTestDatabase, stopIntegrationTestDatabase, } from "../helpers/test-db" let prisma: PrismaClient let createUserUseCase: typeof import("@/use-cases/user.use-cases").createUserUseCase let updateUserUseCase: typeof import("@/use-cases/user.use-cases").updateUserUseCase let setUserActiveUseCase: typeof import("@/use-cases/user.use-cases").setUserActiveUseCase let resetUserPasswordUseCase: typeof import("@/use-cases/user.use-cases").resetUserPasswordUseCase let verifyPassword: typeof import("@/lib/security").verifyPassword beforeAll(async () => { await startIntegrationTestDatabase() const prismaModule = await import("@/lib/prisma") const userUseCases = await import("@/use-cases/user.use-cases") const security = await import("@/lib/security") prisma = prismaModule.prisma createUserUseCase = userUseCases.createUserUseCase updateUserUseCase = userUseCases.updateUserUseCase setUserActiveUseCase = userUseCases.setUserActiveUseCase resetUserPasswordUseCase = userUseCases.resetUserPasswordUseCase verifyPassword = security.verifyPassword }) beforeEach(async () => { await resetIntegrationTestDatabase(prisma) }) afterAll(async () => { await prisma?.$disconnect() await stopIntegrationTestDatabase() }) describe("user use-cases", () => { it("creates a user with a hashed password", async () => { const result = await createUserUseCase({ username: "new-user", name: "New User", email: "new-user@example.test", password: "secure-password", role: "STAFF", isActive: true, }) expect(result).toEqual({ success: true }) const user = await prisma.user.findUniqueOrThrow({ where: { username: "new-user" }, }) expect(user).toMatchObject({ username: "new-user", name: "New User", email: "new-user@example.test", role: "STAFF", isActive: true, }) expect(user.password).not.toBe("secure-password") await expect( verifyPassword("secure-password", user.password), ).resolves.toBe(true) }) it("rejects duplicate usernames and duplicate emails", async () => { await createTestUser(prisma, { username: "existing-user", email: "existing@example.test", }) await expect( createUserUseCase({ username: "existing-user", name: "Duplicate Username", email: "unique@example.test", password: "secure-password", role: "STAFF", isActive: true, }), ).resolves.toEqual({ success: false, errors: { username: ["Username already exists"] }, }) await expect( createUserUseCase({ username: "unique-user", name: "Duplicate Email", email: "existing@example.test", password: "secure-password", role: "STAFF", isActive: true, }), ).resolves.toEqual({ success: false, errors: { email: ["Email already exists"] }, }) await expect(prisma.user.count()).resolves.toBe(1) await expect( prisma.user.findUniqueOrThrow({ where: { username: "existing-user" } }), ).resolves.toMatchObject({ email: "existing@example.test" }) }) it("updates a user while preserving uniqueness constraints", async () => { const actor = await createTestUser(prisma, { role: "ADMIN" }) const user = await createTestUser(prisma, { username: "editable-user", email: "editable@example.test", role: "STAFF", }) const other = await createTestUser(prisma, { username: "other-user", email: "other@example.test", role: "STAFF", }) await expect( updateUserUseCase({ actorId: actor.id, id: user.id, username: "edited-user", name: "Edited User", email: "edited@example.test", role: "MANAGER", isActive: true, }), ).resolves.toEqual({ success: true }) await expect( prisma.user.findUniqueOrThrow({ where: { id: user.id } }), ).resolves.toMatchObject({ username: "edited-user", name: "Edited User", email: "edited@example.test", role: "MANAGER", isActive: true, }) await expect( updateUserUseCase({ actorId: actor.id, id: user.id, username: other.username, name: "Edited User", email: "another-email@example.test", role: "MANAGER", isActive: true, }), ).resolves.toEqual({ success: false, errors: { username: ["Username already exists"] }, }) await expect( prisma.user.findUniqueOrThrow({ where: { id: user.id } }), ).resolves.toMatchObject({ username: "edited-user", email: "edited@example.test", }) }) it("prevents an administrator from removing their own administrator access", async () => { const admin = await createTestUser(prisma, { role: "ADMIN" }) await expect( updateUserUseCase({ actorId: admin.id, id: admin.id, username: admin.username, name: admin.name, email: admin.email, role: "STAFF", isActive: true, }), ).resolves.toEqual({ success: false, errors: { id: ["You cannot remove your own administrator access"] }, }) await expect( prisma.user.findUniqueOrThrow({ where: { id: admin.id } }), ).resolves.toMatchObject({ role: "ADMIN", isActive: true }) }) it("protects the last active administrator but allows deactivation when another active admin exists", async () => { const firstAdmin = await createTestUser(prisma, { username: "first-admin", email: "first-admin@example.test", role: "ADMIN", }) const staffActor = await createTestUser(prisma, { username: "staff-actor", email: "staff-actor@example.test", role: "STAFF", }) await expect( setUserActiveUseCase({ actorId: staffActor.id, id: firstAdmin.id, isActive: false, }), ).resolves.toEqual({ success: false, errors: { id: ["Cannot remove access from the last active administrator"], }, }) const secondAdmin = await createTestUser(prisma, { username: "second-admin", email: "second-admin@example.test", role: "ADMIN", }) await expect( setUserActiveUseCase({ actorId: secondAdmin.id, id: firstAdmin.id, isActive: false, }), ).resolves.toEqual({ success: true }) await expect( prisma.user.findUniqueOrThrow({ where: { id: firstAdmin.id } }), ).resolves.toMatchObject({ isActive: false }) }) it("prevents self-deactivation", async () => { const admin = await createTestUser(prisma, { role: "ADMIN" }) await createTestUser(prisma, { role: "ADMIN" }) await expect( setUserActiveUseCase({ actorId: admin.id, id: admin.id, isActive: false, }), ).resolves.toEqual({ success: false, errors: { id: ["You cannot deactivate your own user"] }, }) await expect( prisma.user.findUniqueOrThrow({ where: { id: admin.id } }), ).resolves.toMatchObject({ isActive: true }) }) it("resets a user password and rejects missing users", async () => { const user = await createTestUser(prisma, { username: "password-user", email: "password-user@example.test", role: "STAFF", }) await expect( resetUserPasswordUseCase({ id: user.id, password: "new-secure-password", }), ).resolves.toEqual({ success: true }) const updatedUser = await prisma.user.findUniqueOrThrow({ where: { id: user.id }, }) expect(updatedUser.password).not.toBe(user.password) await expect( verifyPassword("new-secure-password", updatedUser.password), ).resolves.toBe(true) await expect( resetUserPasswordUseCase({ id: "00000000-0000-0000-0000-000000000000", password: "new-secure-password", }), ).resolves.toEqual({ success: false, errors: { id: ["User not found"] }, }) }) })