"prisma migrate diff" kennt die Trigramm-Indizes aus prisma/sql/volltextsuche.sql nicht und schreibt deshalb in jede Migration ein DROP INDEX dafuer. Fuenfmal mussten die Zeilen von Hand raus. Auf dem Server waere es folgenlos geblieben - update.sh legt die Indizes hinterher neu an -, in der Entwicklung dagegen waere die Suche still langsam geworden. npm run db:migration -- <name> [--anwenden] npm run db:pruefen Aufbau: - Die Liste der geschuetzten Indizes steht an einer Stelle. Wer in prisma/sql/ einen hinzufuegt, traegt ihn dort ein. - dropZeilenEntfernen() ist bewusst eng: Nur eine DROP-Anweisung, die einen geschuetzten Index nennt, faellt weg. Ein DROP INDEX auf einen Prisma-eigenen Index bleibt stehen - der soll ja weg. Ein Kommentar, der den Namen bloss erwaehnt, ebenfalls. - Der Kopf der erzeugten Datei sagt, was entfernt wurde und warum. - Nach --anwenden wird gegen die Datenbank geprueft, ob die Indizes wirklich noch stehen. Das ist der Unterschied zwischen "sollte gutgegangen sein" und "nachgesehen". Beim Probelauf gefunden und behoben: Stimmen Schema und Datenbank ueberein, besteht der ganze Diff aus den beiden bekannten DROP-Zeilen. Das Skript legte dafuer eine leere Migration an - und eine leere Migration in der Historie ist schlimmer als keine, weil sie als angewendet gilt und niemand mehr weiss, wofuer sie gut war. Jetzt bricht es vorher ab, ohne einen Ordner zu hinterlassen. Die Typen liegen als .d.mts daneben: Das Skript bleibt .mjs und laeuft ohne Bauschritt, die Tests laufen trotzdem nicht gegen "any". Geprueft: zwoelf Einheitstests, dazu von Hand der Lauf mit echter Schemaaenderung und der Lauf ohne. Refs #24
129 lines
4.3 KiB
TypeScript
129 lines
4.3 KiB
TypeScript
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',
|
|
)
|
|
})
|
|
})
|