Files
AfiS-Aktensystem/worker/tests/mitgliedschaft.test.ts
T
HarlekinandClaude Opus 5 2e71dfee78 Notfallzugang gilt auch im Worker
Der Voll-Abgleich hat den Administrator alle fünf Minuten hinausgeworfen:
enforceMembership kannte BOOTSTRAP_ADMIN_IDS nicht und sah nur, dass keine
seiner Rollen zur Anmeldung berechtigt. Solange das Rollen-Mapping noch leer
ist, trifft das zwangsläufig zu — der Notfallzugang war damit genau in der
Lage wirkungslos, für die es ihn gibt.

Gefunden beim ersten Lauf gegen den echten Discord-Server, nicht im Test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 19:24:13 +02:00

111 lines
3.9 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { createPrismaClient, type PrismaClient } from '@afis/db'
import { enforceMembership, handleDeparture } from '../src/sync.js'
/**
* „Rollenentzug in Discord beendet laufende Sitzungen" aus
* docs/03-auth-und-rechte.md.
*
* Das Zeitbudget von fünf Sekunden ist im Betrieb die Summe aus
* Gateway-Ereignis und diesem Schritt. Geprüft wird hier der Schritt, der in
* unserer Hand liegt: die Sitzungen müssen weg sein, und der Datensatz muss
* stehen bleiben, damit Autorenangaben in alten Akten gültig bleiben.
*/
const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL
const suite = url ? describe : describe.skip
let db: PrismaClient
const marke = `worker-${Date.now()}`
const rolleAfis = `${marke}-afis`
const rolleGast = `${marke}-gast`
suite('Sitzungen beim Rollenentzug', () => {
beforeAll(async () => {
db = createPrismaClient(url)
await db.roleMapping.createMany({
data: [
{ discordRoleId: rolleAfis, label: 'AfiS', category: 'MEMBERSHIP', grantsMembership: true },
{ discordRoleId: rolleGast, label: 'Gast', category: 'UNSORTED' },
],
})
})
afterAll(async () => {
await db.$disconnect()
})
async function nutzerMitSitzung(id: string, rollen: string[]): Promise<void> {
await db.user.create({ data: { id, username: id, displayName: id } })
if (rollen.length > 0) {
await db.userRole.createMany({
data: rollen.map((discordRoleId) => ({ userId: id, discordRoleId })),
})
}
await db.session.create({
data: { userId: id, expiresAt: new Date(Date.now() + 86_400_000) },
})
}
it('lässt eine Sitzung stehen, solange die AfiS-Rolle vorliegt', async () => {
const id = `${marke}-bleibt`
await nutzerMitSitzung(id, [rolleAfis])
const beendet = await enforceMembership(db, id)
expect(beendet).toBe(false)
expect(await db.session.count({ where: { userId: id } })).toBe(1)
})
it('beendet alle Sitzungen, sobald die AfiS-Rolle fehlt', async () => {
const id = `${marke}-verliert`
await nutzerMitSitzung(id, [rolleAfis, rolleGast])
await db.session.create({
data: { userId: id, expiresAt: new Date(Date.now() + 86_400_000) },
})
expect(await db.session.count({ where: { userId: id } })).toBe(2)
// Rollenentzug in Discord, hier direkt nachgestellt.
await db.userRole.deleteMany({ where: { userId: id, discordRoleId: rolleAfis } })
const start = Date.now()
const beendet = await enforceMembership(db, id)
const dauer = Date.now() - start
expect(beendet).toBe(true)
expect(await db.session.count({ where: { userId: id } })).toBe(0)
expect(dauer).toBeLessThan(5000)
// Der Datensatz bleibt, nur inaktiv.
const nutzer = await db.user.findUnique({ where: { id } })
expect(nutzer).not.toBeNull()
expect(nutzer?.isActive).toBe(false)
})
it('wirft den Notfallzugang nicht hinaus, auch ohne AfiS-Rolle', async () => {
// Sonst wirft der Voll-Abgleich den Administrator alle fünf Minuten
// hinaus, solange noch keine Rolle zur Anmeldung berechtigt — und
// einrichten kann er das Rollen-Mapping dann nie.
const id = `${marke}-notfall`
await nutzerMitSitzung(id, [rolleGast])
const beendet = await enforceMembership(db, id, [id])
expect(beendet).toBe(false)
expect(await db.session.count({ where: { userId: id } })).toBe(1)
expect((await db.user.findUnique({ where: { id } }))?.isActive).toBe(true)
})
it('beendet Sitzungen beim Serveraustritt und behält den Nutzer', async () => {
const id = `${marke}-austritt`
await nutzerMitSitzung(id, [rolleAfis])
await handleDeparture(db, id)
expect(await db.session.count({ where: { userId: id } })).toBe(0)
expect(await db.userRole.count({ where: { userId: id } })).toBe(0)
const nutzer = await db.user.findUnique({ where: { id } })
expect(nutzer?.isActive).toBe(false)
})
})