import { describe, it, expect, vi, beforeEach } from 'vitest' import { loadPublicEvent } from '../../../server/utils/loadEvent.js' import { createMockEvent } from '../helpers/createMockEvent.js' const { mockFindById, mockFindOne, mockObjectIdIsValid, mockGetOptionalMember } = vi.hoisted(() => ({ mockFindById: vi.fn(), mockFindOne: vi.fn(), mockObjectIdIsValid: vi.fn(), mockGetOptionalMember: vi.fn() })) vi.mock('../../../server/models/event.js', () => ({ default: { findById: mockFindById, findOne: mockFindOne } })) vi.mock('../../../server/utils/mongoose.js', () => ({ connectDB: vi.fn() })) vi.mock('../../../server/utils/auth.js', () => ({ getOptionalMember: mockGetOptionalMember })) vi.mock('mongoose', () => ({ default: { Types: { ObjectId: { isValid: mockObjectIdIsValid } } } })) function chainableResult(value) { const query = { select: vi.fn().mockReturnThis(), lean: vi.fn().mockReturnThis(), then: (resolve) => Promise.resolve(value).then(resolve) } return query } describe('loadPublicEvent', () => { beforeEach(() => { vi.clearAllMocks() mockObjectIdIsValid.mockReturnValue(false) mockGetOptionalMember.mockResolvedValue(null) }) it('throws 400 when identifier is missing', async () => { const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/' }) await expect(loadPublicEvent(reqEvent, '')).rejects.toMatchObject({ statusCode: 400 }) }) it('throws 404 when event is not found by slug', async () => { mockFindOne.mockReturnValue(chainableResult(null)) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/missing' }) await expect(loadPublicEvent(reqEvent, 'missing')).rejects.toMatchObject({ statusCode: 404 }) expect(mockFindOne).toHaveBeenCalledWith({ slug: 'missing' }) }) it('returns visible event for any caller (no admin check)', async () => { const eventDoc = { _id: 'e1', slug: 'visible-slug', isVisible: true } mockFindOne.mockReturnValue(chainableResult(eventDoc)) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/visible-slug' }) const result = await loadPublicEvent(reqEvent, 'visible-slug') expect(result).toBe(eventDoc) expect(mockGetOptionalMember).not.toHaveBeenCalled() }) it('returns hidden event for admin caller', async () => { const eventDoc = { _id: 'e1', slug: 'hidden-slug', isVisible: false } mockFindOne.mockReturnValue(chainableResult(eventDoc)) mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'admin' }) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/hidden-slug' }) const result = await loadPublicEvent(reqEvent, 'hidden-slug') expect(result).toBe(eventDoc) }) it('throws 404 on hidden event for non-admin member', async () => { const eventDoc = { _id: 'e1', slug: 'hidden-slug', isVisible: false } mockFindOne.mockReturnValue(chainableResult(eventDoc)) mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member' }) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/hidden-slug' }) await expect(loadPublicEvent(reqEvent, 'hidden-slug')).rejects.toMatchObject({ statusCode: 404 }) }) it('throws 404 on hidden event for unauthenticated caller', async () => { const eventDoc = { _id: 'e1', slug: 'hidden-slug', isVisible: false } mockFindOne.mockReturnValue(chainableResult(eventDoc)) mockGetOptionalMember.mockResolvedValue(null) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/hidden-slug' }) await expect(loadPublicEvent(reqEvent, 'hidden-slug')).rejects.toMatchObject({ statusCode: 404 }) }) it('applies select and lean options when provided', async () => { const eventDoc = { _id: 'e1', slug: 'x', isVisible: true } const query = chainableResult(eventDoc) mockFindOne.mockReturnValue(query) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/x' }) await loadPublicEvent(reqEvent, 'x', { select: '-registrations.email', lean: true }) expect(query.select).toHaveBeenCalledWith('-registrations.email') expect(query.lean).toHaveBeenCalled() }) it('looks up by ObjectId when identifier is a valid ObjectId', async () => { const eventDoc = { _id: 'e1', slug: 'x', isVisible: true } mockObjectIdIsValid.mockReturnValue(true) mockFindById.mockReturnValue(chainableResult(eventDoc)) const reqEvent = createMockEvent({ method: 'GET', path: '/api/events/507f1f77bcf86cd799439011' }) const result = await loadPublicEvent(reqEvent, '507f1f77bcf86cd799439011') expect(mockFindById).toHaveBeenCalledWith('507f1f77bcf86cd799439011') expect(mockFindOne).not.toHaveBeenCalled() expect(result).toBe(eventDoc) }) })