Improve server scaffolding
This commit is contained in:
parent
8af9a6813c
commit
d4aa3a7a80
6 changed files with 103 additions and 10 deletions
2
main.go
2
main.go
|
@ -8,6 +8,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
server := &PlayerServer{&store.InMemoryPlayerStore{}}
|
server := NewPlayerServer(&store.InMemoryPlayerStore{})
|
||||||
log.Fatal(http.ListenAndServe(":3000", server))
|
log.Fatal(http.ListenAndServe(":3000", server))
|
||||||
}
|
}
|
||||||
|
|
61
main_test.go
61
main_test.go
|
@ -1,10 +1,15 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.andreafazzi.eu/andrea/testhub/store"
|
||||||
"github.com/remogatto/prettytest"
|
"github.com/remogatto/prettytest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +21,7 @@ type testSuite struct {
|
||||||
type StubPlayerStore struct {
|
type StubPlayerStore struct {
|
||||||
scores map[string]int
|
scores map[string]int
|
||||||
winCalls []string
|
winCalls []string
|
||||||
|
league []store.Player
|
||||||
}
|
}
|
||||||
|
|
||||||
func (store *StubPlayerStore) GetPlayerScore(player string) int {
|
func (store *StubPlayerStore) GetPlayerScore(player string) int {
|
||||||
|
@ -26,6 +32,10 @@ func (s *StubPlayerStore) RecordWin(name string) {
|
||||||
s.winCalls = append(s.winCalls, name)
|
s.winCalls = append(s.winCalls, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *StubPlayerStore) GetLeague() []store.Player {
|
||||||
|
return store.league
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunner(t *testing.T) {
|
func TestRunner(t *testing.T) {
|
||||||
prettytest.Run(
|
prettytest.Run(
|
||||||
t,
|
t,
|
||||||
|
@ -33,7 +43,7 @@ func TestRunner(t *testing.T) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testSuite) TestGET() {
|
func (t *testSuite) TestGETPlayers() {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
expectedScore string
|
expectedScore string
|
||||||
|
@ -50,9 +60,10 @@ func (t *testSuite) TestGET() {
|
||||||
"Floyd": 10,
|
"Floyd": 10,
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
[]store.Player{},
|
||||||
}
|
}
|
||||||
|
|
||||||
server := &PlayerServer{store}
|
server := NewPlayerServer(store)
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
request, _ := http.NewRequest(http.MethodGet, "/players/"+test.name, nil)
|
request, _ := http.NewRequest(http.MethodGet, "/players/"+test.name, nil)
|
||||||
|
@ -65,13 +76,14 @@ func (t *testSuite) TestGET() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *testSuite) TestPOST() {
|
func (t *testSuite) TestPOSTPlayers() {
|
||||||
store := StubPlayerStore{
|
store := &StubPlayerStore{
|
||||||
map[string]int{},
|
map[string]int{},
|
||||||
nil,
|
nil,
|
||||||
|
[]store.Player{},
|
||||||
}
|
}
|
||||||
|
|
||||||
server := &PlayerServer{&store}
|
server := NewPlayerServer(store)
|
||||||
|
|
||||||
request, _ := http.NewRequest(http.MethodPost, "/players/Pepper", nil)
|
request, _ := http.NewRequest(http.MethodPost, "/players/Pepper", nil)
|
||||||
response := httptest.NewRecorder()
|
response := httptest.NewRecorder()
|
||||||
|
@ -80,3 +92,42 @@ func (t *testSuite) TestPOST() {
|
||||||
t.Equal(1, len(store.winCalls))
|
t.Equal(1, len(store.winCalls))
|
||||||
t.Equal(http.StatusAccepted, response.Code)
|
t.Equal(http.StatusAccepted, response.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *testSuite) TestGETLeague() {
|
||||||
|
expectedResult := []store.Player{
|
||||||
|
{Name: "Cleo", Wins: 20},
|
||||||
|
{Name: "Chris", Wins: 10},
|
||||||
|
}
|
||||||
|
|
||||||
|
store := &StubPlayerStore{
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
expectedResult,
|
||||||
|
}
|
||||||
|
|
||||||
|
server := NewPlayerServer(store)
|
||||||
|
|
||||||
|
request, _ := http.NewRequest(http.MethodGet, "/league", nil)
|
||||||
|
response := httptest.NewRecorder()
|
||||||
|
|
||||||
|
server.ServeHTTP(response, request)
|
||||||
|
|
||||||
|
result := getLeagueFromResponse(response.Body)
|
||||||
|
|
||||||
|
t.True(leaguesAreEqual(expectedResult, result))
|
||||||
|
t.Equal(http.StatusOK, response.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLeagueFromResponse(body io.Reader) (league []store.Player) {
|
||||||
|
err := json.NewDecoder(body).Decode(&league)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("Unable to parse response from server %q into slice of Player, '%v'", body, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func leaguesAreEqual(got, want []store.Player) bool {
|
||||||
|
return reflect.DeepEqual(got, want)
|
||||||
|
}
|
||||||
|
|
6
models/player.go
Normal file
6
models/player.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type Player struct {
|
||||||
|
Name string
|
||||||
|
Wins int
|
||||||
|
}
|
31
server.go
31
server.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -8,16 +9,33 @@ import (
|
||||||
"git.andreafazzi.eu/andrea/testhub/store"
|
"git.andreafazzi.eu/andrea/testhub/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const jsonContentType = "application/json"
|
||||||
|
|
||||||
type PlayerServer struct {
|
type PlayerServer struct {
|
||||||
store store.PlayerStore
|
store store.PlayerStore
|
||||||
|
http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PlayerServer) processWin(w http.ResponseWriter, player string) {
|
func NewPlayerServer(store store.PlayerStore) *PlayerServer {
|
||||||
ps.store.RecordWin(player)
|
ps := new(PlayerServer)
|
||||||
w.WriteHeader(http.StatusAccepted)
|
ps.store = store
|
||||||
|
|
||||||
|
router := http.NewServeMux()
|
||||||
|
|
||||||
|
router.Handle("/players/", http.HandlerFunc(ps.playersHandler))
|
||||||
|
router.Handle("/league", http.HandlerFunc(ps.leagueHandler))
|
||||||
|
|
||||||
|
ps.Handler = router
|
||||||
|
|
||||||
|
return ps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (ps *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("content-type", jsonContentType)
|
||||||
|
json.NewEncoder(w).Encode(ps.store.GetLeague())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ps *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
player := strings.TrimPrefix(r.URL.Path, "/players/")
|
player := strings.TrimPrefix(r.URL.Path, "/players/")
|
||||||
|
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
|
@ -31,3 +49,8 @@ func (ps *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
ps.processWin(w, player)
|
ps.processWin(w, player)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *PlayerServer) processWin(w http.ResponseWriter, player string) {
|
||||||
|
ps.store.RecordWin(player)
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.andreafazzi.eu/andrea/testhub/models"
|
||||||
|
)
|
||||||
|
|
||||||
type InMemoryPlayerStore struct{}
|
type InMemoryPlayerStore struct{}
|
||||||
|
|
||||||
func (i *InMemoryPlayerStore) GetPlayerScore(player string) int {
|
func (i *InMemoryPlayerStore) GetPlayerScore(player string) int {
|
||||||
|
@ -9,3 +13,7 @@ func (i *InMemoryPlayerStore) GetPlayerScore(player string) int {
|
||||||
func (i *InMemoryPlayerStore) RecordWin(player string) {
|
func (i *InMemoryPlayerStore) RecordWin(player string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *InMemoryPlayerStore) GetLeague() []models.Player {
|
||||||
|
return []models.Player{}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package store
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.andreafazzi.eu/andrea/testhub/models"
|
||||||
|
)
|
||||||
|
|
||||||
type PlayerStore interface {
|
type PlayerStore interface {
|
||||||
GetPlayerScore(player string) int
|
GetPlayerScore(player string) int
|
||||||
RecordWin(player string)
|
RecordWin(player string)
|
||||||
|
GetLeague() []models.Player
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue