yap-blog/tests/integration/auth.test.ts
2026-07-02 21:32:33 -04:00

82 lines
2.7 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { eq } from "drizzle-orm";
import { db } from "@/db";
import { sessions, users } from "@/db/schema";
import { hashPassword, verifyPassword } from "@/lib/auth/password";
import {
createSession,
deleteSession,
validateSessionToken,
} from "@/lib/auth/session";
import { resetDb } from "../helpers/db";
beforeEach(resetDb);
async function insertUser() {
const [user] = await db
.insert(users)
.values({ username: "admin", passwordHash: await hashPassword("correct horse") })
.returning();
return user;
}
describe("password hashing", () => {
it("verifies the original password and rejects others", async () => {
const hash = await hashPassword("s3cret-passphrase");
expect(hash.startsWith("scrypt$")).toBe(true);
expect(hash).not.toContain("s3cret-passphrase");
expect(await verifyPassword(hash, "s3cret-passphrase")).toBe(true);
expect(await verifyPassword(hash, "wrong")).toBe(false);
});
it("produces unique salts per hash", async () => {
expect(await hashPassword("same")).not.toBe(await hashPassword("same"));
});
it("rejects malformed stored hashes without throwing", async () => {
expect(await verifyPassword("garbage", "x")).toBe(false);
expect(await verifyPassword("scrypt$bad$data", "x")).toBe(false);
});
});
describe("sessions", () => {
it("round-trips a valid session token", async () => {
const user = await insertUser();
const { token, expiresAt } = await createSession(user.id);
expect(expiresAt.getTime()).toBeGreaterThan(Date.now());
const sessionUser = await validateSessionToken(token);
expect(sessionUser).toEqual({ id: user.id, username: "admin" });
// Only a hash of the token is stored.
const rows = await db.select().from(sessions);
expect(rows).toHaveLength(1);
expect(rows[0].id).not.toBe(token);
});
it("rejects unknown tokens", async () => {
await insertUser();
expect(await validateSessionToken("not-a-real-token")).toBeNull();
expect(await validateSessionToken("")).toBeNull();
});
it("treats expired sessions as absent and deletes them", async () => {
const user = await insertUser();
const { token } = await createSession(user.id);
await db
.update(sessions)
.set({ expiresAt: new Date(Date.now() - 1000) })
.where(eq(sessions.userId, user.id));
expect(await validateSessionToken(token)).toBeNull();
expect(await db.select().from(sessions)).toHaveLength(0);
});
it("invalidates a session on logout", async () => {
const user = await insertUser();
const { token } = await createSession(user.id);
await deleteSession(token);
expect(await validateSessionToken(token)).toBeNull();
});
});