package ui import ( "testing" "time" "github.com/humdrum-tiv/sportsball/internal/model" ) // tickerGames must exclude the watched game and order league-first then state: // same-league live, same-league final, other-league live, other-league final. // Only today's games count; pre-game and other days are dropped. func TestTickerGamesOrderingAndExclusion(t *testing.T) { now := time.Now() a := App{ leagues: model.Leagues, detail: model.Game{ID: "watch", League: model.MLB}, games: map[model.LeagueID][]model.Game{ model.MLB: { {ID: "watch", League: model.MLB, State: model.StateLive, Start: now}, {ID: "mlb-live", League: model.MLB, State: model.StateLive, Start: now}, {ID: "mlb-final", League: model.MLB, State: model.StateFinal, Start: now}, {ID: "mlb-pre", League: model.MLB, State: model.StatePre, Start: now}, {ID: "mlb-old", League: model.MLB, State: model.StateFinal, Start: now.AddDate(0, 0, -2)}, }, model.NBA: { {ID: "nba-live", League: model.NBA, State: model.StateLive, Start: now}, {ID: "nba-final", League: model.NBA, State: model.StateFinal, Start: now}, }, }, } got := a.tickerGames() var ids []string for _, g := range got { ids = append(ids, g.ID) } want := []string{"mlb-live", "mlb-final", "nba-live", "nba-final"} if len(ids) != len(want) { t.Fatalf("got %v, want %v", ids, want) } for i := range want { if ids[i] != want[i] { t.Fatalf("order mismatch at %d: got %v, want %v", i, ids, want) } } } // A favorited game must lead the ticker even when it's a final and the live // games belong to the watched league. func TestTickerGamesFavoritesLead(t *testing.T) { now := time.Now() fav := model.Team{ID: "22", Abbr: "PHI"} a := App{ leagues: model.Leagues, detail: model.Game{ID: "watch", League: model.MLB}, favs: map[string]bool{favKey(model.NBA, fav): true}, games: map[model.LeagueID][]model.Game{ model.MLB: { {ID: "mlb-live", League: model.MLB, State: model.StateLive, Start: now}, }, model.NBA: { {ID: "nba-fav-final", League: model.NBA, State: model.StateFinal, Start: now, Home: fav}, }, }, } got := a.tickerGames() if len(got) == 0 || got[0].ID != "nba-fav-final" { t.Fatalf("favorite game should lead, got %v", got) } } // The previously-viewed game (back-breadcrumb) must lead the ticker, ahead of // even favorites. func TestTickerGamesPrevLeads(t *testing.T) { now := time.Now() fav := model.Team{ID: "22", Abbr: "PHI"} a := App{ leagues: model.Leagues, detail: model.Game{ID: "watch", League: model.MLB}, detailPrev: "nba-prev", favs: map[string]bool{favKey(model.MLB, fav): true}, games: map[model.LeagueID][]model.Game{ model.MLB: { {ID: "mlb-fav-live", League: model.MLB, State: model.StateLive, Start: now, Home: fav}, }, model.NBA: { {ID: "nba-prev", League: model.NBA, State: model.StateFinal, Start: now}, }, }, } got := a.tickerGames() if len(got) == 0 || got[0].ID != "nba-prev" { t.Fatalf("previous game should lead, got %v", got) } } // detailView with a populated ticker must render without panicking and include // a sibling game's abbreviation in the strip. func TestDetailViewRendersTicker(t *testing.T) { now := time.Now() a := App{ width: 120, height: 40, leagues: model.Leagues, mode: viewDetail, detail: model.Game{ID: "watch", League: model.MLB, State: model.StateLive, Start: now, Home: model.Team{Abbr: "PHI"}, Away: model.Team{Abbr: "NYM"}}, games: map[model.LeagueID][]model.Game{model.MLB: {{ID: "sib", League: model.MLB, State: model.StateLive, Start: now, Home: model.Team{Abbr: "LAD"}, Away: model.Team{Abbr: "SDP"}}}}, detailData: map[string]model.GameDetail{}, } out := a.detailView() if out == "" { t.Fatal("empty detailView output") } if !contains(out, "LAD") || !contains(out, "SDP") { t.Errorf("ticker missing sibling game abbrs in output") } } func contains(s, sub string) bool { for i := 0; i+len(sub) <= len(s); i++ { if s[i:i+len(sub)] == sub { return true } } return false } // tickerWindow keeps the selected index inside the returned [lo,hi) span. func TestTickerWindowKeepsSelectionVisible(t *testing.T) { cases := []struct{ n, sel, fit int }{ {10, 0, 4}, {10, 9, 4}, {10, 5, 4}, {3, 2, 4}, } for _, c := range cases { lo, hi := tickerWindow(c.n, c.sel, c.fit) if lo < 0 || hi > c.n || lo > hi { t.Fatalf("invalid window [%d,%d) for %+v", lo, hi, c) } if c.sel < lo || c.sel >= hi { t.Errorf("sel %d outside window [%d,%d) for %+v", c.sel, lo, hi, c) } if c.n >= c.fit && hi-lo != c.fit { t.Errorf("window width %d != fit %d for %+v", hi-lo, c.fit, c) } } }