Fix a race in TestClient - #31
Merged
Merged
Conversation
The test picked a free port by binding a listener and closing it, started ListenAndServe in a goroutine, and then immediately dialed without waiting for the server to come up. Nothing ordered the bind before the first request, so on a loaded runner the client could win and the test failed with "connection refused". Closing the listener before rebinding also left a window for another process to take the port. httptest.NewServer binds before returning and hands back the address it actually bound, which removes both races and the localhost IPv4/IPv6 mismatch in the failure. pkg/frontend already tests this way. This was pre-existing flakiness rather than a regression; it happened to surface on the merge of an unrelated change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What happened
CI failed on the merge of #30. That PR added only four files under
frontend/— no Go, nogo.mod, no workflow changes — so it didn't cause this.TestClienthas been racy all along and happened to lose on that run.The race
Nothing orders the bind before the first request. On an idle machine the goroutine wins; on a loaded CI runner it doesn't. There's a second, narrower race too: closing the listener before rebinding leaves a window for another process to take the port.
The
[::1]in the error is a symptom of the same design —localhostwas reassembled from a port number rather than using the address actually bound, so the client could resolve to a different stack than the server used.The fix
httptest.NewServerbinds before it returns and hands back the address it actually bound. That closes both races and the IPv4/IPv6 mismatch at once, and dropsgetFreePort, thesync.WaitGroup, and the manualShutdown.pkg/frontend/server_test.goalready tests this way, so this makes the two consistent.Verification
I could not reproduce the failure locally — expected for a scheduling race on an idle machine. I tried
-count=500,GOMAXPROCS=1, and saturating every core; it passed throughout, which is why the fix is reasoned from the code rather than from a red test.After the change: 500 runs, 50 runs under
-race, 300 underGOMAXPROCS=1, the full unit suite, and the end-to-end./test.shall pass.🤖 Generated with Claude Code