Files
fn_registry/functions/infra/migration_validate.go
egutierrez ec36278c7b feat: tipos Migration/MigrationStatus y funciones puras migration_parse + migration_validate
Fase 1 del issue 0015. Tipos Go en functions/infra/migration.go con metadata en
types/infra/. Funciones puras: MigrationParse (parsea filename NNN_name.sql +
bloques -- +up/-- +down) y MigrationValidate (verifica secuencia, huecos,
duplicados, bloques vacios). 16 tests, todos pasan.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 02:01:34 +02:00

72 lines
1.9 KiB
Go

package infra
import (
"fmt"
"sort"
)
// MigrationValidate checks a slice of migrations for consistency errors.
// It verifies that:
// - Versions are sequential starting from 1 with no gaps (1, 2, 3...)
// - No duplicate versions exist
// - Each migration has non-empty UpSQL
// - Each migration has a non-empty Name
//
// Returns a slice of human-readable error strings. An empty slice means all
// migrations are valid. The function does not mutate the input slice.
func MigrationValidate(migrations []Migration) []string {
var errs []string
if len(migrations) == 0 {
return errs
}
// Work on a sorted copy to detect gaps and duplicates
sorted := make([]Migration, len(migrations))
copy(sorted, migrations)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i].Version < sorted[j].Version
})
// Check individual fields and collect duplicates
seen := make(map[int]int) // version -> count
for _, m := range sorted {
seen[m.Version]++
if m.Name == "" {
errs = append(errs, fmt.Sprintf("version %d has empty name", m.Version))
}
if m.UpSQL == "" {
errs = append(errs, fmt.Sprintf("version %d (%s) has empty up_sql", m.Version, m.Name))
}
}
// Report duplicates
for v, count := range seen {
if count > 1 {
errs = append(errs, fmt.Sprintf("duplicate version %d appears %d times", v, count))
}
}
// Check sequential numbering starting from 1 (no gaps)
// Build unique sorted versions
versions := make([]int, 0, len(seen))
for v := range seen {
versions = append(versions, v)
}
sort.Ints(versions)
if len(versions) > 0 && versions[0] != 1 {
errs = append(errs, fmt.Sprintf("versions must start at 1, got %d", versions[0]))
}
for i := 1; i < len(versions); i++ {
expected := versions[i-1] + 1
if versions[i] != expected {
errs = append(errs, fmt.Sprintf("gap in versions: missing %d (have %d then %d)", expected, versions[i-1], versions[i]))
}
}
return errs
}