172 lines
5.3 KiB
JavaScript
172 lines
5.3 KiB
JavaScript
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
|
|
import { createMockEvent } from '../../helpers/createMockEvent.js'
|
|
|
|
const {
|
|
mockFind,
|
|
mockSort,
|
|
mockSelect,
|
|
mockLean,
|
|
mockGetOptionalMember,
|
|
mockLoadPublicEvent
|
|
} = vi.hoisted(() => ({
|
|
mockFind: vi.fn(),
|
|
mockSort: vi.fn(),
|
|
mockSelect: vi.fn(),
|
|
mockLean: vi.fn(),
|
|
mockGetOptionalMember: vi.fn(),
|
|
mockLoadPublicEvent: vi.fn()
|
|
}))
|
|
|
|
vi.mock('../../../../server/models/event.js', () => ({
|
|
default: { find: mockFind }
|
|
}))
|
|
|
|
vi.mock('../../../../server/utils/mongoose.js', () => ({
|
|
connectDB: vi.fn()
|
|
}))
|
|
|
|
vi.mock('../../../../server/utils/auth.js', () => ({
|
|
getOptionalMember: mockGetOptionalMember
|
|
}))
|
|
|
|
vi.mock('../../../../server/utils/loadEvent.js', () => ({
|
|
loadPublicEvent: mockLoadPublicEvent
|
|
}))
|
|
|
|
function setupFindChain(result = []) {
|
|
mockLean.mockResolvedValue(result)
|
|
mockSelect.mockReturnValue({ lean: mockLean })
|
|
mockSort.mockReturnValue({ select: mockSelect })
|
|
mockFind.mockReturnValue({ sort: mockSort })
|
|
}
|
|
|
|
function mkReq({ method = 'GET', path = '/', id } = {}) {
|
|
const req = createMockEvent({ method, path })
|
|
if (id) req.context = { params: { id } }
|
|
return req
|
|
}
|
|
|
|
describe('GET /api/events — membersOnly visibility', () => {
|
|
let listHandler
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks()
|
|
vi.resetModules()
|
|
setupFindChain([])
|
|
listHandler = (await import('../../../../server/api/events/index.get.js')).default
|
|
})
|
|
|
|
it('hides membersOnly events from anonymous callers', async () => {
|
|
mockGetOptionalMember.mockResolvedValue(null)
|
|
|
|
await listHandler(mkReq({ path: '/api/events' }))
|
|
|
|
const filter = mockFind.mock.calls[0][0]
|
|
expect(filter.membersOnly).toEqual({ $ne: true })
|
|
})
|
|
|
|
it('hides membersOnly events from guest/cancelled/suspended members', async () => {
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member', status: 'guest' })
|
|
|
|
await listHandler(mkReq({ path: '/api/events' }))
|
|
|
|
const filter = mockFind.mock.calls[0][0]
|
|
expect(filter.membersOnly).toEqual({ $ne: true })
|
|
})
|
|
|
|
it('shows membersOnly events to active members', async () => {
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member', status: 'active' })
|
|
|
|
await listHandler(mkReq({ path: '/api/events' }))
|
|
|
|
const filter = mockFind.mock.calls[0][0]
|
|
expect(filter.membersOnly).toBeUndefined()
|
|
})
|
|
|
|
it('shows membersOnly events to pending_payment members', async () => {
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member', status: 'pending_payment' })
|
|
|
|
await listHandler(mkReq({ path: '/api/events' }))
|
|
|
|
const filter = mockFind.mock.calls[0][0]
|
|
expect(filter.membersOnly).toBeUndefined()
|
|
})
|
|
|
|
it('shows membersOnly events to admins regardless of status', async () => {
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'admin', status: 'cancelled' })
|
|
|
|
await listHandler(mkReq({ path: '/api/events' }))
|
|
|
|
const filter = mockFind.mock.calls[0][0]
|
|
expect(filter.membersOnly).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe('GET /api/events/[id] — membersOnly visibility', () => {
|
|
let detailHandler
|
|
|
|
beforeEach(async () => {
|
|
vi.clearAllMocks()
|
|
vi.resetModules()
|
|
detailHandler = (await import('../../../../server/api/events/[id].get.js')).default
|
|
})
|
|
|
|
const baseEvent = (overrides = {}) => ({
|
|
_id: 'e1',
|
|
slug: 'members-only-event',
|
|
title: 'Members Only',
|
|
isVisible: true,
|
|
membersOnly: true,
|
|
registrations: [],
|
|
...overrides
|
|
})
|
|
|
|
it('404s a membersOnly event for anonymous callers', async () => {
|
|
mockLoadPublicEvent.mockResolvedValue(baseEvent())
|
|
mockGetOptionalMember.mockResolvedValue(null)
|
|
|
|
await expect(
|
|
detailHandler(mkReq({ path: '/api/events/members-only-event', id: 'members-only-event' }))
|
|
).rejects.toMatchObject({ statusCode: 404 })
|
|
})
|
|
|
|
it('404s a membersOnly event for guest members', async () => {
|
|
mockLoadPublicEvent.mockResolvedValue(baseEvent())
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member', status: 'guest' })
|
|
|
|
await expect(
|
|
detailHandler(mkReq({ path: '/api/events/members-only-event', id: 'members-only-event' }))
|
|
).rejects.toMatchObject({ statusCode: 404 })
|
|
})
|
|
|
|
it('returns the event to active members', async () => {
|
|
mockLoadPublicEvent.mockResolvedValue(baseEvent())
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'member', status: 'active' })
|
|
|
|
const result = await detailHandler(
|
|
mkReq({ path: '/api/events/members-only-event', id: 'members-only-event' })
|
|
)
|
|
expect(result.slug).toBe('members-only-event')
|
|
})
|
|
|
|
it('returns the event to admins', async () => {
|
|
mockLoadPublicEvent.mockResolvedValue(baseEvent())
|
|
mockGetOptionalMember.mockResolvedValue({ _id: 'm1', role: 'admin', status: 'active' })
|
|
|
|
const result = await detailHandler(
|
|
mkReq({ path: '/api/events/members-only-event', id: 'members-only-event' })
|
|
)
|
|
expect(result.slug).toBe('members-only-event')
|
|
})
|
|
|
|
it('does not gate non-membersOnly events (auth check skipped)', async () => {
|
|
mockLoadPublicEvent.mockResolvedValue(baseEvent({ membersOnly: false }))
|
|
|
|
const result = await detailHandler(
|
|
mkReq({ path: '/api/events/members-only-event', id: 'members-only-event' })
|
|
)
|
|
expect(result.slug).toBe('members-only-event')
|
|
expect(mockGetOptionalMember).not.toHaveBeenCalled()
|
|
})
|
|
})
|