import { describe, expect, it } from "vitest"; import { datenbankVerfuegbar, testPrisma } from "./helpers/db"; /** * Wächter über die Datenbankobjekte, die Prisma nicht kennt (#009). * * Zusammengesetzte Fremdschlüssel, Trigramm-Indizes und Trigger lassen sich im * Prisma-Schema nicht ausdrücken. Deshalb erzeugt `prisma migrate dev` bei * **jeder** künftigen Migration Löschbefehle für sie — sie sind aus Prismas * Sicht Abweichungen von der Vorgabe. Wer die Löschbefehle einmal übersieht, * entfernt stillschweigend die Mandantengrenze, die Archivsuche oder den Schutz * der Unveränderlichkeit. * * Dieser Test macht das laut. Er prüft nicht, dass die Regeln *wirken* — das tun * `tests/datenmodell.test.ts` und `tests/vorlagen.test.ts` —, sondern nur, dass * die Objekte überhaupt noch da sind. */ async function namen(abfrage: string): Promise { const zeilen = await testPrisma.$queryRawUnsafe<{ name: string }[]>(abfrage); return zeilen.map((zeile) => zeile.name).sort(); } describe.runIf(datenbankVerfuegbar)("Handgeschriebene Datenbankobjekte", () => { it("hat die zusammengesetzten Fremdschlüssel der Mandantengrenze", async () => { // Ohne sie kann ein Lauf ein Gerät eines fremden Kunden ziehen. expect( await namen(`SELECT conname AS name FROM pg_constraint WHERE conname LIKE '%gleicher_kunde'`), ).toEqual([ "Geraet_besitzer_gleicher_kunde", "Lauf_abteilung_gleicher_kunde", "Lauf_geraet_gleicher_kunde", "Lauf_mitarbeiter_gleicher_kunde", "Mitarbeiter_vorgesetzter_gleicher_kunde", ]); }); it("hat die Trigramm-Indizes für die Archivsuche", async () => { // Ohne sie wird jede Archivsuche zum vollständigen Durchlauf der Tabelle. expect( await namen( `SELECT indexname AS name FROM pg_indexes WHERE schemaname='public' AND indexdef LIKE '%gin_trgm_ops%'`, ), ).toEqual([ "Kommentar_rumpf_trgm_idx", "Lauf_titel_trgm_idx", "Schrittergebnis_notiz_trgm_idx", "Schrittergebnis_wert_trgm_idx", ]); }); it("hat die Trigger, die eine veröffentlichte Version schützen", async () => { expect(await namen(`SELECT tgname AS name FROM pg_trigger WHERE NOT tgisinternal`)).toEqual([ "VorlagenAbschnitt_unveraenderlich", "VorlagenSchritt_unveraenderlich", "VorlagenVariable_unveraenderlich", "VorlagenVersion_status_einbahn", ]); }); it("hat die partiellen Eindeutigkeitsindizes", async () => { expect( await namen( `SELECT indexname AS name FROM pg_indexes WHERE schemaname='public' AND indexdef LIKE '%WHERE%'`, ), ).toEqual([ "Kunde_eigene_organisation_einmalig", "Vorlage_seedSchluessel_einmalig", "VorlagenVersion_ein_entwurf", ]); }); it("hat alle Prüfbedingungen", async () => { const vorhanden = await namen( `SELECT conname AS name FROM pg_constraint WHERE contype='c' AND connamespace='public'::regnamespace`, ); // Namentlich aufgezählt, damit auch das Verschwinden einer einzelnen auffällt. expect(vorhanden).toEqual([ "Einstellung_einzelne_zeile", "Geraet_registriert_braucht_namen", "Geraet_tagcode_form", "Kunde_kuerzel_form", "Lauf_abgeschlossen_mit_zeitpunkt", "Lauf_archiviert_mit_zeitpunkt", "Lauf_begonnen_mit_zeitpunkt", "Lauf_mitarbeiter_eindeutig", "Mitarbeiter_vorgesetzter_nicht_selbst", "Schrittergebnis_begruendung_noetig", "TagCodeVergabe_form", "Verzeichnisquelle_bindpasswort_verschluesselt", "VorlagenVersion_veroeffentlicht_mit_zeitpunkt", ]); }); it("hat die Erweiterung für die Trigramm-Suche", async () => { const zeilen = await testPrisma.$queryRaw<{ extname: string }[]>` SELECT extname FROM pg_extension WHERE extname = 'pg_trgm' `; expect(zeilen).toHaveLength(1); }); });