package espn import ( "testing" "github.com/humdrum-tiv/sportsball/internal/model" ) // mapStandings must flatten nested children (conference → entries) into one // group per table, select the per-sport columns, and rank by entry order. func TestMapStandingsNestedAndColumns(t *testing.T) { root := standingsNode{ Name: "National Basketball Association", Children: []standingsNode{ { Name: "Eastern Conference", Standings: standingsBlock{Entries: []standingsEntry{ { Team: teamJSON{ID: "1", Abbreviation: "BOS", ShortDisplayName: "Celtics"}, Stats: []standingStat{{Type: "wins", DisplayValue: "50"}, {Type: "losses", DisplayValue: "20"}, {Type: "winpercent", DisplayValue: ".714"}, {Type: "gamesbehind", DisplayValue: "-"}}, }, { Team: teamJSON{ID: "2", Abbreviation: "NYK", ShortDisplayName: "Knicks"}, Stats: []standingStat{{Type: "wins", DisplayValue: "45"}, {Type: "losses", DisplayValue: "25"}}, }, }}, }, }, } s := mapStandings(model.NBA, "basketball", root) if len(s.Groups) != 1 { t.Fatalf("want 1 group, got %d", len(s.Groups)) } g := s.Groups[0] if g.Name != "Eastern Conference" { t.Errorf("group name = %q", g.Name) } wantCols := []string{"W", "L", "PCT", "GB"} if len(g.Columns) != len(wantCols) { t.Fatalf("columns = %v", g.Columns) } for i, c := range wantCols { if g.Columns[i] != c { t.Errorf("col %d = %q, want %q", i, g.Columns[i], c) } } if g.Rows[0].Rank != 1 || g.Rows[0].Team.Abbr != "BOS" { t.Errorf("row0 = %+v", g.Rows[0]) } if got := g.Rows[0].Values; got[0] != "50" || got[1] != "20" || got[2] != ".714" || got[3] != "-" { t.Errorf("row0 values = %v", got) } // Missing stats map to empty strings, not a panic. if got := g.Rows[1].Values; got[2] != "" || got[3] != "" { t.Errorf("row1 missing stats should be blank: %v", got) } } // ESPN returns entries unsorted; mapStandings must order each group by the // sport's rank stat (basketball = playoffseed) before assigning ranks. func TestMapStandingsSortsByRankStat(t *testing.T) { root := standingsNode{Standings: standingsBlock{Entries: []standingsEntry{ {Team: teamJSON{Abbreviation: "ATL"}, Stats: []standingStat{{Type: "playoffseed", DisplayValue: "6"}, {Type: "wins", DisplayValue: "46"}}}, {Team: teamJSON{Abbreviation: "DET"}, Stats: []standingStat{{Type: "playoffseed", DisplayValue: "1"}, {Type: "wins", DisplayValue: "60"}}}, {Team: teamJSON{Abbreviation: "BOS"}, Stats: []standingStat{{Type: "playoffseed", DisplayValue: "2"}, {Type: "wins", DisplayValue: "56"}}}, }}} g := mapStandings(model.NBA, "basketball", root).Groups[0] want := []string{"DET", "BOS", "ATL"} for i, abbr := range want { if g.Rows[i].Team.Abbr != abbr || g.Rows[i].Rank != i+1 { t.Errorf("row %d = %s rank %d, want %s rank %d", i, g.Rows[i].Team.Abbr, g.Rows[i].Rank, abbr, i+1) } } } func TestStandingsRowAt(t *testing.T) { s := model.Standings{Groups: []model.StandingsGroup{ {Rows: []model.StandingsRow{{Team: model.Team{Abbr: "A"}}, {Team: model.Team{Abbr: "B"}}}}, {Rows: []model.StandingsRow{{Team: model.Team{Abbr: "C"}}}}, }} if s.RowCount() != 3 { t.Fatalf("RowCount = %d", s.RowCount()) } if r, ok := s.RowAt(2); !ok || r.Team.Abbr != "C" { t.Errorf("RowAt(2) = %+v ok=%v, want C", r, ok) } if _, ok := s.RowAt(3); ok { t.Error("RowAt(3) should be out of range") } }