mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-03 21:44:33 +08:00
test(gin): resolve race conditions in integration tests (#4453)
- Implement TestRebuild404Handlers to verify 404 handler chain rebuilding when global middleware is added via Use() - Add waitForServerReady helper with exponential backoff to replace unreliable time.Sleep() calls in integration tests - Fix race conditions in TestRunEmpty, TestRunEmptyWithEnv, and TestRunWithPort by using proper server readiness checks - All tests now pass consistently with -race flag This addresses the empty test function and eliminates flaky test failures caused by insufficient wait times for server startup. Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
This commit is contained in:
parent
583db590ec
commit
f416d1e594
@ -70,9 +70,10 @@ func TestRunEmpty(t *testing.T) {
|
|||||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
assert.NoError(t, router.Run())
|
assert.NoError(t, router.Run())
|
||||||
}()
|
}()
|
||||||
// have to wait for the goroutine to start and run the server
|
|
||||||
// otherwise the main thread will complete
|
// Wait for server to be ready with exponential backoff
|
||||||
time.Sleep(5 * time.Millisecond)
|
err := waitForServerReady("http://localhost:8080/example", 10)
|
||||||
|
require.NoError(t, err, "server should start successfully")
|
||||||
|
|
||||||
require.Error(t, router.Run(":8080"))
|
require.Error(t, router.Run(":8080"))
|
||||||
testRequest(t, "http://localhost:8080/example")
|
testRequest(t, "http://localhost:8080/example")
|
||||||
@ -213,9 +214,10 @@ func TestRunEmptyWithEnv(t *testing.T) {
|
|||||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
assert.NoError(t, router.Run())
|
assert.NoError(t, router.Run())
|
||||||
}()
|
}()
|
||||||
// have to wait for the goroutine to start and run the server
|
|
||||||
// otherwise the main thread will complete
|
// Wait for server to be ready with exponential backoff
|
||||||
time.Sleep(5 * time.Millisecond)
|
err := waitForServerReady("http://localhost:3123/example", 10)
|
||||||
|
require.NoError(t, err, "server should start successfully")
|
||||||
|
|
||||||
require.Error(t, router.Run(":3123"))
|
require.Error(t, router.Run(":3123"))
|
||||||
testRequest(t, "http://localhost:3123/example")
|
testRequest(t, "http://localhost:3123/example")
|
||||||
@ -234,9 +236,10 @@ func TestRunWithPort(t *testing.T) {
|
|||||||
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
router.GET("/example", func(c *Context) { c.String(http.StatusOK, "it worked") })
|
||||||
assert.NoError(t, router.Run(":5150"))
|
assert.NoError(t, router.Run(":5150"))
|
||||||
}()
|
}()
|
||||||
// have to wait for the goroutine to start and run the server
|
|
||||||
// otherwise the main thread will complete
|
// Wait for server to be ready with exponential backoff
|
||||||
time.Sleep(5 * time.Millisecond)
|
err := waitForServerReady("http://localhost:5150/example", 10)
|
||||||
|
require.NoError(t, err, "server should start successfully")
|
||||||
|
|
||||||
require.Error(t, router.Run(":5150"))
|
require.Error(t, router.Run(":5150"))
|
||||||
testRequest(t, "http://localhost:5150/example")
|
testRequest(t, "http://localhost:5150/example")
|
||||||
|
|||||||
23
gin_test.go
23
gin_test.go
@ -545,6 +545,29 @@ func TestNoMethodWithoutGlobalHandlers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRebuild404Handlers(t *testing.T) {
|
func TestRebuild404Handlers(t *testing.T) {
|
||||||
|
var middleware0 HandlerFunc = func(c *Context) {}
|
||||||
|
var middleware1 HandlerFunc = func(c *Context) {}
|
||||||
|
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
// Initially, allNoRoute should be nil
|
||||||
|
assert.Nil(t, router.allNoRoute)
|
||||||
|
|
||||||
|
// Set NoRoute handlers
|
||||||
|
router.NoRoute(middleware0)
|
||||||
|
assert.Len(t, router.allNoRoute, 1)
|
||||||
|
assert.Len(t, router.noRoute, 1)
|
||||||
|
compareFunc(t, router.allNoRoute[0], middleware0)
|
||||||
|
|
||||||
|
// Add Use middleware should trigger rebuild404Handlers
|
||||||
|
router.Use(middleware1)
|
||||||
|
assert.Len(t, router.allNoRoute, 2)
|
||||||
|
assert.Len(t, router.Handlers, 1)
|
||||||
|
assert.Len(t, router.noRoute, 1)
|
||||||
|
|
||||||
|
// Global middleware should come first
|
||||||
|
compareFunc(t, router.allNoRoute[0], middleware1)
|
||||||
|
compareFunc(t, router.allNoRoute[1], middleware0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoMethodWithGlobalHandlers(t *testing.T) {
|
func TestNoMethodWithGlobalHandlers(t *testing.T) {
|
||||||
|
|||||||
@ -4,7 +4,11 @@
|
|||||||
|
|
||||||
package gin
|
package gin
|
||||||
|
|
||||||
import "net/http"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
// CreateTestContext returns a fresh Engine and a Context associated with it.
|
// CreateTestContext returns a fresh Engine and a Context associated with it.
|
||||||
// This is useful for tests that need to set up a new Gin engine instance
|
// This is useful for tests that need to set up a new Gin engine instance
|
||||||
@ -29,3 +33,28 @@ func CreateTestContextOnly(w http.ResponseWriter, r *Engine) (c *Context) {
|
|||||||
c.writermem.reset(w)
|
c.writermem.reset(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// waitForServerReady waits for a server to be ready by making HTTP requests
|
||||||
|
// with exponential backoff. This is more reliable than time.Sleep() for testing.
|
||||||
|
func waitForServerReady(url string, maxAttempts int) error {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 100 * time.Millisecond,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < maxAttempts; i++ {
|
||||||
|
resp, err := client.Get(url)
|
||||||
|
if err == nil {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exponential backoff: 10ms, 20ms, 40ms, 80ms, 160ms...
|
||||||
|
backoff := time.Duration(10*(1<<uint(i))) * time.Millisecond
|
||||||
|
if backoff > 500*time.Millisecond {
|
||||||
|
backoff = 500 * time.Millisecond
|
||||||
|
}
|
||||||
|
time.Sleep(backoff)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("server at %s did not become ready after %d attempts", url, maxAttempts)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user