@@ -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)
182187func 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