Files
MusicServer/internal/backend/queue_integration_test.go
T
Sansan a705d7b476 Store each client's play queue in the database
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.
2026-10-07 20:03:03 +02:00

90 lines
2.8 KiB
Go

//go:build integration
package backend
import (
"context"
"testing"
"music-server/internal/db/repository"
"music-server/internal/testutil/pgtest"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestQueueWithPostgres checks the queue queries against a real database.
func TestQueueWithPostgres(t *testing.T) {
ctx := context.Background()
database := pgtest.NewMigratedDatabase(t)
q := repository.New(database.Pool)
s := NewSyncService(q, copyTestMusic(t), nil)
require.True(t, s.Start(false))
s.Wait()
m := NewMusicService(q, t.TempDir())
queueID, err := m.ResolveQueue(ctx, "")
require.NoError(t, err)
require.NoError(t, m.AddLatestToQueue(ctx, queueID), "nothing fetched yet")
var paths []string
for range 3 {
path, err := m.RandomSong(ctx, queueID)
require.NoError(t, err)
require.NoError(t, m.AddLatestToQueue(ctx, queueID))
paths = append(paths, path)
}
require.NoError(t, m.AddLatestToQueue(ctx, queueID), "a fetched song is only added once")
played, err := m.PlayedSongs(ctx, queueID)
require.NoError(t, err)
require.Len(t, played, 3)
for i, song := range played {
assert.Equal(t, i, song.SongNo)
assert.Equal(t, i == 2, song.CurrentlyPlaying, "the last added song is current")
assert.NotEmpty(t, song.Soundtrack)
assert.NotEqual(t, uuid.Nil, song.SoundtrackUuid)
}
path, err := m.PreviousSong(ctx, queueID)
require.NoError(t, err)
assert.Equal(t, paths[1], path)
require.NoError(t, m.AddLatestPlayed(ctx, queueID))
info, err := m.SongInfo(ctx, queueID)
require.NoError(t, err)
assert.Equal(t, 1, info.SongNo)
assert.Equal(t, int32(1), info.SongPlayed)
// A new service, as after a restart, sees the same queue.
restarted := NewMusicService(q, t.TempDir())
same, err := restarted.ResolveQueue(ctx, queueID.String())
require.NoError(t, err)
assert.Equal(t, queueID, same)
newID, err := m.NewQueue(ctx, queueID)
require.NoError(t, err)
assert.NotEqual(t, queueID, newID)
resolved, err := m.ResolveQueue(ctx, queueID.String())
require.NoError(t, err)
assert.NotEqual(t, queueID, resolved, "an ended queue is not used again")
other, err := m.ResolveQueue(ctx, "")
require.NoError(t, err)
require.NoError(t, m.EndAllQueues(ctx))
var active int
require.NoError(t, database.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM queue WHERE ended_at IS NULL").Scan(&active))
assert.Zero(t, active)
_, err = m.ResolveQueue(ctx, other.String())
require.NoError(t, err)
// Ended queues keep their songs, also after the songs are removed from the library.
require.NoError(t, q.ClearSongs(ctx))
played, err = m.PlayedSongs(ctx, queueID)
require.NoError(t, err)
require.Len(t, played, 3)
assert.NotEmpty(t, played[0].Song)
_, err = m.Song(ctx, queueID, 0)
assert.ErrorIs(t, err, ErrNotFound)
}