import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest" import type { PrismaClient } from "@/generated/prisma/client" import { createTestPerson, createTestUser } from "../helpers/factories" import { resetIntegrationTestDatabase, startIntegrationTestDatabase, stopIntegrationTestDatabase, } from "../helpers/test-db" let prisma: PrismaClient let createPersonUserUseCase: typeof import("@/use-cases/person.use-cases").createPersonUserUseCase beforeAll(async () => { await startIntegrationTestDatabase() const prismaModule = await import("@/lib/prisma") const personUseCases = await import("@/use-cases/person.use-cases") prisma = prismaModule.prisma createPersonUserUseCase = personUseCases.createPersonUserUseCase }) beforeEach(async () => { await resetIntegrationTestDatabase(prisma) }) afterAll(async () => { await prisma?.$disconnect() await stopIntegrationTestDatabase() }) describe("createPersonUserUseCase", () => { describe("NO_USER role (person-only creation)", () => { it("creates a Person without a User record when role is NO_USER", async () => { const result = await createPersonUserUseCase({ firstName: "John", lastName: "Doe", department: "IT", email: "john@example.test", phone: null, role: "NO_USER", isActive: true, }) expect(result).toEqual({ success: true }) const person = await prisma.person.findFirstOrThrow({ where: { firstName: "John", lastName: "Doe" }, }) expect(person).toMatchObject({ firstName: "John", lastName: "Doe", department: "IT", email: "john@example.test", phone: null, userId: null, }) // No User record created await expect( prisma.user.findUnique({ where: { email: "john@example.test" } }), ).resolves.toBeNull() }) it("creates a Person with null email when not providing email and role is NO_USER", async () => { const result = await createPersonUserUseCase({ firstName: "Jane", lastName: "Smith", department: "ENGINEERING", email: "jane-noemail@example.test", phone: "555-1234", role: "NO_USER", isActive: true, }) expect(result).toEqual({ success: true }) const person = await prisma.person.findFirstOrThrow({ where: { firstName: "Jane", lastName: "Smith" }, }) expect(person.phone).toBe("555-1234") }) }) describe("real role (person + user creation)", () => { it("creates Person and User with linked userId when role is ADMIN", async () => { const result = await createPersonUserUseCase({ firstName: "Admin", lastName: "User", department: "IT", email: "admin@example.test", phone: null, role: "ADMIN", password: "secure-password", isActive: true, }) expect(result).toEqual({ success: true }) const person = await prisma.person.findFirstOrThrow({ where: { firstName: "Admin", lastName: "User" }, }) expect(person).toMatchObject({ firstName: "Admin", lastName: "User", department: "IT", email: "admin@example.test", }) // User record should exist with derived name expect(person.userId).not.toBeNull() const user = await prisma.user.findUniqueOrThrow({ where: { id: person.userId! }, }) expect(user).toMatchObject({ name: "Admin User", email: "admin@example.test", role: "ADMIN", isActive: true, }) }) it("creates Person and User for all real roles (MANAGER, STAFF, VIEWER)", async () => { const roles = ["MANAGER", "STAFF", "VIEWER"] as const for (const role of roles) { const suffix = role.toLowerCase() const result = await createPersonUserUseCase({ firstName: "Person", lastName: suffix, department: "IT", email: `${suffix}@example.test`, phone: null, role, password: "secure-password", isActive: true, }) expect(result).toEqual({ success: true }) const person = await prisma.person.findFirstOrThrow({ where: { lastName: suffix }, }) expect(person.userId).not.toBeNull() const user = await prisma.user.findUniqueOrThrow({ where: { id: person.userId! }, }) expect(user.role).toBe(role) expect(user.name).toBe(`Person ${suffix}`) } }) it("derives User.name from firstName + lastName", async () => { await createPersonUserUseCase({ firstName: "Maria", lastName: "Garcia", department: "SALES", email: "maria@example.test", phone: null, role: "STAFF", password: "secure-password", isActive: true, }) const user = await prisma.user.findUniqueOrThrow({ where: { email: "maria@example.test" }, }) expect(user.name).toBe("Maria Garcia") }) it("hashes the password when creating a User", async () => { await createPersonUserUseCase({ firstName: "Hash", lastName: "Test", department: "IT", email: "hash-test@example.test", phone: null, role: "STAFF", password: "plaintext-password", isActive: true, }) const user = await prisma.user.findUniqueOrThrow({ where: { email: "hash-test@example.test" }, }) expect(user.password).not.toBe("plaintext-password") const { verifyPassword } = await import("@/lib/security") await expect( verifyPassword("plaintext-password", user.password), ).resolves.toBe(true) }) }) describe("cross-table email uniqueness", () => { it("rejects submission when email already exists in Person table", async () => { await createTestPerson(prisma, { email: "existing-person@example.test" }) const result = await createPersonUserUseCase({ firstName: "Duplicate", lastName: "Person", department: "IT", email: "existing-person@example.test", phone: null, role: "NO_USER", isActive: true, }) expect(result).toEqual({ success: false, errors: { email: ["Email already exists"] }, }) await expect(prisma.person.count()).resolves.toBe(1) }) it("rejects submission when email already exists in User table", async () => { await createTestUser(prisma, { email: "existing-user@example.test" }) const result = await createPersonUserUseCase({ firstName: "Duplicate", lastName: "User", department: "IT", email: "existing-user@example.test", phone: null, role: "STAFF", password: "secure-password", isActive: true, }) expect(result).toEqual({ success: false, errors: { email: ["Email already exists"] }, }) // No new Person or User was created await expect(prisma.person.count()).resolves.toBe(0) await expect(prisma.user.count()).resolves.toBe(1) }) it("accepts submission when email is unique across both tables", async () => { // Create a Person and a User with different emails await createTestPerson(prisma, { email: "person@example.test" }) await createTestUser(prisma, { email: "user@example.test" }) const result = await createPersonUserUseCase({ firstName: "New", lastName: "Person", department: "IT", email: "new@example.test", phone: null, role: "NO_USER", isActive: true, }) expect(result).toEqual({ success: true }) }) }) })