Publish current app and server
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ponzischeme89/memby/server/internal/recommend"
|
||||
"github.com/ponzischeme89/memby/server/internal/store"
|
||||
)
|
||||
|
||||
func TestPersonalizeHomeRowsGraduallyDemotesIgnoredRows(t *testing.T) {
|
||||
rows := []recommend.Row{
|
||||
{ID: "continue"},
|
||||
{ID: "ignored"},
|
||||
{ID: "new-row"},
|
||||
{ID: "engaged"},
|
||||
}
|
||||
stats := []store.RowStat{
|
||||
{RowID: "ignored", Impressions: 20},
|
||||
// Under the five-impression confidence floor, new-row keeps its authored score.
|
||||
{RowID: "new-row", Impressions: 4},
|
||||
{RowID: "engaged", Impressions: 20, Focuses: 8, Selects: 3, DwellMs: 120_000},
|
||||
}
|
||||
|
||||
got := personalizeHomeRows(rows, stats)
|
||||
want := []string{"continue", "engaged", "new-row", "ignored"}
|
||||
for i, id := range want {
|
||||
if got[i].ID != id {
|
||||
t.Fatalf("row %d = %q, want %q; rows=%+v", i, got[i].ID, id, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDifferentHistoriesChangeRowAndPosterOrdering(t *testing.T) {
|
||||
now := time.Date(2026, 7, 31, 20, 0, 0, 0, time.UTC)
|
||||
item := func(id, name, genre string) recommend.Item {
|
||||
raw, _ := json.Marshal(map[string]any{
|
||||
"Id": id, "Name": name, "Type": "Movie", "Genres": []string{genre},
|
||||
"CommunityRating": 7.0,
|
||||
})
|
||||
return recommend.Item{
|
||||
ID: id, Name: name, Type: "Movie", Genres: []string{genre},
|
||||
CommunityRating: 7, Raw: raw,
|
||||
}
|
||||
}
|
||||
dramaA, dramaB := item("drama-a", "Drama A", "Drama"), item("drama-b", "Drama B", "Drama")
|
||||
comedyA, comedyB := item("comedy-a", "Comedy A", "Comedy"), item("comedy-b", "Comedy B", "Comedy")
|
||||
profileFor := func(genre string) recommend.WeightedProfile {
|
||||
return recommend.BuildWeightedProfile([]recommend.ViewingEvidence{
|
||||
{Item: item("seen-1", "Seen 1", genre), Completion: 1, OccurredAt: now.Add(-time.Hour)},
|
||||
{Item: item("seen-2", "Seen 2", genre), Completion: 1, OccurredAt: now.Add(-2 * time.Hour)},
|
||||
}, now, time.UTC)
|
||||
}
|
||||
pageFor := func(profile recommend.WeightedProfile) []recommend.Row {
|
||||
row := func(id string, items []recommend.Item) recommend.Row {
|
||||
ranked := recommend.WeightedRank(
|
||||
profile, items, nil, recommend.RankIntent{Now: now},
|
||||
recommend.DefaultWeightedConfig(), 10,
|
||||
)
|
||||
raws := make([]json.RawMessage, 0, len(ranked))
|
||||
for _, value := range ranked {
|
||||
raws = append(raws, recommend.EnrichRankedItem(value))
|
||||
}
|
||||
return recommend.Row{ID: id, Items: raws}
|
||||
}
|
||||
return personalizeRowsByTitleScores([]recommend.Row{
|
||||
row("drama-row", []recommend.Item{dramaA, dramaB}),
|
||||
row("comedy-row", []recommend.Item{comedyA, comedyB}),
|
||||
row("mixed-row", []recommend.Item{comedyA, dramaA}),
|
||||
})
|
||||
}
|
||||
dramaPage := pageFor(profileFor("Drama"))
|
||||
comedyPage := pageFor(profileFor("Comedy"))
|
||||
if dramaPage[0].ID != "drama-row" || comedyPage[0].ID != "comedy-row" {
|
||||
t.Fatalf("row orders did not personalize: drama=%s comedy=%s",
|
||||
dramaPage[0].ID, comedyPage[0].ID)
|
||||
}
|
||||
firstMixedID := func(rows []recommend.Row) string {
|
||||
for _, row := range rows {
|
||||
if row.ID == "mixed-row" {
|
||||
return recommend.Decode(row.Items)[0].ID
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
if firstMixedID(dramaPage) != "drama-a" || firstMixedID(comedyPage) != "comedy-a" {
|
||||
t.Fatalf("poster orders did not personalize: drama=%s comedy=%s",
|
||||
firstMixedID(dramaPage), firstMixedID(comedyPage))
|
||||
}
|
||||
}
|
||||
|
||||
func TestPersonalizeHomeRowsPreservesColdStartDefaults(t *testing.T) {
|
||||
rows := []recommend.Row{{ID: "continue"}, {ID: "next-up"}, {ID: "latest"}}
|
||||
got := personalizeHomeRows(rows, nil)
|
||||
for i := range rows {
|
||||
if got[i].ID != rows[i].ID {
|
||||
t.Fatalf("cold-start order changed: %+v", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparedHomeRowsPromotesAbandonedShowsAndTimeAwarePicks(t *testing.T) {
|
||||
prepared := []recommend.Row{
|
||||
{
|
||||
ID: "for-you:pick-up", Title: "old pickup title", Kind: "for-you",
|
||||
Items: []json.RawMessage{json.RawMessage(`{"Id":"abandoned"}`)},
|
||||
},
|
||||
{
|
||||
ID: "for-you:picks", Title: "old picks title", Kind: "for-you",
|
||||
Items: []json.RawMessage{json.RawMessage(`{"Id":"pick"}`)},
|
||||
},
|
||||
{ID: "for-you:genre:drama", Title: "Drama", Kind: "for-you"},
|
||||
}
|
||||
|
||||
rows := preparedHomeForYouRows(prepared, homeForYouWindow{
|
||||
ID: "afternoon", Title: "An hour for your afternoon", Minutes: 60,
|
||||
})
|
||||
if len(rows) != 2 {
|
||||
t.Fatalf("home For You rows = %+v", rows)
|
||||
}
|
||||
if rows[0].ID != "for-you:pick-up" ||
|
||||
rows[0].Title != "Pick this show up again" {
|
||||
t.Fatalf("pickup row = %+v", rows[0])
|
||||
}
|
||||
if rows[1].ID != "for-you:home:afternoon" ||
|
||||
rows[1].Title != "An hour for your afternoon" {
|
||||
t.Fatalf("time-aware row = %+v", rows[1])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user