package backend import ( "context" "path/filepath" "testing" "time" "music-server/internal/db/repository" "music-server/internal/metrics" "music-server/internal/testutil/fakedb" "github.com/google/uuid" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/testutil" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func requested(mode PickMode) float64 { return testutil.ToFloat64(metrics.SongsRequested.WithLabelValues(string(mode))) } func played(mode PickMode) float64 { return testutil.ToFloat64(metrics.SongsPlayed.WithLabelValues(string(mode))) } func syncCount(syncType string) uint64 { m := &dto.Metric{} _ = metrics.SyncDuration.WithLabelValues(syncType).(prometheus.Metric).Write(m) return m.GetHistogram().GetSampleCount() } func TestSongMetricsByPickMode(t *testing.T) { store := fakedb.New() addSoundtrackWithSongs(t, store, t.TempDir(), "Game A", 0, "Song 1") m := NewMusicService(store, t.TempDir()) ctx := context.Background() q := newQueue(t, m) before := map[PickMode][2]float64{} for _, mode := range []PickMode{PickRandom, PickLow, PickClassic} { before[mode] = [2]float64{requested(mode), played(mode)} } pick := map[PickMode]func(context.Context, uuid.UUID) (string, error){ PickRandom: func(ctx context.Context, id uuid.UUID) (string, error) { return m.RandomSong(ctx, id) }, PickLow: func(ctx context.Context, id uuid.UUID) (string, error) { return m.RandomSongLowChance(ctx, id) }, PickClassic: func(ctx context.Context, id uuid.UUID) (string, error) { return m.RandomSongClassic(ctx, id) }, } for songNo, mode := range []PickMode{PickRandom, PickLow, PickClassic} { _, err := pick[mode](ctx, q) require.NoError(t, err) require.NoError(t, m.AddLatestToQueue(ctx, q)) require.NoError(t, m.SetPlayed(ctx, q, songNo)) } // Fetched but never added: requested, not played. _, err := m.RandomSong(ctx, q) require.NoError(t, err) require.NoError(t, m.AddLatestPlayed(ctx, q), "the current song is the classic one") assert.Equal(t, before[PickRandom][0]+2, requested(PickRandom)) assert.Equal(t, before[PickLow][0]+1, requested(PickLow)) assert.Equal(t, before[PickClassic][0]+1, requested(PickClassic)) assert.Equal(t, before[PickRandom][1]+1, played(PickRandom)) assert.Equal(t, before[PickLow][1]+1, played(PickLow)) assert.Equal(t, before[PickClassic][1]+2, played(PickClassic)) } func TestSongsPlayedIgnoresSongsOutsideTheQueue(t *testing.T) { m := NewMusicService(fakedb.New(), t.TempDir()) before := played(PickRandom) require.NoError(t, m.SetPlayed(context.Background(), newQueue(t, m), 3)) assert.Equal(t, before, played(PickRandom)) } func TestSyncMetrics(t *testing.T) { f := newSyncFixture(t) f.write("Game A/01.mp3", "a1") f.write("Game A/02.mp3", "a2") f.write("Game B/01.mp3", "b1") changesBefore, fullBefore := syncCount("changes"), syncCount("full") metrics.SyncLastSuccess.Set(0) f.run(false) assert.Equal(t, changesBefore+1, syncCount("changes")) assert.Equal(t, 2.0, testutil.ToFloat64(metrics.LibrarySoundtracks)) assert.Equal(t, 3.0, testutil.ToFloat64(metrics.LibrarySongs)) assert.InDelta(t, float64(time.Now().Unix()), testutil.ToFloat64(metrics.SyncLastSuccess), 5) f.store.AddTestSong(repository.Song{SoundtrackID: 999, SongName: "gone", Path: "/does/not/exist.mp3"}) f.run(true) assert.Equal(t, fullBefore+1, syncCount("full")) assert.Equal(t, 1.0, testutil.ToFloat64(metrics.BrokenSongs)) assert.Equal(t, 3.0, testutil.ToFloat64(metrics.LibrarySongs), "the broken song is removed") } func TestSyncWithErrorsIsNotALastSuccess(t *testing.T) { metrics.SyncLastSuccess.Set(0) s := NewSyncService(fakedb.New(), filepath.Join(t.TempDir(), "missing"), nil) require.True(t, s.Start(false)) s.Wait() require.NotEmpty(t, s.Result().CatchedErrors) assert.Zero(t, testutil.ToFloat64(metrics.SyncLastSuccess)) }