Files
traefikoidc/goroutine_manager_test.go
T

203 lines
4.4 KiB
Go

package traefikoidc
import (
"context"
"sync/atomic"
"testing"
"time"
)
func TestGoroutineManager(t *testing.T) {
logger := NewLogger("debug")
t.Run("start and stop goroutine", func(t *testing.T) {
manager := NewGoroutineManager(logger)
started := make(chan struct{})
stopped := make(chan struct{})
manager.StartGoroutine("test", func(ctx context.Context) {
close(started)
<-ctx.Done()
close(stopped)
})
// Wait for goroutine to start
select {
case <-started:
// Good
case <-time.After(time.Second):
t.Fatal("Goroutine did not start")
}
// Stop the goroutine
manager.StopGoroutine("test")
// Wait for goroutine to stop
select {
case <-stopped:
// Good
case <-time.After(time.Second):
t.Fatal("Goroutine did not stop")
}
// Cleanup
manager.Shutdown(time.Second)
})
t.Run("periodic task", func(t *testing.T) {
manager := NewGoroutineManager(logger)
var counter int32
manager.StartPeriodicTask("counter", 50*time.Millisecond, func() {
atomic.AddInt32(&counter, 1)
})
// Let it run for a bit
time.Sleep(200 * time.Millisecond)
// Should have executed at least 3 times
count := atomic.LoadInt32(&counter)
if count < 3 {
t.Errorf("Expected at least 3 executions, got %d", count)
}
// Cleanup
manager.Shutdown(time.Second)
// Counter should stop increasing
finalCount := atomic.LoadInt32(&counter)
time.Sleep(100 * time.Millisecond)
if atomic.LoadInt32(&counter) != finalCount {
t.Error("Task continued after shutdown")
}
})
t.Run("multiple goroutines", func(t *testing.T) {
manager := NewGoroutineManager(logger)
var running int32
for i := 0; i < 5; i++ {
name := string(rune('a' + i))
manager.StartGoroutine(name, func(ctx context.Context) {
atomic.AddInt32(&running, 1)
<-ctx.Done()
atomic.AddInt32(&running, -1)
})
}
// Wait for all to start
time.Sleep(50 * time.Millisecond)
if atomic.LoadInt32(&running) != 5 {
t.Errorf("Expected 5 running goroutines, got %d", running)
}
// Shutdown all
err := manager.Shutdown(time.Second)
if err != nil {
t.Fatalf("Shutdown failed: %v", err)
}
if atomic.LoadInt32(&running) != 0 {
t.Errorf("Expected 0 running goroutines after shutdown, got %d", running)
}
})
t.Run("duplicate goroutine name", func(t *testing.T) {
manager := NewGoroutineManager(logger)
var starts int32
for i := 0; i < 3; i++ {
manager.StartGoroutine("same-name", func(ctx context.Context) {
atomic.AddInt32(&starts, 1)
<-ctx.Done()
})
}
time.Sleep(50 * time.Millisecond)
// Should only start once
if atomic.LoadInt32(&starts) != 1 {
t.Errorf("Expected 1 start for duplicate name, got %d", starts)
}
manager.Shutdown(time.Second)
})
t.Run("panic recovery", func(t *testing.T) {
manager := NewGoroutineManager(logger)
defer manager.Shutdown(time.Second)
// Start a goroutine that will panic
manager.StartGoroutine("panicker", func(ctx context.Context) {
panic("test panic")
})
// Give it time to panic and recover
time.Sleep(100 * time.Millisecond)
// Manager should still be functional - test with a simple task
completed := make(chan struct{})
manager.StartGoroutine("after-panic", func(ctx context.Context) {
close(completed)
})
select {
case <-completed:
// Good - manager is still functional
case <-time.After(time.Second):
t.Error("Manager not functional after panic")
}
})
t.Run("status tracking", func(t *testing.T) {
manager := NewGoroutineManager(logger)
manager.StartGoroutine("task1", func(ctx context.Context) {
<-ctx.Done()
})
manager.StartGoroutine("task2", func(ctx context.Context) {
time.Sleep(10 * time.Millisecond)
})
time.Sleep(50 * time.Millisecond)
status := manager.GetStatus()
if len(status) != 2 {
t.Errorf("Expected 2 goroutines in status, got %d", len(status))
}
if status["task1"].Running != true {
t.Error("task1 should be running")
}
if status["task2"].Running != false {
t.Error("task2 should have finished")
}
manager.Shutdown(time.Second)
})
t.Run("shutdown timeout", func(t *testing.T) {
manager := NewGoroutineManager(logger)
// Start a goroutine that won't stop
manager.StartGoroutine("stubborn", func(ctx context.Context) {
// Ignore context cancellation
select {}
})
// Try to shutdown with short timeout
err := manager.Shutdown(100 * time.Millisecond)
if err == nil {
t.Error("Expected shutdown timeout error")
}
})
}