test: Integrations-, Migrations- und E2E-Tests ergänzen
Der Integrationstest prüft gegen eine echte Datenbank, was Vitest nicht sieht: Eindeutigkeit, Fremdschlüssel, Kaskaden und dass das Protokoll den gelöschten Benutzer überlebt. Er läuft bewusst ohne umschließende Transaktion — er prüft absichtlich Fälle, die scheitern müssen, und ein gescheiterter Befehl bricht in PostgreSQL die ganze Transaktion ab. Stattdessen tragen alle Testdaten dieselbe Kennung und werden am Ende wieder entfernt. Der Migrationstest legt eine Wegwerf-Datenbank an, spielt alle Migrationen ein und vergleicht das Ergebnis mit dem Schema. Dass er Abweichungen wirklich bemerkt, wurde mit einem probeweise ergänzten Feld nachgewiesen. Der E2E-Rauchtest deckt den Weg ab, der in M1 trägt: Umleitung ohne Anmeldung samt gemerktem Ziel, Abweisung falscher Zugangsdaten, erfolgreiche Anmeldung und die Verwaltungsseite. Dafür neu: eine Middleware, die nur den angefragten Pfad als Kopfzeile durchreicht. Ein Layout kennt seinen Pfad nicht, braucht ihn aber für die Rückkehr nach der Anmeldung. Anmeldelogik steht dort bewusst nicht — die Middleware läuft im Edge-Umfeld ohne Prisma. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
24a799d6ba
commit
126ad93301
@@ -1,7 +1,7 @@
|
||||
import { Kopfleiste } from '@/components/kopfleiste'
|
||||
import { INSTANZ_NAME } from '@/lib/instanz'
|
||||
import { versionAnzeige } from '@/lib/version'
|
||||
import { verlangeAnmeldung } from '@/server/berechtigung'
|
||||
import { angefragterPfad, verlangeAnmeldung } from '@/server/berechtigung'
|
||||
import { aktuelleInstanz } from '@/server/instanz'
|
||||
|
||||
/**
|
||||
@@ -14,7 +14,9 @@ export default async function AngemeldetesLayout({
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
const benutzer = await verlangeAnmeldung()
|
||||
// Der Pfad kommt aus der Kopfzeile der Middleware, damit es nach der
|
||||
// Anmeldung dort weitergeht, wo man hinwollte.
|
||||
const benutzer = await verlangeAnmeldung(await angefragterPfad())
|
||||
const instanz = aktuelleInstanz()
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { NextResponse, type NextRequest } from 'next/server'
|
||||
|
||||
export const PFAD_KOPFZEILE = 'x-planer-pfad'
|
||||
|
||||
/**
|
||||
* Reicht den angefragten Pfad als Kopfzeile weiter.
|
||||
*
|
||||
* Ein Layout kennt seinen eigenen Pfad nicht, braucht ihn aber, um nach der
|
||||
* Anmeldung wieder dorthin zurückzuführen. Hier steht bewusst keine
|
||||
* Anmeldelogik: die Middleware läuft im Edge-Umfeld, in dem weder Prisma noch
|
||||
* der Adapter verfügbar sind. Geprüft wird weiterhin im Layout, in der Seite
|
||||
* und in jeder Server Action.
|
||||
*/
|
||||
export function middleware(anfrage: NextRequest) {
|
||||
const kopfzeilen = new Headers(anfrage.headers)
|
||||
kopfzeilen.set(PFAD_KOPFZEILE, `${anfrage.nextUrl.pathname}${anfrage.nextUrl.search}`)
|
||||
return NextResponse.next({ request: { headers: kopfzeilen } })
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
// Alles außer den Auth-Routen, statischen Dateien und Bildern.
|
||||
'/((?!api/auth|_next/static|_next/image|favicon.ico).*)',
|
||||
],
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import 'server-only'
|
||||
import { headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import type { Session } from 'next-auth'
|
||||
import type { Rolle } from '@/generated/prisma/enums'
|
||||
import { rolleReichtAus } from '@/lib/rollen'
|
||||
import { auth } from '@/server/auth'
|
||||
import { PFAD_KOPFZEILE } from '@/middleware'
|
||||
|
||||
/**
|
||||
* Rollenpruefung.
|
||||
@@ -72,3 +74,13 @@ export async function pruefeRolle(verlangt: Rolle): Promise<Angemeldet> {
|
||||
export async function verlangeAnmeldungFuerApi(): Promise<Angemeldet | null> {
|
||||
return angemeldet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Der angefragte Pfad, den die Middleware durchgereicht hat. Wird gebraucht,
|
||||
* um nach der Anmeldung wieder dorthin zurueckzufuehren.
|
||||
*/
|
||||
export async function angefragterPfad(): Promise<string | undefined> {
|
||||
const kopfzeilen = await headers()
|
||||
const pfad = kopfzeilen.get(PFAD_KOPFZEILE)
|
||||
return pfad && pfad.startsWith('/') ? pfad : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user