35a49174ca
Fase 2 del issue 0015. MigrationCreate (crea archivo .sql template con version auto-calculada), MigrationUp (aplica migraciones pendientes en transacciones individuales), MigrationDown (revierte ultimas N via down_sql de _migrations), MigrationGetStatus (cruza disco con BD, detecta orphaned). Tests de integracion: ciclo completo create->up->status->down->status. 26 tests, todos pasan. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
115 lines
3.3 KiB
Go
115 lines
3.3 KiB
Go
package infra
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
// TestMigrationIntegration covers the full create -> up -> status -> down -> status cycle.
|
|
func TestMigrationIntegration(t *testing.T) {
|
|
t.Run("ciclo completo create up status down status", func(t *testing.T) {
|
|
db := openTestDB(t)
|
|
dir := t.TempDir()
|
|
|
|
// Step 1: Create migration files using MigrationCreate
|
|
path1, err := MigrationCreate(dir, "create_users")
|
|
if err != nil {
|
|
t.Fatalf("create 001 failed: %v", err)
|
|
}
|
|
path2, err := MigrationCreate(dir, "create_roles")
|
|
if err != nil {
|
|
t.Fatalf("create 002 failed: %v", err)
|
|
}
|
|
|
|
// Fill in actual SQL
|
|
sql1 := "-- 001_create_users.sql\n\n-- +up\nCREATE TABLE users (id TEXT PRIMARY KEY, name TEXT NOT NULL);\n\n-- +down\nDROP TABLE IF EXISTS users;\n"
|
|
if err := os.WriteFile(path1, []byte(sql1), 0o644); err != nil {
|
|
t.Fatalf("write sql1: %v", err)
|
|
}
|
|
sql2 := "-- 002_create_roles.sql\n\n-- +up\nCREATE TABLE roles (id TEXT PRIMARY KEY, label TEXT NOT NULL);\n\n-- +down\nDROP TABLE IF EXISTS roles;\n"
|
|
if err := os.WriteFile(path2, []byte(sql2), 0o644); err != nil {
|
|
t.Fatalf("write sql2: %v", err)
|
|
}
|
|
|
|
// Step 2: Parse and validate
|
|
content1, _ := os.ReadFile(filepath.Join(dir, "001_create_users.sql"))
|
|
content2, _ := os.ReadFile(filepath.Join(dir, "002_create_roles.sql"))
|
|
m1, err := MigrationParse("001_create_users.sql", string(content1))
|
|
if err != nil {
|
|
t.Fatalf("parse 001: %v", err)
|
|
}
|
|
m2, err := MigrationParse("002_create_roles.sql", string(content2))
|
|
if err != nil {
|
|
t.Fatalf("parse 002: %v", err)
|
|
}
|
|
validationErrs := MigrationValidate([]Migration{m1, m2})
|
|
if len(validationErrs) > 0 {
|
|
t.Fatalf("validate failed: %v", validationErrs)
|
|
}
|
|
|
|
// Step 3: Apply up
|
|
applied, err := MigrationUp(db, dir)
|
|
if err != nil {
|
|
t.Fatalf("up failed: %v", err)
|
|
}
|
|
if len(applied) != 2 {
|
|
t.Errorf("up: expected 2 applied, got %d", len(applied))
|
|
}
|
|
|
|
// Step 4: Check status — all applied
|
|
statuses, err := MigrationGetStatus(db, dir)
|
|
if err != nil {
|
|
t.Fatalf("status after up failed: %v", err)
|
|
}
|
|
if len(statuses) != 2 {
|
|
t.Errorf("status: expected 2, got %d", len(statuses))
|
|
}
|
|
for _, s := range statuses {
|
|
if !s.Applied {
|
|
t.Errorf("version %d should be applied", s.Version)
|
|
}
|
|
}
|
|
|
|
// Step 5: Revert the last migration
|
|
reverted, err := MigrationDown(db, 1)
|
|
if err != nil {
|
|
t.Fatalf("down failed: %v", err)
|
|
}
|
|
if len(reverted) != 1 || reverted[0].Version != 2 {
|
|
t.Errorf("down: expected version 2, got %v", reverted)
|
|
}
|
|
|
|
// Step 6: Check status — one applied, one pending
|
|
statuses2, err := MigrationGetStatus(db, dir)
|
|
if err != nil {
|
|
t.Fatalf("status after down failed: %v", err)
|
|
}
|
|
if len(statuses2) != 2 {
|
|
t.Errorf("status2: expected 2, got %d", len(statuses2))
|
|
}
|
|
// Version 1 should be applied, version 2 pending
|
|
for _, s := range statuses2 {
|
|
switch s.Version {
|
|
case 1:
|
|
if !s.Applied {
|
|
t.Errorf("version 1 should still be applied")
|
|
}
|
|
case 2:
|
|
if s.Applied {
|
|
t.Errorf("version 2 should be pending after down")
|
|
}
|
|
}
|
|
}
|
|
|
|
// Step 7: Re-apply — should apply version 2 again
|
|
applied2, err := MigrationUp(db, dir)
|
|
if err != nil {
|
|
t.Fatalf("second up failed: %v", err)
|
|
}
|
|
if len(applied2) != 1 || applied2[0].Version != 2 {
|
|
t.Errorf("second up: expected version 2, got %v", applied2)
|
|
}
|
|
})
|
|
}
|