Add comprehensive test coverage for server/ package
All checks were successful
CI / test (push) Successful in 30s

Cover edge cases: statusWriter multi-call/default/unwrap, UUID v4 format
and uniqueness, non-string panics, recovery body and log attributes,
4xx log level, default status in logging, request ID propagation,
server defaults/options/listen-error/multiple-hooks/logger, router
groups with empty prefix/inherited middleware/ordering/path params/
isolation, mount trailing slash, health content-type and POST rejection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 13:55:22 +03:00
parent cea75d198b
commit 7fae6247d5
4 changed files with 680 additions and 0 deletions

View File

@@ -140,4 +140,197 @@ func TestRouterMount(t *testing.T) {
t.Fatalf("got body %q, want %q", body, "info")
}
})
t.Run("mount with trailing slash", func(t *testing.T) {
sub := http.NewServeMux()
sub.HandleFunc("GET /data", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("data"))
})
r := server.NewRouter()
r.Mount("/sub/", sub)
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/sub/data", nil)
r.ServeHTTP(w, req)
body, _ := io.ReadAll(w.Body)
if string(body) != "data" {
t.Fatalf("got body %q, want %q", body, "data")
}
})
}
func TestRouter_PatternWithoutMethod(t *testing.T) {
r := server.NewRouter()
r.HandleFunc("/static/", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("static"))
})
for _, method := range []string{http.MethodGet, http.MethodPost} {
w := httptest.NewRecorder()
req := httptest.NewRequest(method, "/static/file.css", nil)
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("%s /static/file.css: got status %d, want %d", method, w.Code, http.StatusOK)
}
}
}
func TestRouter_GroupEmptyPrefix(t *testing.T) {
r := server.NewRouter()
g := r.Group("")
g.HandleFunc("GET /hello", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("hello"))
})
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/hello", nil)
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", w.Code, http.StatusOK)
}
if body := w.Body.String(); body != "hello" {
t.Fatalf("got body %q, want %q", body, "hello")
}
}
func TestRouter_GroupInheritsMiddleware(t *testing.T) {
var order []string
parentMW := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
order = append(order, "parent")
next.ServeHTTP(w, r)
})
}
childMW := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
order = append(order, "child")
next.ServeHTTP(w, r)
})
}
r := server.NewRouter()
parent := r.Group("/api", parentMW)
child := parent.Group("/v1", childMW)
child.HandleFunc("GET /items", func(w http.ResponseWriter, _ *http.Request) {
order = append(order, "handler")
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/v1/items", nil)
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", w.Code, http.StatusOK)
}
expected := []string{"parent", "child", "handler"}
if len(order) != len(expected) {
t.Fatalf("got %v, want %v", order, expected)
}
for i, v := range expected {
if order[i] != v {
t.Fatalf("order[%d] = %q, want %q", i, order[i], v)
}
}
}
func TestRouter_GroupMiddlewareOrder(t *testing.T) {
var order []string
mwA := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
order = append(order, "A")
next.ServeHTTP(w, r)
})
}
mwB := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
order = append(order, "B")
next.ServeHTTP(w, r)
})
}
r := server.NewRouter()
g := r.Group("/api", mwA)
sub := g.Group("/v1", mwB)
sub.HandleFunc("GET /test", func(w http.ResponseWriter, _ *http.Request) {
order = append(order, "handler")
w.WriteHeader(http.StatusOK)
})
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/v1/test", nil)
r.ServeHTTP(w, req)
// Parent MW (A) should run before child MW (B), then handler.
expected := []string{"A", "B", "handler"}
if len(order) != len(expected) {
t.Fatalf("got %v, want %v", order, expected)
}
for i, v := range expected {
if order[i] != v {
t.Fatalf("order[%d] = %q, want %q", i, order[i], v)
}
}
}
func TestRouter_PathParamWithGroup(t *testing.T) {
r := server.NewRouter()
api := r.Group("/api")
api.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte("id=" + req.PathValue("id")))
})
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/users/42", nil)
r.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("got status %d, want %d", w.Code, http.StatusOK)
}
if body := w.Body.String(); body != "id=42" {
t.Fatalf("got body %q, want %q", body, "id=42")
}
}
func TestRouter_MiddlewareNotAppliedToOtherRoutes(t *testing.T) {
var mwCalled bool
mw := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mwCalled = true
next.ServeHTTP(w, r)
})
}
r := server.NewRouter()
// Add middleware only to /admin group.
admin := r.Group("/admin", mw)
admin.HandleFunc("GET /dashboard", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("admin"))
})
// Route outside the group.
r.HandleFunc("GET /public", func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("public"))
})
// Request to /public should NOT trigger group middleware.
mwCalled = false
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/public", nil)
r.ServeHTTP(w, req)
if mwCalled {
t.Fatal("group middleware should not be called for routes outside the group")
}
if w.Body.String() != "public" {
t.Fatalf("got body %q, want %q", w.Body.String(), "public")
}
}