package database import ( "context" "os" "strings" "testing" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5/pgxpool" ) // Integration testing uses a disposable schema on an explicitly provided test database. func TestMigrationIntegration(t *testing.T) { dsn := os.Getenv("VEYRA_TEST_DATABASE_URL") if dsn == "" { t.Skip("set VEYRA_TEST_DATABASE_URL to run PostgreSQL integration tests") } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() admin, err := pgxpool.New(ctx, dsn) if err != nil { t.Fatal(err) } defer admin.Close() schema := "veyra_test_" + strings.ReplaceAll(uuid.NewString(), "-", "") if _, err := admin.Exec(ctx, "CREATE SCHEMA "+schema); err != nil { t.Fatal(err) } defer func() { if _, err := admin.Exec(context.Background(), "DROP SCHEMA "+schema+" CASCADE"); err != nil { t.Error(err) } }() cfg, err := pgxpool.ParseConfig(dsn) if err != nil { t.Fatal(err) } cfg.ConnConfig.RuntimeParams["search_path"] = schema pool, err := pgxpool.NewWithConfig(ctx, cfg) if err != nil { t.Fatal(err) } defer pool.Close() for i := 0; i < 2; i++ { if err := Migrate(ctx, pool); err != nil { t.Fatal(err) } } var count int if err := pool.QueryRow(ctx, "SELECT count(*) FROM veyra_schema_migration").Scan(&count); err != nil || count != 2 { t.Fatalf("migration ledger count=%d err=%v", count, err) } if _, err := pool.Exec(ctx, "INSERT INTO veyra_user(id,display_name) VALUES($1,$2)", uuid.NewString(), "Test"); err != nil { t.Fatal(err) } if _, err := pool.Exec(ctx, "INSERT INTO veyra_user(id,display_name) VALUES('invalid','Test')"); err == nil { t.Fatal("invalid UUID was accepted") } if _, err := pool.Exec(ctx, "UPDATE veyra_schema_migration SET checksum='changed'"); err != nil { t.Fatal(err) } if err := Migrate(ctx, pool); err == nil { t.Fatal("modified migration checksum was accepted") } }