//go:build integration package db_test import ( "context" "fmt" "testing" "music-server/internal/db" "music-server/internal/testutil/pgtest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func openDatabase(t *testing.T, dbname string) *db.Database { t.Helper() database, err := db.NewDatabase(context.Background(), pgtest.ConnString(dbname)) require.NoError(t, err) t.Cleanup(database.Close) return database } func count(t *testing.T, database *db.Database, query string) int { t.Helper() var n int require.NoError(t, database.Pool.QueryRow(context.Background(), query).Scan(&n)) return n } // TestMigrationsKeepData applies the migrations step by step with data in between, // like a database that was created by an older version. func TestMigrationsKeepData(t *testing.T) { ctx := context.Background() database := openDatabase(t, pgtest.NewDatabase(t)) m, closeFn, err := database.NewMigrator() require.NoError(t, err) defer closeFn() require.NoError(t, m.Steps(4)) for i := 1; i <= 5; i++ { _, err := database.Pool.Exec(ctx, `INSERT INTO game (game_name, path, hash, added) VALUES ($1, $2, $3, NOW())`, fmt.Sprintf("Manual Game %d", i), fmt.Sprintf("/music/game%d/", i), fmt.Sprintf("hash-%d", i)) require.NoError(t, err) } for i, game := range []int{1, 1, 2, 3, 4, 4, 4, 5, 5} { _, err := database.Pool.Exec(ctx, `INSERT INTO song (game_id, song_name, path, hash) VALUES ($1, $2, $3, $4)`, game, fmt.Sprintf("Song %d", i), fmt.Sprintf("/music/game%d/%d.mp3", game, i), fmt.Sprintf("song-hash-%d", i)) require.NoError(t, err) } require.NoError(t, m.Up()) version, dirty, err := m.Version() require.NoError(t, err) assert.False(t, dirty) assert.Equal(t, uint(6), version) assert.Equal(t, 5, count(t, database, "SELECT COUNT(*) FROM soundtrack")) assert.Equal(t, 9, count(t, database, "SELECT COUNT(*) FROM song")) assert.Equal(t, 0, count(t, database, "SELECT COUNT(*) FROM song WHERE uuid IS NULL OR soundtrack_uuid IS NULL")) assert.Equal(t, 5, count(t, database, "SELECT COUNT(*) FROM soundtrack WHERE fingerprint = ''")) _, err = database.Pool.Exec(ctx, "SELECT 1 FROM game") assert.Error(t, err, "game table is renamed") } func TestFingerprintAndQueueMigrationDown(t *testing.T) { database := openDatabase(t, pgtest.NewDatabase(t)) m, closeFn, err := database.NewMigrator() require.NoError(t, err) defer closeFn() require.NoError(t, m.Up()) require.NoError(t, m.Steps(-1)) assert.Equal(t, 0, count(t, database, "SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'soundtrack' AND column_name = 'fingerprint'")) assert.Equal(t, 0, count(t, database, "SELECT COUNT(*) FROM information_schema.tables WHERE table_name IN ('queue', 'queue_song')")) require.NoError(t, m.Up()) } func TestRunMigrationsIsIdempotent(t *testing.T) { database := openDatabase(t, pgtest.NewDatabase(t)) require.NoError(t, database.RunMigrations()) require.NoError(t, database.RunMigrations()) assert.Equal(t, "up", database.Health(context.Background())["status"]) }