package db import ( "database/sql" "fmt" "testing" _ "github.com/lib/pq" "github.com/stretchr/testify/require" ) // TestMigrationsStepByStep tests applying migrations incrementally // Then adding data manually, then completing migrations func TestMigrationsStepByStep(t *testing.T) { host := "localhost" port := "5432" user := "postgres" password := "postgres" // Use a unique database name for this test dbname := "music_server_migration_test" // Clean up: drop database if it exists cleanupDB(t, host, port, user, password, dbname) defer cleanupDB(t, host, port, user, password, dbname) // Create the database createTestDB(t, host, port, user, password, dbname) // Step 1: Apply first 4 migrations (before soundtrack rename) // This creates: game, song, vgmq, song_list tables // And sessions table with indexes t.Run("ApplyFirst4Migrations", func(t *testing.T) { applyMigrations(t, host, port, user, password, dbname, 4) }) // Step 2: Add data manually to game and song tables t.Run("AddManualData", func(t *testing.T) { connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) db, err := sql.Open("postgres", connStr) require.NoError(t, err) defer db.Close() // Insert 5 games manually for i := 1; i <= 5; i++ { gameName := fmt.Sprintf("Manual Game %d", i) path := fmt.Sprintf("/manual/path/game%d", i) hash := fmt.Sprintf("hash-%d", i) _, err := db.Exec(`INSERT INTO game (game_name, path, hash, added) VALUES ($1, $2, $3, NOW())`, gameName, path, hash) require.NoError(t, err, "Failed to insert game %d", i) } // Insert songs for each game songs := []struct { gameID int name string path string }{ {1, "Song A", "/path/a.mp3"}, {1, "Song B", "/path/b.mp3"}, {2, "Song C", "/path/c.mp3"}, {2, "Song D", "/path/d.mp3"}, {3, "Song E", "/path/e.mp3"}, {4, "Song F", "/path/f.mp3"}, {4, "Song G", "/path/g.mp3"}, {4, "Song H", "/path/h.mp3"}, {5, "Song I", "/path/i.mp3"}, } for _, s := range songs { _, err := db.Exec(`INSERT INTO song (game_id, song_name, path) VALUES ($1, $2, $3)`, s.gameID, s.name, s.path) require.NoError(t, err, "Failed to insert song %s", s.name) } // Verify data was inserted var gameCount int err = db.QueryRow("SELECT COUNT(*) FROM game").Scan(&gameCount) require.NoError(t, err) require.Equal(t, 5, gameCount, "Expected 5 games") var songCount int err = db.QueryRow("SELECT COUNT(*) FROM song").Scan(&songCount) require.NoError(t, err) require.Equal(t, 8, songCount, "Expected 8 songs") t.Log("✓ Manually inserted 5 games with 8 songs") }) // Step 3: Apply migration 5 (rename game→soundtrack) t.Run("ApplyMigration5", func(t *testing.T) { // Apply the remaining migrations (just migration 5) applyMigrations(t, host, port, user, password, dbname, 1) // Verify tables were renamed connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) db, err := sql.Open("postgres", connStr) require.NoError(t, err) defer db.Close() // Check that soundtrack table exists var soundtrackCount int err = db.QueryRow("SELECT COUNT(*) FROM soundtrack").Scan(&soundtrackCount) require.NoError(t, err) require.Equal(t, 5, soundtrackCount, "Expected 5 soundtracks after migration") // Check that game table no longer exists _, err = db.Exec("SELECT 1 FROM game LIMIT 1") require.Error(t, err, "game table should not exist after migration") // Check that song table has soundtrack_id column var songCount int err = db.QueryRow("SELECT COUNT(*) FROM song").Scan(&songCount) require.NoError(t, err) require.Equal(t, 8, songCount, "Expected 8 songs after migration") // Verify data integrity: soundtrack_name values rows, err := db.Query("SELECT soundtrack_name FROM soundtrack ORDER BY id") require.NoError(t, err) defer rows.Close() expectedNames := []string{"Manual Game 1", "Manual Game 2", "Manual Game 3", "Manual Game 4", "Manual Game 5"} actualNames := make([]string, 0) for rows.Next() { var name string err := rows.Scan(&name) require.NoError(t, err) actualNames = append(actualNames, name) } require.Equal(t, expectedNames, actualNames, "Soundtrack names should match original game names") t.Log("✓ Migration 5 applied successfully, data preserved") }) } // cleanupDB drops the test database func cleanupDB(t *testing.T, host, port, user, password, dbname string) { connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=postgres sslmode=disable", host, port, user, password) db, err := sql.Open("postgres", connStr) if err != nil { t.Logf("Warning: could not connect to cleanup DB: %v", err) return } defer db.Close() // Check if database exists before dropping var exists int err = db.QueryRow("SELECT 1 FROM pg_database WHERE datname = $1", dbname).Scan(&exists) if err != nil && err != sql.ErrNoRows { t.Logf("Warning: could not check if DB exists: %v", err) return } if exists == 1 { _, err = db.Exec("DROP DATABASE " + dbname + " WITH (FORCE)") if err != nil { t.Logf("Warning: could not drop DB: %v", err) } } } // createTestDB creates a fresh test database func createTestDB(t *testing.T, host, port, user, password, dbname string) { connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=postgres sslmode=disable", host, port, user, password) db, err := sql.Open("postgres", connStr) require.NoError(t, err) defer db.Close() // Drop if exists cleanupDB(t, host, port, user, password, dbname) // Create database _, err = db.Exec("CREATE DATABASE " + dbname) require.NoError(t, err) // Enable UUID extension if needed connStrDB := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) db2, err := sql.Open("postgres", connStrDB) require.NoError(t, err) defer db2.Close() _, err = db2.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"") if err != nil { t.Logf("Note: uuid-ossp extension may not be available: %v", err) } } // applyMigrations applies n migrations to the database func applyMigrations(t *testing.T, host, port, user, password, dbname string, steps int) { connStr := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", host, port, user, password, dbname) db, err := sql.Open("postgres", connStr) require.NoError(t, err) defer db.Close() // This is a simplified version - in a real test you'd use the migrate library // For now, we'll just log that migrations should be applied t.Logf("Note: To fully test migrations, configure test DB and use migrate library") t.Logf("Would apply %d migration(s) to database: %s", steps, dbname) }