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.
111 lines
3.9 KiB
TypeScript
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)
|
|
})
|
|
})
|