http server
This commit is contained in:
parent
95928c5a76
commit
d8a50a8b11
BIN
http-server/http-server
Executable file
BIN
http-server/http-server
Executable file
Binary file not shown.
17
http-server/in_memory_player_store.go
Normal file
17
http-server/in_memory_player_store.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
func NewInMemoryPlayerStore() *InMemoryPlayerStore {
|
||||
return &InMemoryPlayerStore{map[string]int{}}
|
||||
}
|
||||
|
||||
type InMemoryPlayerStore struct {
|
||||
store map[string]int
|
||||
}
|
||||
|
||||
func (i *InMemoryPlayerStore) RecordWin(name string) {
|
||||
i.store[name]++
|
||||
}
|
||||
|
||||
func (i *InMemoryPlayerStore) GetPlayerScore(name string) int {
|
||||
return i.store[name]
|
||||
}
|
11
http-server/main.go
Normal file
11
http-server/main.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server := &PlayerServer{NewInMemoryPlayerStore()}
|
||||
log.Fatal(http.ListenAndServe(":5000", server))
|
||||
}
|
43
http-server/server.go
Normal file
43
http-server/server.go
Normal file
@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PlayerStore interface {
|
||||
GetPlayerScore(name string) int
|
||||
RecordWin(name string)
|
||||
}
|
||||
|
||||
type PlayerServer struct {
|
||||
store PlayerStore
|
||||
}
|
||||
|
||||
func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
player := strings.TrimPrefix(r.URL.Path, "/players/")
|
||||
|
||||
switch r.Method {
|
||||
case http.MethodPost:
|
||||
p.processWin(w, player)
|
||||
case http.MethodGet:
|
||||
p.showScore(w, player)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (p *PlayerServer) showScore(w http.ResponseWriter, player string) {
|
||||
score := p.store.GetPlayerScore(player)
|
||||
|
||||
if score == 0 {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
|
||||
fmt.Fprint(w, score)
|
||||
}
|
||||
|
||||
func (p *PlayerServer) processWin(w http.ResponseWriter, player string) {
|
||||
p.store.RecordWin(player)
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
}
|
23
http-server/server_integration_test.go
Normal file
23
http-server/server_integration_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRecordingWinsAndRetrievingThem(t *testing.T) {
|
||||
store := NewInMemoryPlayerStore()
|
||||
server := PlayerServer{store}
|
||||
player := "Pepper"
|
||||
|
||||
server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))
|
||||
server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))
|
||||
server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))
|
||||
|
||||
response := httptest.NewRecorder()
|
||||
server.ServeHTTP(response, newGetScoreRequest(player))
|
||||
assertStatus(t, response.Code, http.StatusOK)
|
||||
|
||||
assertResponseBody(t, response.Body.String(), "3")
|
||||
}
|
112
http-server/server_test.go
Normal file
112
http-server/server_test.go
Normal file
@ -0,0 +1,112 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type StubPlayerStore struct {
|
||||
scores map[string] int
|
||||
winCalls []string
|
||||
}
|
||||
|
||||
func (s *StubPlayerStore) GetPlayerScore(name string) int {
|
||||
score := s.scores[name]
|
||||
return score
|
||||
}
|
||||
|
||||
func (s *StubPlayerStore) RecordWin(name string) {
|
||||
s.winCalls = append(s.winCalls, name)
|
||||
}
|
||||
|
||||
func TestGETPlayers(t *testing.T) {
|
||||
store := StubPlayerStore{
|
||||
map[string]int{
|
||||
"Pepper": 20,
|
||||
"Floyd": 10,
|
||||
},
|
||||
nil,
|
||||
}
|
||||
|
||||
server := &PlayerServer{&store}
|
||||
|
||||
t.Run("Returns Pepper's score", func(t *testing.T) {
|
||||
request := newGetScoreRequest("Pepper")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
server.ServeHTTP(response, request)
|
||||
|
||||
assertStatus(t, response.Code, http.StatusOK)
|
||||
assertResponseBody(t, response.Body.String(), "20")
|
||||
})
|
||||
t.Run("Return Floyd's score", func(t *testing.T) {
|
||||
request := newGetScoreRequest("Floyd")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
server.ServeHTTP(response, request)
|
||||
|
||||
assertStatus(t, response.Code, http.StatusOK)
|
||||
assertResponseBody(t, response.Body.String(), "10")
|
||||
})
|
||||
t.Run("Return 404 on missing players", func(t *testing.T) {
|
||||
request := newGetScoreRequest("Apollo")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
server.ServeHTTP(response, request)
|
||||
|
||||
assertStatus(t, response.Code, http.StatusNotFound)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStoreWins(t *testing.T) {
|
||||
store := StubPlayerStore{
|
||||
map[string]int{},
|
||||
nil,
|
||||
}
|
||||
server := &PlayerServer{&store}
|
||||
|
||||
t.Run("it records wins when POST", func(t *testing.T) {
|
||||
player := "Pepper"
|
||||
|
||||
request := newPostWinRequest(player)
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
server.ServeHTTP(response, request)
|
||||
|
||||
assertStatus(t, response.Code, http.StatusAccepted)
|
||||
|
||||
if len(store.winCalls) != 1 {
|
||||
t.Errorf("got %d calls to RecordWin want %d", len(store.winCalls), 1)
|
||||
}
|
||||
|
||||
if store.winCalls[0] != player {
|
||||
t.Errorf("did not store correct winner got %q want %q", store.winCalls[0], player)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func assertStatus(t testing.TB, got, want int) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("did not get the correct status, got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func newGetScoreRequest(name string) *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/players/%s", name), nil)
|
||||
return req
|
||||
}
|
||||
|
||||
func newPostWinRequest(name string) *http.Request {
|
||||
req, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/players/%s", name), nil)
|
||||
return req
|
||||
}
|
||||
|
||||
func assertResponseBody(t testing.TB, got, want string) {
|
||||
t.Helper()
|
||||
if got != want {
|
||||
t.Errorf("response body is wrong, got %q, want %q", got, want)
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user