Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
// Client is a client for the Sumo API.
type Client interface {
SearchRikishisAPI
GetRikishiAPI
ListRankChangesAPI
ListShikonaChangesAPI
ListMeasurementChangesAPI
Expand Down
36 changes: 36 additions & 0 deletions get_rikishi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package sumoapi

import (
"context"
"fmt"
"net/url"
)

// GetRikishiAPI defines the methods available for retrieving a single Rikishi.
type GetRikishiAPI interface {
// GetRikishi calls the GET /api/rikishi/{rikishiID} endpoint.
GetRikishi(ctx context.Context, req GetRikishiRequest) (*Rikishi, error)
}

// GetRikishiRequest represents the request parameters for the GetRikishi method.
type GetRikishiRequest struct {
RikishiID int `json:"rikishiId" jsonschema:"The unique identifier of the Rikishi to retrieve. Example: 45 = Terunofuji"`
IncludeRanks bool `json:"includeRanks,omitempty" jsonschema:"Whether to include rank records over time in the Rikishi data."`
IncludeShikonas bool `json:"includeShikonas,omitempty" jsonschema:"Whether to include shikona (ring name) records over time in the Rikishi data."`
IncludeMeasurements bool `json:"includeMeasurements,omitempty" jsonschema:"Whether to include measurement records over time in the Rikishi data."`
}

func (c *client) GetRikishi(ctx context.Context, req GetRikishiRequest) (*Rikishi, error) {
query := make(url.Values)
if req.IncludeMeasurements {
query.Set("measurements", "true")
}
if req.IncludeRanks {
query.Set("ranks", "true")
}
if req.IncludeShikonas {
query.Set("shikonas", "true")
}
path := fmt.Sprintf("/rikishi/%d", req.RikishiID)
return getObject[Rikishi](ctx, c, path, query)
}
285 changes: 285 additions & 0 deletions get_rikishi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
package sumoapi_test

import (
"context"
"io"
"net/http"
"strings"
"testing"

. "github.com/onsi/gomega"

"github.com/sumo-mcp/sumoapi-go"
)

func TestClient_GetRikishi(t *testing.T) {
t.Run("get rikishi by ID", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji",
"shikonaJp": "照ノ富士",
"currentRank": "Yokozuna",
"heya": "Isegahama"
}`

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/rikishi/45"))
g.Expect(req.URL.Query().Has("measurements")).To(BeFalse())
g.Expect(req.URL.Query().Has("ranks")).To(BeFalse())
g.Expect(req.URL.Query().Has("shikonas")).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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
g.Expect(resp.ID).To(Equal(45))
g.Expect(resp.ShikonaEnglish).To(Equal("Terunofuji"))
g.Expect(resp.ShikonaJapanese).To(Equal("照ノ富士"))
g.Expect(resp.CurrentRank).To(Equal("Yokozuna"))
g.Expect(resp.Heya).To(Equal("Isegahama"))
})

t.Run("get rikishi with include ranks", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji",
"rankHistory": [
{
"id": "202501-45",
"bashoId": "202501",
"rikishiId": 45,
"rank": "Yokozuna 1 East"
}
]
}`

transport := &mockTransport{
validateRequest: func(req *http.Request) error {
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
g.Expect(req.URL.Query().Get("ranks")).To(Equal("true"))
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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
IncludeRanks: true,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
g.Expect(resp.RankHistory).To(HaveLen(1))
g.Expect(resp.RankHistory[0].RikishiID).To(Equal(45))
})

t.Run("get rikishi with include shikonas", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji",
"shikonaHistory": [
{
"id": "202501-45",
"bashoId": "202501",
"rikishiId": 45,
"shikonaEn": "Terunofuji",
"shikonaJp": "照ノ富士"
}
]
}`

transport := &mockTransport{
validateRequest: func(req *http.Request) error {
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
g.Expect(req.URL.Query().Get("shikonas")).To(Equal("true"))
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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
IncludeShikonas: true,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
g.Expect(resp.ShikonaHistory).To(HaveLen(1))
g.Expect(resp.ShikonaHistory[0].RikishiID).To(Equal(45))
})

t.Run("get rikishi with include measurements", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji",
"measurementHistory": [
{
"id": "202501-45",
"bashoId": "202501",
"rikishiId": 45,
"height": 193.0,
"weight": 180.0
}
]
}`

transport := &mockTransport{
validateRequest: func(req *http.Request) error {
g.Expect(req.URL.Path).To(Equal("/api/rikishi/45"))
g.Expect(req.URL.Query().Get("measurements")).To(Equal("true"))
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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
IncludeMeasurements: true,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
g.Expect(resp.MeasurementHistory).To(HaveLen(1))
g.Expect(resp.MeasurementHistory[0].RikishiID).To(Equal(45))
})

t.Run("get rikishi with all includes", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji",
"rankHistory": [],
"shikonaHistory": [],
"measurementHistory": []
}`

transport := &mockTransport{
validateRequest: func(req *http.Request) error {
query := req.URL.Query()
g.Expect(query.Get("ranks")).To(Equal("true"))
g.Expect(query.Get("shikonas")).To(Equal("true"))
g.Expect(query.Get("measurements")).To(Equal("true"))
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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
IncludeRanks: true,
IncludeShikonas: true,
IncludeMeasurements: true,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
})

t.Run("context is propagated", func(t *testing.T) {
g := NewWithT(t)

mockResp := `{
"id": 45,
"shikonaEn": "Terunofuji"
}`

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.GetRikishi(ctx, sumoapi.GetRikishiRequest{
RikishiID: 45,
})

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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
})

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.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
})

g.Expect(err).To(HaveOccurred())
g.Expect(err.Error()).To(ContainSubstring("error unmarshaling response body"))
g.Expect(resp).To(BeNil())
})
}
34 changes: 34 additions & 0 deletions tests/integration/get_rikishi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package integration_test

import (
"context"
"testing"

. "github.com/onsi/gomega"

"github.com/sumo-mcp/sumoapi-go"
)

func TestIntegration_GetRikishi(t *testing.T) {
g := NewWithT(t)

client := sumoapi.New()

resp, err := client.GetRikishi(context.Background(), sumoapi.GetRikishiRequest{
RikishiID: 45,
IncludeRanks: true,
IncludeShikonas: true,
IncludeMeasurements: true,
})

g.Expect(err).ToNot(HaveOccurred())
g.Expect(resp).ToNot(BeNil())
g.Expect(resp.ID).To(Equal(45))
g.Expect(resp.ShikonaEnglish).To(Equal("Terunofuji Haruo"))
g.Expect(resp.ShikonaJapanese).To(Equal("照ノ富士 春雄"))
g.Expect(resp.Heya).To(Equal("Isegahama"))

g.Expect(resp.RankHistory).ToNot(BeEmpty())
g.Expect(resp.ShikonaHistory).ToNot(BeEmpty())
g.Expect(resp.MeasurementHistory).ToNot(BeEmpty())
}
2 changes: 2 additions & 0 deletions tests/integration/list_rank_changes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ func TestIntegration_ListRankChanges(t *testing.T) {

client := sumoapi.New()

// Here we test a specific Rikishi that had rank changes to make sure the API
// is returning exactly one change when filtering by RikishiID and BashoID.
resp, err := client.ListRankChanges(context.Background(), sumoapi.ListRikishiChangesRequest{
RikishiID: 3081,
BashoID: &sumoapi.BashoID{
Expand Down