Skip to content

Instantly share code, notes, and snippets.

@florentsorel
Last active March 21, 2024 22:17
Show Gist options
  • Save florentsorel/50894759a5e13dc17d8c616c163fc6b2 to your computer and use it in GitHub Desktop.
Save florentsorel/50894759a5e13dc17d8c616c163fc6b2 to your computer and use it in GitHub Desktop.
package main
import (
"fmt"
"log/slog"
"net/http"
"os"
)
type Api struct {
server *http.Server
logger *slog.Logger
}
func NewApi(cfg *config) *Api {
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
srv := &http.Server{
Addr: fmt.Sprintf(":%d", cfg.port),
Handler: NewRouter(logger),
}
return &Api{
server: srv,
logger: logger,
}
}
func NewRouter(logger *slog.Logger) *http.ServeMux {
mux := http.NewServeMux()
mux.Handle("/", homeHandler(logger))
mux.Handle("/healthcheck", healthcheckHandler())
return mux
}
func (a *Api) Run() {
a.logger.Info(fmt.Sprintf("Starting server on %s", a.server.Addr))
err := a.server.ListenAndServe()
if err != nil {
a.logger.Error("Error starting server: %s", err)
os.Exit(1)
}
}
package main
type config struct {
port int
}
package main
import (
"log/slog"
"net/http"
)
func homeHandler(logger *slog.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logger.Info("Request received")
w.Write([]byte("Hello, World!"))
})
}
func healthcheckHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
})
}
package main
import (
"bytes"
"log/slog"
"net/http"
"net/http/httptest"
"regexp"
"strings"
"testing"
)
func TestHomeHandler(t *testing.T) {
var buff bytes.Buffer
logger := slog.New(slog.NewTextHandler(&buff, nil))
sut := homeHandler(logger)
req := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
sut.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status code %d, got %d", http.StatusOK, w.Code)
}
if w.Body.String() != "Hello, World!" {
t.Errorf("Expected body %s, got %s", "Hello, World!", w.Body.String())
}
// Usefull only if we need to assert the log message content
if getLogMsg(buff.String()) != "Request received" {
t.Errorf("Expected log message %s, got %s", "Request received", buff.String())
}
}
func TestHealthcheckHandler(t *testing.T) {
sut := healthcheckHandler()
req := httptest.NewRequest("GET", "/healthcheck", nil)
w := httptest.NewRecorder()
sut.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status code %d, got %d", http.StatusOK, w.Code)
}
if w.Body.String() != "OK" {
t.Errorf("Expected body %s, got %s", "OK", w.Body.String())
}
}
/*
Log looks like:
time=2024-03-21T22:59:19.786+01:00 level=INFO msg="Starting server on :8080"
time=2024-03-21T22:59:31.821+01:00 level=INFO msg="Request received"
*/
func parseLog(log string) map[string]string {
logMap := make(map[string]string)
re := regexp.MustCompile(`(\w+)=("[^"]+"|\S+)`)
matches := re.FindAllStringSubmatch(log, -1)
for _, match := range matches {
key := match[1]
value := strings.Trim(match[2], `"`)
logMap[key] = value
}
return logMap
}
func getLogMsg(log string) string {
return parseLog(log)["msg"]
}
package main
func main() {
config := &config{
port: 8080,
}
NewApi(config).Run()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment