import { describe, expect, it, vi } from "vitest" vi.mock("@/lib/prisma", () => ({ default: {}, })) import { getUserById, getUserByEmail, getUserCredentialsByEmail, } from "@/services/user.service" describe("getUserById", () => { it("does not select passwordHash across the broad user lookup boundary", async () => { const findFirst = vi.fn().mockResolvedValue(null) const db = { user: { findFirst, }, } await getUserById("user-1", db as never) expect(findFirst).toHaveBeenCalledWith({ where: expect.objectContaining({ id: "user-1", }), select: expect.not.objectContaining({ passwordHash: true, }), }) }) }) describe("getUserByEmail", () => { it("queries emailNormalized with a normalized email", async () => { const findFirst = vi.fn().mockResolvedValue(null) const db = { user: { findFirst, }, } await getUserByEmail(" Admin@Example.Test ", db as never) expect(findFirst).toHaveBeenCalledWith({ where: expect.objectContaining({ emailNormalized: "admin@example.test", }), select: expect.not.objectContaining({ passwordHash: true, }), }) }) it("does not return passwordHash across the broad user lookup boundary", async () => { const findFirst = vi.fn().mockResolvedValue({ id: "user-1", name: "Admin", email: "admin@example.test", role: "ADMIN", status: "ACTIVE", createdAt: new Date("2024-01-01T00:00:00.000Z"), updatedAt: new Date("2024-01-01T00:00:00.000Z"), }) const db = { user: { findFirst, }, } const user = await getUserByEmail("admin@example.test", db as never) expect(user).not.toHaveProperty("passwordHash") }) }) describe("getUserCredentialsByEmail", () => { it("selects passwordHash only for credential verification", async () => { const findFirst = vi.fn().mockResolvedValue(null) const db = { user: { findFirst, }, } await getUserCredentialsByEmail("Admin@Example.Test", db as never) expect(findFirst).toHaveBeenCalledWith({ where: expect.objectContaining({ emailNormalized: "admin@example.test", }), select: expect.objectContaining({ passwordHash: true, }), }) }) })