http server

This commit is contained in:
Michael Thomson 2024-06-03 19:44:16 -04:00
parent 95928c5a76
commit d8a50a8b11
No known key found for this signature in database
6 changed files with 206 additions and 0 deletions

BIN
http-server/http-server Executable file

Binary file not shown.

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

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