import { beforeEach, describe, expect, it, vi } from 'vitest'; import jwt from 'jsonwebtoken'; vi.mock('@vercel/postgres', () => ({ sql: vi.fn() })); import { sql } from '@vercel/postgres'; import { JWT_SECRET } from '../../lib/auth-secret.js'; import { getUserFromRequest } from '../../lib/permission-middleware.js'; function makeToken(payload, opts = {}) { return jwt.sign(payload, JWT_SECRET, { expiresIn: opts.expiresIn ?? '1h' }); } describe('getUserFromRequest', () => { beforeEach(() => { sql.mockReset(); sql.mockResolvedValue({ rows: [] }); vi.spyOn(console, 'error').mockImplementation(() => {}); }); it('returns null when the Authorization header is missing', async () => { const user = await getUserFromRequest({ headers: {} }); expect(user).toBeNull(); expect(sql).not.toHaveBeenCalled(); }); it('returns null when the Authorization header is not a Bearer scheme', async () => { const user = await getUserFromRequest({ headers: { authorization: 'Basic foo' }, }); expect(user).toBeNull(); expect(sql).not.toHaveBeenCalled(); }); it('returns null when the token is malformed', async () => { const user = await getUserFromRequest({ headers: { authorization: 'Bearer not-a-jwt' }, }); expect(user).toBeNull(); expect(sql).not.toHaveBeenCalled(); }); it('returns null when the token signature uses a wrong secret', async () => { const token = jwt.sign({ userId: 1 }, 'other-secret', { expiresIn: '1h' }); const user = await getUserFromRequest({ headers: { authorization: `Bearer ${token}` }, }); expect(user).toBeNull(); expect(sql).not.toHaveBeenCalled(); }); it('returns null when the token is expired', async () => { const token = makeToken({ userId: 1 }, { expiresIn: '-1s' }); const user = await getUserFromRequest({ headers: { authorization: `Bearer ${token}` }, }); expect(user).toBeNull(); expect(sql).not.toHaveBeenCalled(); }); it('returns null when the token is valid but no user row matches', async () => { sql.mockResolvedValue({ rows: [] }); const token = makeToken({ userId: 42 }); const user = await getUserFromRequest({ headers: { authorization: `Bearer ${token}` }, }); expect(user).toBeNull(); expect(sql).toHaveBeenCalledTimes(1); }); it('returns the user object when the token is valid and the user row exists', async () => { sql.mockResolvedValue({ rows: [{ id: 42, email: 'a@b.c', role: 'user' }], }); const token = makeToken({ userId: 42 }); const user = await getUserFromRequest({ headers: { authorization: `Bearer ${token}` }, }); expect(user).toEqual({ userId: 42, email: 'a@b.c', role: 'user' }); expect(sql).toHaveBeenCalledTimes(1); }); it('does NOT return the synthetic admin shape when no Authorization header is present (Brief 2 regression lock)', async () => { const user = await getUserFromRequest({ headers: {} }); expect(user).not.toEqual({ userId: 1, email: 'admin@tcgvault.com', role: 'admin', }); expect(user).toBeNull(); }); });