diff --git a/internal/ircserver/server_test.go b/internal/ircserver/server_test.go index f30b826..1cc4316 100644 --- a/internal/ircserver/server_test.go +++ b/internal/ircserver/server_test.go @@ -137,20 +137,6 @@ func newTestEnvWithResolver( brk := broker.New() - var listenConfig net.ListenConfig - - listener, err := listenConfig.Listen(t.Context(), "tcp", "127.0.0.1:0") - if err != nil { - t.Fatalf("listen: %v", err) - } - - addr := listener.Addr().String() - - err = listener.Close() - if err != nil { - t.Fatalf("close listener: %v", err) - } - log := slog.New(slog.NewTextHandler( os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}, //nolint:exhaustruct @@ -160,7 +146,9 @@ func newTestEnvWithResolver( log, cfg, database, brk, interval, deadline, resolve, ) - err = srv.Start(addr) + // Port 0 makes the system pick a free port. dial reads it + // back from the server's listener. + err = srv.Start("127.0.0.1:0") if err != nil { t.Fatalf("start irc server: %v", err) } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 874231e..f4d7cde 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -8,7 +8,6 @@ import ( "fmt" "net" "net/http" - "strconv" "strings" "testing" "time" @@ -45,10 +44,9 @@ const ( // for a long poll the HTTP server is serving to finish, and // that the poll gets its response. func TestStopWaitsForRequestInProgress(t *testing.T) { - port := freePort(t) - addr := net.JoinHostPort("127.0.0.1", strconv.Itoa(port)) + var srv *server.Server - app, brk, database := startApp(t, port) + app, brk, database := startApp(t, fx.Populate(&srv)) sessionID, _, token, err := database.CreateSession( t.Context(), "poller", "poller", "", "", @@ -57,9 +55,20 @@ func TestStopWaitsForRequestInProgress(t *testing.T) { t.Fatalf("create session: %v", err) } - waitUntil(t, "the HTTP server to listen", func() bool { - return canConnect(t, addr) - }) + // The test cannot read the port the system gave the app, so + // it serves the same HTTP server on a listener of its own. + var listenConfig net.ListenConfig + + listener, err := listenConfig.Listen( + t.Context(), "tcp", "127.0.0.1:0", + ) + if err != nil { + t.Fatalf("listen: %v", err) + } + + go srv.ServeUntilShutdown(listener) + + addr := listener.Addr().String() statuses := make(chan int, 1) @@ -145,7 +154,7 @@ func TestStartFailsWhenHTTPPortIsTaken(t *testing.T) { func TestServeFailureStopsApp(t *testing.T) { var srv *server.Server - app, _, _ := startApp(t, freePort(t), fx.Populate(&srv)) + app, _, _ := startApp(t, fx.Populate(&srv)) shutdowns := app.Wait() @@ -168,7 +177,7 @@ func TestServeFailureStopsApp(t *testing.T) { func TestServeAfterStopDoesNotStopApp(t *testing.T) { var srv *server.Server - app, _, _ := startApp(t, freePort(t), fx.Populate(&srv)) + app, _, _ := startApp(t, fx.Populate(&srv)) shutdowns := app.Wait() @@ -186,16 +195,15 @@ func TestServeAfterStopDoesNotStopApp(t *testing.T) { } } -// startApp starts the app newApp builds, with the HTTP server -// listening on port. +// startApp starts the app newApp builds, with the HTTP port +// set to 0, so the system gives the server a free port. func startApp( t *testing.T, - port int, options ...fx.Option, ) (*fxtest.App, *broker.Broker, *db.Database) { t.Helper() - app, brk, database := newApp(t, port, options...) + app, brk, database := newApp(t, 0, options...) app.RequireStart() @@ -254,21 +262,6 @@ func newApp( return app, brk, database } -// freePort returns a TCP port that nothing was listening on a -// moment ago. -func freePort(t *testing.T) int { - t.Helper() - - listener := closedListener(t) - - tcpAddr, ok := listener.Addr().(*net.TCPAddr) - if !ok { - t.Fatalf("listener address %v is not TCP", listener.Addr()) - } - - return tcpAddr.Port -} - // closedListener returns a TCP listener that has already been // closed, so that accepting a connection on it fails. func closedListener(t *testing.T) net.Listener { @@ -291,22 +284,6 @@ func closedListener(t *testing.T) net.Listener { return listener } -// canConnect reports whether addr accepts a TCP connection. -func canConnect(t *testing.T, addr string) bool { - t.Helper() - - var dialer net.Dialer - - conn, err := dialer.DialContext(t.Context(), "tcp", addr) - if err != nil { - return false - } - - _ = conn.Close() - - return true -} - // waitUntil calls done every checkStep until it returns true, // and fails the test if it does not within testTimeout. func waitUntil(t *testing.T, what string, done func() bool) {