@@ -23,6 +23,22 @@ func newTestExecutor(buf *bytes.Buffer, sigChan chan os.Signal, opts *logsOption
2323 }
2424}
2525
26+ // waitForOutput polls the buffer until it contains the expected string or times out.
27+ func waitForOutput (buf * bytes.Buffer , contains string , timeout time.Duration ) bool {
28+ deadline := time .After (timeout )
29+ for {
30+ select {
31+ case <- deadline :
32+ return false
33+ default :
34+ if strings .Contains (buf .String (), contains ) {
35+ return true
36+ }
37+ time .Sleep (5 * time .Millisecond )
38+ }
39+ }
40+ }
41+
2642func TestLogsExecutor_FollowLogsViaDashboard (t * testing.T ) {
2743 t .Run ("ping error" , func (t * testing.T ) {
2844 var buf bytes.Buffer
@@ -60,7 +76,10 @@ func TestLogsExecutor_FollowLogsViaDashboard(t *testing.T) {
6076 done <- executor .followLogsViaDashboard (ctx , mockClient , nil , LogLevelAll , nil , & buf )
6177 }()
6278
63- time .Sleep (100 * time .Millisecond )
79+ // Poll for output instead of fixed sleep
80+ if ! waitForOutput (& buf , "Log" , 2 * time .Second ) {
81+ t .Fatal ("timed out waiting for log output" )
82+ }
6483 cancel ()
6584
6685 err := <- done
@@ -98,7 +117,10 @@ func TestLogsExecutor_FollowLogsViaDashboard(t *testing.T) {
98117 done <- executor .followLogsViaDashboard (ctx , mockClient , nil , service .LogLevelError , nil , & buf )
99118 }()
100119
101- time .Sleep (100 * time .Millisecond )
120+ // Wait for the error log to appear (it passes the filter)
121+ if ! waitForOutput (& buf , "Error log" , 2 * time .Second ) {
122+ t .Log ("Warning: no filtered output appeared within timeout" )
123+ }
102124 cancel ()
103125
104126 <- done
@@ -133,7 +155,10 @@ func TestLogsExecutor_FollowLogsViaDashboard(t *testing.T) {
133155 done <- executor .followLogsViaDashboard (ctx , mockClient , []string {"api" , "worker" }, LogLevelAll , nil , & buf )
134156 }()
135157
136- time .Sleep (100 * time .Millisecond )
158+ // Wait for the API log to appear (it passes the service filter)
159+ if ! waitForOutput (& buf , "API log" , 2 * time .Second ) {
160+ t .Log ("Warning: no filtered output appeared within timeout" )
161+ }
137162 cancel ()
138163
139164 <- done
@@ -153,17 +178,21 @@ func TestLogsExecutor_FollowLogsViaDashboard(t *testing.T) {
153178
154179 ctx := context .Background ()
155180
156- done := make (chan error )
181+ done := make (chan error , 1 )
157182 go func () {
158183 done <- executor .followLogsViaDashboard (ctx , mockClient , nil , LogLevelAll , nil , & buf )
159184 }()
160185
161- time . Sleep ( 10 * time . Millisecond )
186+ // Buffered channel - signal will be picked up when goroutine reaches select
162187 sigChan <- os .Interrupt
163188
164- err := <- done
165- if err != nil {
166- t .Errorf ("Expected nil error on signal, got: %v" , err )
189+ select {
190+ case err := <- done :
191+ if err != nil {
192+ t .Errorf ("Expected nil error on signal, got: %v" , err )
193+ }
194+ case <- time .After (5 * time .Second ):
195+ t .Fatal ("timed out waiting for signal to interrupt streaming" )
167196 }
168197 })
169198}
@@ -177,17 +206,20 @@ func TestLogsExecutor_FollowLogsInMemory(t *testing.T) {
177206 subscriptions := make (map [string ]chan service.LogEntry )
178207 mockLM := newMockLogManager ()
179208
180- done := make (chan error )
209+ done := make (chan error , 1 )
181210 go func () {
182211 done <- executor .followLogsInMemory (subscriptions , mockLM , LogLevelAll , nil , & buf )
183212 }()
184213
185- time .Sleep (10 * time .Millisecond )
186214 sigChan <- os .Interrupt
187215
188- err := <- done
189- if err != nil {
190- t .Errorf ("Expected nil error on signal, got: %v" , err )
216+ select {
217+ case err := <- done :
218+ if err != nil {
219+ t .Errorf ("Expected nil error on signal, got: %v" , err )
220+ }
221+ case <- time .After (5 * time .Second ):
222+ t .Fatal ("timed out waiting for signal to interrupt streaming" )
191223 }
192224 })
193225
@@ -217,7 +249,10 @@ func TestLogsExecutor_FollowLogsInMemory(t *testing.T) {
217249 now := time .Now ()
218250 logChan <- service.LogEntry {Service : "api" , Level : service .LogLevelInfo , Message : "Test message" , Timestamp : now }
219251
220- time .Sleep (50 * time .Millisecond )
252+ // Poll for output instead of fixed sleep
253+ if ! waitForOutput (& buf , "Test message" , 2 * time .Second ) {
254+ t .Fatal ("timed out waiting for log output" )
255+ }
221256 sigChan <- os .Interrupt
222257
223258 <- done
@@ -253,7 +288,10 @@ func TestLogsExecutor_FollowLogsInMemory(t *testing.T) {
253288 logChan <- service.LogEntry {Service : "api" , Level : service .LogLevelInfo , Message : "Info msg" , Timestamp : now }
254289 logChan <- service.LogEntry {Service : "api" , Level : service .LogLevelError , Message : "Error msg" , Timestamp : now }
255290
256- time .Sleep (50 * time .Millisecond )
291+ // Poll for the error message to appear (it passes the filter)
292+ if ! waitForOutput (& buf , "Error msg" , 2 * time .Second ) {
293+ t .Log ("Warning: no filtered output appeared within timeout" )
294+ }
257295 sigChan <- os .Interrupt
258296
259297 <- done
@@ -276,17 +314,21 @@ func TestLogsExecutor_FollowLogsInMemory(t *testing.T) {
276314 buf3 , _ := service .NewLogBuffer ("api" , 100 , false , "" )
277315 mockLM .buffers ["api" ] = buf3
278316
279- done := make (chan error )
317+ done := make (chan error , 1 )
280318 go func () {
281319 done <- executor .followLogsInMemory (subscriptions , mockLM , LogLevelAll , nil , & buf )
282320 }()
283321
284- time . Sleep ( 10 * time . Millisecond )
322+ // Close channel - buffered done chan ensures we don't block
285323 close (logChan )
286324
287- err := <- done
288- if err != nil {
289- t .Errorf ("Expected nil error when channels close, got: %v" , err )
325+ select {
326+ case err := <- done :
327+ if err != nil {
328+ t .Errorf ("Expected nil error when channels close, got: %v" , err )
329+ }
330+ case <- time .After (5 * time .Second ):
331+ t .Fatal ("timed out waiting for close to propagate" )
290332 }
291333 })
292334
@@ -311,7 +353,9 @@ func TestLogsExecutor_FollowLogsInMemory(t *testing.T) {
311353 now := time .Now ()
312354 logChan <- service.LogEntry {Service : "api" , Level : service .LogLevelInfo , Message : "JSON test" , Timestamp : now }
313355
314- time .Sleep (50 * time .Millisecond )
356+ if ! waitForOutput (& buf , "JSON test" , 2 * time .Second ) {
357+ t .Fatal ("timed out waiting for JSON output" )
358+ }
315359 sigChan <- os .Interrupt
316360
317361 <- done
@@ -330,16 +374,19 @@ func TestLogsExecutor_FollowLogs(t *testing.T) {
330374 executor := newTestExecutor (& buf , sigChan , & logsOptions {format : "text" })
331375
332376 mockClient := & mockDashboardClient {}
333- done := make (chan error )
377+ done := make (chan error , 1 )
334378 go func () {
335379 done <- executor .followLogsViaDashboard (context .Background (), mockClient , nil , LogLevelAll , nil , & buf )
336380 }()
337381
338- time .Sleep (10 * time .Millisecond )
339382 sigChan <- os .Interrupt
340- err := <- done
341- if err != nil {
342- t .Errorf ("Expected nil error, got: %v" , err )
383+ select {
384+ case err := <- done :
385+ if err != nil {
386+ t .Errorf ("Expected nil error, got: %v" , err )
387+ }
388+ case <- time .After (5 * time .Second ):
389+ t .Fatal ("timed out waiting for completion" )
343390 }
344391 })
345392
@@ -356,16 +403,19 @@ func TestLogsExecutor_FollowLogs(t *testing.T) {
356403 buf6 , _ := service .NewLogBuffer ("api" , 100 , false , "" )
357404 mockLM .buffers ["api" ] = buf6
358405
359- done := make (chan error )
406+ done := make (chan error , 1 )
360407 go func () {
361408 done <- executor .followLogsInMemory (subscriptions , mockLM , LogLevelAll , nil , & buf )
362409 }()
363410
364- time .Sleep (10 * time .Millisecond )
365411 sigChan <- os .Interrupt
366- err := <- done
367- if err != nil {
368- t .Errorf ("Expected nil error, got: %v" , err )
412+ select {
413+ case err := <- done :
414+ if err != nil {
415+ t .Errorf ("Expected nil error, got: %v" , err )
416+ }
417+ case <- time .After (5 * time .Second ):
418+ t .Fatal ("timed out waiting for completion" )
369419 }
370420 })
371421}
@@ -390,16 +440,19 @@ func TestLogsExecutor_FollowLogsOrchestration(t *testing.T) {
390440 pingErr : context .DeadlineExceeded ,
391441 }
392442
393- done := make (chan error )
443+ done := make (chan error , 1 )
394444 go func () {
395445 done <- executor .followLogs (context .Background (), tmpDir , mockLM , mockClient , nil , LogLevelAll , nil , & buf )
396446 }()
397447
398- time .Sleep (10 * time .Millisecond )
399448 sigChan <- os .Interrupt
400- err := <- done
401- if err != nil {
402- t .Errorf ("Expected nil error, got: %v" , err )
449+ select {
450+ case err := <- done :
451+ if err != nil {
452+ t .Errorf ("Expected nil error, got: %v" , err )
453+ }
454+ case <- time .After (5 * time .Second ):
455+ t .Fatal ("timed out waiting for completion" )
403456 }
404457 })
405458
@@ -414,16 +467,19 @@ func TestLogsExecutor_FollowLogsOrchestration(t *testing.T) {
414467
415468 mockClient := & mockDashboardClient {}
416469
417- done := make (chan error )
470+ done := make (chan error , 1 )
418471 go func () {
419472 done <- executor .followLogs (context .Background (), tmpDir , mockLM , mockClient , []string {"api" }, LogLevelAll , nil , & buf )
420473 }()
421474
422- time .Sleep (10 * time .Millisecond )
423475 sigChan <- os .Interrupt
424- err := <- done
425- if err != nil {
426- t .Errorf ("Expected nil error, got: %v" , err )
476+ select {
477+ case err := <- done :
478+ if err != nil {
479+ t .Errorf ("Expected nil error, got: %v" , err )
480+ }
481+ case <- time .After (5 * time .Second ):
482+ t .Fatal ("timed out waiting for completion" )
427483 }
428484 })
429485
@@ -435,16 +491,19 @@ func TestLogsExecutor_FollowLogsOrchestration(t *testing.T) {
435491 mockLM := newMockLogManager ()
436492 mockClient := & mockDashboardClient {}
437493
438- done := make (chan error )
494+ done := make (chan error , 1 )
439495 go func () {
440496 done <- executor .followLogs (context .Background (), tmpDir , mockLM , mockClient , nil , LogLevelAll , nil , & buf )
441497 }()
442498
443- time .Sleep (10 * time .Millisecond )
444499 sigChan <- os .Interrupt
445- err := <- done
446- if err != nil {
447- t .Errorf ("Expected nil error, got: %v" , err )
500+ select {
501+ case err := <- done :
502+ if err != nil {
503+ t .Errorf ("Expected nil error, got: %v" , err )
504+ }
505+ case <- time .After (5 * time .Second ):
506+ t .Fatal ("timed out waiting for completion" )
448507 }
449508 })
450509
@@ -459,16 +518,19 @@ func TestLogsExecutor_FollowLogsOrchestration(t *testing.T) {
459518
460519 mockClient := & mockDashboardClient {}
461520
462- done := make (chan error )
521+ done := make (chan error , 1 )
463522 go func () {
464523 done <- executor .followLogs (context .Background (), tmpDir , mockLM , mockClient , []string {"nonexistent" }, LogLevelAll , nil , & buf )
465524 }()
466525
467- time .Sleep (10 * time .Millisecond )
468526 sigChan <- os .Interrupt
469- err := <- done
470- if err != nil {
471- t .Errorf ("Expected nil error, got: %v" , err )
527+ select {
528+ case err := <- done :
529+ if err != nil {
530+ t .Errorf ("Expected nil error, got: %v" , err )
531+ }
532+ case <- time .After (5 * time .Second ):
533+ t .Fatal ("timed out waiting for completion" )
472534 }
473535 })
474536}
0 commit comments