package balancer import ( "context" "net/http" "net/http/httptest" "sync/atomic" "testing" "time" ) func TestHealthChecker_InitialProbeClassifiesEndpoints(t *testing.T) { healthy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })) defer healthy.Close() unhealthy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusServiceUnavailable) })) defer unhealthy.Close() eps := []Endpoint{{URL: healthy.URL}, {URL: unhealthy.URL}} hc := newHealthChecker() hc.Start(eps) // runs an initial synchronous probe defer hc.Stop() if !hc.IsHealthy(eps[0]) { t.Errorf("healthy endpoint reported unhealthy") } if hc.IsHealthy(eps[1]) { t.Errorf("unhealthy endpoint reported healthy") } got := hc.Healthy(eps) if len(got) != 1 || got[0].URL != healthy.URL { t.Errorf("Healthy() = %v, want only %s", got, healthy.URL) } } func TestHealthChecker_DetectsRecovery(t *testing.T) { var up atomic.Bool srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { if up.Load() { w.WriteHeader(http.StatusOK) return } w.WriteHeader(http.StatusServiceUnavailable) })) defer srv.Close() eps := []Endpoint{{URL: srv.URL}} hc := newHealthChecker() hc.Start(eps) defer hc.Stop() if hc.IsHealthy(eps[0]) { t.Fatalf("endpoint should start unhealthy") } // Recover the backend and force a deterministic re-probe. up.Store(true) hc.probe(context.Background(), eps) if !hc.IsHealthy(eps[0]) { t.Fatalf("endpoint should be healthy after recovery") } } func TestHealthChecker_StopTerminatesLoop(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })) defer srv.Close() hc := newHealthChecker(WithHealthInterval(time.Millisecond)) hc.Start([]Endpoint{{URL: srv.URL}}) done := make(chan struct{}) go func() { hc.Stop() close(done) }() select { case <-done: case <-time.After(2 * time.Second): t.Fatal("Stop did not return within 2s — loop goroutine leaked") } } func TestHealthChecker_UnknownEndpointIsUnhealthy(t *testing.T) { hc := newHealthChecker() if hc.IsHealthy(Endpoint{URL: "http://never-probed.example"}) { t.Error("unknown endpoint should be reported unhealthy") } }