Skip to content

Commit

Permalink
Adds method to retrieve team data + test cases. Refs #1
Browse files Browse the repository at this point in the history
  • Loading branch information
ariel17 committed Mar 6, 2023
1 parent abc26a5 commit 6e37bf7
Show file tree
Hide file tree
Showing 3 changed files with 630 additions and 15 deletions.
60 changes: 51 additions & 9 deletions pkg/clients/football.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ package clients
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
)

const BASE_API_URL = "http://api.football-data.org/v4/"

var (
client *http.Client
)

type Area struct {
ID int64 `json:"id"`
Name string `json:"name"`
Expand All @@ -24,13 +29,25 @@ type League struct {
Type string `json:"type"`
}

type Team struct {
Area Area `json:"area"`
ID int64 `json:"id"`
Name string `json:"name"`
ShortName string `json:"shortName"`
TLA string `json:"tla"`
Address string `json:"address"`
}

// FootballAPIClient is the behavior contract that every implementation must
// comply. It offers access to football-data.org data with handy methods. It is
// NOT a full client implementation but access to required resources.
type FootballAPIClient interface {

// GetLeagueByCode retrieves league data by its code.
GetLeagueByCode(code string) (*League, error)
GetTeamByID(id int64) (*Team, error)

// TODO GetTeam
// TODO GetPlayer
// TODO GetCoach
}

// NewFootballAPIClient creates a new instance of real API client.
Expand All @@ -40,10 +57,8 @@ func NewFootballAPIClient(apiKey string) FootballAPIClient {
}
return &realAPIClient{
baseURL: BASE_API_URL,
client: &http.Client{
Timeout: time.Second,
},
apiKey: apiKey,
client: client,
apiKey: apiKey,
}
}

Expand All @@ -53,9 +68,7 @@ type realAPIClient struct {
apiKey string
}

func (r *realAPIClient) GetLeagueByCode(code string) (*League, error) {
url := r.baseURL + "/competitions/" + code

func (r *realAPIClient) get(url string) ([]byte, error) {
request, _ := http.NewRequest(http.MethodGet, url, nil)
request.Header.Set("X-Auth-Token", r.apiKey)

Expand All @@ -75,10 +88,39 @@ func (r *realAPIClient) GetLeagueByCode(code string) (*League, error) {
return nil, errors.New("failed to retrieve content: " + string(body))
}

return body, nil
}

func (r *realAPIClient) GetLeagueByCode(code string) (*League, error) {
url := fmt.Sprintf("%s/competitions/%s", r.baseURL, code)
body, err := r.get(url)
if err != nil {
return nil, err
}
league := League{}
err = json.Unmarshal(body, &league)
if err != nil {
return nil, err
}
return &league, nil
}

func (r *realAPIClient) GetTeamByID(id int64) (*Team, error) {
url := fmt.Sprintf("%s/teams/%d", r.baseURL, id)
body, err := r.get(url)
if err != nil {
return nil, err
}
team := Team{}
err = json.Unmarshal(body, &team)
if err != nil {
return nil, err
}
return &team, nil
}

func init() {
client = &http.Client{
Timeout: time.Second,
}
}
61 changes: 55 additions & 6 deletions pkg/clients/football_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package clients

import (
"fmt"
"net/http"
"net/http/httptest"
"os"
Expand All @@ -11,6 +12,13 @@ import (
"github.com/stretchr/testify/assert"
)

var (
httpClient = &http.Client{
Timeout: time.Second,
}
apiKey = "abc123"
)

func TestNewFootballAPIClient(t *testing.T) {
t.Run("fails without api key", func(t *testing.T) {
defer func() {
Expand All @@ -22,7 +30,7 @@ func TestNewFootballAPIClient(t *testing.T) {
})

t.Run("ok", func(t *testing.T) {
NewFootballAPIClient("abc123")
NewFootballAPIClient(apiKey)
})
}

Expand All @@ -45,10 +53,8 @@ func TestGetLeagueByCode(t *testing.T) {

c := &realAPIClient{
baseURL: server.URL,
client: &http.Client{
Timeout: time.Second,
},
apiKey: "abc123",
client: httpClient,
apiKey: apiKey,
}
response, err := c.GetLeagueByCode(tc.code)
assert.Equal(t, err == nil, tc.isSuccess)
Expand All @@ -65,9 +71,52 @@ func TestGetLeagueByCode(t *testing.T) {
}
}

func TestGetTeamByID(t *testing.T) {
client = &http.Client{
Timeout: time.Second,
}

testCases := []struct {
name string
id int64
statusCode int
isSuccess bool
}{
{"ok", 2061, 200, true},
{"not found", 999, 404, false},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
apiContent := loadGoldenFile(t.Name())
server := newTestServer(fmt.Sprintf("/teams/%d", tc.id), tc.statusCode, apiContent)
defer server.Close()

c := &realAPIClient{
baseURL: server.URL,
client: httpClient,
apiKey: apiKey,
}
response, err := c.GetTeamByID(tc.id)
assert.Equal(t, err == nil, tc.isSuccess)
assert.Equal(t, response != nil, tc.isSuccess)

if tc.isSuccess {
assert.Equal(t, "CA Boca Juniors", response.Name)
assert.Equal(t, 2061, response.ID)
assert.Equal(t, "Boca Juniors", response.ShortName)
assert.Equal(t, "BOC", response.TLA)
assert.Equal(t, "Brandsen 805, La Boca Buenos Aires, Buenos Aires 1161", response.Address)
assert.Equal(t, "Argentina", response.Area.Name)
} else {
assert.True(t, strings.Contains(err.Error(), "failed to retrieve content:"))
}
})
}
}

// loadGoldenFiles uses the test name to load a JSON value as expected result.
func loadGoldenFile(testName string) []byte {
testName = strings.ReplaceAll(testName, " ", "_")
content, err := os.ReadFile("./golden/" + testName + ".json")
if err != nil {
panic(err)
Expand Down

0 comments on commit 6e37bf7

Please sign in to comment.