From 091add5ae3dc0ab3265e45b4417fc06fb8a05a39 Mon Sep 17 00:00:00 2001 From: Amirhf Date: Tue, 17 Feb 2026 15:11:38 +0330 Subject: [PATCH] caddytest: make TestReverseProxyHealthCheck deterministic with poll instead of sleep (#7474) Start lightweight backend servers before starting Caddy so active health checks probe a ready backend instead of the same Caddy instance during provisioning. This removes the startup race without fixed sleeps or polling. --- caddytest/integration/reverseproxy_test.go | 44 +++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/caddytest/integration/reverseproxy_test.go b/caddytest/integration/reverseproxy_test.go index cbfe8433b..0b992d3e3 100644 --- a/caddytest/integration/reverseproxy_test.go +++ b/caddytest/integration/reverseproxy_test.go @@ -8,7 +8,6 @@ import ( "runtime" "strings" "testing" - "time" "github.com/caddyserver/caddy/v2/caddytest" ) @@ -327,6 +326,41 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) { } func TestReverseProxyHealthCheck(t *testing.T) { + // Start lightweight backend servers so they're ready before Caddy's + // active health checker runs; this avoids a startup race where the + // health checker probes backends that haven't yet begun accepting + // connections and marks them unhealthy. + // + // This mirrors how health checks are typically used in practice (to a separate + // backend service) and avoids probing the same Caddy instance while it's still + // provisioning and not ready to accept connections. + + // backend server that responds to proxied requests + helloSrv := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + _, _ = w.Write([]byte("Hello, World!")) + }), + } + ln0, err := net.Listen("tcp", "127.0.0.1:2020") + if err != nil { + t.Fatalf("failed to listen on 127.0.0.1:2020: %v", err) + } + go helloSrv.Serve(ln0) + t.Cleanup(func() { helloSrv.Close(); ln0.Close() }) + + // backend server that serves health checks + healthSrv := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + _, _ = w.Write([]byte("ok")) + }), + } + ln1, err := net.Listen("tcp", "127.0.0.1:2021") + if err != nil { + t.Fatalf("failed to listen on 127.0.0.1:2021: %v", err) + } + go healthSrv.Serve(ln1) + t.Cleanup(func() { healthSrv.Close(); ln1.Close() }) + tester := caddytest.NewTester(t) tester.InitServer(` { @@ -336,12 +370,6 @@ func TestReverseProxyHealthCheck(t *testing.T) { https_port 9443 grace_period 1ns } - http://localhost:2020 { - respond "Hello, World!" - } - http://localhost:2021 { - respond "ok" - } http://localhost:9080 { reverse_proxy { to localhost:2020 @@ -355,8 +383,6 @@ func TestReverseProxyHealthCheck(t *testing.T) { } } `, "caddyfile") - - time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!") }