feat(recommendation): add ContextualMatchScore (max over non-seed likes)
This commit is contained in:
@@ -92,3 +92,66 @@ func TestSimilarity_BagOfCountsCollapsesToSet(t *testing.T) {
|
||||
t.Errorf("set-collapse = %v, want 1.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextualMatchScore_NoLikes_Returns0(t *testing.T) {
|
||||
current := SessionVector{Artists: []string{"a"}, Tags: map[string]int{"rock": 1}}
|
||||
got := ContextualMatchScore(current, nil, DefaultSimilarityWeights)
|
||||
if !approxEq(got, 0.0) {
|
||||
t.Errorf("no likes = %v, want 0.0", got)
|
||||
}
|
||||
got = ContextualMatchScore(current, []SessionVector{}, DefaultSimilarityWeights)
|
||||
if !approxEq(got, 0.0) {
|
||||
t.Errorf("empty likes = %v, want 0.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextualMatchScore_CurrentSeed_Returns0(t *testing.T) {
|
||||
current := SessionVector{Seed: true}
|
||||
likes := []SessionVector{
|
||||
{Artists: []string{"a"}, Tags: map[string]int{"rock": 1}},
|
||||
}
|
||||
got := ContextualMatchScore(current, likes, DefaultSimilarityWeights)
|
||||
if !approxEq(got, 0.0) {
|
||||
t.Errorf("current seed = %v, want 0.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextualMatchScore_AllLikesSeed_Returns0(t *testing.T) {
|
||||
current := SessionVector{Artists: []string{"a"}, Tags: map[string]int{"rock": 1}}
|
||||
likes := []SessionVector{
|
||||
{Seed: true, Artists: []string{"a"}, Tags: map[string]int{"rock": 1}},
|
||||
{Seed: true, Artists: []string{"a"}, Tags: map[string]int{"rock": 1}},
|
||||
}
|
||||
got := ContextualMatchScore(current, likes, DefaultSimilarityWeights)
|
||||
if !approxEq(got, 0.0) {
|
||||
t.Errorf("all-seed likes = %v, want 0.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextualMatchScore_TakesMax(t *testing.T) {
|
||||
// Three likes: full match, partial match, mismatch. Expect full match (1.0).
|
||||
current := SessionVector{Artists: []string{"a1"}, Tags: map[string]int{"rock": 1}}
|
||||
likes := []SessionVector{
|
||||
{Artists: []string{"a1"}, Tags: map[string]int{"rock": 1}}, // 1.0
|
||||
{Artists: []string{"a2"}, Tags: map[string]int{"rock": 1}}, // 0.7
|
||||
{Artists: []string{"a99"}, Tags: map[string]int{"jazz": 1}}, // 0.0
|
||||
}
|
||||
got := ContextualMatchScore(current, likes, DefaultSimilarityWeights)
|
||||
if !approxEq(got, 1.0) {
|
||||
t.Errorf("takes-max = %v, want 1.0", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextualMatchScore_FiltersSeedThenMaxes(t *testing.T) {
|
||||
// One Seed=true match (would be 1.0 if not filtered) + one partial match.
|
||||
current := SessionVector{Artists: []string{"a1"}, Tags: map[string]int{"rock": 1}}
|
||||
likes := []SessionVector{
|
||||
{Seed: true, Artists: []string{"a1"}, Tags: map[string]int{"rock": 1}},
|
||||
{Artists: []string{"a2"}, Tags: map[string]int{"rock": 1}},
|
||||
}
|
||||
got := ContextualMatchScore(current, likes, DefaultSimilarityWeights)
|
||||
// Seed=true filtered out → only partial match counts → 0.7
|
||||
if !approxEq(got, 0.7) {
|
||||
t.Errorf("filter-then-max = %v, want 0.7", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user