mirror of
https://github.com/fatedier/frp.git
synced 2026-03-21 08:23:29 +08:00
test/e2e: replace sleeps with event-driven waits in chaos/group/store tests (#5231)
* test/e2e: replace sleeps with event-driven waits in chaos/group/store tests Replace 21 time.Sleep calls with deterministic waiting using WaitForOutput, WaitForTCPReady, and a new WaitForTCPUnreachable helper. Add CountOutput method for snapshot-based incremental log matching. * test/e2e: validate interval and cap dial/sleep to remaining deadline in WaitForTCPUnreachable
This commit is contained in:
@@ -144,6 +144,30 @@ func waitForClientProxyReady(configPath string, p *process.Process, timeout time
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WaitForTCPUnreachable polls a TCP address until a connection fails or timeout.
|
||||||
|
func WaitForTCPUnreachable(addr string, interval, timeout time.Duration) error {
|
||||||
|
if interval <= 0 {
|
||||||
|
return fmt.Errorf("invalid interval for TCP unreachable on %s: interval must be positive", addr)
|
||||||
|
}
|
||||||
|
if timeout <= 0 {
|
||||||
|
return fmt.Errorf("invalid timeout for TCP unreachable on %s: timeout must be positive", addr)
|
||||||
|
}
|
||||||
|
deadline := time.Now().Add(timeout)
|
||||||
|
for {
|
||||||
|
remaining := time.Until(deadline)
|
||||||
|
if remaining <= 0 {
|
||||||
|
return fmt.Errorf("timeout waiting for TCP unreachable on %s", addr)
|
||||||
|
}
|
||||||
|
dialTimeout := min(interval, remaining)
|
||||||
|
conn, err := net.DialTimeout("tcp", addr, dialTimeout)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
conn.Close()
|
||||||
|
time.Sleep(min(interval, time.Until(deadline)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WaitForTCPReady polls a TCP address until a connection succeeds or timeout.
|
// WaitForTCPReady polls a TCP address until a connection succeeds or timeout.
|
||||||
func WaitForTCPReady(addr string, timeout time.Duration) error {
|
func WaitForTCPReady(addr string, timeout time.Duration) error {
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ func (p *Process) Output() string {
|
|||||||
return p.stdOutput.String() + p.errorOutput.String()
|
return p.stdOutput.String() + p.errorOutput.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountOutput returns how many times pattern appears in the current accumulated output.
|
||||||
|
func (p *Process) CountOutput(pattern string) int {
|
||||||
|
return strings.Count(p.Output(), pattern)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Process) SetBeforeStopHandler(fn func()) {
|
func (p *Process) SetBeforeStopHandler(fn func()) {
|
||||||
p.beforeStopHandler = fn
|
p.beforeStopHandler = fn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,24 +41,24 @@ var _ = ginkgo.Describe("[Feature: Chaos]", func() {
|
|||||||
|
|
||||||
// 2. stop frps, expect request failed
|
// 2. stop frps, expect request failed
|
||||||
_ = ps.Stop()
|
_ = ps.Stop()
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
||||||
|
|
||||||
// 3. restart frps, expect request success
|
// 3. restart frps, expect request success
|
||||||
|
successCount := pc.CountOutput("[tcp] start proxy success")
|
||||||
_, _, err = f.RunFrps("-c", serverConfigPath)
|
_, _, err = f.RunFrps("-c", serverConfigPath)
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
time.Sleep(2 * time.Second)
|
framework.ExpectNoError(pc.WaitForOutput("[tcp] start proxy success", successCount+1, 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
|
|
||||||
// 4. stop frpc, expect request failed
|
// 4. stop frpc, expect request failed
|
||||||
_ = pc.Stop()
|
_ = pc.Stop()
|
||||||
time.Sleep(200 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPUnreachable(fmt.Sprintf("127.0.0.1:%d", remotePort), 100*time.Millisecond, 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
||||||
|
|
||||||
// 5. restart frpc, expect request success
|
// 5. restart frpc, expect request success
|
||||||
_, _, err = f.RunFrpc("-c", clientConfigPath)
|
newPc, _, err := f.RunFrpc("-c", clientConfigPath)
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(newPc.WaitForOutput("[tcp] start proxy success", 1, 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
|
|||||||
healthCheck.intervalSeconds = 1
|
healthCheck.intervalSeconds = 1
|
||||||
`, fooPort, remotePort, barPort, remotePort)
|
`, fooPort, remotePort, barPort, remotePort)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
_, clientProcesses := f.RunProcesses(serverConf, []string{clientConf})
|
||||||
|
|
||||||
// check foo and bar is ok
|
// check foo and bar is ok
|
||||||
results := []string{}
|
results := []string{}
|
||||||
@@ -299,15 +299,17 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
|
|||||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||||
|
|
||||||
// close bar server, check foo is ok
|
// close bar server, check foo is ok
|
||||||
|
failedCount := clientProcesses[0].CountOutput("[bar] health check failed")
|
||||||
barServer.Close()
|
barServer.Close()
|
||||||
time.Sleep(2 * time.Second)
|
framework.ExpectNoError(clientProcesses[0].WaitForOutput("[bar] health check failed", failedCount+1, 5*time.Second))
|
||||||
for range 10 {
|
for range 10 {
|
||||||
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).ExpectResp([]byte("foo")).Ensure()
|
||||||
}
|
}
|
||||||
|
|
||||||
// resume bar server, check foo and bar is ok
|
// resume bar server, check foo and bar is ok
|
||||||
|
successCount := clientProcesses[0].CountOutput("[bar] health check success")
|
||||||
f.RunServer("", barServer)
|
f.RunServer("", barServer)
|
||||||
time.Sleep(2 * time.Second)
|
framework.ExpectNoError(clientProcesses[0].WaitForOutput("[bar] health check success", successCount+1, 5*time.Second))
|
||||||
results = []string{}
|
results = []string{}
|
||||||
for range 10 {
|
for range 10 {
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
|
framework.NewRequestExpect(f).Port(remotePort).Ensure(validateFooBarResponse, func(resp *request.Response) bool {
|
||||||
@@ -357,7 +359,7 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
|
|||||||
healthCheck.path = "/healthz"
|
healthCheck.path = "/healthz"
|
||||||
`, fooPort, barPort)
|
`, fooPort, barPort)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
_, clientProcesses := f.RunProcesses(serverConf, []string{clientConf})
|
||||||
|
|
||||||
// send first HTTP request
|
// send first HTTP request
|
||||||
var contents []string
|
var contents []string
|
||||||
@@ -387,15 +389,17 @@ var _ = ginkgo.Describe("[Feature: Group]", func() {
|
|||||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||||
|
|
||||||
// close bar server, check foo is ok
|
// close bar server, check foo is ok
|
||||||
|
failedCount := clientProcesses[0].CountOutput("[bar] health check failed")
|
||||||
barServer.Close()
|
barServer.Close()
|
||||||
time.Sleep(2 * time.Second)
|
framework.ExpectNoError(clientProcesses[0].WaitForOutput("[bar] health check failed", failedCount+1, 5*time.Second))
|
||||||
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
||||||
framework.ExpectContainElements(results, []string{"foo"})
|
framework.ExpectContainElements(results, []string{"foo"})
|
||||||
framework.ExpectNotContainElements(results, []string{"bar"})
|
framework.ExpectNotContainElements(results, []string{"bar"})
|
||||||
|
|
||||||
// resume bar server, check foo and bar is ok
|
// resume bar server, check foo and bar is ok
|
||||||
|
successCount := clientProcesses[0].CountOutput("[bar] health check success")
|
||||||
f.RunServer("", barServer)
|
f.RunServer("", barServer)
|
||||||
time.Sleep(2 * time.Second)
|
framework.ExpectNoError(clientProcesses[0].WaitForOutput("[bar] health check success", successCount+1, 5*time.Second))
|
||||||
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
results = doFooBarHTTPRequest(vhostPort, "example.com")
|
||||||
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
framework.ExpectContainElements(results, []string{"foo", "bar"})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
proxyConfig := map[string]any{
|
proxyConfig := map[string]any{
|
||||||
"name": "test-tcp",
|
"name": "test-tcp",
|
||||||
@@ -52,7 +52,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", remotePort), 5*time.Second))
|
||||||
|
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
})
|
})
|
||||||
@@ -72,7 +72,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
proxyConfig := map[string]any{
|
proxyConfig := map[string]any{
|
||||||
"name": "test-tcp",
|
"name": "test-tcp",
|
||||||
@@ -93,7 +93,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", remotePort1), 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort1).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort1).Ensure()
|
||||||
|
|
||||||
proxyConfig["tcp"].(map[string]any)["remotePort"] = remotePort2
|
proxyConfig["tcp"].(map[string]any)["remotePort"] = remotePort2
|
||||||
@@ -107,7 +107,8 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", remotePort2), 5*time.Second))
|
||||||
|
framework.ExpectNoError(framework.WaitForTCPUnreachable(fmt.Sprintf("127.0.0.1:%d", remotePort1), 100*time.Millisecond, 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort2).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort2).Ensure()
|
||||||
framework.NewRequestExpect(f).Port(remotePort1).ExpectError(true).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort1).ExpectError(true).Ensure()
|
||||||
})
|
})
|
||||||
@@ -126,7 +127,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
proxyConfig := map[string]any{
|
proxyConfig := map[string]any{
|
||||||
"name": "test-tcp",
|
"name": "test-tcp",
|
||||||
@@ -147,7 +148,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", remotePort), 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).Ensure()
|
||||||
|
|
||||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||||
@@ -156,7 +157,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(time.Second)
|
framework.ExpectNoError(framework.WaitForTCPUnreachable(fmt.Sprintf("127.0.0.1:%d", remotePort), 100*time.Millisecond, 5*time.Second))
|
||||||
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
framework.NewRequestExpect(f).Port(remotePort).ExpectError(true).Ensure()
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -174,7 +175,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
proxyConfig := map[string]any{
|
proxyConfig := map[string]any{
|
||||||
"name": "test-tcp",
|
"name": "test-tcp",
|
||||||
@@ -195,8 +196,6 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
return resp.Code == 200
|
return resp.Code == 200
|
||||||
})
|
})
|
||||||
|
|
||||||
time.Sleep(500 * time.Millisecond)
|
|
||||||
|
|
||||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||||
r.HTTP().Port(adminPort).HTTPPath("/api/store/proxies")
|
r.HTTP().Port(adminPort).HTTPPath("/api/store/proxies")
|
||||||
}).Ensure(func(resp *request.Response) bool {
|
}).Ensure(func(resp *request.Response) bool {
|
||||||
@@ -226,7 +225,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort)
|
`, adminPort)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
|
||||||
r.HTTP().Port(adminPort).HTTPPath("/api/store/proxies")
|
r.HTTP().Port(adminPort).HTTPPath("/api/store/proxies")
|
||||||
@@ -248,7 +247,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
invalidBody, _ := json.Marshal(map[string]any{
|
invalidBody, _ := json.Marshal(map[string]any{
|
||||||
"name": "bad-proxy",
|
"name": "bad-proxy",
|
||||||
@@ -281,7 +280,7 @@ var _ = ginkgo.Describe("[Feature: Store]", func() {
|
|||||||
`, adminPort, f.TempDirectory)
|
`, adminPort, f.TempDirectory)
|
||||||
|
|
||||||
f.RunProcesses(serverConf, []string{clientConf})
|
f.RunProcesses(serverConf, []string{clientConf})
|
||||||
time.Sleep(500 * time.Millisecond)
|
framework.ExpectNoError(framework.WaitForTCPReady(fmt.Sprintf("127.0.0.1:%d", adminPort), 5*time.Second))
|
||||||
|
|
||||||
createBody, _ := json.Marshal(map[string]any{
|
createBody, _ := json.Marshal(map[string]any{
|
||||||
"name": "proxy-a",
|
"name": "proxy-a",
|
||||||
|
|||||||
Reference in New Issue
Block a user