Skip to content

Commit

Permalink
Adds client implementation for football API and test cases (league da…
Browse files Browse the repository at this point in the history
…ta). Refs #1
  • Loading branch information
ariel17 committed Mar 6, 2023
1 parent 7b1a63a commit d8a5e69
Show file tree
Hide file tree
Showing 4 changed files with 1,767 additions and 0 deletions.
84 changes: 84 additions & 0 deletions pkg/clients/football.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package clients

import (
"encoding/json"
"errors"
"io"
"net/http"
"time"
)

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

type Area struct {
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
}

type League struct {
Area Area `json:"area"`
ID int64 `json:"id"`
Name string `json:"name"`
Code string `json:"code"`
Type string `json:"type"`
}

// 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)
}

// NewFootballAPIClient creates a new instance of real API client.
func NewFootballAPIClient(apiKey string) FootballAPIClient {
if apiKey == "" {
panic("cannot work without a key")
}
return &realAPIClient{
baseURL: BASE_API_URL,
client: &http.Client{
Timeout: time.Second,
},
apiKey: apiKey,
}
}

type realAPIClient struct {
baseURL string
client *http.Client
apiKey string
}

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

request, _ := http.NewRequest(http.MethodGet, url, nil)
request.Header.Set("X-Auth-Token", r.apiKey)

response, err := r.client.Do(request)
if err != nil {
return nil, err
}

var body []byte
defer response.Body.Close()
body, err = io.ReadAll(response.Body)
if err != nil {
return nil, err
}

if response.StatusCode != 200 {
return nil, errors.New("failed to retrieve content: " + string(body))
}

league := League{}
err = json.Unmarshal(body, &league)
if err != nil {
return nil, err
}
return &league, nil
}
86 changes: 86 additions & 0 deletions pkg/clients/football_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package clients

import (
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestNewFootballAPIClient(t *testing.T) {
t.Run("fails without api key", func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
t.Errorf("The code did not panic")
}
}()
NewFootballAPIClient("")
})

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

func TestGetLeagueByCode(t *testing.T) {
testCases := []struct {
name string
code string
statusCode int
isSuccess bool
}{
{"ok", "PL", 200, true},
{"not found", "XXX", 404, false},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
apiContent := loadGoldenFile(t.Name())
server := newTestServer("/competitions/"+tc.code, tc.statusCode, apiContent)
defer server.Close()

c := &realAPIClient{
baseURL: server.URL,
client: &http.Client{
Timeout: time.Second,
},
apiKey: "abc123",
}
response, err := c.GetLeagueByCode(tc.code)
assert.Equal(t, err == nil, tc.isSuccess)
assert.Equal(t, response != nil, tc.isSuccess)

if tc.isSuccess {
assert.Equal(t, "Premier League", response.Name)
assert.Equal(t, "PL", response.Code)
assert.Equal(t, "England", 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)
}
return content
}

func newTestServer(url string, statusCode int, responseBody []byte) *httptest.Server {
mux := http.NewServeMux()
mux.HandleFunc(url, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(statusCode)
w.Header().Set("Content-Type", "application/json")
w.Write(responseBody)
})
return httptest.NewServer(mux)
}
4 changes: 4 additions & 0 deletions pkg/clients/golden/TestGetLeagueByCode/not_found.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"message": "The resource you are looking for does not exist.",
"error": 404
}

0 comments on commit d8a5e69

Please sign in to comment.