test: add recommendation rationale coverage

This commit is contained in:
2026-06-27 17:55:47 +02:00
parent 6d5e9d0a2c
commit 42fc505068
2 changed files with 72 additions and 0 deletions

View File

@@ -74,6 +74,47 @@ func TestScoreRecommendationCandidateRewardsProfileOverlap(t *testing.T) {
} }
} }
func TestScoreRecommendationCandidateBuildsRationaleFromProfileMatches(t *testing.T) {
now := time.Date(2026, time.June, 4, 12, 0, 0, 0, time.UTC)
profile := userTasteProfile{
genres: map[int]float64{1: 2.0},
themes: map[int]float64{10: 1.5},
studios: map[int]float64{20: 1.0},
demographics: map[int]float64{30: 1.0},
}
candidate := scoreRecommendationCandidate(now, profile, jikan.Anime{
MalID: 10,
Genres: []jikan.NamedEntity{{MalID: 1, Name: "Action"}},
Themes: []jikan.NamedEntity{{MalID: 10, Name: "School"}},
Studios: []jikan.NamedEntity{{MalID: 20, Name: "Production I.G"}},
Demographics: []jikan.NamedEntity{{MalID: 30, Name: "Shounen"}},
}, 5.0, 0)
want := []string{"Action", "School", "Production I.G", "Shounen"}
if !slices.Equal(candidate.rationale, want) {
t.Fatalf("expected profile match rationale %v, got %v", want, candidate.rationale)
}
}
func TestScoreRecommendationCandidateOmitsRationaleWhenSignalsAreWeak(t *testing.T) {
now := time.Date(2026, time.June, 4, 12, 0, 0, 0, time.UTC)
profile := userTasteProfile{
genres: map[int]float64{1: 2.0},
themes: map[int]float64{},
studios: map[int]float64{},
demographics: map[int]float64{},
}
candidate := scoreRecommendationCandidate(now, profile, jikan.Anime{
MalID: 10,
}, 5.0, 0)
if len(candidate.rationale) != 0 {
t.Fatalf("expected no rationale for weak signals, got %v", candidate.rationale)
}
}
func TestBuildTasteProfileUsesSeedWeights(t *testing.T) { func TestBuildTasteProfileUsesSeedWeights(t *testing.T) {
now := time.Date(2026, time.June, 4, 12, 0, 0, 0, time.UTC) now := time.Date(2026, time.June, 4, 12, 0, 0, 0, time.UTC)

View File

@@ -107,6 +107,37 @@ func TestRenderWithNonStringFragment(t *testing.T) {
} }
} }
func TestTopPicksTemplateRendersRecommendationRationale(t *testing.T) {
r, err := ProvideRenderer()
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
err = r.ExecuteFragment(&buf, "top_picks.gohtml", "content", map[string]any{
"Animes": []domain.Anime{
{
Anime: jikan.Anime{
MalID: 1,
Title: "Haikyuu!!",
},
RecommendationRationale: []string{"Sports", "Production I.G"},
},
},
"WatchlistMap": map[int64]bool{},
})
if err != nil {
t.Fatalf("ExecuteFragment error: %v", err)
}
body := buf.String()
for _, want := range []string{"Why this was picked", "Sports", "Production I.G"} {
if !strings.Contains(body, want) {
t.Fatalf("top picks template missing %q in output:\n%s", want, body)
}
}
}
func TestWatchTemplateEscapesJSONDataAttributes(t *testing.T) { func TestWatchTemplateEscapesJSONDataAttributes(t *testing.T) {
r, err := ProvideRenderer() r, err := ProvideRenderer()
if err != nil { if err != nil {