Files
MusicServer/internal/backend/sync_integration_test.go
T
Sansan 2c3e97190d
CI / docker (push) Successful in 46s
CI / integration (push) Successful in 23s
CI / test (push) Successful in 30s
CI / publish (push) Skipped
Prefer UUIDs in the API (Phase 3 of the UUID migration)
- New /api/v1 endpoints: GET /soundtracks, /soundtracks/{uuid},
  /songs/{uuid} and /songs/{uuid}/file (streams the song, Range support,
  locked during sync)
- UUIDs added to every response with soundtracks or songs: queue info and
  list (SoundtrackUuid, SongUuid) and statistics (soundtrack_uuid,
  song_uuid). Existing fields stay for backward compatibility
- Play counts and broken-song removal use song UUIDs instead of
  soundtrack ID + song name, so songs with the same name are counted
  separately
- Statistics return an empty song list for soundtracks without songs
  instead of a single empty entry
- The legacy .id file cleanup stays until Phase 4 (#31)

Closes #29
2026-10-06 15:54:41 +02:00

116 lines
3.7 KiB
Go

//go:build integration
package backend
import (
"context"
"os"
"path/filepath"
"testing"
"music-server/internal/db/repository"
"music-server/internal/logging"
"music-server/internal/testutil/pgtest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMain(m *testing.M) {
logging.Init("fatal", false)
os.Exit(pgtest.Main(m))
}
// copyTestMusic copies the repository's testMusic folder so tests can change it.
func copyTestMusic(t *testing.T) string {
t.Helper()
dst := t.TempDir()
require.NoError(t, os.CopyFS(dst, os.DirFS(filepath.Join("..", "..", "testMusic"))))
return dst
}
// TestSyncWithPostgres runs syncs against a real database to check the SQL queries.
func TestSyncWithPostgres(t *testing.T) {
ctx := context.Background()
q := repository.New(pgtest.NewMigratedDatabase(t).Pool)
musicDir := copyTestMusic(t)
s := NewSyncService(q, musicDir, nil)
run := func(full bool) SyncResponse {
require.True(t, s.Start(full))
s.Wait()
result := s.Result()
require.Empty(t, result.CatchedErrors)
return result
}
result := run(false)
assert.Equal(t, []string{"A Bird Story", "Clair Obscur - Expedition 33", "Metal Gear Rising - Revengeance"}, result.SoundtracksAdded)
soundtracks, err := q.FindAllSoundtracks(ctx)
require.NoError(t, err)
require.Len(t, soundtracks, 3)
bird := soundtracks[0]
assert.NotEmpty(t, bird.Fingerprint)
songs, err := q.FindSongsFromSoundtrack(ctx, bird.ID)
require.NoError(t, err)
assert.Len(t, songs, 6)
// Play a song, then rename one of its files and, in a later sync, the soundtrack folder.
// (Renaming both at once changes the content hash, so the folder would count as new.)
for _, song := range songs {
if song.SongName == "2. Yesterday" {
require.NoError(t, q.AddSongPlayedByUUID(ctx, song.Uuid))
}
}
birdDir := filepath.Join(musicDir, "A Bird Story")
require.NoError(t, os.Rename(filepath.Join(birdDir, "2. Yesterday.mp3"), filepath.Join(birdDir, "02 Yesterday.mp3")))
result = run(false)
assert.Equal(t, []string{"A Bird Story"}, result.SoundtracksChangedContent)
require.NoError(t, os.Rename(birdDir, filepath.Join(musicDir, "A Bird Story (2014)")))
result = run(false)
assert.Equal(t, map[string]string{"A Bird Story": "A Bird Story (2014)"}, result.SoundtracksChangedTitle)
assert.Empty(t, result.SoundtracksAdded)
assert.Empty(t, result.SoundtracksRemoved)
renamed, err := q.GetSoundtrackById(ctx, bird.ID)
require.NoError(t, err)
assert.Equal(t, "A Bird Story (2014)", renamed.SoundtrackName)
songs, err = q.FindSongsFromSoundtrack(ctx, bird.ID)
require.NoError(t, err)
require.Len(t, songs, 6)
var yesterday repository.Song
for _, song := range songs {
assert.Contains(t, song.Path, "/A Bird Story (2014)/")
if song.SongName == "02 Yesterday" {
yesterday = song
}
}
assert.Equal(t, int32(1), yesterday.TimesPlayed, "play count survives renames")
// Nothing changed: the fingerprint lets the sync skip everything.
result = run(false)
assert.Empty(t, result.SoundtracksChangedContent)
assert.Empty(t, result.SoundtracksChangedTitle)
// Remove a soundtrack and bring it back.
mgr := filepath.Join(musicDir, "Metal Gear Rising - Revengeance")
backup := filepath.Join(t.TempDir(), "mgr")
require.NoError(t, os.Rename(mgr, backup))
result = run(false)
assert.Equal(t, []string{"Metal Gear Rising - Revengeance"}, result.SoundtracksRemoved)
active, err := q.FindAllSoundtracks(ctx)
require.NoError(t, err)
assert.Len(t, active, 2)
require.NoError(t, os.Rename(backup, mgr))
result = run(true)
assert.Equal(t, []string{"Metal Gear Rising - Revengeance"}, result.SoundtracksReAdded)
// Classic random picks only songs of active soundtracks.
row, err := q.GetRandomActiveSong(ctx)
require.NoError(t, err)
assert.NotEmpty(t, row.SoundtrackPath)
}