package retention import ( "context" "os" "os/exec" "path/filepath" "runtime" "testing" "github.com/jackc/pgx/v5/pgxpool" ) // TestMigration_ForwardAndBackward ist Pruefung 3 fuer Akzeptanzkriterium // 3: Migration laeuft gegen leere Datenbank durch UND ist rueckrollbar - // real gegen die TATSAECHLICHEN Migrationsdateien, kein Nachbau. func TestMigration_ForwardAndBackward(t *testing.T) { dsn := os.Getenv("TEST_TENANT_DSN") if dsn == "" { t.Skip("TEST_TENANT_DSN nicht gesetzt, Integrationstest uebersprungen") } if _, err := exec.LookPath("psql"); err != nil { t.Skip("psql nicht installiert, Integrationstest uebersprungen") } _, thisFile, _, _ := runtime.Caller(0) migrationsDir := filepath.Join(filepath.Dir(thisFile), "..", "..", "migrations") upSQL := filepath.Join(migrationsDir, "0002_retention.up.sql") downSQL := filepath.Join(migrationsDir, "0002_retention.down.sql") ctx := context.Background() pool, err := pgxpool.New(ctx, dsn) if err != nil { t.Fatalf("pool: %v", err) } defer pool.Close() // sauberer Ausgangszustand, falls von einem frueheren Testlauf uebrig. _, _ = pool.Exec(ctx, `DROP TABLE IF EXISTS retention_class_assignments, retention_objects CASCADE`) runPsql := func(sqlFile string) []byte { t.Helper() cmd := exec.CommandContext(ctx, "psql", dsn, "-v", "ON_ERROR_STOP=1", "-f", sqlFile) out, err := cmd.CombinedOutput() if err != nil { t.Fatalf("psql -f %s: %v (ausgabe: %s)", sqlFile, err, out) } return out } tableExists := func(name string) bool { var exists bool if err := pool.QueryRow(ctx, `SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = $1)`, name).Scan(&exists); err != nil { t.Fatalf("tabellenexistenz pruefen: %v", err) } return exists } // vorwaerts runPsql(upSQL) if !tableExists("retention_objects") || !tableExists("retention_class_assignments") { t.Fatal("migration vorwaerts: erwartete tabellen fehlen") } // rueckwaerts runPsql(downSQL) if tableExists("retention_objects") || tableExists("retention_class_assignments") { t.Fatal("migration rueckwaerts: tabellen haetten entfernt sein muessen") } // erneut vorwaerts (beweist: rueckwaerts hat wirklich sauber // aufgeraeumt, kein Rest, der einen zweiten Vorwaertslauf bloeckieren wuerde) runPsql(upSQL) if !tableExists("retention_objects") { t.Fatal("zweiter vorwaertslauf nach rollback fehlgeschlagen") } // aufraeumen _, _ = pool.Exec(ctx, `DROP TABLE IF EXISTS retention_class_assignments, retention_objects CASCADE`) }