fix(discover): compare against a baseline run, not a hardcoded score — #2377
test-go / test (push) Successful in 56s
test-go / integration (push) Successful in 4m52s

TestSuggestArtists_UntaggedCandidateSurvivesAlongsideTagged asserted the
untagged candidate's score was 0.9 — the raw similarity value I'd seeded.
It's actually 1.61, because the pool score is signal-weighted by the seed
query: ln(1+signal) x similarity, and a liked seed carries signal 5, so
ln(6) x 0.9.

The assertion was testing the seeding arithmetic, which is a different
layer and not what the test is about. Rewritten to run the same request
twice — once with the tag term disabled, once enabled — and assert the
untagged candidate's score is IDENTICAL across both. That states the real
property (the blend leaves untagged candidates alone) without depending on
how the pool score is derived, so it survives future changes to seeding.

Added a sanity assertion that the TAGGED candidate's score did move, so
the comparison can't pass by both runs being trivially identical — the
same "a test that cannot fail" trap recorded for this milestone.

Exact-preservation at the arithmetic level is already covered where it
belongs, by TestApplyTagOverlap_UntaggedCandidateScoreIsUnchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-02 23:45:48 -04:00
co-authored by Claude Opus 5
parent 799dab029a
commit cf0d37bf8e
@@ -399,23 +399,57 @@ func TestSuggestArtists_UntaggedCandidateSurvivesAlongsideTagged(t *testing.T) {
seedCandidateTag(t, pool, "match", "shoegaze", 1.0)
// "loud" is deliberately left with NO cached tags at all.
out, err := SuggestArtists(context.Background(), pool, user.ID, 30, 12, 1.0)
// Compared against the same request with the term disabled, rather than
// against a literal. The pool score is signal-weighted by the seed query
// (ln(1+signal) x similarity), so hardcoding a number here would assert
// against the seeding maths — a different layer — and break whenever that
// changes. The property under test is only that the blend leaves an
// untagged candidate alone.
blended, err := SuggestArtists(context.Background(), pool, user.ID, 30, 12, 1.0)
if err != nil {
t.Fatalf("SuggestArtists: %v", err)
t.Fatalf("SuggestArtists(blended): %v", err)
}
if len(out) != 2 {
t.Fatalf("len = %d, want 2 — the untagged candidate must still appear", len(out))
baseline, err := SuggestArtists(context.Background(), pool, user.ID, 30, 12, 0)
if err != nil {
t.Fatalf("SuggestArtists(baseline): %v", err)
}
var loud *ArtistSuggestion
for i := range out {
if out[i].MBID == "loud" {
loud = &out[i]
if len(blended) != 2 {
t.Fatalf("len = %d, want 2 — the untagged candidate must still appear", len(blended))
}
scoreOf := func(out []ArtistSuggestion, mbid string) (float64, bool) {
for _, s := range out {
if s.MBID == mbid {
return s.Score, true
}
}
return 0, false
}
if loud == nil {
t.Fatal("untagged candidate vanished from the deck")
want, ok := scoreOf(baseline, "loud")
if !ok {
t.Fatal("untagged candidate missing from the baseline deck")
}
if loud.Score != 0.9 {
t.Errorf("untagged score = %v, want 0.9 unchanged", loud.Score)
got, ok := scoreOf(blended, "loud")
if !ok {
t.Fatal("untagged candidate vanished once the tag term was enabled")
}
if got != want {
t.Errorf("untagged score = %v, want %v (identical to the term-disabled run)", got, want)
}
// Sanity: the tagged candidate DID move, so the comparison above is
// meaningful rather than both runs being trivially identical.
if tagged, _ := scoreOf(blended, "match"); tagged == mustScore(t, baseline, "match") {
t.Error("the tagged candidate's score did not change — the term did nothing")
}
}
func mustScore(t *testing.T, out []ArtistSuggestion, mbid string) float64 {
t.Helper()
for _, s := range out {
if s.MBID == mbid {
return s.Score
}
}
t.Fatalf("no candidate %q in %v", mbid, out)
return 0
}