Files
minstrel/internal/recommendation/home_integration_test.go
T
bvandeusen 302a15a209 test(recommendation): fix mislabeled compile-time assert in home tests
Replaces var _ = func() pgtype.UUID { ... } with var _ map[pgtype.UUID]struct{}
which actually verifies map-key comparability, matching the comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:54:40 -04:00

131 lines
4.4 KiB
Go

package recommendation
import (
"context"
"testing"
"time"
"github.com/jackc/pgx/v5/pgtype"
)
func TestHomeData_NewUser_AllSectionsEmpty(t *testing.T) {
pool := newPool(t)
user := seedUser(t, pool, "home-newuser")
got, err := HomeData(context.Background(), pool, user.ID)
if err != nil {
t.Fatalf("HomeData: %v", err)
}
if got == nil {
t.Fatal("HomeData returned nil payload")
}
if len(got.RecentlyAddedAlbums) != 0 {
t.Errorf("RecentlyAddedAlbums len = %d, want 0", len(got.RecentlyAddedAlbums))
}
if len(got.RediscoverAlbums) != 0 {
t.Errorf("RediscoverAlbums len = %d, want 0", len(got.RediscoverAlbums))
}
if len(got.RediscoverArtists) != 0 {
t.Errorf("RediscoverArtists len = %d, want 0", len(got.RediscoverArtists))
}
if len(got.MostPlayedTracks) != 0 {
t.Errorf("MostPlayedTracks len = %d, want 0", len(got.MostPlayedTracks))
}
if len(got.LastPlayedArtists) != 0 {
t.Errorf("LastPlayedArtists len = %d, want 0", len(got.LastPlayedArtists))
}
}
// Compile-time assert that pgtype.UUID is a valid map key — used by the
// service when de-duping rediscover fallback rows. If this changes, the
// service needs to switch to keying by string.
var _ map[pgtype.UUID]struct{}
func TestHomeData_RecentlyAdded_OrderedByCreatedAt(t *testing.T) {
pool := newPool(t)
user := seedUser(t, pool, "home-recents")
a1 := seedArtist(t, pool, "ArtistA", "")
// Three albums, oldest first — created_at increases naturally with each insert.
al1 := seedAlbumForArtist(t, pool, a1.ID, "Older")
al2 := seedAlbumForArtist(t, pool, a1.ID, "Middle")
al3 := seedAlbumForArtist(t, pool, a1.ID, "Newest")
got, err := HomeData(context.Background(), pool, user.ID)
if err != nil {
t.Fatalf("HomeData: %v", err)
}
if len(got.RecentlyAddedAlbums) != 3 {
t.Fatalf("len = %d, want 3", len(got.RecentlyAddedAlbums))
}
if got.RecentlyAddedAlbums[0].Album.ID != al3.ID ||
got.RecentlyAddedAlbums[1].Album.ID != al2.ID ||
got.RecentlyAddedAlbums[2].Album.ID != al1.ID {
t.Errorf("order = [%s, %s, %s], want newest→oldest",
got.RecentlyAddedAlbums[0].Album.Title,
got.RecentlyAddedAlbums[1].Album.Title,
got.RecentlyAddedAlbums[2].Album.Title)
}
}
func TestHomeData_MostPlayed_RanksByCount_HonorsQuarantine(t *testing.T) {
pool := newPool(t)
user := seedUser(t, pool, "home-mostplayed")
artist := seedArtist(t, pool, "ArtistB", "")
album := seedAlbumForArtist(t, pool, artist.ID, "AlbumB")
tHigh := seedTrackOnAlbum(t, pool, album.ID, artist.ID, "High")
tLow := seedTrackOnAlbum(t, pool, album.ID, artist.ID, "Low")
tQuar := seedTrackOnAlbum(t, pool, album.ID, artist.ID, "Quarantined")
now := time.Now()
for i := 0; i < 3; i++ {
insertPlayEvent(t, pool, user.ID, tHigh.ID, now.Add(-time.Duration(i)*time.Minute))
}
insertPlayEvent(t, pool, user.ID, tLow.ID, now)
for i := 0; i < 5; i++ {
insertPlayEvent(t, pool, user.ID, tQuar.ID, now)
}
if _, err := pool.Exec(context.Background(),
`INSERT INTO lidarr_quarantine (user_id, track_id, reason) VALUES ($1, $2, 'bad_rip')`,
user.ID, tQuar.ID,
); err != nil {
t.Fatalf("insert quarantine: %v", err)
}
got, err := HomeData(context.Background(), pool, user.ID)
if err != nil {
t.Fatalf("HomeData: %v", err)
}
if len(got.MostPlayedTracks) != 2 {
t.Fatalf("len = %d, want 2 (quarantined excluded)", len(got.MostPlayedTracks))
}
if got.MostPlayedTracks[0].Track.ID != tHigh.ID || got.MostPlayedTracks[1].Track.ID != tLow.ID {
t.Errorf("ranking wrong: got [%s, %s], want [High, Low]",
got.MostPlayedTracks[0].Track.Title,
got.MostPlayedTracks[1].Track.Title)
}
}
func TestHomeData_RediscoverAlbums_FallbackWhenSparse(t *testing.T) {
pool := newPool(t)
user := seedUser(t, pool, "home-rediscover")
artist := seedArtist(t, pool, "ArtistC", "")
// Like one album NOW (won't satisfy >30d eligibility) — should still
// surface via fallback because primary returns 0 rows.
al := seedAlbumForArtist(t, pool, artist.ID, "RecentLike")
if _, err := pool.Exec(context.Background(),
`INSERT INTO general_likes_albums (user_id, album_id, liked_at) VALUES ($1, $2, now())`,
user.ID, al.ID,
); err != nil {
t.Fatalf("insert like: %v", err)
}
got, err := HomeData(context.Background(), pool, user.ID)
if err != nil {
t.Fatalf("HomeData: %v", err)
}
if len(got.RediscoverAlbums) != 1 {
t.Fatalf("len = %d, want 1 (from fallback)", len(got.RediscoverAlbums))
}
if got.RediscoverAlbums[0].Album.ID != al.ID {
t.Errorf("got %s, want %s", got.RediscoverAlbums[0].Album.Title, al.Title)
}
}