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.
- package/AGENTS.md +63 -0
- package/CONTRIBUTORS.md +75 -0
- package/LICENSE +21 -0
- package/Makefile +131 -0
- package/README.md +130 -0
- package/bin/cricinfo.js +44 -0
- package/cmd/cricinfo/main.go +15 -0
- package/go.mod +10 -0
- package/go.sum +10 -0
- package/internal/app/app.go +11 -0
- package/internal/app/app_test.go +122 -0
- package/internal/buildinfo/buildinfo.go +16 -0
- package/internal/cli/analysis.go +262 -0
- package/internal/cli/analysis_test.go +175 -0
- package/internal/cli/competitions.go +154 -0
- package/internal/cli/competitions_test.go +165 -0
- package/internal/cli/leagues.go +297 -0
- package/internal/cli/leagues_test.go +194 -0
- package/internal/cli/matches.go +403 -0
- package/internal/cli/matches_test.go +413 -0
- package/internal/cli/players.go +263 -0
- package/internal/cli/players_test.go +384 -0
- package/internal/cli/root.go +141 -0
- package/internal/cli/search.go +119 -0
- package/internal/cli/teams.go +214 -0
- package/internal/cli/teams_test.go +192 -0
- package/internal/cricinfo/analysis.go +1401 -0
- package/internal/cricinfo/analysis_phase15_test.go +267 -0
- package/internal/cricinfo/client.go +471 -0
- package/internal/cricinfo/client_test.go +280 -0
- package/internal/cricinfo/cmd/fixture-refresh/main.go +145 -0
- package/internal/cricinfo/competitions.go +405 -0
- package/internal/cricinfo/competitions_phase13_test.go +234 -0
- package/internal/cricinfo/coverage_ledger.go +122 -0
- package/internal/cricinfo/coverage_ledger_test.go +253 -0
- package/internal/cricinfo/decode.go +115 -0
- package/internal/cricinfo/decode_test.go +100 -0
- package/internal/cricinfo/entity_index.go +618 -0
- package/internal/cricinfo/entity_index_test.go +175 -0
- package/internal/cricinfo/fixture_matrix.go +243 -0
- package/internal/cricinfo/fixture_matrix_test.go +49 -0
- package/internal/cricinfo/fixtures_test.go +264 -0
- package/internal/cricinfo/historical_hydration.go +1641 -0
- package/internal/cricinfo/historical_phase14_test.go +542 -0
- package/internal/cricinfo/leagues.go +1210 -0
- package/internal/cricinfo/leagues_phase12_test.go +324 -0
- package/internal/cricinfo/live_leagues_test.go +169 -0
- package/internal/cricinfo/live_matches_test.go +203 -0
- package/internal/cricinfo/live_matrix_test.go +118 -0
- package/internal/cricinfo/live_players_test.go +122 -0
- package/internal/cricinfo/live_search_test.go +86 -0
- package/internal/cricinfo/live_smoke_test.go +213 -0
- package/internal/cricinfo/live_teams_test.go +104 -0
- package/internal/cricinfo/matches.go +1508 -0
- package/internal/cricinfo/matches_phase7_test.go +207 -0
- package/internal/cricinfo/matches_phase9_test.go +253 -0
- package/internal/cricinfo/normalize_entities.go +1727 -0
- package/internal/cricinfo/normalize_leagues.go +346 -0
- package/internal/cricinfo/players.go +1332 -0
- package/internal/cricinfo/players_phase10_test.go +174 -0
- package/internal/cricinfo/players_phase11_test.go +373 -0
- package/internal/cricinfo/render_contract.go +1088 -0
- package/internal/cricinfo/render_phase4_test.go +633 -0
- package/internal/cricinfo/renderer.go +1689 -0
- package/internal/cricinfo/resolver.go +813 -0
- package/internal/cricinfo/resolver_test.go +244 -0
- package/internal/cricinfo/teams.go +603 -0
- package/internal/cricinfo/teams_phase8_test.go +231 -0
- package/internal/cricinfo/testdata/fixtures/README.md +43 -0
- package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/broadcasts.json +11 -0
- package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/officials.json +150 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-110.json +157 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-52545007.json +145 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-52559021.json +143 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/plays.json +15 -0
- package/internal/cricinfo/testdata/fixtures/endpoint-matrix.tsv +19 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow-1.json +12 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow.json +42 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/innings-1-2.json +38 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnership-1.json +31 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnerships.json +42 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-offdays.json +20 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-ondays.json +21 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar.json +14 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-2025.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-group-1.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-groups.json +11 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-type-1.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-types.json +11 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/seasons.json +30 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-item-1.json +72 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-root.json +3 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings.json +15 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/competition.json +460 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/event-1529474.json +86 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/matchcards-1527966.json +368 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/situation-1529474.json +10 -0
- package/internal/cricinfo/testdata/fixtures/players/athlete-1361257-statistics.json +126 -0
- package/internal/cricinfo/testdata/fixtures/players/athlete-1361257.json +113 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-1-statistics-0.json +208 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-2-statistics-0.json +252 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores.json +74 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-statistics-0.json +1008 -0
- package/internal/cricinfo/testdata/fixtures/root-discovery/events.json +72 -0
- package/internal/cricinfo/testdata/fixtures/root-discovery/root.json +28 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/competitor-789643.json +40 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/leaders-789643.json +353 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/records-789643.json +91 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772-object.json +231 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772.json +235 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-789643.json +322 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/scores-789643.json +19 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/statistics-789643.json +629 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643-athletes.json +7 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643.json +67 -0
- package/internal/cricinfo/testdata/golden/match-empty.golden +1 -0
- package/internal/cricinfo/testdata/golden/match-list.golden +2 -0
- package/internal/cricinfo/testdata/golden/match-partial.golden +3 -0
- package/internal/cricinfo/types.go +54 -0
- package/package.json +51 -0
- package/scripts/postinstall.js +153 -0
|
@@ -0,0 +1,165 @@
|
|
|
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 fakeCompetitionService struct {
|
|
14
|
+
showResult cricinfo.NormalizedResult
|
|
15
|
+
officialsResult cricinfo.NormalizedResult
|
|
16
|
+
broadcastsResult cricinfo.NormalizedResult
|
|
17
|
+
ticketsResult cricinfo.NormalizedResult
|
|
18
|
+
oddsResult cricinfo.NormalizedResult
|
|
19
|
+
metadataResult cricinfo.NormalizedResult
|
|
20
|
+
|
|
21
|
+
showQueries []string
|
|
22
|
+
officialsQueries []string
|
|
23
|
+
broadcastsQueries []string
|
|
24
|
+
metadataQueries []string
|
|
25
|
+
showOpts []cricinfo.CompetitionLookupOptions
|
|
26
|
+
officialsOpts []cricinfo.CompetitionLookupOptions
|
|
27
|
+
broadcastsOpts []cricinfo.CompetitionLookupOptions
|
|
28
|
+
metadataOpts []cricinfo.CompetitionLookupOptions
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func (f *fakeCompetitionService) Close() error { return nil }
|
|
32
|
+
|
|
33
|
+
func (f *fakeCompetitionService) Show(_ context.Context, query string, opts cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
34
|
+
f.showQueries = append(f.showQueries, query)
|
|
35
|
+
f.showOpts = append(f.showOpts, opts)
|
|
36
|
+
return f.showResult, nil
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
func (f *fakeCompetitionService) Officials(_ context.Context, query string, opts cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
40
|
+
f.officialsQueries = append(f.officialsQueries, query)
|
|
41
|
+
f.officialsOpts = append(f.officialsOpts, opts)
|
|
42
|
+
return f.officialsResult, nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func (f *fakeCompetitionService) Broadcasts(_ context.Context, query string, opts cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
46
|
+
f.broadcastsQueries = append(f.broadcastsQueries, query)
|
|
47
|
+
f.broadcastsOpts = append(f.broadcastsOpts, opts)
|
|
48
|
+
return f.broadcastsResult, nil
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (f *fakeCompetitionService) Tickets(context.Context, string, cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
52
|
+
return f.ticketsResult, nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func (f *fakeCompetitionService) Odds(context.Context, string, cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
56
|
+
return f.oddsResult, nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func (f *fakeCompetitionService) Metadata(_ context.Context, query string, opts cricinfo.CompetitionLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
60
|
+
f.metadataQueries = append(f.metadataQueries, query)
|
|
61
|
+
f.metadataOpts = append(f.metadataOpts, opts)
|
|
62
|
+
return f.metadataResult, nil
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
func TestCompetitionsCommandsRenderAndPreserveLookupInputs(t *testing.T) {
|
|
66
|
+
service := &fakeCompetitionService{
|
|
67
|
+
showResult: cricinfo.NewDataResult(cricinfo.EntityCompetition, cricinfo.Competition{
|
|
68
|
+
ID: "1529474",
|
|
69
|
+
CompetitionID: "1529474",
|
|
70
|
+
LeagueID: "19138",
|
|
71
|
+
EventID: "1529474",
|
|
72
|
+
Description: "3rd Match",
|
|
73
|
+
ShortDescription: "3rd Match",
|
|
74
|
+
OfficialsRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/officials",
|
|
75
|
+
}),
|
|
76
|
+
officialsResult: cricinfo.NewListResult(cricinfo.EntityCompOfficial, []any{
|
|
77
|
+
cricinfo.CompetitionMetadataEntry{DisplayName: "Anil Chaugai", Role: "umpire", Order: 1},
|
|
78
|
+
}),
|
|
79
|
+
broadcastsResult: cricinfo.NewListResult(cricinfo.EntityCompBroadcast, []any{}),
|
|
80
|
+
ticketsResult: cricinfo.NewListResult(cricinfo.EntityCompTicket, []any{}),
|
|
81
|
+
oddsResult: cricinfo.NewListResult(cricinfo.EntityCompOdds, []any{
|
|
82
|
+
cricinfo.CompetitionMetadataEntry{Name: "Win Probability", Value: "0.61"},
|
|
83
|
+
}),
|
|
84
|
+
metadataResult: cricinfo.NewDataResult(cricinfo.EntityCompMetadata, cricinfo.CompetitionMetadataSummary{
|
|
85
|
+
Competition: cricinfo.Competition{ID: "1529474"},
|
|
86
|
+
Officials: []cricinfo.CompetitionMetadataEntry{{DisplayName: "Anil Chaugai"}},
|
|
87
|
+
Broadcasts: []cricinfo.CompetitionMetadataEntry{},
|
|
88
|
+
Tickets: []cricinfo.CompetitionMetadataEntry{},
|
|
89
|
+
Odds: []cricinfo.CompetitionMetadataEntry{{Name: "Win Probability", Value: "0.61"}},
|
|
90
|
+
}),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
originalFactory := newCompetitionService
|
|
94
|
+
newCompetitionService = func() (competitionCommandService, error) { return service, nil }
|
|
95
|
+
defer func() {
|
|
96
|
+
newCompetitionService = originalFactory
|
|
97
|
+
}()
|
|
98
|
+
|
|
99
|
+
var showOut bytes.Buffer
|
|
100
|
+
var showErr bytes.Buffer
|
|
101
|
+
if err := Run([]string{"competitions", "show", "3rd", "Match", "--league", "19138", "--format", "json"}, &showOut, &showErr); err != nil {
|
|
102
|
+
t.Fatalf("Run competitions show error: %v", err)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
var showPayload map[string]any
|
|
106
|
+
if err := json.Unmarshal(showOut.Bytes(), &showPayload); err != nil {
|
|
107
|
+
t.Fatalf("decode competitions show payload: %v", err)
|
|
108
|
+
}
|
|
109
|
+
if showPayload["kind"] != string(cricinfo.EntityCompetition) {
|
|
110
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityCompetition, showPayload["kind"])
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
var officialsOut bytes.Buffer
|
|
114
|
+
var officialsErr bytes.Buffer
|
|
115
|
+
if err := Run([]string{"competitions", "officials", "1529474", "--league", "19138", "--format", "text"}, &officialsOut, &officialsErr); err != nil {
|
|
116
|
+
t.Fatalf("Run competitions officials error: %v", err)
|
|
117
|
+
}
|
|
118
|
+
if !strings.Contains(officialsOut.String(), "Anil Chaugai") {
|
|
119
|
+
t.Fatalf("expected officials text output to include official name, got %q", officialsOut.String())
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
var broadcastsOut bytes.Buffer
|
|
123
|
+
var broadcastsErr bytes.Buffer
|
|
124
|
+
if err := Run([]string{"competitions", "broadcasts", "1529474", "--league", "19138", "--format", "text"}, &broadcastsOut, &broadcastsErr); err != nil {
|
|
125
|
+
t.Fatalf("Run competitions broadcasts error: %v", err)
|
|
126
|
+
}
|
|
127
|
+
if !strings.Contains(strings.ToLower(broadcastsOut.String()), "no competition broadcasts found") {
|
|
128
|
+
t.Fatalf("expected clean zero-result message for broadcasts, got %q", broadcastsOut.String())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var metadataOut bytes.Buffer
|
|
132
|
+
var metadataErr bytes.Buffer
|
|
133
|
+
if err := Run([]string{"competitions", "metadata", "3rd", "Match", "--league", "19138", "--format", "json"}, &metadataOut, &metadataErr); err != nil {
|
|
134
|
+
t.Fatalf("Run competitions metadata error: %v", err)
|
|
135
|
+
}
|
|
136
|
+
var metadataPayload map[string]any
|
|
137
|
+
if err := json.Unmarshal(metadataOut.Bytes(), &metadataPayload); err != nil {
|
|
138
|
+
t.Fatalf("decode competitions metadata payload: %v", err)
|
|
139
|
+
}
|
|
140
|
+
if metadataPayload["kind"] != string(cricinfo.EntityCompMetadata) {
|
|
141
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityCompMetadata, metadataPayload["kind"])
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if len(service.showQueries) != 1 || service.showQueries[0] != "3rd Match" {
|
|
145
|
+
t.Fatalf("expected joined show query, got %+v", service.showQueries)
|
|
146
|
+
}
|
|
147
|
+
if len(service.officialsQueries) != 1 || service.officialsQueries[0] != "1529474" {
|
|
148
|
+
t.Fatalf("expected officials query 1529474, got %+v", service.officialsQueries)
|
|
149
|
+
}
|
|
150
|
+
if len(service.metadataQueries) != 1 || service.metadataQueries[0] != "3rd Match" {
|
|
151
|
+
t.Fatalf("expected joined metadata query, got %+v", service.metadataQueries)
|
|
152
|
+
}
|
|
153
|
+
if len(service.showOpts) != 1 || service.showOpts[0].LeagueID != "19138" {
|
|
154
|
+
t.Fatalf("expected show league option to propagate, got %+v", service.showOpts)
|
|
155
|
+
}
|
|
156
|
+
if len(service.officialsOpts) != 1 || service.officialsOpts[0].LeagueID != "19138" {
|
|
157
|
+
t.Fatalf("expected officials league option to propagate, got %+v", service.officialsOpts)
|
|
158
|
+
}
|
|
159
|
+
if len(service.broadcastsOpts) != 1 || service.broadcastsOpts[0].LeagueID != "19138" {
|
|
160
|
+
t.Fatalf("expected broadcasts league option to propagate, got %+v", service.broadcastsOpts)
|
|
161
|
+
}
|
|
162
|
+
if len(service.metadataOpts) != 1 || service.metadataOpts[0].LeagueID != "19138" {
|
|
163
|
+
t.Fatalf("expected metadata league option to propagate, got %+v", service.metadataOpts)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
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 leagueCommandService interface {
|
|
13
|
+
Close() error
|
|
14
|
+
List(ctx context.Context, opts cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error)
|
|
15
|
+
Show(ctx context.Context, leagueQuery string) (cricinfo.NormalizedResult, error)
|
|
16
|
+
Events(ctx context.Context, leagueQuery string, opts cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error)
|
|
17
|
+
Calendar(ctx context.Context, leagueQuery string) (cricinfo.NormalizedResult, error)
|
|
18
|
+
Athletes(ctx context.Context, leagueQuery string, opts cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error)
|
|
19
|
+
Standings(ctx context.Context, leagueQuery string) (cricinfo.NormalizedResult, error)
|
|
20
|
+
Seasons(ctx context.Context, leagueQuery string) (cricinfo.NormalizedResult, error)
|
|
21
|
+
SeasonShow(ctx context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error)
|
|
22
|
+
SeasonTypes(ctx context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error)
|
|
23
|
+
SeasonGroups(ctx context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type leagueRuntimeOptions struct {
|
|
27
|
+
limit int
|
|
28
|
+
season string
|
|
29
|
+
typ string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
var newLeagueService = func() (leagueCommandService, error) {
|
|
33
|
+
return cricinfo.NewLeagueService(cricinfo.LeagueServiceConfig{})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func newLeaguesCommand(global *globalOptions) *cobra.Command {
|
|
37
|
+
opts := &leagueRuntimeOptions{}
|
|
38
|
+
|
|
39
|
+
cmd := &cobra.Command{
|
|
40
|
+
Use: "leagues",
|
|
41
|
+
Short: "League discovery and navigation across events, calendar, athletes, standings, and seasons.",
|
|
42
|
+
Long: strings.Join([]string{
|
|
43
|
+
"Resolve leagues by ID/ref/alias and drill into events, calendar views, athletes, standings, and season navigation.",
|
|
44
|
+
"",
|
|
45
|
+
"Next steps:",
|
|
46
|
+
" cricinfo leagues list",
|
|
47
|
+
" cricinfo leagues show <league>",
|
|
48
|
+
" cricinfo leagues events <league>",
|
|
49
|
+
" cricinfo leagues calendar <league>",
|
|
50
|
+
" cricinfo leagues athletes <league>",
|
|
51
|
+
" cricinfo leagues standings <league>",
|
|
52
|
+
" cricinfo leagues seasons <league>",
|
|
53
|
+
}, "\n"),
|
|
54
|
+
Args: cobra.NoArgs,
|
|
55
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
56
|
+
return cmd.Help()
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
listCmd := &cobra.Command{
|
|
61
|
+
Use: "list",
|
|
62
|
+
Short: "List leagues from /leagues",
|
|
63
|
+
Args: cobra.NoArgs,
|
|
64
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
65
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
66
|
+
return service.List(ctx, cricinfo.LeagueListOptions{Limit: opts.limit})
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
listCmd.Flags().IntVar(&opts.limit, "limit", 20, "Maximum number of leagues to return")
|
|
71
|
+
|
|
72
|
+
showCmd := &cobra.Command{
|
|
73
|
+
Use: "show <league>",
|
|
74
|
+
Short: "Show one league summary",
|
|
75
|
+
Args: cobra.MinimumNArgs(1),
|
|
76
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
77
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
78
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
79
|
+
return service.Show(ctx, leagueQuery)
|
|
80
|
+
})
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
eventsCmd := &cobra.Command{
|
|
85
|
+
Use: "events <league>",
|
|
86
|
+
Short: "List matches/events for one league",
|
|
87
|
+
Args: cobra.MinimumNArgs(1),
|
|
88
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
89
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
90
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
91
|
+
return service.Events(ctx, leagueQuery, cricinfo.LeagueListOptions{Limit: opts.limit})
|
|
92
|
+
})
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
eventsCmd.Flags().IntVar(&opts.limit, "limit", 20, "Maximum number of league events/matches to return")
|
|
96
|
+
|
|
97
|
+
calendarCmd := &cobra.Command{
|
|
98
|
+
Use: "calendar <league>",
|
|
99
|
+
Short: "Show normalized league calendar day entries",
|
|
100
|
+
Args: cobra.MinimumNArgs(1),
|
|
101
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
102
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
103
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
104
|
+
return service.Calendar(ctx, leagueQuery)
|
|
105
|
+
})
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
athletesCmd := &cobra.Command{
|
|
110
|
+
Use: "athletes <league>",
|
|
111
|
+
Short: "Show league athlete views, with roster-driven fallback when direct pages are sparse",
|
|
112
|
+
Args: cobra.MinimumNArgs(1),
|
|
113
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
114
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
115
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
116
|
+
return service.Athletes(ctx, leagueQuery, cricinfo.LeagueListOptions{Limit: opts.limit})
|
|
117
|
+
})
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
athletesCmd.Flags().IntVar(&opts.limit, "limit", 20, "Maximum number of athletes to return")
|
|
121
|
+
|
|
122
|
+
standingsCmd := &cobra.Command{
|
|
123
|
+
Use: "standings <league>",
|
|
124
|
+
Short: "Show normalized standings groups for one league",
|
|
125
|
+
Args: cobra.MinimumNArgs(1),
|
|
126
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
127
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
128
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
129
|
+
return service.Standings(ctx, leagueQuery)
|
|
130
|
+
})
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
seasonsCmd := &cobra.Command{
|
|
135
|
+
Use: "seasons <league>",
|
|
136
|
+
Short: "Show season refs for one league",
|
|
137
|
+
Args: cobra.MinimumNArgs(1),
|
|
138
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
139
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
140
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
141
|
+
return service.Seasons(ctx, leagueQuery)
|
|
142
|
+
})
|
|
143
|
+
},
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
cmd.AddCommand(
|
|
147
|
+
listCmd,
|
|
148
|
+
showCmd,
|
|
149
|
+
eventsCmd,
|
|
150
|
+
calendarCmd,
|
|
151
|
+
athletesCmd,
|
|
152
|
+
standingsCmd,
|
|
153
|
+
seasonsCmd,
|
|
154
|
+
)
|
|
155
|
+
return cmd
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
func newSeasonsCommand(global *globalOptions) *cobra.Command {
|
|
159
|
+
opts := &leagueRuntimeOptions{}
|
|
160
|
+
|
|
161
|
+
cmd := &cobra.Command{
|
|
162
|
+
Use: "seasons",
|
|
163
|
+
Short: "Season navigation across league season, type, and group hierarchies.",
|
|
164
|
+
Long: strings.Join([]string{
|
|
165
|
+
"Resolve seasons, types, and groups by league while hiding pointer-heavy upstream traversal.",
|
|
166
|
+
"",
|
|
167
|
+
"Next steps:",
|
|
168
|
+
" cricinfo seasons show <league> --season <season>",
|
|
169
|
+
" cricinfo seasons types <league> --season <season>",
|
|
170
|
+
" cricinfo seasons groups <league> --season <season> --type <type>",
|
|
171
|
+
}, "\n"),
|
|
172
|
+
Args: cobra.NoArgs,
|
|
173
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
174
|
+
return cmd.Help()
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
showCmd := &cobra.Command{
|
|
179
|
+
Use: "show <league>",
|
|
180
|
+
Short: "Show one selected league season",
|
|
181
|
+
Args: cobra.MinimumNArgs(1),
|
|
182
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
183
|
+
if strings.TrimSpace(opts.season) == "" {
|
|
184
|
+
return fmt.Errorf("--season is required")
|
|
185
|
+
}
|
|
186
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
187
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
188
|
+
return service.SeasonShow(ctx, leagueQuery, cricinfo.SeasonLookupOptions{
|
|
189
|
+
SeasonQuery: opts.season,
|
|
190
|
+
})
|
|
191
|
+
})
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
showCmd.Flags().StringVar(&opts.season, "season", "", "Required: season ID/ref (for example 2025)")
|
|
195
|
+
|
|
196
|
+
typesCmd := &cobra.Command{
|
|
197
|
+
Use: "types <league>",
|
|
198
|
+
Short: "List normalized season types for a selected league season",
|
|
199
|
+
Args: cobra.MinimumNArgs(1),
|
|
200
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
201
|
+
if strings.TrimSpace(opts.season) == "" {
|
|
202
|
+
return fmt.Errorf("--season is required")
|
|
203
|
+
}
|
|
204
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
205
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
206
|
+
return service.SeasonTypes(ctx, leagueQuery, cricinfo.SeasonLookupOptions{
|
|
207
|
+
SeasonQuery: opts.season,
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
typesCmd.Flags().StringVar(&opts.season, "season", "", "Required: season ID/ref (for example 2025)")
|
|
213
|
+
|
|
214
|
+
groupsCmd := &cobra.Command{
|
|
215
|
+
Use: "groups <league>",
|
|
216
|
+
Short: "List normalized season groups for a selected season type",
|
|
217
|
+
Args: cobra.MinimumNArgs(1),
|
|
218
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
219
|
+
if strings.TrimSpace(opts.season) == "" {
|
|
220
|
+
return fmt.Errorf("--season is required")
|
|
221
|
+
}
|
|
222
|
+
if strings.TrimSpace(opts.typ) == "" {
|
|
223
|
+
return fmt.Errorf("--type is required")
|
|
224
|
+
}
|
|
225
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
226
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
227
|
+
return service.SeasonGroups(ctx, leagueQuery, cricinfo.SeasonLookupOptions{
|
|
228
|
+
SeasonQuery: opts.season,
|
|
229
|
+
TypeQuery: opts.typ,
|
|
230
|
+
})
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
}
|
|
234
|
+
groupsCmd.Flags().StringVar(&opts.season, "season", "", "Required: season ID/ref (for example 2025)")
|
|
235
|
+
groupsCmd.Flags().StringVar(&opts.typ, "type", "", "Required: type ID/ref/name (for example 1)")
|
|
236
|
+
|
|
237
|
+
cmd.AddCommand(showCmd, typesCmd, groupsCmd)
|
|
238
|
+
return cmd
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
func newStandingsCommand(global *globalOptions) *cobra.Command {
|
|
242
|
+
cmd := &cobra.Command{
|
|
243
|
+
Use: "standings",
|
|
244
|
+
Short: "Standings traversal by league.",
|
|
245
|
+
Long: strings.Join([]string{
|
|
246
|
+
"Traverse league standings routes and normalize ref-heavy standings resources into group entries.",
|
|
247
|
+
"",
|
|
248
|
+
"Next steps:",
|
|
249
|
+
" cricinfo standings show <league>",
|
|
250
|
+
" cricinfo leagues standings <league>",
|
|
251
|
+
}, "\n"),
|
|
252
|
+
Args: cobra.NoArgs,
|
|
253
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
254
|
+
return cmd.Help()
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
showCmd := &cobra.Command{
|
|
259
|
+
Use: "show <league>",
|
|
260
|
+
Short: "Show normalized standings groups for one league",
|
|
261
|
+
Args: cobra.MinimumNArgs(1),
|
|
262
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
263
|
+
leagueQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
264
|
+
return runLeagueCommand(cmd, global, func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error) {
|
|
265
|
+
return service.Standings(ctx, leagueQuery)
|
|
266
|
+
})
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
cmd.AddCommand(showCmd)
|
|
271
|
+
return cmd
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
func runLeagueCommand(
|
|
275
|
+
cmd *cobra.Command,
|
|
276
|
+
global *globalOptions,
|
|
277
|
+
fn func(ctx context.Context, service leagueCommandService) (cricinfo.NormalizedResult, error),
|
|
278
|
+
) error {
|
|
279
|
+
service, err := newLeagueService()
|
|
280
|
+
if err != nil {
|
|
281
|
+
return err
|
|
282
|
+
}
|
|
283
|
+
defer func() {
|
|
284
|
+
_ = service.Close()
|
|
285
|
+
}()
|
|
286
|
+
|
|
287
|
+
result, err := fn(cmd.Context(), service)
|
|
288
|
+
if err != nil {
|
|
289
|
+
return err
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return cricinfo.Render(cmd.OutOrStdout(), result, cricinfo.RenderOptions{
|
|
293
|
+
Format: global.format,
|
|
294
|
+
Verbose: global.verbose,
|
|
295
|
+
AllFields: global.allFields,
|
|
296
|
+
})
|
|
297
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
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 fakeLeagueService struct {
|
|
14
|
+
listResult cricinfo.NormalizedResult
|
|
15
|
+
showResult cricinfo.NormalizedResult
|
|
16
|
+
eventsResult cricinfo.NormalizedResult
|
|
17
|
+
calendarResult cricinfo.NormalizedResult
|
|
18
|
+
athletesResult cricinfo.NormalizedResult
|
|
19
|
+
standingsResult cricinfo.NormalizedResult
|
|
20
|
+
seasonsResult cricinfo.NormalizedResult
|
|
21
|
+
seasonShow cricinfo.NormalizedResult
|
|
22
|
+
seasonTypes cricinfo.NormalizedResult
|
|
23
|
+
seasonGroups cricinfo.NormalizedResult
|
|
24
|
+
|
|
25
|
+
showQueries []string
|
|
26
|
+
standingsQueries []string
|
|
27
|
+
seasonShowQueries []string
|
|
28
|
+
seasonTypeQueries []string
|
|
29
|
+
seasonGroupQueries []string
|
|
30
|
+
seasonShowOpts []cricinfo.SeasonLookupOptions
|
|
31
|
+
seasonTypeOpts []cricinfo.SeasonLookupOptions
|
|
32
|
+
seasonGroupOpts []cricinfo.SeasonLookupOptions
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
func (f *fakeLeagueService) Close() error { return nil }
|
|
36
|
+
|
|
37
|
+
func (f *fakeLeagueService) List(context.Context, cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error) {
|
|
38
|
+
return f.listResult, nil
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
func (f *fakeLeagueService) Show(_ context.Context, leagueQuery string) (cricinfo.NormalizedResult, error) {
|
|
42
|
+
f.showQueries = append(f.showQueries, leagueQuery)
|
|
43
|
+
return f.showResult, nil
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func (f *fakeLeagueService) Events(context.Context, string, cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error) {
|
|
47
|
+
return f.eventsResult, nil
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func (f *fakeLeagueService) Calendar(context.Context, string) (cricinfo.NormalizedResult, error) {
|
|
51
|
+
return f.calendarResult, nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func (f *fakeLeagueService) Athletes(context.Context, string, cricinfo.LeagueListOptions) (cricinfo.NormalizedResult, error) {
|
|
55
|
+
return f.athletesResult, nil
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func (f *fakeLeagueService) Standings(_ context.Context, leagueQuery string) (cricinfo.NormalizedResult, error) {
|
|
59
|
+
f.standingsQueries = append(f.standingsQueries, leagueQuery)
|
|
60
|
+
return f.standingsResult, nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func (f *fakeLeagueService) Seasons(context.Context, string) (cricinfo.NormalizedResult, error) {
|
|
64
|
+
return f.seasonsResult, nil
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func (f *fakeLeagueService) SeasonShow(_ context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
68
|
+
f.seasonShowQueries = append(f.seasonShowQueries, leagueQuery)
|
|
69
|
+
f.seasonShowOpts = append(f.seasonShowOpts, opts)
|
|
70
|
+
return f.seasonShow, nil
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func (f *fakeLeagueService) SeasonTypes(_ context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
74
|
+
f.seasonTypeQueries = append(f.seasonTypeQueries, leagueQuery)
|
|
75
|
+
f.seasonTypeOpts = append(f.seasonTypeOpts, opts)
|
|
76
|
+
return f.seasonTypes, nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
func (f *fakeLeagueService) SeasonGroups(_ context.Context, leagueQuery string, opts cricinfo.SeasonLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
80
|
+
f.seasonGroupQueries = append(f.seasonGroupQueries, leagueQuery)
|
|
81
|
+
f.seasonGroupOpts = append(f.seasonGroupOpts, opts)
|
|
82
|
+
return f.seasonGroups, nil
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func TestLeaguesAndStandingsCommandsResolveLeagueName(t *testing.T) {
|
|
86
|
+
service := &fakeLeagueService{
|
|
87
|
+
showResult: cricinfo.NewDataResult(cricinfo.EntityLeague, cricinfo.League{
|
|
88
|
+
ID: "19138",
|
|
89
|
+
Name: "Mirwais Nika Provincial 3-Day",
|
|
90
|
+
Slug: "19138",
|
|
91
|
+
}),
|
|
92
|
+
standingsResult: cricinfo.NewListResult(cricinfo.EntityStandingsGroup, []any{
|
|
93
|
+
cricinfo.StandingsGroup{ID: "1", SeasonID: "2026", GroupID: "1"},
|
|
94
|
+
}),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
originalFactory := newLeagueService
|
|
98
|
+
newLeagueService = func() (leagueCommandService, error) { return service, nil }
|
|
99
|
+
defer func() {
|
|
100
|
+
newLeagueService = originalFactory
|
|
101
|
+
}()
|
|
102
|
+
|
|
103
|
+
var showOut bytes.Buffer
|
|
104
|
+
var showErr bytes.Buffer
|
|
105
|
+
if err := Run([]string{"leagues", "show", "Mirwais", "Nika", "--format", "json"}, &showOut, &showErr); err != nil {
|
|
106
|
+
t.Fatalf("Run leagues show error: %v", err)
|
|
107
|
+
}
|
|
108
|
+
if len(service.showQueries) != 1 || service.showQueries[0] != "Mirwais Nika" {
|
|
109
|
+
t.Fatalf("expected joined league alias query, got %+v", service.showQueries)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
var showPayload map[string]any
|
|
113
|
+
if err := json.Unmarshal(showOut.Bytes(), &showPayload); err != nil {
|
|
114
|
+
t.Fatalf("decode leagues show payload: %v", err)
|
|
115
|
+
}
|
|
116
|
+
if showPayload["kind"] != string(cricinfo.EntityLeague) {
|
|
117
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityLeague, showPayload["kind"])
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
var standingsOut bytes.Buffer
|
|
121
|
+
var standingsErr bytes.Buffer
|
|
122
|
+
if err := Run([]string{"standings", "show", "Mirwais", "Nika", "--format", "json"}, &standingsOut, &standingsErr); err != nil {
|
|
123
|
+
t.Fatalf("Run standings show error: %v", err)
|
|
124
|
+
}
|
|
125
|
+
if len(service.standingsQueries) != 1 || service.standingsQueries[0] != "Mirwais Nika" {
|
|
126
|
+
t.Fatalf("expected joined standings league query, got %+v", service.standingsQueries)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func TestSeasonsCommandsRequireAndPropagateSelectors(t *testing.T) {
|
|
131
|
+
service := &fakeLeagueService{
|
|
132
|
+
seasonShow: cricinfo.NewDataResult(cricinfo.EntitySeason, cricinfo.Season{
|
|
133
|
+
ID: "2025",
|
|
134
|
+
Year: 2025,
|
|
135
|
+
LeagueID: "19138",
|
|
136
|
+
}),
|
|
137
|
+
seasonTypes: cricinfo.NewListResult(cricinfo.EntitySeasonType, []any{
|
|
138
|
+
cricinfo.SeasonType{ID: "1", SeasonID: "2025", LeagueID: "19138", Name: "Regular Season"},
|
|
139
|
+
}),
|
|
140
|
+
seasonGroups: cricinfo.NewListResult(cricinfo.EntitySeasonGroup, []any{
|
|
141
|
+
cricinfo.SeasonGroup{ID: "1", SeasonID: "2025", TypeID: "1", LeagueID: "1479935"},
|
|
142
|
+
}),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
originalFactory := newLeagueService
|
|
146
|
+
newLeagueService = func() (leagueCommandService, error) { return service, nil }
|
|
147
|
+
defer func() {
|
|
148
|
+
newLeagueService = originalFactory
|
|
149
|
+
}()
|
|
150
|
+
|
|
151
|
+
var showOut bytes.Buffer
|
|
152
|
+
var showErr bytes.Buffer
|
|
153
|
+
if err := Run([]string{"seasons", "show", "Mirwais", "Nika", "--season", "2025", "--format", "json"}, &showOut, &showErr); err != nil {
|
|
154
|
+
t.Fatalf("Run seasons show error: %v", err)
|
|
155
|
+
}
|
|
156
|
+
if len(service.seasonShowOpts) != 1 || service.seasonShowOpts[0].SeasonQuery != "2025" {
|
|
157
|
+
t.Fatalf("expected season selector to be propagated, got %+v", service.seasonShowOpts)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
var typesOut bytes.Buffer
|
|
161
|
+
var typesErr bytes.Buffer
|
|
162
|
+
if err := Run([]string{"seasons", "types", "Mirwais", "Nika", "--season", "2025", "--format", "json"}, &typesOut, &typesErr); err != nil {
|
|
163
|
+
t.Fatalf("Run seasons types error: %v", err)
|
|
164
|
+
}
|
|
165
|
+
if len(service.seasonTypeOpts) != 1 || service.seasonTypeOpts[0].SeasonQuery != "2025" {
|
|
166
|
+
t.Fatalf("expected season selector for types, got %+v", service.seasonTypeOpts)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
var groupsOut bytes.Buffer
|
|
170
|
+
var groupsErr bytes.Buffer
|
|
171
|
+
if err := Run([]string{"seasons", "groups", "Mirwais", "Nika", "--season", "2025", "--type", "1", "--format", "json"}, &groupsOut, &groupsErr); err != nil {
|
|
172
|
+
t.Fatalf("Run seasons groups error: %v", err)
|
|
173
|
+
}
|
|
174
|
+
if len(service.seasonGroupOpts) != 1 {
|
|
175
|
+
t.Fatalf("expected one season groups invocation, got %+v", service.seasonGroupOpts)
|
|
176
|
+
}
|
|
177
|
+
if service.seasonGroupOpts[0].SeasonQuery != "2025" || service.seasonGroupOpts[0].TypeQuery != "1" {
|
|
178
|
+
t.Fatalf("expected season/type selectors to be propagated, got %+v", service.seasonGroupOpts[0])
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
var missingSeasonOut bytes.Buffer
|
|
182
|
+
var missingSeasonErr bytes.Buffer
|
|
183
|
+
err := Run([]string{"seasons", "show", "Mirwais", "Nika"}, &missingSeasonOut, &missingSeasonErr)
|
|
184
|
+
if err == nil || !strings.Contains(err.Error(), "--season is required") {
|
|
185
|
+
t.Fatalf("expected --season required error, got %v", err)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
var missingTypeOut bytes.Buffer
|
|
189
|
+
var missingTypeErr bytes.Buffer
|
|
190
|
+
err = Run([]string{"seasons", "groups", "Mirwais", "Nika", "--season", "2025"}, &missingTypeOut, &missingTypeErr)
|
|
191
|
+
if err == nil || !strings.Contains(err.Error(), "--type is required") {
|
|
192
|
+
t.Fatalf("expected --type required error, got %v", err)
|
|
193
|
+
}
|
|
194
|
+
}
|