cricinfo-cli-go 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/AGENTS.md +63 -0
  2. package/CONTRIBUTORS.md +75 -0
  3. package/LICENSE +21 -0
  4. package/Makefile +131 -0
  5. package/README.md +130 -0
  6. package/bin/cricinfo.js +44 -0
  7. package/cmd/cricinfo/main.go +15 -0
  8. package/go.mod +10 -0
  9. package/go.sum +10 -0
  10. package/internal/app/app.go +11 -0
  11. package/internal/app/app_test.go +122 -0
  12. package/internal/buildinfo/buildinfo.go +16 -0
  13. package/internal/cli/analysis.go +262 -0
  14. package/internal/cli/analysis_test.go +175 -0
  15. package/internal/cli/competitions.go +154 -0
  16. package/internal/cli/competitions_test.go +165 -0
  17. package/internal/cli/leagues.go +297 -0
  18. package/internal/cli/leagues_test.go +194 -0
  19. package/internal/cli/matches.go +403 -0
  20. package/internal/cli/matches_test.go +413 -0
  21. package/internal/cli/players.go +263 -0
  22. package/internal/cli/players_test.go +384 -0
  23. package/internal/cli/root.go +141 -0
  24. package/internal/cli/search.go +119 -0
  25. package/internal/cli/teams.go +214 -0
  26. package/internal/cli/teams_test.go +192 -0
  27. package/internal/cricinfo/analysis.go +1401 -0
  28. package/internal/cricinfo/analysis_phase15_test.go +267 -0
  29. package/internal/cricinfo/client.go +471 -0
  30. package/internal/cricinfo/client_test.go +280 -0
  31. package/internal/cricinfo/cmd/fixture-refresh/main.go +145 -0
  32. package/internal/cricinfo/competitions.go +405 -0
  33. package/internal/cricinfo/competitions_phase13_test.go +234 -0
  34. package/internal/cricinfo/coverage_ledger.go +122 -0
  35. package/internal/cricinfo/coverage_ledger_test.go +253 -0
  36. package/internal/cricinfo/decode.go +115 -0
  37. package/internal/cricinfo/decode_test.go +100 -0
  38. package/internal/cricinfo/entity_index.go +618 -0
  39. package/internal/cricinfo/entity_index_test.go +175 -0
  40. package/internal/cricinfo/fixture_matrix.go +243 -0
  41. package/internal/cricinfo/fixture_matrix_test.go +49 -0
  42. package/internal/cricinfo/fixtures_test.go +264 -0
  43. package/internal/cricinfo/historical_hydration.go +1641 -0
  44. package/internal/cricinfo/historical_phase14_test.go +542 -0
  45. package/internal/cricinfo/leagues.go +1210 -0
  46. package/internal/cricinfo/leagues_phase12_test.go +324 -0
  47. package/internal/cricinfo/live_leagues_test.go +169 -0
  48. package/internal/cricinfo/live_matches_test.go +203 -0
  49. package/internal/cricinfo/live_matrix_test.go +118 -0
  50. package/internal/cricinfo/live_players_test.go +122 -0
  51. package/internal/cricinfo/live_search_test.go +86 -0
  52. package/internal/cricinfo/live_smoke_test.go +213 -0
  53. package/internal/cricinfo/live_teams_test.go +104 -0
  54. package/internal/cricinfo/matches.go +1508 -0
  55. package/internal/cricinfo/matches_phase7_test.go +207 -0
  56. package/internal/cricinfo/matches_phase9_test.go +253 -0
  57. package/internal/cricinfo/normalize_entities.go +1727 -0
  58. package/internal/cricinfo/normalize_leagues.go +346 -0
  59. package/internal/cricinfo/players.go +1332 -0
  60. package/internal/cricinfo/players_phase10_test.go +174 -0
  61. package/internal/cricinfo/players_phase11_test.go +373 -0
  62. package/internal/cricinfo/render_contract.go +1088 -0
  63. package/internal/cricinfo/render_phase4_test.go +633 -0
  64. package/internal/cricinfo/renderer.go +1689 -0
  65. package/internal/cricinfo/resolver.go +813 -0
  66. package/internal/cricinfo/resolver_test.go +244 -0
  67. package/internal/cricinfo/teams.go +603 -0
  68. package/internal/cricinfo/teams_phase8_test.go +231 -0
  69. package/internal/cricinfo/testdata/fixtures/README.md +43 -0
  70. package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/broadcasts.json +11 -0
  71. package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/officials.json +150 -0
  72. package/internal/cricinfo/testdata/fixtures/details-plays/detail-110.json +157 -0
  73. package/internal/cricinfo/testdata/fixtures/details-plays/detail-52545007.json +145 -0
  74. package/internal/cricinfo/testdata/fixtures/details-plays/detail-52559021.json +143 -0
  75. package/internal/cricinfo/testdata/fixtures/details-plays/plays.json +15 -0
  76. package/internal/cricinfo/testdata/fixtures/endpoint-matrix.tsv +19 -0
  77. package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow-1.json +12 -0
  78. package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow.json +42 -0
  79. package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/innings-1-2.json +38 -0
  80. package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnership-1.json +31 -0
  81. package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnerships.json +42 -0
  82. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-offdays.json +20 -0
  83. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-ondays.json +21 -0
  84. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar.json +14 -0
  85. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-2025.json +13 -0
  86. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-group-1.json +13 -0
  87. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-groups.json +11 -0
  88. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-type-1.json +13 -0
  89. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-types.json +11 -0
  90. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/seasons.json +30 -0
  91. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-item-1.json +72 -0
  92. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-root.json +3 -0
  93. package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings.json +15 -0
  94. package/internal/cricinfo/testdata/fixtures/matches-competitions/competition.json +460 -0
  95. package/internal/cricinfo/testdata/fixtures/matches-competitions/event-1529474.json +86 -0
  96. package/internal/cricinfo/testdata/fixtures/matches-competitions/matchcards-1527966.json +368 -0
  97. package/internal/cricinfo/testdata/fixtures/matches-competitions/situation-1529474.json +10 -0
  98. package/internal/cricinfo/testdata/fixtures/players/athlete-1361257-statistics.json +126 -0
  99. package/internal/cricinfo/testdata/fixtures/players/athlete-1361257.json +113 -0
  100. package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-1-statistics-0.json +208 -0
  101. package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-2-statistics-0.json +252 -0
  102. package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores.json +74 -0
  103. package/internal/cricinfo/testdata/fixtures/players/roster-1361257-statistics-0.json +1008 -0
  104. package/internal/cricinfo/testdata/fixtures/root-discovery/events.json +72 -0
  105. package/internal/cricinfo/testdata/fixtures/root-discovery/root.json +28 -0
  106. package/internal/cricinfo/testdata/fixtures/team-competitor/competitor-789643.json +40 -0
  107. package/internal/cricinfo/testdata/fixtures/team-competitor/leaders-789643.json +353 -0
  108. package/internal/cricinfo/testdata/fixtures/team-competitor/records-789643.json +91 -0
  109. package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772-object.json +231 -0
  110. package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772.json +235 -0
  111. package/internal/cricinfo/testdata/fixtures/team-competitor/roster-789643.json +322 -0
  112. package/internal/cricinfo/testdata/fixtures/team-competitor/scores-789643.json +19 -0
  113. package/internal/cricinfo/testdata/fixtures/team-competitor/statistics-789643.json +629 -0
  114. package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643-athletes.json +7 -0
  115. package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643.json +67 -0
  116. package/internal/cricinfo/testdata/golden/match-empty.golden +1 -0
  117. package/internal/cricinfo/testdata/golden/match-list.golden +2 -0
  118. package/internal/cricinfo/testdata/golden/match-partial.golden +3 -0
  119. package/internal/cricinfo/types.go +54 -0
  120. package/package.json +51 -0
  121. package/scripts/postinstall.js +153 -0
@@ -0,0 +1,214 @@
1
+ package cli
2
+
3
+ import (
4
+ "context"
5
+ "fmt"
6
+ "strings"
7
+
8
+ "github.com/amxv/cricinfo-cli/internal/cricinfo"
9
+ "github.com/spf13/cobra"
10
+ )
11
+
12
+ type teamCommandService interface {
13
+ Close() error
14
+ Show(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
15
+ Roster(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
16
+ Scores(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
17
+ Leaders(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
18
+ Statistics(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
19
+ Records(ctx context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error)
20
+ }
21
+
22
+ type teamRuntimeOptions struct {
23
+ leagueID string
24
+ match string
25
+ }
26
+
27
+ var newTeamService = func() (teamCommandService, error) {
28
+ return cricinfo.NewTeamService(cricinfo.TeamServiceConfig{})
29
+ }
30
+
31
+ func newTeamsCommand(global *globalOptions) *cobra.Command {
32
+ opts := &teamRuntimeOptions{}
33
+
34
+ cmd := &cobra.Command{
35
+ Use: "teams",
36
+ Short: "Team and competitor views with roster, leaders, scores, statistics, and records.",
37
+ Long: strings.Join([]string{
38
+ "Resolve teams by ID/ref/alias and drill into global team resources or match-scoped competitor resources.",
39
+ "Use --match to force competitor scope when a route is match-specific.",
40
+ "",
41
+ "Next steps:",
42
+ " cricinfo teams show <team>",
43
+ " cricinfo teams roster <team>",
44
+ " cricinfo teams roster <team> --match <match>",
45
+ " cricinfo teams leaders <team> --match <match>",
46
+ }, "\n"),
47
+ Args: cobra.NoArgs,
48
+ RunE: func(cmd *cobra.Command, _ []string) error {
49
+ return cmd.Help()
50
+ },
51
+ }
52
+
53
+ cmd.PersistentFlags().StringVar(&opts.leagueID, "league", "", "Preferred league ID for resolver context")
54
+
55
+ showCmd := &cobra.Command{
56
+ Use: "show <team>",
57
+ Short: "Show one team summary",
58
+ Long: strings.Join([]string{
59
+ "Resolve a team by ID/ref/alias and show normalized identity fields.",
60
+ "",
61
+ "Next steps:",
62
+ " cricinfo teams roster <team>",
63
+ " cricinfo teams leaders <team> --match <match>",
64
+ }, "\n"),
65
+ Args: cobra.MinimumNArgs(1),
66
+ RunE: func(cmd *cobra.Command, args []string) error {
67
+ query := strings.TrimSpace(strings.Join(args, " "))
68
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
69
+ return service.Show(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID})
70
+ })
71
+ },
72
+ }
73
+
74
+ rosterCmd := &cobra.Command{
75
+ Use: "roster <team>",
76
+ Short: "Show team roster (global athletes or match-scoped competitor roster)",
77
+ Long: strings.Join([]string{
78
+ "Without --match, roster resolves global team athletes.",
79
+ "With --match, roster resolves the match competitor roster and bridges entries to player refs.",
80
+ "",
81
+ "Next steps:",
82
+ " cricinfo teams leaders <team> --match <match>",
83
+ " cricinfo teams statistics <team> --match <match>",
84
+ }, "\n"),
85
+ Args: cobra.MinimumNArgs(1),
86
+ RunE: func(cmd *cobra.Command, args []string) error {
87
+ query := strings.TrimSpace(strings.Join(args, " "))
88
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
89
+ return service.Roster(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID, MatchQuery: opts.match})
90
+ })
91
+ },
92
+ }
93
+ rosterCmd.Flags().StringVar(&opts.match, "match", "", "Match ID/ref/alias for match-scoped competitor roster")
94
+
95
+ scoresCmd := &cobra.Command{
96
+ Use: "scores <team>",
97
+ Short: "Show team score from a specific match competitor",
98
+ Long: strings.Join([]string{
99
+ "Resolve a team and match, then show the competitor score payload for that match.",
100
+ "",
101
+ "Next steps:",
102
+ " cricinfo teams leaders <team> --match <match>",
103
+ " cricinfo teams records <team> --match <match>",
104
+ }, "\n"),
105
+ Args: cobra.MinimumNArgs(1),
106
+ RunE: func(cmd *cobra.Command, args []string) error {
107
+ if strings.TrimSpace(opts.match) == "" {
108
+ return fmt.Errorf("--match is required")
109
+ }
110
+ query := strings.TrimSpace(strings.Join(args, " "))
111
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
112
+ return service.Scores(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID, MatchQuery: opts.match})
113
+ })
114
+ },
115
+ }
116
+ scoresCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias for competitor score route")
117
+
118
+ leadersCmd := &cobra.Command{
119
+ Use: "leaders <team>",
120
+ Short: "Show team batting and bowling leaders for a specific match",
121
+ Long: strings.Join([]string{
122
+ "Resolve a team and match, then render batting and bowling leaderboards from the competitor leaders route.",
123
+ "",
124
+ "Next steps:",
125
+ " cricinfo teams statistics <team> --match <match>",
126
+ " cricinfo teams records <team> --match <match>",
127
+ }, "\n"),
128
+ Args: cobra.MinimumNArgs(1),
129
+ RunE: func(cmd *cobra.Command, args []string) error {
130
+ if strings.TrimSpace(opts.match) == "" {
131
+ return fmt.Errorf("--match is required")
132
+ }
133
+ query := strings.TrimSpace(strings.Join(args, " "))
134
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
135
+ return service.Leaders(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID, MatchQuery: opts.match})
136
+ })
137
+ },
138
+ }
139
+ leadersCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias for competitor leaders route")
140
+
141
+ statisticsCmd := &cobra.Command{
142
+ Use: "statistics <team>",
143
+ Short: "Show team statistics categories for a specific match",
144
+ Long: strings.Join([]string{
145
+ "Resolve a team and match, then render competitor statistics categories.",
146
+ "",
147
+ "Next steps:",
148
+ " cricinfo teams records <team> --match <match>",
149
+ " cricinfo teams leaders <team> --match <match>",
150
+ }, "\n"),
151
+ Args: cobra.MinimumNArgs(1),
152
+ RunE: func(cmd *cobra.Command, args []string) error {
153
+ if strings.TrimSpace(opts.match) == "" {
154
+ return fmt.Errorf("--match is required")
155
+ }
156
+ query := strings.TrimSpace(strings.Join(args, " "))
157
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
158
+ return service.Statistics(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID, MatchQuery: opts.match})
159
+ })
160
+ },
161
+ }
162
+ statisticsCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias for competitor statistics route")
163
+
164
+ recordsCmd := &cobra.Command{
165
+ Use: "records <team>",
166
+ Short: "Show team records categories for a specific match",
167
+ Long: strings.Join([]string{
168
+ "Resolve a team and match, then render competitor records categories.",
169
+ "",
170
+ "Next steps:",
171
+ " cricinfo teams scores <team> --match <match>",
172
+ " cricinfo teams leaders <team> --match <match>",
173
+ }, "\n"),
174
+ Args: cobra.MinimumNArgs(1),
175
+ RunE: func(cmd *cobra.Command, args []string) error {
176
+ if strings.TrimSpace(opts.match) == "" {
177
+ return fmt.Errorf("--match is required")
178
+ }
179
+ query := strings.TrimSpace(strings.Join(args, " "))
180
+ return runTeamCommand(cmd, global, func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error) {
181
+ return service.Records(ctx, query, cricinfo.TeamLookupOptions{LeagueID: opts.leagueID, MatchQuery: opts.match})
182
+ })
183
+ },
184
+ }
185
+ recordsCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias for competitor records route")
186
+
187
+ cmd.AddCommand(showCmd, rosterCmd, scoresCmd, leadersCmd, statisticsCmd, recordsCmd)
188
+ return cmd
189
+ }
190
+
191
+ func runTeamCommand(
192
+ cmd *cobra.Command,
193
+ global *globalOptions,
194
+ fn func(ctx context.Context, service teamCommandService) (cricinfo.NormalizedResult, error),
195
+ ) error {
196
+ service, err := newTeamService()
197
+ if err != nil {
198
+ return err
199
+ }
200
+ defer func() {
201
+ _ = service.Close()
202
+ }()
203
+
204
+ result, err := fn(cmd.Context(), service)
205
+ if err != nil {
206
+ return err
207
+ }
208
+
209
+ return cricinfo.Render(cmd.OutOrStdout(), result, cricinfo.RenderOptions{
210
+ Format: global.format,
211
+ Verbose: global.verbose,
212
+ AllFields: global.allFields,
213
+ })
214
+ }
@@ -0,0 +1,192 @@
1
+ package cli
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "encoding/json"
7
+ "strings"
8
+ "testing"
9
+
10
+ "github.com/amxv/cricinfo-cli/internal/cricinfo"
11
+ )
12
+
13
+ type fakeTeamService struct {
14
+ showResult cricinfo.NormalizedResult
15
+ rosterResult cricinfo.NormalizedResult
16
+ scoresResult cricinfo.NormalizedResult
17
+ leadersResult cricinfo.NormalizedResult
18
+ statisticsResult cricinfo.NormalizedResult
19
+ recordsResult cricinfo.NormalizedResult
20
+
21
+ showQueries []string
22
+ rosterQueries []string
23
+ leadersQueries []string
24
+ rosterOpts []cricinfo.TeamLookupOptions
25
+ leadersOpts []cricinfo.TeamLookupOptions
26
+ }
27
+
28
+ func (f *fakeTeamService) Close() error { return nil }
29
+
30
+ func (f *fakeTeamService) Show(_ context.Context, query string, _ cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
31
+ f.showQueries = append(f.showQueries, query)
32
+ return f.showResult, nil
33
+ }
34
+
35
+ func (f *fakeTeamService) Roster(_ context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
36
+ f.rosterQueries = append(f.rosterQueries, query)
37
+ f.rosterOpts = append(f.rosterOpts, opts)
38
+ return f.rosterResult, nil
39
+ }
40
+
41
+ func (f *fakeTeamService) Scores(context.Context, string, cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
42
+ return f.scoresResult, nil
43
+ }
44
+
45
+ func (f *fakeTeamService) Leaders(_ context.Context, query string, opts cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
46
+ f.leadersQueries = append(f.leadersQueries, query)
47
+ f.leadersOpts = append(f.leadersOpts, opts)
48
+ return f.leadersResult, nil
49
+ }
50
+
51
+ func (f *fakeTeamService) Statistics(context.Context, string, cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
52
+ return f.statisticsResult, nil
53
+ }
54
+
55
+ func (f *fakeTeamService) Records(context.Context, string, cricinfo.TeamLookupOptions) (cricinfo.NormalizedResult, error) {
56
+ return f.recordsResult, nil
57
+ }
58
+
59
+ func TestTeamsCommandsSupportIDAndAliasInputs(t *testing.T) {
60
+ leaders := cricinfo.TeamLeaders{
61
+ TeamID: "789643",
62
+ MatchID: "1529474",
63
+ Categories: []cricinfo.TeamLeaderCategory{
64
+ {
65
+ Name: "runs",
66
+ DisplayName: "Runs",
67
+ Leaders: []cricinfo.TeamLeaderEntry{
68
+ {AthleteID: "1108510", AthleteName: "Mohammad Ishaq", DisplayValue: "107", Balls: "141", Fours: "11", Sixes: "1"},
69
+ },
70
+ },
71
+ {
72
+ Name: "wickets",
73
+ DisplayName: "Wickets",
74
+ Leaders: []cricinfo.TeamLeaderEntry{
75
+ {AthleteID: "1076674", AthleteName: "Amanullah Safi", DisplayValue: "7", Overs: "19.0", Runs: "81", EconomyRate: "4.26"},
76
+ },
77
+ },
78
+ },
79
+ }
80
+
81
+ service := &fakeTeamService{
82
+ showResult: cricinfo.NewDataResult(cricinfo.EntityTeam, cricinfo.Team{
83
+ ID: "789643",
84
+ Name: "Boost Region",
85
+ ShortName: "BOOST",
86
+ }),
87
+ rosterResult: cricinfo.NewListResult(cricinfo.EntityTeamRoster, []any{
88
+ cricinfo.TeamRosterEntry{PlayerID: "1361257", PlayerRef: "http://core.espnuk.org/v2/sports/cricket/athletes/1361257", DisplayName: "Fazal Haq"},
89
+ }),
90
+ leadersResult: cricinfo.NewDataResult(cricinfo.EntityTeamLeaders, leaders),
91
+ }
92
+
93
+ originalFactory := newTeamService
94
+ newTeamService = func() (teamCommandService, error) { return service, nil }
95
+ defer func() {
96
+ newTeamService = originalFactory
97
+ }()
98
+
99
+ var idOut bytes.Buffer
100
+ var idErr bytes.Buffer
101
+ if err := Run([]string{"teams", "show", "789643", "--format", "json"}, &idOut, &idErr); err != nil {
102
+ t.Fatalf("Run teams show id error: %v", err)
103
+ }
104
+
105
+ payload := decodeCLIJSONMap(t, idOut.Bytes())
106
+ if payload["kind"] != string(cricinfo.EntityTeam) {
107
+ t.Fatalf("expected kind %q, got %#v", cricinfo.EntityTeam, payload["kind"])
108
+ }
109
+
110
+ var aliasOut bytes.Buffer
111
+ var aliasErr bytes.Buffer
112
+ if err := Run([]string{"teams", "show", "Boost", "Region", "--format", "json"}, &aliasOut, &aliasErr); err != nil {
113
+ t.Fatalf("Run teams show alias error: %v", err)
114
+ }
115
+
116
+ if len(service.showQueries) != 2 {
117
+ t.Fatalf("expected 2 show queries, got %d", len(service.showQueries))
118
+ }
119
+ if service.showQueries[0] != "789643" {
120
+ t.Fatalf("expected first show query to be team ID, got %q", service.showQueries[0])
121
+ }
122
+ if service.showQueries[1] != "Boost Region" {
123
+ t.Fatalf("expected second show query to be alias, got %q", service.showQueries[1])
124
+ }
125
+
126
+ var rosterIDOut bytes.Buffer
127
+ var rosterIDErr bytes.Buffer
128
+ if err := Run([]string{"teams", "roster", "789643", "--match", "1529474", "--format", "json"}, &rosterIDOut, &rosterIDErr); err != nil {
129
+ t.Fatalf("Run teams roster id error: %v", err)
130
+ }
131
+
132
+ var rosterAliasOut bytes.Buffer
133
+ var rosterAliasErr bytes.Buffer
134
+ if err := Run([]string{"teams", "roster", "Boost", "Region", "--match", "3rd Match", "--format", "json"}, &rosterAliasOut, &rosterAliasErr); err != nil {
135
+ t.Fatalf("Run teams roster alias error: %v", err)
136
+ }
137
+
138
+ if len(service.rosterQueries) != 2 {
139
+ t.Fatalf("expected 2 roster queries, got %d", len(service.rosterQueries))
140
+ }
141
+ if service.rosterQueries[0] != "789643" {
142
+ t.Fatalf("expected roster id query, got %q", service.rosterQueries[0])
143
+ }
144
+ if service.rosterQueries[1] != "Boost Region" {
145
+ t.Fatalf("expected roster alias query, got %q", service.rosterQueries[1])
146
+ }
147
+ if service.rosterOpts[0].MatchQuery != "1529474" || service.rosterOpts[1].MatchQuery != "3rd Match" {
148
+ t.Fatalf("expected roster match opts to preserve caller input, got %+v", service.rosterOpts)
149
+ }
150
+
151
+ var leadersOut bytes.Buffer
152
+ var leadersErr bytes.Buffer
153
+ if err := Run([]string{"teams", "leaders", "Boost", "Region", "--match", "3rd Match", "--format", "text"}, &leadersOut, &leadersErr); err != nil {
154
+ t.Fatalf("Run teams leaders text error: %v", err)
155
+ }
156
+ leadersText := leadersOut.String()
157
+ if !strings.Contains(leadersText, "Batting Leaders") || !strings.Contains(leadersText, "Bowling Leaders") {
158
+ t.Fatalf("expected batting and bowling sections in leaders text output, got %q", leadersText)
159
+ }
160
+ if !strings.Contains(leadersText, "Mohammad Ishaq") || !strings.Contains(leadersText, "Amanullah Safi") {
161
+ t.Fatalf("expected leader names in text output, got %q", leadersText)
162
+ }
163
+ }
164
+
165
+ func TestTeamsMatchScopedCommandsRequireMatchFlag(t *testing.T) {
166
+ service := &fakeTeamService{}
167
+
168
+ originalFactory := newTeamService
169
+ newTeamService = func() (teamCommandService, error) { return service, nil }
170
+ defer func() {
171
+ newTeamService = originalFactory
172
+ }()
173
+
174
+ var out bytes.Buffer
175
+ var errBuf bytes.Buffer
176
+ err := Run([]string{"teams", "scores", "789643"}, &out, &errBuf)
177
+ if err == nil {
178
+ t.Fatalf("expected error when --match is missing")
179
+ }
180
+ if !strings.Contains(err.Error(), "--match is required") {
181
+ t.Fatalf("expected --match required message, got %v", err)
182
+ }
183
+ }
184
+
185
+ func decodeCLIJSONMap(t *testing.T, data []byte) map[string]any {
186
+ t.Helper()
187
+ var payload map[string]any
188
+ if err := json.Unmarshal(data, &payload); err != nil {
189
+ t.Fatalf("decode CLI JSON: %v", err)
190
+ }
191
+ return payload
192
+ }