Skip to content

Commit 7630eda

Browse files
committed
fix: make integration tests more robust against event ordering
The tests now use waitForLogContaining which continuously collects logs until finding the expected message, rather than expecting specific messages in the first batch of logs received. This fixes flaky tests caused by Kubernetes scheduling events arriving before pod lifecycle events.
1 parent c731c5f commit 7630eda

File tree

1 file changed

+31
-71
lines changed

1 file changed

+31
-71
lines changed

integration_test.go

Lines changed: 31 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -73,23 +73,26 @@ func createTestNamespace(t *testing.T, ctx context.Context, client kubernetes.In
7373
return name
7474
}
7575

76-
// waitForLogs waits for logs to be received on the channel with a timeout.
77-
func waitForLogs(t *testing.T, ctx context.Context, api *fakeAgentAPI, timeout time.Duration) []string {
76+
// waitForLogContaining waits until a log containing the given substring is received.
77+
// It collects all logs seen and returns them along with whether the target was found.
78+
func waitForLogContaining(t *testing.T, ctx context.Context, api *fakeAgentAPI, timeout time.Duration, substring string) (allLogs []string, found bool) {
7879
t.Helper()
7980

8081
timeoutCtx, cancel := context.WithTimeout(ctx, timeout)
8182
defer cancel()
8283

83-
select {
84-
case logs := <-api.logs:
85-
var outputs []string
86-
for _, log := range logs {
87-
outputs = append(outputs, log.Output)
84+
for {
85+
select {
86+
case logs := <-api.logs:
87+
for _, log := range logs {
88+
allLogs = append(allLogs, log.Output)
89+
if strings.Contains(log.Output, substring) {
90+
return allLogs, true
91+
}
92+
}
93+
case <-timeoutCtx.Done():
94+
return allLogs, false
8895
}
89-
return outputs
90-
case <-timeoutCtx.Done():
91-
t.Fatal("timeout waiting for logs")
92-
return nil
9396
}
9497
}
9598

@@ -173,35 +176,17 @@ func TestIntegration_PodEvents(t *testing.T) {
173176
// Wait for log source registration
174177
waitForLogSource(t, ctx, api, 30*time.Second)
175178

176-
// Wait for the "Created pod" log
177-
logs := waitForLogs(t, ctx, api, 30*time.Second)
178-
require.NotEmpty(t, logs)
179-
180-
var foundCreatedPod bool
181-
for _, log := range logs {
182-
if strings.Contains(log, "Created pod") {
183-
foundCreatedPod = true
184-
break
185-
}
186-
}
187-
require.True(t, foundCreatedPod, "expected 'Created pod' log, got: %v", logs)
179+
// Wait for the "Created pod" log (may receive other logs first like scheduling warnings)
180+
logs, found := waitForLogContaining(t, ctx, api, 30*time.Second, "Created pod")
181+
require.True(t, found, "expected 'Created pod' log, got: %v", logs)
188182

189183
// Delete the pod and verify deletion event
190184
err = client.CoreV1().Pods(namespace).Delete(ctx, pod.Name, metav1.DeleteOptions{})
191185
require.NoError(t, err)
192186

193187
// Wait for the "Deleted pod" log
194-
logs = waitForLogs(t, ctx, api, 30*time.Second)
195-
require.NotEmpty(t, logs)
196-
197-
var foundDeletedPod bool
198-
for _, log := range logs {
199-
if strings.Contains(log, "Deleted pod") {
200-
foundDeletedPod = true
201-
break
202-
}
203-
}
204-
require.True(t, foundDeletedPod, "expected 'Deleted pod' log, got: %v", logs)
188+
logs, found = waitForLogContaining(t, ctx, api, 30*time.Second, "Deleted pod")
189+
require.True(t, found, "expected 'Deleted pod' log, got: %v", logs)
205190
}
206191

207192
func TestIntegration_ReplicaSetEvents(t *testing.T) {
@@ -285,34 +270,16 @@ func TestIntegration_ReplicaSetEvents(t *testing.T) {
285270
waitForLogSource(t, ctx, api, 30*time.Second)
286271

287272
// Wait for the "Queued pod from ReplicaSet" log
288-
logs := waitForLogs(t, ctx, api, 30*time.Second)
289-
require.NotEmpty(t, logs)
290-
291-
var foundQueuedPod bool
292-
for _, log := range logs {
293-
if strings.Contains(log, "Queued pod from ReplicaSet") {
294-
foundQueuedPod = true
295-
break
296-
}
297-
}
298-
require.True(t, foundQueuedPod, "expected 'Queued pod from ReplicaSet' log, got: %v", logs)
273+
logs, found := waitForLogContaining(t, ctx, api, 30*time.Second, "Queued pod from ReplicaSet")
274+
require.True(t, found, "expected 'Queued pod from ReplicaSet' log, got: %v", logs)
299275

300276
// Delete the ReplicaSet
301277
err = client.AppsV1().ReplicaSets(namespace).Delete(ctx, rs.Name, metav1.DeleteOptions{})
302278
require.NoError(t, err)
303279

304280
// Wait for the "Deleted ReplicaSet" log
305-
logs = waitForLogs(t, ctx, api, 30*time.Second)
306-
require.NotEmpty(t, logs)
307-
308-
var foundDeletedRS bool
309-
for _, log := range logs {
310-
if strings.Contains(log, "Deleted ReplicaSet") {
311-
foundDeletedRS = true
312-
break
313-
}
314-
}
315-
require.True(t, foundDeletedRS, "expected 'Deleted ReplicaSet' log, got: %v", logs)
281+
logs, found = waitForLogContaining(t, ctx, api, 30*time.Second, "Deleted ReplicaSet")
282+
require.True(t, found, "expected 'Deleted ReplicaSet' log, got: %v", logs)
316283
}
317284

318285
func TestIntegration_MultiNamespace(t *testing.T) {
@@ -380,8 +347,8 @@ func TestIntegration_MultiNamespace(t *testing.T) {
380347

381348
// Wait for log source and logs from first pod
382349
waitForLogSource(t, ctx, api, 30*time.Second)
383-
logs := waitForLogs(t, ctx, api, 30*time.Second)
384-
require.NotEmpty(t, logs)
350+
logs, found := waitForLogContaining(t, ctx, api, 30*time.Second, "Created pod")
351+
require.True(t, found, "expected 'Created pod' log for first pod, got: %v", logs)
385352

386353
// Create a pod in namespace2
387354
pod2 := &corev1.Pod{
@@ -414,8 +381,8 @@ func TestIntegration_MultiNamespace(t *testing.T) {
414381

415382
// Wait for log source and logs from second pod
416383
waitForLogSource(t, ctx, api, 30*time.Second)
417-
logs = waitForLogs(t, ctx, api, 30*time.Second)
418-
require.NotEmpty(t, logs)
384+
logs, found = waitForLogContaining(t, ctx, api, 30*time.Second, "Created pod")
385+
require.True(t, found, "expected 'Created pod' log for second pod, got: %v", logs)
419386

420387
// Both namespaces should have received events
421388
t.Log("Successfully received events from both namespaces")
@@ -520,19 +487,12 @@ func TestIntegration_LabelSelector(t *testing.T) {
520487
// Wait for log source registration - this should only happen for the labeled pod
521488
waitForLogSource(t, ctx, api, 30*time.Second)
522489

523-
// Wait for logs
524-
logs := waitForLogs(t, ctx, api, 30*time.Second)
525-
require.NotEmpty(t, logs)
490+
// Wait for logs - look specifically for "Created pod" with the labeled pod name
491+
logs, found := waitForLogContaining(t, ctx, api, 30*time.Second, "Created pod")
492+
require.True(t, found, "expected 'Created pod' log for labeled pod, got: %v", logs)
526493

527-
// Verify that the log is for the labeled pod, not the unlabeled one
528-
var foundLabeledPod bool
494+
// Verify that none of the logs mention the unlabeled pod
529495
for _, log := range logs {
530-
if strings.Contains(log, "Created pod") && strings.Contains(log, "test-pod-with-label") {
531-
foundLabeledPod = true
532-
break
533-
}
534-
// Make sure we didn't get logs for the unlabeled pod
535496
require.NotContains(t, log, "test-pod-no-label", "should not receive logs for unlabeled pod")
536497
}
537-
require.True(t, foundLabeledPod, "expected 'Created pod' log for labeled pod, got: %v", logs)
538498
}

0 commit comments

Comments
 (0)