win 110902ad4b feat(health): split liveness and readiness probes
Add HealthService with Liveness (no-op) and Readiness (DB+Redis ping
with per-component timeout) checks. Expose three endpoints:

- /healthz : new liveness endpoint, zero-dependency, always 200
- /ready   : new readiness endpoint, returns 503 with details on dep
             failure; suitable for K8s readinessProbe and load balancers
- /health  : preserved for backward compatibility, equivalent to
             /healthz

Switch primary docker-compose healthcheck to /ready so the container
is only marked healthy once DB+Redis are reachable. Standalone/dev/
local compose files keep /health to avoid disrupting existing setups.

Tests: unit tests cover liveness, readiness with both deps healthy,
each dep failing independently, and per-component timeout enforcement.
2026-04-28 23:39:50 +08:00

50 lines
1.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package routes
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func newTestRouter(t *testing.T, hs *service.HealthService) *gin.Engine {
t.Helper()
gin.SetMode(gin.TestMode)
r := gin.New()
RegisterCommonRoutes(r, hs)
return r
}
func TestCommonRoutes_LivenessEndpoints(t *testing.T) {
r := newTestRouter(t, service.NewHealthService(nil, nil))
for _, path := range []string{"/healthz", "/health"} {
req := httptest.NewRequest(http.MethodGet, path, nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code, "liveness path %s should be 200", path)
}
}
func TestCommonRoutes_ReadyEndpoint_NoDepsReturnsOK(t *testing.T) {
// 没有 DB/Redis 依赖时 readiness 视为 ok早期启动场景
r := newTestRouter(t, service.NewHealthService(nil, nil))
req := httptest.NewRequest(http.MethodGet, "/ready", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), "\"ok\":true")
}
func TestCommonRoutes_SetupStatusUnchanged(t *testing.T) {
// 验证我们没有破坏既有的 /setup/status 行为(前端依赖)。
r := newTestRouter(t, service.NewHealthService(nil, nil))
req := httptest.NewRequest(http.MethodGet, "/setup/status", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
require.Contains(t, w.Body.String(), "needs_setup")
}