261 lines
7.6 KiB
TypeScript
261 lines
7.6 KiB
TypeScript
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 })
|
|
})
|
|
})
|
|
}) |