ghostguild-org/tests/server/api/admin-alerts.test.js

153 lines
5.2 KiB
JavaScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
vi.mock('../../../server/utils/adminAlerts.js', () => ({
computeAllAlerts: vi.fn()
}))
vi.mock('../../../server/models/adminAlertDismissal.js', () => ({
default: {
findOneAndUpdate: vi.fn()
},
ADMIN_ALERT_TYPES: [
'slack_invite_failed',
'no_slack_handle_week',
'stuck_pending_payment',
'member_suspended',
'preregistrant_selected_not_invited',
'preregistrant_expired',
'event_draft_imminent',
'event_near_capacity',
'tag_suggestions_pending'
]
}))
vi.mock('../../../server/utils/mongoose.js', () => ({
connectDB: vi.fn()
}))
vi.stubGlobal('adminAlertDismissSchema', {})
import getHandler from '../../../server/api/admin/alerts/index.get.js'
import dismissHandler from '../../../server/api/admin/alerts/dismiss.post.js'
import { computeAllAlerts } from '../../../server/utils/adminAlerts.js'
import AdminAlertDismissal from '../../../server/models/adminAlertDismissal.js'
import { createMockEvent } from '../helpers/createMockEvent.js'
describe('GET /api/admin/alerts', () => {
beforeEach(() => {
vi.clearAllMocks()
requireAdmin.mockResolvedValue({ _id: { toString: () => 'admin-1' } })
})
describe('source inspection', () => {
const source = readFileSync(
resolve(import.meta.dirname, '../../../server/api/admin/alerts/index.get.js'),
'utf-8'
)
it('calls requireAdmin before any business logic', () => {
const adminIdx = source.indexOf('requireAdmin(event)')
const computeIdx = source.indexOf('computeAllAlerts(')
expect(adminIdx).toBeGreaterThan(-1)
expect(computeIdx).toBeGreaterThan(-1)
expect(adminIdx).toBeLessThan(computeIdx)
})
})
it('returns the active alerts for the current admin', async () => {
computeAllAlerts.mockResolvedValue([
{
type: 'slack_invite_failed',
severity: 'critical',
title: 'Slack invites failed',
count: 1,
items: [{ id: 'm1', label: 'Alex', sublabel: 'alex@example.com', href: '/admin/members/m1' }],
signature: 'abc'
}
])
const event = createMockEvent({ method: 'GET', path: '/api/admin/alerts' })
const result = await getHandler(event)
expect(result).toEqual({
alerts: [
{
type: 'slack_invite_failed',
severity: 'critical',
title: 'Slack invites failed',
count: 1,
items: [{ id: 'm1', label: 'Alex', sublabel: 'alex@example.com', href: '/admin/members/m1' }],
signature: 'abc'
}
]
})
expect(computeAllAlerts).toHaveBeenCalledWith('admin-1')
})
it('rejects non-admin requests', async () => {
requireAdmin.mockRejectedValue(createError({ statusCode: 403, statusMessage: 'Forbidden' }))
const event = createMockEvent({ method: 'GET', path: '/api/admin/alerts' })
await expect(getHandler(event)).rejects.toMatchObject({ statusCode: 403 })
})
})
describe('POST /api/admin/alerts/dismiss', () => {
beforeEach(() => {
vi.clearAllMocks()
requireAdmin.mockResolvedValue({ _id: { toString: () => 'admin-1' } })
validateBody.mockResolvedValue({ alertType: 'slack_invite_failed', signature: 'abc' })
})
describe('source inspection', () => {
const source = readFileSync(
resolve(import.meta.dirname, '../../../server/api/admin/alerts/dismiss.post.js'),
'utf-8'
)
it('calls requireAdmin before validateBody', () => {
const adminIdx = source.indexOf('requireAdmin(event)')
const validateIdx = source.indexOf('validateBody(event')
expect(adminIdx).toBeGreaterThan(-1)
expect(validateIdx).toBeGreaterThan(-1)
expect(adminIdx).toBeLessThan(validateIdx)
})
})
it('upserts a dismissal with the current signature', async () => {
AdminAlertDismissal.findOneAndUpdate.mockResolvedValue({})
const event = createMockEvent({
method: 'POST',
path: '/api/admin/alerts/dismiss',
body: { alertType: 'slack_invite_failed', signature: 'abc' }
})
const result = await dismissHandler(event)
expect(result).toEqual({ ok: true })
expect(AdminAlertDismissal.findOneAndUpdate).toHaveBeenCalledWith(
{ adminId: 'admin-1', alertType: 'slack_invite_failed' },
{ $set: { signature: 'abc', dismissedAt: expect.any(Date) } },
{ upsert: true, new: true }
)
})
it('rejects non-admin requests', async () => {
requireAdmin.mockRejectedValue(createError({ statusCode: 403, statusMessage: 'Forbidden' }))
const event = createMockEvent({
method: 'POST',
path: '/api/admin/alerts/dismiss',
body: { alertType: 'slack_invite_failed', signature: 'abc' }
})
await expect(dismissHandler(event)).rejects.toMatchObject({ statusCode: 403 })
})
it('rejects invalid body via validateBody', async () => {
validateBody.mockRejectedValue(createError({ statusCode: 400, statusMessage: 'Validation failed' }))
const event = createMockEvent({
method: 'POST',
path: '/api/admin/alerts/dismiss',
body: { alertType: 'unknown', signature: 'abc' }
})
await expect(dismissHandler(event)).rejects.toMatchObject({ statusCode: 400 })
})
})