//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.) require.NoError(t, q.AddSongPlayed(ctx, repository.AddSongPlayedParams{SoundtrackID: bird.ID, SongName: "2. Yesterday"})) 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) }