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>
154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package infra
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func makeMig(version int, name, upSQL string) Migration {
|
|
return Migration{Version: version, Name: name, UpSQL: upSQL, DownSQL: "DROP TABLE IF EXISTS t;"}
|
|
}
|
|
|
|
func TestMigrationValidate(t *testing.T) {
|
|
t.Run("secuencia valida retorna sin errores", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
|
|
makeMig(2, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
|
|
makeMig(3, "create_roles", "CREATE TABLE roles (id TEXT PRIMARY KEY);"),
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected no errors, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("secuencia vacia retorna sin errores", func(t *testing.T) {
|
|
errs := MigrationValidate([]Migration{})
|
|
if len(errs) != 0 {
|
|
t.Errorf("expected no errors for empty slice, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("version duplicada reporta error", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
|
|
makeMig(1, "create_users_dup", "CREATE TABLE users2 (id TEXT PRIMARY KEY);"),
|
|
makeMig(2, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected error for duplicate version, got none")
|
|
}
|
|
found := false
|
|
for _, e := range errs {
|
|
if containsStr(e, "duplicate") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected 'duplicate' in errors, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("hueco en versiones reporta version faltante", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
|
|
makeMig(3, "create_roles", "CREATE TABLE roles (id TEXT PRIMARY KEY);"),
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected error for gap in versions, got none")
|
|
}
|
|
found := false
|
|
for _, e := range errs {
|
|
if containsStr(e, "gap") || containsStr(e, "missing 2") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected gap error in errors, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("up_sql vacio reporta error", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
{Version: 1, Name: "create_users", UpSQL: "", DownSQL: "DROP TABLE users;"},
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected error for empty up_sql, got none")
|
|
}
|
|
found := false
|
|
for _, e := range errs {
|
|
if containsStr(e, "empty up_sql") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected 'empty up_sql' in errors, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("nombre vacio reporta error", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
{Version: 1, Name: "", UpSQL: "CREATE TABLE users (id TEXT PRIMARY KEY);"},
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected error for empty name, got none")
|
|
}
|
|
found := false
|
|
for _, e := range errs {
|
|
if containsStr(e, "empty name") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected 'empty name' in errors, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("versiones que no empiezan en 1 reportan error", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
makeMig(2, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
|
|
makeMig(3, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
if len(errs) == 0 {
|
|
t.Fatal("expected error for versions not starting at 1, got none")
|
|
}
|
|
found := false
|
|
for _, e := range errs {
|
|
if containsStr(e, "start at 1") {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("expected 'start at 1' error, got: %v", errs)
|
|
}
|
|
})
|
|
|
|
t.Run("multiple errores se reportan todos", func(t *testing.T) {
|
|
migrations := []Migration{
|
|
{Version: 2, Name: "", UpSQL: ""},
|
|
{Version: 4, Name: "something", UpSQL: "CREATE TABLE x (id TEXT);"},
|
|
}
|
|
errs := MigrationValidate(migrations)
|
|
// Expect: not starting at 1, gap between 2 and 4, empty name for v2, empty up_sql for v2
|
|
if len(errs) < 3 {
|
|
t.Errorf("expected at least 3 errors, got %d: %v", len(errs), errs)
|
|
}
|
|
})
|
|
}
|
|
|
|
func containsStr(s, sub string) bool {
|
|
return len(s) >= len(sub) && (s == sub || len(sub) == 0 ||
|
|
func() bool {
|
|
for i := 0; i <= len(s)-len(sub); i++ {
|
|
if s[i:i+len(sub)] == sub {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}())
|
|
}
|