package ui import ( "testing" "time" "github.com/humdrum-tiv/sportsball/internal/model" ) // newLeagueApp builds an App with the given order + in-season map and resolves // the visible set, mirroring what New() does. func newLeagueApp(order []model.LeagueID, inSeason map[model.LeagueID]bool) App { a := App{ order: order, hide: map[model.LeagueID]bool{}, show: map[model.LeagueID]bool{}, inSeason: inSeason, } a.applyAutoLeagues() return a } // No custom order → catalog order, every league. func TestOrderedCatalogDefault(t *testing.T) { a := App{} got := a.orderedCatalog() if len(got) != len(model.Leagues) { t.Fatalf("got %d, want %d", len(got), len(model.Leagues)) } for i, l := range model.Leagues { if got[i].ID != l.ID { t.Errorf("at %d got %s want %s", i, got[i].ID, l.ID) } } } // A custom order lists those first, then the rest in catalog order. func TestOrderedCatalogCustom(t *testing.T) { a := App{order: []model.LeagueID{model.NBA, model.WorldCup}} got := a.orderedCatalog() if got[0].ID != model.NBA || got[1].ID != model.WorldCup { t.Fatalf("custom order not honored: %s, %s", got[0].ID, got[1].ID) } if len(got) != len(model.Leagues) { t.Errorf("should still contain all leagues, got %d", len(got)) } } // applyAutoLeagues keeps only visible (in-season, no override) leagues in order; // unprobed leagues count as in-season so startup shows everything. func TestApplyAutoLeagues(t *testing.T) { a := newLeagueApp(nil, map[model.LeagueID]bool{ model.WorldCup: true, model.MLB: true, model.WNBA: true, model.NBA: false, model.NHL: false, model.NFL: false, }) got := a.leagueIDs() want := []model.LeagueID{model.WorldCup, model.MLB, model.WNBA} if len(got) != len(want) { t.Fatalf("got %v, want %v", got, want) } for i := range want { if got[i] != want[i] { t.Errorf("at %d got %s want %s", i, got[i], want[i]) } } } // Everything off-season must never blank the app — fall back to the full catalog. func TestApplyAutoLeaguesNeverBlank(t *testing.T) { in := map[model.LeagueID]bool{} for _, l := range model.Leagues { in[l.ID] = false } a := newLeagueApp(nil, in) if len(a.leagues) != len(model.Leagues) { t.Errorf("all-off-season should fall back to all leagues, got %d", len(a.leagues)) } } // Force-show reveals an off-season league; force-hide hides an in-season one, // and these overrides beat the season default. func TestOverridesBeatSeason(t *testing.T) { // NFL off-season, force-shown → visible. a := newLeagueApp(nil, map[model.LeagueID]bool{model.NFL: false}) a.show[model.NFL] = true a.applyAutoLeagues() if _, ok := a.leagueIndex()[model.NFL]; !ok { t.Error("force-shown off-season league should be visible") } // MLB in-season, force-hidden → gone. a.hide[model.MLB] = true a.inSeason[model.MLB] = true a.applyAutoLeagues() if _, ok := a.leagueIndex()[model.MLB]; ok { t.Error("force-hidden in-season league should be hidden") } } // Toggling a league back to what the season would do clears its override // (returns to auto) rather than pinning the opposite state. func TestToggleClearsBackToAuto(t *testing.T) { a := App{ hide: map[model.LeagueID]bool{}, show: map[model.LeagueID]bool{}, inSeason: map[model.LeagueID]bool{model.MLB: true, model.NFL: false}, } // In-season MLB: hide it (override), then toggle back → override cleared. a.toggleLeagueVisibility(model.MLB) if !a.hide[model.MLB] { t.Fatal("hiding an in-season league should set force-hide") } a.toggleLeagueVisibility(model.MLB) if a.hide[model.MLB] || a.show[model.MLB] { t.Error("toggling back to auto should clear the override") } // Off-season NFL: show it (override), then toggle back → cleared. a.toggleLeagueVisibility(model.NFL) if !a.show[model.NFL] { t.Fatal("showing an off-season league should set force-show") } a.toggleLeagueVisibility(model.NFL) if a.hide[model.NFL] || a.show[model.NFL] { t.Error("toggling back to auto should clear the override") } } // moveLeague reorders within the visible set and clamps at the ends. func TestMoveLeague(t *testing.T) { a := newLeagueApp(nil, map[model.LeagueID]bool{}) // all unprobed → all visible // catalog: WorldCup, MLB, NBA, ... ; move NBA (idx 2) up to idx 1. a.moveLeague(2, -1) a.applyAutoLeagues() if got := a.leagueIDs(); got[1] != model.NBA { t.Errorf("nba should move to index 1, order = %v", got) } a.moveLeague(0, -1) // already top: no-op a.applyAutoLeagues() if got := a.leagueIDs(); got[0] != model.WorldCup { t.Errorf("top move should be a no-op, order = %v", got) } } // Custom league order drives the recent state-filter ordering: within a shared // date, league order follows the user's arrangement. func TestStateFilterRespectsCustomLeagueOrder(t *testing.T) { now := time.Now() a := newLeagueApp([]model.LeagueID{model.NBA, model.WorldCup}, map[model.LeagueID]bool{model.NBA: true, model.WorldCup: true}) a.stateFilter = filterRecent a.games = map[model.LeagueID][]model.Game{ model.WorldCup: {{ID: "wc", League: model.WorldCup, State: model.StateFinal, Start: now}}, model.NBA: {{ID: "nba", League: model.NBA, State: model.StateFinal, Start: now}}, } ids := sectionIDs(a.sections()) if len(ids) != 2 || ids[0] != "nba" || ids[1] != "wc" { t.Errorf("custom order not honored: %v", ids) } }