ec36278c7b
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>
72 lines
1.9 KiB
Go
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
|
|
}
|