The queue lived in memory and was shared by all clients, so a restart or a finished sync cleared it. Every client now has its own queue in the new queue and queue_song tables (added to the undeployed migration 000006). The music endpoints return the queue ID in the X-Queue-Id header and expect it back. A request without an ID, or with an ended one, starts a new queue. /music/reset ends the queue and returns a new ID, and a sync ends all running queues. Ended queues are kept as history. The deprecated legacy routes without the /api/v1 prefix are removed.
89 lines
3.0 KiB
Go
89 lines
3.0 KiB
Go
//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"])
|
|
}
|