The same rewrite as d191a282, for four packages whose comments carried the
most history: tuning stories, quotes from the operator, single-install
measurements in library_scale.go, and 'used to' accounts of bugs. The
constraint each story protected stays as a present-tense reason (why tier 3
is reduced not removed, why the randomised arms take an order seed, why the
seed artist is eligible in Songs-like). Comments naming system_cron.go and
config.RecommendationConfig, neither of which holds what they claimed, now
name the scheduler and internal/recsettings.
Checked: non-comment tokens identical to HEAD; gofmt clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
105 lines
3.3 KiB
Go
105 lines
3.3 KiB
Go
package playlists
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
func mixTrack(b byte, kind string) discoverTrack {
|
|
return discoverTrack{ID: pgtype.UUID{Bytes: [16]byte{b}, Valid: true}, PickKind: kind}
|
|
}
|
|
|
|
func idsOf(pool []discoverTrack) []byte {
|
|
out := make([]byte, len(pool))
|
|
for i, t := range pool {
|
|
out[i] = t.ID.Bytes[0]
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestRotateForDay_RotatesWithinTierBlocks(t *testing.T) {
|
|
// Tiered pools arrive tier-ordered; rotation must vary the daily
|
|
// slice WITHIN each tier without hoisting tier-3 filler above
|
|
// tier-1's exact fits.
|
|
pool := []discoverTrack{
|
|
mixTrack(1, pickKindTier1), mixTrack(2, pickKindTier1), mixTrack(3, pickKindTier1),
|
|
mixTrack(4, pickKindTier2), mixTrack(5, pickKindTier2),
|
|
mixTrack(6, pickKindTier3),
|
|
}
|
|
u := pgtype.UUID{Bytes: [16]byte{42}, Valid: true}
|
|
got := rotateForDay(pool, u, "2026-07-03")
|
|
if len(got) != len(pool) {
|
|
t.Fatalf("len = %d, want %d", len(got), len(pool))
|
|
}
|
|
wantKinds := []string{
|
|
pickKindTier1, pickKindTier1, pickKindTier1,
|
|
pickKindTier2, pickKindTier2, pickKindTier3,
|
|
}
|
|
for i, k := range wantKinds {
|
|
if got[i].PickKind != k {
|
|
t.Fatalf("pos %d kind = %q, want %q (tier order broken: %v)",
|
|
i, got[i].PickKind, k, idsOf(got))
|
|
}
|
|
}
|
|
// Each block is a rotation of its input: contiguity check for the
|
|
// 3-element tier-1 block (successor relation preserved cyclically).
|
|
wantOrder := map[byte]byte{1: 2, 2: 3, 3: 1}
|
|
for i := 0; i < 2; i++ {
|
|
if got[i+1].ID.Bytes[0] != wantOrder[got[i].ID.Bytes[0]] {
|
|
t.Errorf("tier1 block is not a rotation: %v", idsOf(got[:3]))
|
|
break
|
|
}
|
|
}
|
|
// Determinism within a day.
|
|
again := rotateForDay(pool, u, "2026-07-03")
|
|
for i := range got {
|
|
if got[i].ID != again[i].ID {
|
|
t.Fatal("rotation must be deterministic for the same (user, day)")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRotateForDay_UntieredPoolIsWholeRotation(t *testing.T) {
|
|
// Untiered pools (all PickKind "") are one block — the original
|
|
// whole-pool rotation semantics.
|
|
pool := []discoverTrack{mixTrack(1, ""), mixTrack(2, ""), mixTrack(3, ""), mixTrack(4, "")}
|
|
got := rotateForDay(pool, pgtype.UUID{Bytes: [16]byte{7}, Valid: true}, "2026-07-03")
|
|
if len(got) != 4 {
|
|
t.Fatalf("len = %d, want 4", len(got))
|
|
}
|
|
wantNext := map[byte]byte{1: 2, 2: 3, 3: 4, 4: 1}
|
|
for i := 0; i < 3; i++ {
|
|
cur, next := got[i].ID.Bytes[0], got[i+1].ID.Bytes[0]
|
|
if wantNext[cur] != next {
|
|
t.Errorf("not a rotation of the input: %v", idsOf(got))
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFinishMix_PropagatesPickKind(t *testing.T) {
|
|
// Tier stamps ride discoverTrack through diversify/truncate into
|
|
// the rankedCandidates that insertSystemPlaylist persists.
|
|
pool := []discoverTrack{mixTrack(1, pickKindTier1), mixTrack(2, pickKindTier2)}
|
|
got := finishMix(pool, false)
|
|
if len(got) != 2 {
|
|
t.Fatalf("len = %d, want 2", len(got))
|
|
}
|
|
if got[0].PickKind != pickKindTier1 || got[1].PickKind != pickKindTier2 {
|
|
t.Errorf("pick kinds = %q, %q — want tier1, tier2", got[0].PickKind, got[1].PickKind)
|
|
}
|
|
}
|
|
|
|
func TestPickKindForMixTier(t *testing.T) {
|
|
cases := []struct {
|
|
tier int32
|
|
want string
|
|
}{{1, pickKindTier1}, {2, pickKindTier2}, {3, pickKindTier3}, {9, pickKindTier3}}
|
|
for _, c := range cases {
|
|
if got := pickKindForMixTier(c.tier); got != c.want {
|
|
t.Errorf("pickKindForMixTier(%d) = %q, want %q", c.tier, got, c.want)
|
|
}
|
|
}
|
|
}
|