- Add separate handlers for liveness (/healthz), readiness (/readyz), and startup (/startupz) probes - Implement WithLivenessHandler, WithReadinessHandler, WithStartupHandler, and WithServiceName options - Add probe-specific JSON response formats - Add comprehensive package documentation with usage examples - Maintain backward compatibility for /__health and / endpoints - Add tests for all probe types and fallback scenarios Enables proper Kubernetes health monitoring with different probe types.
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
package health
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
)
|
|
|
|
func TestBasicHealthHandler(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/__health", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
basicHealth(w, req)
|
|
|
|
res := w.Result()
|
|
defer res.Body.Close()
|
|
data, err := io.ReadAll(res.Body)
|
|
if err != nil {
|
|
t.Errorf("expected error to be nil got %v", err)
|
|
}
|
|
if string(data) != "ok" {
|
|
t.Fail()
|
|
t.Errorf("expected ok got %q", string(data))
|
|
}
|
|
}
|
|
|
|
func TestProbeHandlers(t *testing.T) {
|
|
// Test with separate handlers for each probe type
|
|
srv := NewServer(nil,
|
|
WithLivenessHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}),
|
|
WithReadinessHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}),
|
|
WithStartupHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}),
|
|
WithServiceName("test-service"),
|
|
)
|
|
|
|
tests := []struct {
|
|
handler http.HandlerFunc
|
|
expectedStatus int
|
|
expectedBody string
|
|
}{
|
|
{srv.createProbeHandler("liveness"), 200, `{"status":"alive"}`},
|
|
{srv.createProbeHandler("readiness"), 200, `{"ready":true}`},
|
|
{srv.createProbeHandler("startup"), 200, `{"started":true}`},
|
|
{srv.createGeneralHandler(), 200, `{"service":"test-service","status":"healthy"}`},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
tt.handler(w, req)
|
|
|
|
if w.Code != tt.expectedStatus {
|
|
t.Errorf("expected status %d, got %d", tt.expectedStatus, w.Code)
|
|
}
|
|
|
|
body := w.Body.String()
|
|
if body != tt.expectedBody+"\n" { // json.Encoder adds newline
|
|
t.Errorf("expected body %q, got %q", tt.expectedBody, body)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProbeHandlerFallback(t *testing.T) {
|
|
// Test fallback to general handler when no specific handler is configured
|
|
generalHandler := func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|
|
|
|
srv := NewServer(generalHandler, WithServiceName("test-service"))
|
|
|
|
tests := []struct {
|
|
handler http.HandlerFunc
|
|
expectedStatus int
|
|
expectedBody string
|
|
}{
|
|
{srv.createProbeHandler("liveness"), 200, `{"status":"alive"}`},
|
|
{srv.createProbeHandler("readiness"), 200, `{"ready":true}`},
|
|
{srv.createProbeHandler("startup"), 200, `{"started":true}`},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
t.Run(fmt.Sprintf("fallback_%d", i), func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
tt.handler(w, req)
|
|
|
|
if w.Code != tt.expectedStatus {
|
|
t.Errorf("expected status %d, got %d", tt.expectedStatus, w.Code)
|
|
}
|
|
|
|
body := w.Body.String()
|
|
if body != tt.expectedBody+"\n" { // json.Encoder adds newline
|
|
t.Errorf("expected body %q, got %q", tt.expectedBody, body)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestUnhealthyProbeHandlers(t *testing.T) {
|
|
// Test with handlers that return unhealthy status
|
|
srv := NewServer(nil,
|
|
WithLivenessHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}),
|
|
WithReadinessHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}),
|
|
WithStartupHandler(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusServiceUnavailable)
|
|
}),
|
|
WithServiceName("test-service"),
|
|
)
|
|
|
|
tests := []struct {
|
|
handler http.HandlerFunc
|
|
expectedStatus int
|
|
expectedBody string
|
|
}{
|
|
{srv.createProbeHandler("liveness"), 503, `{"status":"unhealthy"}`},
|
|
{srv.createProbeHandler("readiness"), 503, `{"ready":false}`},
|
|
{srv.createProbeHandler("startup"), 503, `{"started":false}`},
|
|
}
|
|
|
|
for i, tt := range tests {
|
|
t.Run(fmt.Sprintf("unhealthy_%d", i), func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
w := httptest.NewRecorder()
|
|
|
|
tt.handler(w, req)
|
|
|
|
if w.Code != tt.expectedStatus {
|
|
t.Errorf("expected status %d, got %d", tt.expectedStatus, w.Code)
|
|
}
|
|
|
|
body := w.Body.String()
|
|
if body != tt.expectedBody+"\n" { // json.Encoder adds newline
|
|
t.Errorf("expected body %q, got %q", tt.expectedBody, body)
|
|
}
|
|
})
|
|
}
|
|
}
|