import { describe, expect, it } from 'vitest' import { dropZeilenEntfernen, hatAnweisungen, kopfBauen, ordnernameBauen, } from '../scripts/migration-erzeugen.mjs' /** * Der Migrations-Wrapper. * * Die eine Sache, die hier schiefgehen darf und nicht schiefgehen soll: * zu viel entfernen. Ein DROP INDEX, das Prisma zu Recht schreibt, muss * stehen bleiben - sonst repariert das Skript die Suche und zerlegt * dafuer das Schema. */ const GESCHUETZT = [ { name: 'dokument_absender_trgm', quelle: 'prisma/sql/volltextsuche.sql' }, { name: 'vorgang_titel_trgm', quelle: 'prisma/sql/volltextsuche.sql' }, ] describe('DROP-Zeilen entfernen', () => { it('nimmt die geschützte Anweisung samt Überschrift heraus', () => { const roh = [ '-- DropIndex', 'DROP INDEX "dokument_absender_trgm";', '', '-- AlterTable', 'ALTER TABLE "kontakt" ADD COLUMN "anrede" TEXT;', ].join('\n') const { sql, entfernt } = dropZeilenEntfernen(roh, GESCHUETZT) expect(entfernt).toEqual(['dokument_absender_trgm']) expect(sql).not.toContain('DROP INDEX') expect(sql).not.toContain('DropIndex') expect(sql).toContain('ALTER TABLE "kontakt"') }) it('lässt ein DROP INDEX auf einen Prisma-eigenen Index stehen', () => { // Der Kern der Sache: Das Skript darf nicht alles wegräumen, was nach // DROP INDEX aussieht - manche sollen wirklich weg. const roh = ['-- DropIndex', 'DROP INDEX "vertrag_betreuteId_status_idx";'].join('\n') const { sql, entfernt } = dropZeilenEntfernen(roh, GESCHUETZT) expect(entfernt).toEqual([]) expect(sql).toContain('DROP INDEX "vertrag_betreuteId_status_idx";') expect(sql).toContain('-- DropIndex') }) it('entfernt mehrere geschützte Anweisungen und meldet beide', () => { const roh = [ '-- DropIndex', 'DROP INDEX "dokument_absender_trgm";', '', '-- DropIndex', 'DROP INDEX "vorgang_titel_trgm";', '', '-- CreateTable', 'CREATE TABLE "schreibvorlage" ();', ].join('\n') const { sql, entfernt } = dropZeilenEntfernen(roh, GESCHUETZT) expect(entfernt).toEqual(['dokument_absender_trgm', 'vorgang_titel_trgm']) expect(sql.startsWith('-- CreateTable')).toBe(true) }) it('verträgt IF EXISTS und abweichende Schreibweise', () => { const roh = 'drop index if exists "vorgang_titel_trgm";' expect(dropZeilenEntfernen(roh, GESCHUETZT).entfernt).toEqual(['vorgang_titel_trgm']) }) it('lässt eine Migration ohne Treffer unverändert', () => { const roh = ['-- AlterTable', 'ALTER TABLE "vorgang" ADD COLUMN "x" TEXT;'].join('\n') const { sql, entfernt } = dropZeilenEntfernen(roh, GESCHUETZT) expect(entfernt).toEqual([]) expect(sql).toBe(roh) }) it('greift nicht bei einem bloßen Vorkommen des Namens im Text', () => { // Ein Kommentar, der den Index nennt, ist keine Anweisung. const roh = '-- dokument_absender_trgm bleibt bestehen' const { sql, entfernt } = dropZeilenEntfernen(roh, GESCHUETZT) expect(entfernt).toEqual([]) expect(sql).toBe(roh) }) }) describe('Leere Migration erkennen', () => { it('sieht eine echte Anweisung', () => { expect(hatAnweisungen('-- AlterTable\nALTER TABLE "x" ADD COLUMN "y" TEXT;')).toBe(true) }) it('hält reinen Kommentartext für leer', () => { // Der Normalfall: Schema und Datenbank stimmen überein, der ganze // Diff bestand aus den zwei bekannten DROP-Zeilen. Dann darf keine // Migration entstehen — eine leere in der Historie ist schlimmer als // keine. expect(hatAnweisungen('-- nur ein Kommentar\n\n-- noch einer')).toBe(false) }) it('hält Leerraum für leer', () => { expect(hatAnweisungen(' \n\n ')).toBe(false) }) }) describe('Kopfkommentar', () => { it('benennt die entfernten Indizes und ihre Quelle', () => { const kopf = kopfBauen('ansprechpartner', ['vorgang_titel_trgm'], GESCHUETZT) expect(kopf).toContain('vorgang_titel_trgm') expect(kopf).toContain('prisma/sql/volltextsuche.sql') }) it('sagt auch, wenn nichts zu entfernen war', () => { expect(kopfBauen('leer', [], GESCHUETZT)).toContain('Nichts zu entfernen') }) }) describe('Ordnername', () => { it('baut den Zeitstempel im Prisma-Format', () => { expect(ordnernameBauen('anrede', new Date(2026, 7, 23, 9, 5, 3))).toBe( '20260823090503_anrede', ) }) })