ESLint hatte keine Konfiguration — das Skript "npm run lint" pruefte nichts. Jetzt Flat Config ohne Formatierungsregeln, dafuer mit den Regeln, die die Fehler dieses Projekts gemeldet haetten. Er hat gleich zwei gefunden, die niemand gesucht hatte. Das Badge rendert ein span statt eines div. Damit ist die Meldung "div cannot be a descendant of p" weg, die seit Phase 3 auf der Einsatzseite stand — sie ist der klassische Ausloeser fuer einen Hydration-Fehler. Die E2E-Tests laufen gegen eine eigene Wehr. Nach fuenf Phasen standen in der Beispielwehr 120 Atemschutz-Ueberwachungen statt einer und 115 Geraete statt 45; bei jedem Lauf aenderten sich alle Bildschirmfotos. Der Bestand ist einmal zurueckgesetzt, npm run db:test-reset baut die Testwehr neu auf. Behoben nebenbei: - Die Fehlerflaeche der Karte konnte nie etwas anzeigen. - Zwei useMemo der Lagekarte rechneten bei jedem Rendern neu. - tests/e2e war vom Typecheck ausgenommen und liess einen doppelt deklarierten Bezeichner durch. Closes #10 Closes #11 Closes #12 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
138 lines
4.7 KiB
TypeScript
138 lines
4.7 KiB
TypeScript
import "dotenv/config";
|
|
import { spawnSync } from "node:child_process";
|
|
import { PrismaClient } from "@prisma/client";
|
|
|
|
/**
|
|
* Die Testwehr zurücksetzen.
|
|
*
|
|
* npm run db:test-reset
|
|
*
|
|
* Legt einen eigenen Mandanten für die E2E-Tests an — und setzt ihn bei jedem
|
|
* Aufruf auf den Ausgangsstand zurück.
|
|
*
|
|
* **Warum es das gibt.** Bis Phase 8 schrieben die Playwright-Tests in die
|
|
* Beispielwehr `ff-musterhausen`, also in genau den Bestand, den die Anleitung
|
|
* zeigt. Nach fünf Phasen standen dort 94 Atemschutz-Überwachungen statt
|
|
* einer, 49 Geräte statt 45 und Termine mit Namen wie „E2E-Absage-m1x8kq".
|
|
* Zweimal fielen Tests aus, weil ein gesuchter Eintrag von Seite 1 gerutscht
|
|
* war — und bei jedem Testlauf unterschieden sich alle 72 Bildschirmfotos.
|
|
*
|
|
* **Warum Löschen und nicht Aufräumen.** Ein Lauf, der abbricht, lässt beim
|
|
* Aufräumen einen Bestand zurück, den niemand erwartet. Ein vollständiger
|
|
* Neuaufbau ist der einzige Zustand, den man kennt — und er dauert
|
|
* Sekunden, weil der Seed ohnehin schon alles kann.
|
|
*
|
|
* **Die Beispielwehr wird nicht angefasst.** Weder gelesen noch geschrieben:
|
|
* gelöscht wird ausschließlich der Mandant mit der Testkennung.
|
|
*/
|
|
|
|
const SLUG = process.env.E2E_SLUG ?? "ff-teststelle";
|
|
const NAME = process.env.E2E_NAME ?? "Freiwillige Feuerwehr Teststelle";
|
|
const KURZNAME = process.env.E2E_KURZNAME ?? "FF Teststelle";
|
|
const EMAIL = process.env.E2E_ADMIN_EMAIL ?? "e2e@teststelle.de";
|
|
const PASSWORT = process.env.E2E_ADMIN_PASSWORT ?? "Feuerwehr2026";
|
|
|
|
/** Die Beispielwehr. Sie darf unter keinen Umständen getroffen werden. */
|
|
const GESCHUETZT = process.env.SEED_SLUG ?? "ff-musterhausen";
|
|
|
|
const prisma = new PrismaClient({
|
|
datasources: {
|
|
db: { url: process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL ?? "" },
|
|
},
|
|
});
|
|
|
|
function log(nachricht: string): void {
|
|
console.info(`[testdaten] ${nachricht}`);
|
|
}
|
|
|
|
async function main(): Promise<void> {
|
|
if (SLUG === GESCHUETZT) {
|
|
throw new Error(
|
|
`Die Testkennung "${SLUG}" ist dieselbe wie die der Beispielwehr. ` +
|
|
"Das würde den Bestand löschen, den die Anleitung zeigt — abgebrochen.",
|
|
);
|
|
}
|
|
|
|
const vorhanden = await prisma.organisation.findUnique({
|
|
where: { slug: SLUG },
|
|
select: { id: true, name: true },
|
|
});
|
|
|
|
if (vorhanden) {
|
|
log(`Entferne die bisherige Testwehr "${vorhanden.name}" ...`);
|
|
|
|
// Das Audit-Log ist append-only; für das Abräumen einer ganzen Wehr wird
|
|
// die Ausnahme gesetzt, die auch der Aufbewahrungslauf des Workers
|
|
// benutzt. Alles Übrige hängt am Fremdschlüssel und geht mit.
|
|
await prisma.$transaction(async (tx) => {
|
|
await tx.$executeRaw`SELECT set_config('app.audit_bereinigung', 'an', true)`;
|
|
|
|
// Die Trigger, die Protokollzeilen unveränderlich machen, stehen dem
|
|
// Löschen im Weg. Im Betrieb passiert das nie — hier wird eine ganze
|
|
// Wehr entfernt, und zwar eine, die es nur für Tests gibt.
|
|
for (const tabelle of [
|
|
"druckmessung",
|
|
"atemschutz_ereignis",
|
|
"ki_vorgang",
|
|
"kiosk_vorgang",
|
|
]) {
|
|
await tx.$executeRawUnsafe(`ALTER TABLE "${tabelle}" DISABLE TRIGGER USER`);
|
|
}
|
|
|
|
await tx.organisation.delete({ where: { slug: SLUG } });
|
|
|
|
for (const tabelle of [
|
|
"druckmessung",
|
|
"atemschutz_ereignis",
|
|
"ki_vorgang",
|
|
"kiosk_vorgang",
|
|
]) {
|
|
await tx.$executeRawUnsafe(`ALTER TABLE "${tabelle}" ENABLE TRIGGER USER`);
|
|
}
|
|
});
|
|
}
|
|
|
|
log(`Lege "${NAME}" neu an ...`);
|
|
|
|
// Der Seed kann das alles schon — er bekommt nur eine andere Kennung.
|
|
//
|
|
// Über die Shell aufgerufen, weil `npx` auf Windows eine Batchdatei ist:
|
|
// ohne `shell: true` findet spawnSync sie nicht und liefert stillschweigend
|
|
// den Status null statt einer Fehlermeldung.
|
|
const ergebnis = spawnSync("npx tsx prisma/seed.ts", {
|
|
stdio: "inherit",
|
|
shell: true,
|
|
env: {
|
|
...process.env,
|
|
SEED_SLUG: SLUG,
|
|
SEED_NAME: NAME,
|
|
SEED_KURZNAME: KURZNAME,
|
|
SEED_ADMIN_EMAIL: EMAIL,
|
|
SEED_ADMIN_PASSWORT: PASSWORT,
|
|
},
|
|
});
|
|
|
|
if (ergebnis.status !== 0) {
|
|
throw new Error(
|
|
`Der Seed ist mit Code ${ergebnis.status ?? "unbekannt"} abgebrochen.` +
|
|
(ergebnis.error ? ` (${ergebnis.error.message})` : ""),
|
|
);
|
|
}
|
|
|
|
log("");
|
|
log("Testwehr steht. Die E2E-Tests melden sich damit an:");
|
|
log(` E-Mail: ${EMAIL}`);
|
|
log(` Passwort: ${PASSWORT}`);
|
|
log("");
|
|
log(`Die Beispielwehr "${GESCHUETZT}" wurde nicht angefasst.`);
|
|
}
|
|
|
|
main()
|
|
.catch((fehler) => {
|
|
console.error("[testdaten] Abbruch:", fehler instanceof Error ? fehler.message : fehler);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|