From ec92985ad6d7a35e4c9ab08abc2f1ec7d0c8bc82 Mon Sep 17 00:00:00 2001 From: Matheus Pimenta Date: Mon, 24 Nov 2025 07:46:04 +0000 Subject: [PATCH] Add ListKimariteAPI Signed-off-by: Matheus Pimenta --- basho.go | 37 ++- basho_test.go | 144 +++++++++ client.go | 1 + kimarite.go | 8 + list_kimarite.go | 45 +++ list_kimarite_test.go | 381 ++++++++++++++++++++++++ match.go | 10 +- measurement.go | 2 +- rank.go | 2 +- rikishi_change.go | 27 +- shikona.go | 2 +- tests/integration/list_kimarite_test.go | 37 +++ 12 files changed, 673 insertions(+), 23 deletions(-) create mode 100644 kimarite.go create mode 100644 list_kimarite.go create mode 100644 list_kimarite_test.go create mode 100644 tests/integration/list_kimarite_test.go diff --git a/basho.go b/basho.go index 05f91c6..85983e6 100644 --- a/basho.go +++ b/basho.go @@ -6,12 +6,18 @@ import ( "strconv" ) -// BashoID represents the unique identifier for a Basho (sumo tournament). +// BashoID represents the unique identifier for a basho. type BashoID struct { Year int Month int // Month is 1-12. } +// BashoDayID represents a specific day within a basho (1-15), or a playoff match starting from 16. +type BashoDayID struct { + BashoID + Day int +} + func (b BashoID) String() string { return fmt.Sprintf("%04d%02d", b.Year, b.Month) } @@ -40,3 +46,32 @@ func (b *BashoID) UnmarshalJSON(data []byte) error { b.Month = month return nil } + +func (b BashoDayID) String() string { + return fmt.Sprintf("%s-%d", b.BashoID.String(), b.Day) +} + +func (b BashoDayID) MarshalJSON() ([]byte, error) { + return []byte(`"` + b.String() + `"`), nil +} + +func (b *BashoDayID) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return fmt.Errorf("error unmarshaling BashoDayID: %w", err) + } + if len(s) < 8 { + return fmt.Errorf("invalid BashoDayID format: %s", s) + } + var bashoID BashoID + if err := bashoID.UnmarshalJSON([]byte(`"` + s[0:6] + `"`)); err != nil { + return fmt.Errorf("error parsing BashoID from BashoDayID: %w", err) + } + day, err := strconv.Atoi(s[7:]) + if err != nil { + return fmt.Errorf("error parsing day from BashoDayID: %w", err) + } + b.BashoID = bashoID + b.Day = day + return nil +} diff --git a/basho_test.go b/basho_test.go index 0925ced..36ec694 100644 --- a/basho_test.go +++ b/basho_test.go @@ -103,3 +103,147 @@ func TestUnmarshalBashoIDFromJSON(t *testing.T) { g.Expect(w.Basho).To(Equal(sumoapi.BashoID{Year: 2023, Month: 11})) }) } + +func TestMarshalBashoDayIDToJSON(t *testing.T) { + for _, tt := range []struct { + name string + bashoDayID sumoapi.BashoDayID + expectedJSON string + }{ + { + name: "double digit month, single digit day", + bashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2023, Month: 11}, + Day: 1, + }, + expectedJSON: `"202311-1"`, + }, + { + name: "single digit month, double digit day", + bashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 1999, Month: 3}, + Day: 15, + }, + expectedJSON: `"199903-15"`, + }, + { + name: "playoff day", + bashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2025, Month: 1}, + Day: 16, + }, + expectedJSON: `"202501-16"`, + }, + } { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + b, err := json.Marshal(tt.bashoDayID) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(string(b)).To(Equal(tt.expectedJSON)) + }) + } + + t.Run("marshal inside struct", func(t *testing.T) { + g := NewWithT(t) + type wrapper struct { + BashoDay sumoapi.BashoDayID `json:"bashoDay"` + } + w := wrapper{ + BashoDay: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2023, Month: 11}, + Day: 1, + }, + } + b, err := json.Marshal(w) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(string(b)).To(Equal(`{"bashoDay":"202311-1"}`)) + }) +} + +func TestUnmarshalBashoDayIDFromJSON(t *testing.T) { + for _, tt := range []struct { + name string + jsonData string + expectedBashoDayID sumoapi.BashoDayID + expectError bool + }{ + { + name: "double digit month, single digit day", + jsonData: `"202311-1"`, + expectedBashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2023, Month: 11}, + Day: 1, + }, + expectError: false, + }, + { + name: "single digit month, double digit day", + jsonData: `"199903-15"`, + expectedBashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 1999, Month: 3}, + Day: 15, + }, + expectError: false, + }, + { + name: "playoff day", + jsonData: `"202501-16"`, + expectedBashoDayID: sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2025, Month: 1}, + Day: 16, + }, + expectError: false, + }, + { + name: "invalid basho format", + jsonData: `"20A311-1"`, + expectedBashoDayID: sumoapi.BashoDayID{}, + expectError: true, + }, + { + name: "wrong length - too short", + jsonData: `"202311"`, + expectedBashoDayID: sumoapi.BashoDayID{}, + expectError: true, + }, + { + name: "missing day", + jsonData: `"202311-"`, + expectedBashoDayID: sumoapi.BashoDayID{}, + expectError: true, + }, + { + name: "invalid day - not a number", + jsonData: `"202311-X"`, + expectedBashoDayID: sumoapi.BashoDayID{}, + expectError: true, + }, + } { + t.Run(tt.name, func(t *testing.T) { + g := NewWithT(t) + var b sumoapi.BashoDayID + err := json.Unmarshal([]byte(tt.jsonData), &b) + if tt.expectError { + g.Expect(err).To(HaveOccurred()) + } else { + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(b).To(Equal(tt.expectedBashoDayID)) + } + }) + } + + t.Run("unmarshal inside struct", func(t *testing.T) { + g := NewWithT(t) + type wrapper struct { + BashoDay sumoapi.BashoDayID `json:"bashoDay"` + } + jsonData := `{"bashoDay":"202311-1"}` + var w wrapper + err := json.Unmarshal([]byte(jsonData), &w) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(w.BashoDay).To(Equal(sumoapi.BashoDayID{ + BashoID: sumoapi.BashoID{Year: 2023, Month: 11}, + Day: 1, + })) + }) +} diff --git a/client.go b/client.go index 5ab442c..e61459d 100644 --- a/client.go +++ b/client.go @@ -17,6 +17,7 @@ type Client interface { GetRikishiStatsAPI ListRikishiMatchesAPI ListRikishiMatchesAgainstOpponentAPI + ListKimariteAPI ListKimariteMatchesAPI ListMeasurementChangesAPI ListRankChangesAPI diff --git a/kimarite.go b/kimarite.go new file mode 100644 index 0000000..2af0694 --- /dev/null +++ b/kimarite.go @@ -0,0 +1,8 @@ +package sumoapi + +// Kimarite represents a sumo wrestling winning technique. +type Kimarite struct { + Name string `json:"kimarite" jsonschema:"The kimarite (winning technique) name."` + Count int `json:"count" jsonschema:"The number of times this kimarite (winning technique) has been used in a match."` + LastUsage BashoDayID `json:"lastUsage" jsonschema:"The basho (sumo tournament) day (1-15) when this kimarite (winning technique) was last used in the format YYYYMM-{day}. A day value of 16 or higher represents an individual playoff match."` +} diff --git a/list_kimarite.go b/list_kimarite.go new file mode 100644 index 0000000..edc6f79 --- /dev/null +++ b/list_kimarite.go @@ -0,0 +1,45 @@ +package sumoapi + +import ( + "context" + "fmt" + "net/url" +) + +// ListKimariteAPI defines the methods available for listing kimarite. +type ListKimariteAPI interface { + // ListKimarite calls the GET /api/kimarite endpoint. + ListKimarite(ctx context.Context, req ListKimariteRequest) (*ListKimariteResponse, error) +} + +// ListKimariteRequest represents the request parameters for the ListKimarite method. +type ListKimariteRequest struct { + SortField string `json:"sortField" jsonschema:"The field by which to sort the results. Valid values are 'kimarite', 'count' and 'lastUsage'."` + SortOrder string `json:"sortOrder,omitempty" jsonschema:"The order in which to sort the results by the sort field. Valid values are 'asc' for ascending and 'desc' for descending. Default is 'asc'."` + Limit int `json:"limit,omitempty" jsonschema:"The maximum number of results to return."` + Skip int `json:"skip,omitempty" jsonschema:"The number of results to skip over for pagination."` +} + +// ListKimariteResponse represents the response from the ListKimarite method. +type ListKimariteResponse struct { + Limit int `json:"limit" jsonschema:"The maximum number of results that were returned."` + Skip int `json:"skip" jsonschema:"The number of results that were skipped over."` + SortField string `json:"sortField" jsonschema:"The field by which the results are sorted. Values are 'kimarite', 'count' and 'lastUsage'."` + SortOrder string `json:"sortOrder" jsonschema:"The order in which the results are sorted by the sort field. Values are 'asc' for ascending and 'desc' for descending."` + Kimarite []Kimarite `json:"records" jsonschema:"The list of kimarite (winning technique) records matching the filters."` +} + +func (c *client) ListKimarite(ctx context.Context, req ListKimariteRequest) (*ListKimariteResponse, error) { + query := make(url.Values) + query.Set("sortField", req.SortField) + if order := getSortOrder(req.SortOrder); order != "" { + query.Set("sortOrder", order) + } + if req.Limit > 0 { + query.Set("limit", fmt.Sprint(req.Limit)) + } + if req.Skip > 0 { + query.Set("skip", fmt.Sprint(req.Skip)) + } + return getObject[ListKimariteResponse](ctx, c, "/kimarite", query) +} diff --git a/list_kimarite_test.go b/list_kimarite_test.go new file mode 100644 index 0000000..0ce5f80 --- /dev/null +++ b/list_kimarite_test.go @@ -0,0 +1,381 @@ +package sumoapi_test + +import ( + "context" + "io" + "net/http" + "strings" + "testing" + + . "github.com/onsi/gomega" + + "github.com/sumo-mcp/sumoapi-go" +) + +func TestClient_ListKimarite(t *testing.T) { + t.Run("list by kimarite sort field", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "kimarite", + "sortOrder": "asc", + "records": [ + { + "kimarite": "hatakikomi", + "count": 1234, + "lastUsage": "202501-5" + }, + { + "kimarite": "oshidashi", + "count": 5678, + "lastUsage": "202501-10" + } + ] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.Method).To(Equal(http.MethodGet)) + g.Expect(req.URL.Scheme).To(Equal("https")) + g.Expect(req.URL.Host).To(Equal("sumo-api.com")) + g.Expect(req.URL.Path).To(Equal("/api/kimarite")) + g.Expect(req.URL.Query().Get("sortField")).To(Equal("kimarite")) + g.Expect(req.URL.Query().Has("sortOrder")).To(BeFalse()) + g.Expect(req.URL.Query().Has("limit")).To(BeFalse()) + g.Expect(req.URL.Query().Has("skip")).To(BeFalse()) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "kimarite", + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortField).To(Equal("kimarite")) + g.Expect(resp.SortOrder).To(Equal("asc")) + g.Expect(resp.Kimarite).To(HaveLen(2)) + g.Expect(resp.Kimarite[0].Name).To(Equal("hatakikomi")) + g.Expect(resp.Kimarite[0].Count).To(Equal(1234)) + g.Expect(resp.Kimarite[1].Name).To(Equal("oshidashi")) + g.Expect(resp.Kimarite[1].Count).To(Equal(5678)) + }) + + t.Run("list by count sort field", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "count", + "sortOrder": "asc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("sortField")).To(Equal("count")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "count", + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortField).To(Equal("count")) + }) + + t.Run("list by lastUsage sort field", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "lastUsage", + "sortOrder": "asc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("sortField")).To(Equal("lastUsage")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "lastUsage", + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortField).To(Equal("lastUsage")) + }) + + t.Run("list with sort order asc", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "count", + "sortOrder": "asc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("sortOrder")).To(Equal("asc")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "count", + SortOrder: "asc", + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortOrder).To(Equal("asc")) + }) + + t.Run("list with sort order desc", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "count", + "sortOrder": "desc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("sortOrder")).To(Equal("desc")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "count", + SortOrder: "desc", + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortOrder).To(Equal("desc")) + }) + + t.Run("list with limit", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 10, + "skip": 0, + "sortField": "kimarite", + "sortOrder": "asc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("limit")).To(Equal("10")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "kimarite", + Limit: 10, + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.Limit).To(Equal(10)) + }) + + t.Run("list with skip", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 5, + "sortField": "kimarite", + "sortOrder": "asc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.URL.Query().Get("skip")).To(Equal("5")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "kimarite", + Skip: 5, + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.Skip).To(Equal(5)) + }) + + t.Run("list with all parameters", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 20, + "skip": 10, + "sortField": "count", + "sortOrder": "desc", + "records": [] + }` + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + query := req.URL.Query() + g.Expect(query.Get("sortField")).To(Equal("count")) + g.Expect(query.Get("sortOrder")).To(Equal("desc")) + g.Expect(query.Get("limit")).To(Equal("20")) + g.Expect(query.Get("skip")).To(Equal("10")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "count", + SortOrder: "desc", + Limit: 20, + Skip: 10, + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + g.Expect(resp.SortField).To(Equal("count")) + g.Expect(resp.SortOrder).To(Equal("desc")) + g.Expect(resp.Limit).To(Equal(20)) + g.Expect(resp.Skip).To(Equal(10)) + }) + + t.Run("context is propagated", func(t *testing.T) { + g := NewWithT(t) + + mockResp := `{ + "limit": 0, + "skip": 0, + "sortField": "kimarite", + "sortOrder": "asc", + "records": [] + }` + + type testKey struct{} + ctx := context.WithValue(context.Background(), testKey{}, "test-value") + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + g.Expect(req.Context().Value(testKey{})).To(Equal("test-value")) + return nil + }, + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(mockResp)), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + _, err := client.ListKimarite(ctx, sumoapi.ListKimariteRequest{ + SortField: "kimarite", + }) + + g.Expect(err).ToNot(HaveOccurred()) + }) + + t.Run("http request error", func(t *testing.T) { + g := NewWithT(t) + + transport := &mockTransport{ + validateRequest: func(req *http.Request) error { + return http.ErrAbortHandler + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "kimarite", + }) + + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("error making http request")) + g.Expect(resp).To(BeNil()) + }) + + t.Run("invalid JSON response", func(t *testing.T) { + g := NewWithT(t) + + transport := &mockTransport{ + response: &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader("not valid json")), + }, + } + + client := sumoapi.New(sumoapi.WithHTTPClient(&http.Client{Transport: transport})) + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "kimarite", + }) + + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("error unmarshaling response body")) + g.Expect(resp).To(BeNil()) + }) +} diff --git a/match.go b/match.go index fb3a6fc..48156b0 100644 --- a/match.go +++ b/match.go @@ -7,11 +7,11 @@ import ( // Match represents a sumo match. type Match struct { - // ID is optional because it's not returned by the APIs for listing rikishi matches. - ID *MatchID `json:"id,omitempty" jsonschema:"The unique identifier for the match in the format YYYYMM-D-NUM-EASTID-WESTID."` + // ID is optional because it's not returned by the APIs for listing rikishi matches (possibly a bug). + ID *MatchID `json:"id,omitempty" jsonschema:"The unique identifier for the match in the format YYYYMM-{day}-{matchNo}-{eastId}-{westId}."` BashoID BashoID `json:"bashoId" jsonschema:"The ID of the basho (sumo tournament) in which the match took place, in the format YYYYMM."` Division string `json:"division" jsonschema:"The division in which the match took place."` - Day int `json:"day" jsonschema:"The day of the basho (sumo tournament) on which the match took place."` + Day int `json:"day" jsonschema:"The day of the basho (sumo tournament) on which the match took place (1-15), or a playoff match starting from 16."` MatchNumber int `json:"matchNo,omitempty" jsonschema:"The number of the match on the given day."` EastID int `json:"eastId,omitempty" jsonschema:"The unique identifier for the rikishi (sumo wrestler) on the east side."` EastShikona string `json:"eastShikona,omitempty" jsonschema:"The shikona (ring name) in English of the rikishi (sumo wrestler) on the east side."` @@ -47,14 +47,14 @@ func (m *MatchID) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("error unmarshaling MatchID: %w", err) } - if s == "" { // Match ID is optional in some APIs. + if s == "" { // Match ID is optional. *m = MatchID{} return nil } - var bashoID BashoID if len(s) < 11 { return fmt.Errorf("invalid MatchID format: %s", s) } + var bashoID BashoID if err := bashoID.UnmarshalJSON([]byte(`"` + s[0:6] + `"`)); err != nil { return fmt.Errorf("error parsing BashoID from MatchID: %w", err) } diff --git a/measurement.go b/measurement.go index 234364d..0d6899a 100644 --- a/measurement.go +++ b/measurement.go @@ -1,6 +1,6 @@ package sumoapi -// Measurement represents a sumo wrestler's measurement in a specific Basho (sumo tournament). +// Measurement represents a rikishi's measurement in a specific basho. type Measurement struct { ID RikishiChangeID `json:"id" jsonschema:"The unique identifier for the measurement in the format {bashoID}-{rikishiID} where {bashoID} is in the format YYYYMM and {rikishiID} is the unique identifier for the rikishi in the API."` BashoID BashoID `json:"bashoId" jsonschema:"The ID of the basho (sumo tournament) in the format YYYYMM."` diff --git a/rank.go b/rank.go index d0348cb..9d878b6 100644 --- a/rank.go +++ b/rank.go @@ -1,6 +1,6 @@ package sumoapi -// Rank represents a sumo wrestler's rank in a specific Basho (sumo tournament). +// Rank represents a rikishi's rank in a specific basho. type Rank struct { ID RikishiChangeID `json:"id" jsonschema:"The unique identifier for the rank in the format {bashoID}-{rikishiID} where {bashoID} is in the format YYYYMM and {rikishiID} is the unique identifier for the rikishi in the API."` BashoID BashoID `json:"bashoId" jsonschema:"The ID of the basho (sumo tournament) in the format YYYYMM."` diff --git a/rikishi_change.go b/rikishi_change.go index eddf9c6..ca27042 100644 --- a/rikishi_change.go +++ b/rikishi_change.go @@ -6,17 +6,21 @@ import ( "strconv" ) -// RikishiChangeID represents the unique identifier for a Rikishi change in a specific Basho (sumo tournament). +// RikishiChangeID represents the unique identifier for a rikishi change in a specific basho. type RikishiChangeID struct { BashoID RikishiID int } -func (b RikishiChangeID) MarshalJSON() ([]byte, error) { - return []byte(`"` + fmt.Sprintf("%04d%02d-%d", b.Year, b.Month, b.RikishiID) + `"`), nil +func (r RikishiChangeID) String() string { + return fmt.Sprintf("%s-%d", r.BashoID.String(), r.RikishiID) } -func (b *RikishiChangeID) UnmarshalJSON(data []byte) error { +func (r RikishiChangeID) MarshalJSON() ([]byte, error) { + return []byte(`"` + r.String() + `"`), nil +} + +func (r *RikishiChangeID) UnmarshalJSON(data []byte) error { var s string if err := json.Unmarshal(data, &s); err != nil { return fmt.Errorf("error unmarshaling RikishiChangeID: %w", err) @@ -24,20 +28,15 @@ func (b *RikishiChangeID) UnmarshalJSON(data []byte) error { if len(s) < 7 { return fmt.Errorf("invalid RikishiChangeID format: %s", s) } - year, err := strconv.Atoi(s[0:4]) - if err != nil { - return fmt.Errorf("error parsing year from RikishiChangeID: %w", err) - } - month, err := strconv.Atoi(s[4:6]) - if err != nil { - return fmt.Errorf("error parsing month from RikishiChangeID: %w", err) + var bashoID BashoID + if err := bashoID.UnmarshalJSON([]byte(`"` + s[0:6] + `"`)); err != nil { + return fmt.Errorf("error parsing BashoID from RikishiChangeID: %w", err) } - b.Year = year - b.Month = month + r.BashoID = bashoID rikishiID, err := strconv.Atoi(s[7:]) if err != nil { return fmt.Errorf("error parsing RikishiID from RikishiChangeID: %w", err) } - b.RikishiID = rikishiID + r.RikishiID = rikishiID return nil } diff --git a/shikona.go b/shikona.go index d900e91..7bea55b 100644 --- a/shikona.go +++ b/shikona.go @@ -1,6 +1,6 @@ package sumoapi -// Shikona represents a sumo wrestler's shikona (ring name) in a specific Basho (sumo tournament). +// Shikona represents a rikishi's shikona in a specific basho. type Shikona struct { ID RikishiChangeID `json:"id" jsonschema:"The unique identifier for the shikona in the format {bashoID}-{rikishiID} where {bashoID} is in the format YYYYMM and {rikishiID} is the unique identifier for the rikishi in the API."` BashoID BashoID `json:"bashoId" jsonschema:"The ID of the basho (sumo tournament) in the format YYYYMM."` diff --git a/tests/integration/list_kimarite_test.go b/tests/integration/list_kimarite_test.go new file mode 100644 index 0000000..4e6a69c --- /dev/null +++ b/tests/integration/list_kimarite_test.go @@ -0,0 +1,37 @@ +package integration_test + +import ( + "context" + "testing" + + . "github.com/onsi/gomega" + + "github.com/sumo-mcp/sumoapi-go" +) + +func TestIntegration_ListKimarite(t *testing.T) { + g := NewWithT(t) + + client := sumoapi.New() + + resp, err := client.ListKimarite(context.Background(), sumoapi.ListKimariteRequest{ + SortField: "count", + SortOrder: "desc", + Limit: 1, + Skip: 1, + }) + + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(resp).ToNot(BeNil()) + + g.Expect(resp.SortField).To(Equal("count")) + g.Expect(resp.SortOrder).To(Equal("desc")) + g.Expect(resp.Limit).To(Equal(1)) + g.Expect(resp.Skip).To(Equal(1)) + g.Expect(resp.Kimarite).To(HaveLen(1)) + + k := resp.Kimarite[0] + g.Expect(k.Count).To(BeNumerically(">", 0)) + g.Expect(k.LastUsage.BashoID).ToNot(BeZero()) + g.Expect(k.LastUsage.Day).To(BeNumerically(">", 0)) +}