151 lines
5.3 KiB
TypeScript
151 lines
5.3 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.",
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Auch die Nachbarwehr aus Phase 10 und das Konto der Kreisebene.
|
|
*
|
|
* Beide gehören zur Testwehr, liegen aber nicht in ihr: die Nachbarwehr ist
|
|
* eine eigene Organisation, das Konto gehört gar keiner. Blieben sie beim
|
|
* Zurücksetzen stehen, sähe der Seed die Nachbarwehr, hielte alles für
|
|
* erledigt und legte die Mitgliedschaft in der neuen Testwehr nie wieder an
|
|
* — der Wehrwähler zeigte danach nur noch die betreute Wehr.
|
|
*/
|
|
const NACHBAR_SLUG = `${SLUG}-nachbar`;
|
|
const KBI_EMAIL = process.env.SEED_KBI_EMAIL ?? `kbi@${SLUG}.de`;
|
|
|
|
const vorhanden = await prisma.organisation.findFirst({
|
|
where: { slug: { in: [SLUG, NACHBAR_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.deleteMany({ where: { slug: { in: [SLUG, NACHBAR_SLUG] } } });
|
|
await tx.benutzer.deleteMany({ where: { email: KBI_EMAIL } });
|
|
|
|
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();
|
|
});
|