package playlists_test import ( "context" "testing" "time" "github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgxpool" "git.fabledsword.com/bvandeusen/minstrel/internal/playlists" ) // countYouMightLike returns the persisted row counts for a user. func countYouMightLike(t *testing.T, pool *pgxpool.Pool, userID pgtype.UUID) (albums, artists int) { t.Helper() ctx := context.Background() if err := pool.QueryRow(ctx, `SELECT count(*) FROM you_might_like_albums WHERE user_id = $1`, userID, ).Scan(&albums); err != nil { t.Fatalf("count albums: %v", err) } if err := pool.QueryRow(ctx, `SELECT count(*) FROM you_might_like_artists WHERE user_id = $1`, userID, ).Scan(&artists); err != nil { t.Fatalf("count artists: %v", err) } return albums, artists } // TestYouMightLike_ColdStartGate: a thin profile (3 artists × 3 tracks = // 9 distinct tracks, below the 20-track / 5-artist gate) must produce no // You-might-like rows — the build ships nothing rather than near-random // tiles. func TestYouMightLike_ColdStartGate(t *testing.T) { pool := newPool(t) logger := discardLogger() u, _ := seedActiveLibrary(t, pool, "ymlcold", 3, 3) if err := playlists.BuildSystemPlaylists( context.Background(), pool, logger, u.ID, time.Now().UTC(), t.TempDir(), ); err != nil { t.Fatalf("build: %v", err) } albums, artists := countYouMightLike(t, pool, u.ID) if albums != 0 || artists != 0 { t.Errorf("cold-start user should have no you-might-like rows; got %d albums, %d artists", albums, artists) } } // TestYouMightLike_SufficientActivityPopulates: a rich profile (8 artists // × 4 tracks = 32 distinct tracks, clearing both gate bars) populates the // You-might-like tables in the daily build. func TestYouMightLike_SufficientActivityPopulates(t *testing.T) { pool := newPool(t) logger := discardLogger() u, _ := seedActiveLibrary(t, pool, "ymlrich", 8, 4) if err := playlists.BuildSystemPlaylists( context.Background(), pool, logger, u.ID, time.Now().UTC(), t.TempDir(), ); err != nil { t.Fatalf("build: %v", err) } albums, artists := countYouMightLike(t, pool, u.ID) if albums == 0 { t.Error("rich user should have you-might-like album rows; got 0") } if artists == 0 { t.Error("rich user should have you-might-like artist rows; got 0") } } // TestYouMightLike_AtomicReplace: a second build replaces rather than // appends, so counts stay bounded by the persisted depth. func TestYouMightLike_AtomicReplace(t *testing.T) { pool := newPool(t) logger := discardLogger() u, _ := seedActiveLibrary(t, pool, "ymlreplace", 8, 4) ctx := context.Background() for i := 0; i < 2; i++ { if err := playlists.BuildSystemPlaylists( ctx, pool, logger, u.ID, time.Now().UTC(), t.TempDir(), ); err != nil { t.Fatalf("build %d: %v", i, err) } } albums, artists := countYouMightLike(t, pool, u.ID) if albums > 12 { t.Errorf("albums = %d after two builds, want <= 12 (atomic replace)", albums) } if artists > 12 { t.Errorf("artists = %d after two builds, want <= 12 (atomic replace)", artists) } }