ghostguild-org/tests/server/api/admin-auth-guards.test.js

113 lines
2.9 KiB
JavaScript

import { describe, it, expect } from 'vitest'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
const adminDir = resolve(import.meta.dirname, '../../../server/api/admin')
// All admin routes grouped by directory
const adminRoutes = {
'admin/': [
'dashboard.get.js',
'events.get.js',
'events.post.js',
'members.get.js',
'members.post.js',
'series.get.js',
'series.post.js',
'series.put.js'
],
'admin/events/': [
'events/[id].delete.js',
'events/[id].get.js',
'events/[id].put.js'
],
'admin/members/': [
'members/[id].put.js',
'members/[id]/role.patch.js',
'members/import.post.js',
'members/invite.post.js'
],
'admin/series/': [
'series/[id].delete.js',
'series/[id].put.js',
'series/tickets.put.js'
],
'admin/pre-registrants/': [
'pre-registrants/index.get.js',
'pre-registrants/[id].get.js',
'pre-registrants/[id].put.js',
'pre-registrants/bulk-status.patch.js',
'pre-registrants/invite.post.js',
'pre-registrants/stats.get.js'
],
'admin/alerts/': [
'alerts/index.get.js',
'alerts/dismiss.post.js',
'alerts/dismissed.get.js',
'alerts/restore.post.js'
],
'admin/wiki/': [
'wiki/sync.post.js'
]
}
// Business logic markers that must appear after requireAdmin
const businessLogicPatterns = [
'readBody(event)',
'validateBody(event',
'fetch(',
'connectDB()',
'Member.find',
'Member.findOne',
'Member.findById',
'Member.countDocuments',
'Event.find',
'Event.findOne',
'Event.findById',
'Event.countDocuments',
'Series.find',
'Series.findOne',
'Series.findById',
'PreRegistration.find',
'PreRegistration.findById',
'PreRegistration.aggregate',
'PreRegistration.updateMany',
'computeAllAlerts(',
'AdminAlertDismissal.findOneAndUpdate',
'AdminAlertDismissal.find',
'AdminAlertDismissal.deleteMany',
'WikiArticle.find',
'WikiArticle.findOneAndUpdate',
'fetchAllDocuments('
]
describe('Admin endpoint auth guards', () => {
for (const [group, files] of Object.entries(adminRoutes)) {
describe(group, () => {
for (const file of files) {
describe(file, () => {
const source = readFileSync(resolve(adminDir, file), 'utf-8')
it('calls requireAdmin', () => {
expect(source).toContain('requireAdmin(event)')
})
it('calls requireAdmin before any business logic', () => {
const adminIndex = source.indexOf('requireAdmin(event)')
expect(adminIndex).toBeGreaterThan(-1)
for (const pattern of businessLogicPatterns) {
const patternIndex = source.indexOf(pattern)
if (patternIndex > -1) {
expect(
adminIndex,
`requireAdmin must appear before ${pattern} in ${file}`
).toBeLessThan(patternIndex)
}
}
})
})
}
})
}
})