Skip to content

Commit 33c318b

Browse files
committed
feat: improve negative connectivity test logic and error handling
Signed-off-by: l1b0k <libokang.lbk@alibaba-inc.com>
1 parent f993a5a commit 33c318b

File tree

1 file changed

+83
-39
lines changed

1 file changed

+83
-39
lines changed

tests/connectivity_helpers_test.go

Lines changed: 83 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -179,67 +179,111 @@ func PullFail(client klient.Client, namespace, podName, container, target string
179179
}
180180

181181
// PullFailWithConfig executes a connectivity test that expects failure with custom configuration
182+
// Logic:
183+
// 1. If connection succeeds (HTTP 200) → FAIL immediately (unexpected success)
184+
// 2. If connection fails → retry once to confirm it's not flaky
185+
// 3. If both attempts fail due to curl error (connection refused/timeout) → SUCCESS (expected failure)
186+
// 4. If failure is due to kubectl exec API timeout → FAIL (infrastructure issue)
182187
func PullFailWithConfig(client klient.Client, req *PullRequest, config ConnectivityTestConfig, t *testing.T) (PullResponse, error) {
183188
resp := PullResponse{}
184-
errors := []error{}
185189
startTime := time.Now()
186-
attempts := 0
187190

188191
t.Logf("🔍 Starting negative connectivity test (expecting failure): %s", req.String())
189192

190-
err := wait.For(func(ctx context.Context) (done bool, err error) {
191-
attempts++
193+
// Helper function to execute curl and check result
194+
execCurl := func(attempt int) (succeeded bool, isCurlFailure bool, statusLine string, err error) {
195+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
196+
defer cancel()
197+
192198
var stdout, stderr bytes.Buffer
199+
// Use shorter timeout for curl since we expect it to fail
200+
cmd := []string{"curl", "-m", "5", "-I", req.Target}
193201

194-
cmd := []string{"curl", "-m", "2", "--retry", fmt.Sprintf("%d", config.Retries), "-I", req.Target}
195-
196-
t.Logf(" [Attempt %d] Executing command: %v", attempts, cmd)
202+
t.Logf(" [Attempt %d] Executing command: %v", attempt, cmd)
197203

198204
execErr := client.Resources().ExecInPod(ctx, req.Namespace, req.PodName, req.Container, cmd, &stdout, &stderr)
199205
if execErr != nil {
200-
t.Logf(" [Attempt %d] ✓ Expected failure: curl execution failed: %v", attempts, execErr)
201-
resp.StatusLine = "Connection Failed"
202-
resp.FullOutput = execErr.Error()
203-
return true, nil
206+
// Check if it's an API/infrastructure error (not curl failure)
207+
errStr := execErr.Error()
208+
if strings.Contains(errStr, "i/o timeout") && strings.Contains(errStr, "6443") {
209+
// kubectl API timeout - infrastructure issue
210+
return false, false, "", fmt.Errorf("kubectl exec API timeout (infrastructure issue): %v", execErr)
211+
}
212+
// curl failed to connect - this is expected
213+
t.Logf(" [Attempt %d] ✓ curl failed (expected): %v", attempt, execErr)
214+
return false, true, "Connection Failed", nil
204215
}
205216

217+
// curl succeeded - check response
206218
output := stdout.String()
207-
statusLine := strings.Split(output, "\n")[0]
208-
209-
t.Logf(" [Attempt %d] ❌ Unexpected success: %s", attempts, statusLine)
210-
errors = append(errors, fmt.Errorf("expected connection to fail but got success with status: %s", statusLine))
219+
statusLine = strings.Split(output, "\n")[0]
220+
t.Logf(" [Attempt %d] Response: %s", attempt, statusLine)
211221

212-
resp.StatusLine = statusLine
213-
resp.FullOutput = output
214-
215-
return false, nil
216-
},
217-
wait.WithTimeout(config.Timeout),
218-
wait.WithInterval(config.Interval))
219-
220-
duration := time.Since(startTime)
221-
resp.Duration = duration
222-
resp.Attempts = attempts
222+
return true, false, statusLine, nil
223+
}
223224

225+
// First attempt
226+
succeeded, isCurlFailure, statusLine, err := execCurl(1)
224227
if err != nil {
228+
// Infrastructure error
229+
resp.Duration = time.Since(startTime)
230+
resp.Attempts = 1
225231
resp.Success = false
226-
errMsg := fmt.Sprintf("negative connectivity test failed for %s after %d attempts (duration: %v) - connection succeeded when it should have failed",
227-
req.Target, attempts, duration)
232+
t.Logf("❌ Infrastructure error: %v", err)
233+
return resp, err
234+
}
235+
236+
if succeeded {
237+
// Connection succeeded - test should FAIL immediately
238+
resp.Duration = time.Since(startTime)
239+
resp.Attempts = 1
240+
resp.Success = false
241+
resp.StatusLine = statusLine
242+
errMsg := fmt.Sprintf("negative connectivity test FAILED: connection succeeded with %s (expected to fail)", statusLine)
228243
t.Logf("❌ %s", errMsg)
244+
return resp, fmt.Errorf("%s", errMsg)
245+
}
229246

230-
if len(errors) > 0 {
231-
t.Logf("Errors encountered:")
232-
for i, e := range errors {
233-
t.Logf(" [Error %d] %v", i+1, e)
234-
}
247+
if isCurlFailure {
248+
// First attempt failed as expected, retry once to confirm it's not flaky
249+
t.Logf(" [Attempt 1] Connection failed, retrying once to confirm...")
250+
time.Sleep(2 * time.Second)
251+
252+
succeeded2, isCurlFailure2, statusLine2, err2 := execCurl(2)
253+
if err2 != nil {
254+
// Infrastructure error on retry
255+
resp.Duration = time.Since(startTime)
256+
resp.Attempts = 2
257+
resp.Success = false
258+
t.Logf("❌ Infrastructure error on retry: %v", err2)
259+
return resp, err2
235260
}
236261

237-
return resp, utilerrors.NewAggregate(errors)
238-
}
262+
if succeeded2 {
263+
// Second attempt succeeded - first failure was flaky, test should FAIL
264+
resp.Duration = time.Since(startTime)
265+
resp.Attempts = 2
266+
resp.Success = false
267+
resp.StatusLine = statusLine2
268+
errMsg := fmt.Sprintf("negative connectivity test FAILED: connection succeeded on retry with %s (first failure was flaky)", statusLine2)
269+
t.Logf("❌ %s", errMsg)
270+
return resp, fmt.Errorf("%s", errMsg)
271+
}
239272

240-
resp.Success = true
241-
t.Logf("✓ Negative connectivity test succeeded for %s after %d attempts (duration: %v) - connection failed as expected",
242-
req.Target, attempts, duration)
273+
if isCurlFailure2 {
274+
// Both attempts failed - test PASSED (expected behavior)
275+
resp.Duration = time.Since(startTime)
276+
resp.Attempts = 2
277+
resp.Success = true
278+
resp.StatusLine = "Connection Failed"
279+
t.Logf("✓ Negative connectivity test PASSED: connection failed on both attempts (as expected)")
280+
return resp, nil
281+
}
282+
}
243283

244-
return resp, nil
284+
// Should not reach here
285+
resp.Duration = time.Since(startTime)
286+
resp.Attempts = 1
287+
resp.Success = false
288+
return resp, fmt.Errorf("unexpected state in PullFail")
245289
}

0 commit comments

Comments
 (0)