import { describe, it, expect, vi, beforeEach } from 'vitest' vi.mock('../../../server/models/member.js', () => ({ default: { findById: vi.fn() } })) vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() })) vi.mock('jsonwebtoken', () => ({ default: { verify: vi.fn() } })) import Member from '../../../server/models/member.js' import jwt from 'jsonwebtoken' import { requireAuth, requireAdmin } from '../../../server/utils/auth.js' import { createMockEvent } from '../helpers/createMockEvent.js' describe('requireAuth', () => { beforeEach(() => { vi.clearAllMocks() }) it('throws 401 when no auth-token cookie', async () => { const event = createMockEvent({ path: '/api/test' }) await expect(requireAuth(event)).rejects.toMatchObject({ statusCode: 401, statusMessage: 'Authentication required' }) }) it('throws 401 when JWT is invalid', async () => { jwt.verify.mockImplementation(() => { throw new Error('invalid token') }) const event = createMockEvent({ path: '/api/test', cookies: { 'auth-token': 'bad-token' } }) await expect(requireAuth(event)).rejects.toMatchObject({ statusCode: 401, statusMessage: 'Invalid or expired token' }) }) it('throws 401 when member not found in DB', async () => { jwt.verify.mockReturnValue({ memberId: 'member-123' }) Member.findById.mockResolvedValue(null) const event = createMockEvent({ path: '/api/test', cookies: { 'auth-token': 'valid-token' } }) await expect(requireAuth(event)).rejects.toMatchObject({ statusCode: 401, statusMessage: 'Member not found' }) }) it('throws 403 when member is suspended', async () => { jwt.verify.mockReturnValue({ memberId: 'member-123' }) Member.findById.mockResolvedValue({ _id: 'member-123', status: 'suspended', role: 'member' }) const event = createMockEvent({ path: '/api/test', cookies: { 'auth-token': 'valid-token' } }) await expect(requireAuth(event)).rejects.toMatchObject({ statusCode: 403, statusMessage: 'Account is suspended' }) }) it('throws 403 when member is cancelled', async () => { jwt.verify.mockReturnValue({ memberId: 'member-123' }) Member.findById.mockResolvedValue({ _id: 'member-123', status: 'cancelled', role: 'member' }) const event = createMockEvent({ path: '/api/test', cookies: { 'auth-token': 'valid-token' } }) await expect(requireAuth(event)).rejects.toMatchObject({ statusCode: 403, statusMessage: 'Account is cancelled' }) }) it('returns member when token and status are valid', async () => { const mockMember = { _id: 'member-123', status: 'active', role: 'member', email: 'test@example.com' } jwt.verify.mockReturnValue({ memberId: 'member-123' }) Member.findById.mockResolvedValue(mockMember) const event = createMockEvent({ path: '/api/test', cookies: { 'auth-token': 'valid-token' } }) const result = await requireAuth(event) expect(result).toEqual(mockMember) }) }) describe('requireAdmin', () => { beforeEach(() => { vi.clearAllMocks() }) it('throws 403 when member is not admin', async () => { const mockMember = { _id: 'member-123', status: 'active', role: 'member' } jwt.verify.mockReturnValue({ memberId: 'member-123' }) Member.findById.mockResolvedValue(mockMember) const event = createMockEvent({ path: '/api/admin/test', cookies: { 'auth-token': 'valid-token' } }) await expect(requireAdmin(event)).rejects.toMatchObject({ statusCode: 403, statusMessage: 'Admin access required' }) }) it('returns member when role is admin', async () => { const mockMember = { _id: 'admin-123', status: 'active', role: 'admin', email: 'admin@example.com' } jwt.verify.mockReturnValue({ memberId: 'admin-123' }) Member.findById.mockResolvedValue(mockMember) const event = createMockEvent({ path: '/api/admin/test', cookies: { 'auth-token': 'valid-token' } }) const result = await requireAdmin(event) expect(result).toEqual(mockMember) }) })