Files
stock-manager/tests/integration/use-cases/person-user.use-cases.test.ts
T

270 lines
8.1 KiB
TypeScript

import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest"
import type { PrismaClient } from "@/generated/prisma/client"
import { normalizeEmail } from "@/lib/email"
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: { emailNormalized: normalizeEmail("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()
if (!person.userId) throw new Error("Expected linked user")
const user = await prisma.user.findUniqueOrThrow({
where: { id: person.userId },
})
expect(user).toMatchObject({
name: "Admin User",
email: "admin@example.test",
role: "ADMIN",
status: "ACTIVE",
})
expect(user.activatedAt).toBeInstanceOf(Date)
expect(user.passwordChangedAt).toBeInstanceOf(Date)
})
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()
if (!person.userId) throw new Error("Expected linked user")
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: { emailNormalized: normalizeEmail("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: { emailNormalized: normalizeEmail("hash-test@example.test") },
})
expect(user.passwordHash).not.toBe("plaintext-password")
if (!user.passwordHash) throw new Error("Expected password hash")
const { verifyPassword } = await import("@/lib/security")
await expect(
verifyPassword("plaintext-password", user.passwordHash),
).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 })
})
})
})