package httpapi import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "time" ) func TestHealthRemainsLiveWithoutDatabase(t *testing.T) { handler := NewHandler(func(context.Context) error { t.Fatal("liveness must not query the database") return nil }) response := httptest.NewRecorder() handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/healthz", nil)) if response.Code != http.StatusOK || response.Body.String() != "{\"status\":\"ok\"}\n" { t.Fatalf("unexpected response: %d %s", response.Code, response.Body.String()) } } func TestReadinessReflectsDatabaseState(t *testing.T) { for _, tc := range []struct { name string err error code int body string }{ {"ready", nil, http.StatusOK, "{\"status\":\"ok\"}\n"}, {"unavailable", errors.New("private database details"), http.StatusServiceUnavailable, "{\"status\":\"unavailable\"}\n"}, } { t.Run(tc.name, func(t *testing.T) { called := false handler := NewHandler(func(ctx context.Context) error { called = true deadline, ok := ctx.Deadline() if !ok || time.Until(deadline) > 2*time.Second { t.Fatal("database check must have a bounded deadline") } return tc.err }) response := httptest.NewRecorder() handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/readyz", nil)) if !called || response.Code != tc.code || response.Body.String() != tc.body { t.Fatalf("unexpected response: %d %s", response.Code, response.Body.String()) } if !strings.HasPrefix(response.Header().Get("Content-Type"), "application/json") { t.Fatal("health response must be JSON") } }) } } func TestReadinessPreservesRequestCancellation(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) cancel() handler := NewHandler(func(ctx context.Context) error { return ctx.Err() }) response := httptest.NewRecorder() request := httptest.NewRequest(http.MethodGet, "/readyz", nil).WithContext(ctx) handler.ServeHTTP(response, request) if response.Code != http.StatusServiceUnavailable { t.Fatalf("got %d, want 503", response.Code) } }