test: add auth tests
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import bcrypt from "bcryptjs";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { createUser, validateCreateUserInput, verifyUserCredentials } from "@/lib/users";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// MIN_PASSWORD_LENGTH chosen in src/lib/users.ts is 8 — mirrored here as a
|
||||
// local constant so the "too short" test stays intentional/self-documenting
|
||||
// instead of a magic number.
|
||||
const MIN_PASSWORD_LENGTH = 8;
|
||||
|
||||
beforeEach(async () => {
|
||||
await prisma.user.deleteMany();
|
||||
});
|
||||
|
||||
describe("createUser", () => {
|
||||
it("hashes the password instead of storing it in plaintext", async () => {
|
||||
const password = "correct-horse-battery-staple";
|
||||
await createUser({ email: "[email protected]", name: "IT Staff", password });
|
||||
|
||||
const row = await prisma.user.findUnique({ where: { email: "[email protected]" } });
|
||||
expect(row).not.toBeNull();
|
||||
expect(row?.passwordHash).not.toBe(password);
|
||||
expect(row?.passwordHash.length).toBeGreaterThan(0);
|
||||
await expect(bcrypt.compare(password, row?.passwordHash ?? "")).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it("does not expose the password hash on the returned record", async () => {
|
||||
const created = await createUser({
|
||||
email: "[email protected]",
|
||||
name: "No Hash Leak",
|
||||
password: "correct-horse-battery-staple",
|
||||
});
|
||||
|
||||
expect(created).not.toHaveProperty("passwordHash");
|
||||
expect(created).not.toHaveProperty("password_hash");
|
||||
expect(created).toMatchObject({ email: "[email protected]", name: "No Hash Leak" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateCreateUserInput", () => {
|
||||
it("rejects an invalid email format", () => {
|
||||
const result = validateCreateUserInput({
|
||||
email: "not-an-email",
|
||||
name: "Someone",
|
||||
password: "a-long-enough-password",
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/email/i);
|
||||
}
|
||||
});
|
||||
|
||||
it(`rejects a password shorter than ${MIN_PASSWORD_LENGTH} characters`, () => {
|
||||
const result = validateCreateUserInput({
|
||||
email: "[email protected]",
|
||||
name: "Someone",
|
||||
password: "a".repeat(MIN_PASSWORD_LENGTH - 1),
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
if (!result.ok) {
|
||||
expect(result.error).toMatch(/password/i);
|
||||
}
|
||||
});
|
||||
|
||||
it("accepts a valid email, non-empty name, and long-enough password", () => {
|
||||
const result = validateCreateUserInput({
|
||||
email: "[email protected]",
|
||||
name: "Valid Name",
|
||||
password: "a".repeat(MIN_PASSWORD_LENGTH),
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects an empty name", () => {
|
||||
const result = validateCreateUserInput({
|
||||
email: "[email protected]",
|
||||
name: " ",
|
||||
password: "a".repeat(MIN_PASSWORD_LENGTH),
|
||||
});
|
||||
|
||||
expect(result.ok).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("verifyUserCredentials", () => {
|
||||
const email = "[email protected]";
|
||||
const password = "correct-horse-battery-staple";
|
||||
|
||||
beforeEach(async () => {
|
||||
await createUser({ email, name: "Login Test", password });
|
||||
});
|
||||
|
||||
it("succeeds with the correct email and password", async () => {
|
||||
const user = await verifyUserCredentials(email, password);
|
||||
|
||||
expect(user).not.toBeNull();
|
||||
expect(user).toMatchObject({ email, name: "Login Test" });
|
||||
});
|
||||
|
||||
it("fails with the wrong password", async () => {
|
||||
const user = await verifyUserCredentials(email, "wrong-password");
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it("fails with a nonexistent email", async () => {
|
||||
const user = await verifyUserCredentials("[email protected]", password);
|
||||
expect(user).toBeNull();
|
||||
});
|
||||
|
||||
it("fails with non-string email/password input (e.g. from a malformed request)", async () => {
|
||||
expect(await verifyUserCredentials(undefined, password)).toBeNull();
|
||||
expect(await verifyUserCredentials(email, undefined)).toBeNull();
|
||||
expect(await verifyUserCredentials(123, password)).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user