330 lines
8.4 KiB
Go
330 lines
8.4 KiB
Go
package backgroundtask
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestSlowWorkFunc tests the slow work function
|
|
func TestSlowWorkFunc(t *testing.T) {
|
|
t.Run("NormalExecution", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.WorkDuration = 100 * time.Millisecond
|
|
|
|
start := time.Now()
|
|
err := SlowWorkFunc(t.Context(), slog.Default(), config)
|
|
elapsed := time.Since(start)
|
|
|
|
if err != nil {
|
|
t.Errorf("Expected no error, got: %v", err)
|
|
}
|
|
|
|
if elapsed < testConfig.WorkDuration/2 {
|
|
t.Errorf("Work completed too quickly: %v (expected ~%v)", elapsed, testConfig.WorkDuration)
|
|
}
|
|
|
|
if testConfig.Counter.Load() != 1 {
|
|
t.Errorf("Expected counter to be 1, got %d", testConfig.Counter.Load())
|
|
}
|
|
})
|
|
|
|
t.Run("ContextCancellation", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.WorkDuration = 1 * time.Second // Long duration
|
|
|
|
ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond)
|
|
defer cancel()
|
|
|
|
start := time.Now()
|
|
err := SlowWorkFunc(ctx, slog.Default(), config)
|
|
elapsed := time.Since(start)
|
|
|
|
if err == nil {
|
|
t.Error("Expected context cancellation error")
|
|
}
|
|
|
|
if elapsed > 200*time.Millisecond {
|
|
t.Errorf("Work took too long after cancellation: %v", elapsed)
|
|
}
|
|
|
|
if testConfig.Counter.Load() != 1 {
|
|
t.Errorf("Expected counter to be 1, got %d", testConfig.Counter.Load())
|
|
}
|
|
})
|
|
|
|
t.Run("InvalidConfig", func(t *testing.T) {
|
|
config := map[string]any{"invalid": "config"}
|
|
|
|
err := SlowWorkFunc(t.Context(), slog.Default(), config)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error for invalid config")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestBlockingWorkFunc tests the blocking work function
|
|
func TestBlockingWorkFunc(t *testing.T) {
|
|
t.Run("BlocksUntilCancellation", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
|
|
ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond)
|
|
defer cancel()
|
|
|
|
start := time.Now()
|
|
err := BlockingWorkFunc(ctx, slog.Default(), config)
|
|
elapsed := time.Since(start)
|
|
|
|
if err == nil {
|
|
t.Error("Expected context cancellation error")
|
|
}
|
|
|
|
// Should block for approximately the timeout duration
|
|
if elapsed < 40*time.Millisecond || elapsed > 100*time.Millisecond {
|
|
t.Errorf("Unexpected blocking duration: %v", elapsed)
|
|
}
|
|
|
|
if testConfig.Counter.Load() != 1 {
|
|
t.Errorf("Expected counter to be 1, got %d", testConfig.Counter.Load())
|
|
}
|
|
})
|
|
|
|
t.Run("InvalidConfig", func(t *testing.T) {
|
|
config := map[string]any{"invalid": "config"}
|
|
|
|
err := BlockingWorkFunc(t.Context(), slog.Default(), config)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error for invalid config")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestWaitForCondition tests the wait for condition helper
|
|
func TestWaitForCondition(t *testing.T) {
|
|
t.Run("ConditionMet", func(t *testing.T) {
|
|
var conditionMet atomic.Bool
|
|
|
|
// Start a goroutine that will set the condition after a delay
|
|
go func() {
|
|
time.Sleep(50 * time.Millisecond)
|
|
conditionMet.Store(true)
|
|
}()
|
|
|
|
start := time.Now()
|
|
result := WaitForCondition(func() bool {
|
|
return conditionMet.Load()
|
|
}, 200*time.Millisecond)
|
|
elapsed := time.Since(start)
|
|
|
|
if !result {
|
|
t.Error("Expected condition to be met")
|
|
}
|
|
|
|
if elapsed > 100*time.Millisecond {
|
|
t.Errorf("Condition took too long to be met: %v", elapsed)
|
|
}
|
|
})
|
|
|
|
t.Run("ConditionTimeout", func(t *testing.T) {
|
|
start := time.Now()
|
|
result := WaitForCondition(func() bool {
|
|
return false // Never true
|
|
}, 50*time.Millisecond)
|
|
elapsed := time.Since(start)
|
|
|
|
if result {
|
|
t.Error("Expected condition to timeout")
|
|
}
|
|
|
|
if elapsed < 40*time.Millisecond {
|
|
t.Errorf("Timeout too short: %v", elapsed)
|
|
}
|
|
})
|
|
|
|
t.Run("ImmediateCondition", func(t *testing.T) {
|
|
start := time.Now()
|
|
result := WaitForCondition(func() bool {
|
|
return true // Always true
|
|
}, 100*time.Millisecond)
|
|
elapsed := time.Since(start)
|
|
|
|
if !result {
|
|
t.Error("Expected immediate condition to be met")
|
|
}
|
|
|
|
if elapsed > 50*time.Millisecond {
|
|
t.Errorf("Immediate condition took too long: %v", elapsed)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestHelperFunctions tests additional helper functions for coverage
|
|
func TestHelperFunctions(t *testing.T) {
|
|
t.Run("NewTestConfigMap", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
|
|
if config == nil {
|
|
t.Fatal("Expected config map to be created")
|
|
}
|
|
|
|
testConfig, ok := GetTestConfig(config)
|
|
if !ok {
|
|
t.Fatal("Expected test config to be in map")
|
|
}
|
|
|
|
if testConfig.Counter == nil {
|
|
t.Error("Expected counter to be initialized")
|
|
}
|
|
|
|
if testConfig.WorkDuration != 1*time.Millisecond {
|
|
t.Errorf("Expected default work duration to be 1ms, got %v", testConfig.WorkDuration)
|
|
}
|
|
})
|
|
|
|
t.Run("GetTestConfigInvalid", func(t *testing.T) {
|
|
config := map[string]any{"invalid": "config"}
|
|
|
|
_, ok := GetTestConfig(config)
|
|
if ok {
|
|
t.Error("Expected GetTestConfig to return false for invalid config")
|
|
}
|
|
})
|
|
|
|
t.Run("GetTestConfigMissing", func(t *testing.T) {
|
|
config := map[string]any{}
|
|
|
|
_, ok := GetTestConfig(config)
|
|
if ok {
|
|
t.Error("Expected GetTestConfig to return false for missing config")
|
|
}
|
|
})
|
|
|
|
t.Run("GetTestConfigWrongType", func(t *testing.T) {
|
|
config := map[string]any{"testConfig": "not a TestConfig"}
|
|
|
|
_, ok := GetTestConfig(config)
|
|
if ok {
|
|
t.Error("Expected GetTestConfig to return false for wrong type")
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestErrorWorkFuncVariations tests error work function with different configurations
|
|
func TestErrorWorkFuncVariations(t *testing.T) {
|
|
t.Run("CustomErrorMessage", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.ErrorMessage = "custom error message"
|
|
|
|
err := ErrorWorkFunc(t.Context(), slog.Default(), config)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error")
|
|
}
|
|
|
|
if err.Error() != "custom error message" {
|
|
t.Errorf("Expected 'custom error message', got: %v", err.Error())
|
|
}
|
|
|
|
if testConfig.Counter.Load() != 1 {
|
|
t.Errorf("Expected counter to be 1, got %d", testConfig.Counter.Load())
|
|
}
|
|
})
|
|
|
|
t.Run("EmptyErrorMessage", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.ErrorMessage = ""
|
|
|
|
err := ErrorWorkFunc(t.Context(), slog.Default(), config)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error")
|
|
}
|
|
|
|
if err.Error() != "error work function failed" {
|
|
t.Errorf("Expected default error message, got: %v", err.Error())
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestTestWorkFuncVariations tests the test work function with different configurations
|
|
func TestTestWorkFuncVariations(t *testing.T) {
|
|
t.Run("MaxExecutionsLimit", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.MaxExecutions = 2
|
|
|
|
// First execution should work
|
|
err := TestWorkFunc(t.Context(), slog.Default(), config)
|
|
if err != nil {
|
|
t.Errorf("First execution failed: %v", err)
|
|
}
|
|
|
|
// Second execution should work
|
|
err = TestWorkFunc(t.Context(), slog.Default(), config)
|
|
if err != nil {
|
|
t.Errorf("Second execution failed: %v", err)
|
|
}
|
|
|
|
// Third execution should be skipped
|
|
err = TestWorkFunc(t.Context(), slog.Default(), config)
|
|
if err != nil {
|
|
t.Errorf("Third execution failed: %v", err)
|
|
}
|
|
|
|
// Counter should be 2 (third execution was skipped)
|
|
if testConfig.Counter.Load() != 2 {
|
|
t.Errorf("Expected counter to be 2, got %d", testConfig.Counter.Load())
|
|
}
|
|
})
|
|
|
|
t.Run("ErrorConfigured", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.ShouldError = true
|
|
testConfig.ErrorMessage = "configured error"
|
|
|
|
err := TestWorkFunc(t.Context(), slog.Default(), config)
|
|
|
|
if err == nil {
|
|
t.Error("Expected error")
|
|
}
|
|
|
|
if err.Error() != "configured error" {
|
|
t.Errorf("Expected 'configured error', got: %v", err.Error())
|
|
}
|
|
})
|
|
|
|
t.Run("ContextDisrespected", func(t *testing.T) {
|
|
config := NewTestConfigMap()
|
|
testConfig, _ := GetTestConfig(config)
|
|
testConfig.WorkDuration = 100 * time.Millisecond
|
|
testConfig.RespectContext = false
|
|
|
|
ctx, cancel := context.WithTimeout(t.Context(), 10*time.Millisecond)
|
|
defer cancel()
|
|
|
|
start := time.Now()
|
|
err := TestWorkFunc(ctx, slog.Default(), config)
|
|
elapsed := time.Since(start)
|
|
|
|
// Should complete despite context cancellation
|
|
if err != nil {
|
|
t.Errorf("Expected no error when context is disrespected, got: %v", err)
|
|
}
|
|
|
|
// Should take approximately the full work duration
|
|
if elapsed < testConfig.WorkDuration/2 {
|
|
t.Errorf("Work completed too quickly: %v", elapsed)
|
|
}
|
|
})
|
|
}
|