1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
|
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)
}
}
|