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