Files
traefikoidc/memory_leak_fixes_unit_test.go
T
lukaszraczylo ae59a5e88a 0.7.10 (#80)
* Add ability to disable replay protection. - This is useful for runs with multiple traefik replicas to avoid false positives and tokens re-creation.
* Enhance the CI/CD pipelines
* Increase test coverage.
* Update vendored dependencies.
* Update behaviour on forceHTTPS as per issue #82
2025-10-16 10:56:28 +01:00

226 lines
5.4 KiB
Go

package traefikoidc
import (
"net/http"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestNewLazyBackgroundTaskUnit tests LazyBackgroundTask creation without leak detection
func TestNewLazyBackgroundTaskUnit(t *testing.T) {
logger := GetSingletonNoOpLogger()
callCount := 0
taskFunc := func() {
callCount++
}
task := NewLazyBackgroundTask("test-task", 50*time.Millisecond, taskFunc, logger)
require.NotNil(t, task)
assert.NotNil(t, task.BackgroundTask)
assert.False(t, task.started)
// Should not execute before StartIfNeeded
time.Sleep(100 * time.Millisecond)
assert.Equal(t, 0, callCount, "task should not execute before StartIfNeeded")
// Cleanup
if task.started {
task.Stop()
}
}
// TestLazyBackgroundTaskStartIfNeededUnit tests the StartIfNeeded method
func TestLazyBackgroundTaskStartIfNeededUnit(t *testing.T) {
logger := GetSingletonNoOpLogger()
callCount := 0
var mu sync.Mutex
taskFunc := func() {
mu.Lock()
callCount++
mu.Unlock()
}
task := NewLazyBackgroundTask("test-start", 30*time.Millisecond, taskFunc, logger)
require.NotNil(t, task)
// Start the task
task.StartIfNeeded()
assert.True(t, task.started)
// Wait for execution
time.Sleep(100 * time.Millisecond)
mu.Lock()
firstCount := callCount
mu.Unlock()
assert.Greater(t, firstCount, 0, "task should execute after StartIfNeeded")
// Multiple calls should be idempotent
task.StartIfNeeded()
task.StartIfNeeded()
// Cleanup
task.Stop()
}
// TestLazyBackgroundTaskStopUnit tests the Stop method
func TestLazyBackgroundTaskStopUnit(t *testing.T) {
logger := GetSingletonNoOpLogger()
callCount := 0
var mu sync.Mutex
taskFunc := func() {
mu.Lock()
callCount++
mu.Unlock()
}
task := NewLazyBackgroundTask("test-stop", 30*time.Millisecond, taskFunc, logger)
require.NotNil(t, task)
// Start and let it run
task.StartIfNeeded()
time.Sleep(100 * time.Millisecond)
mu.Lock()
countAfterStart := callCount
mu.Unlock()
assert.Greater(t, countAfterStart, 0)
// Stop the task
task.Stop()
assert.False(t, task.started)
// Wait and verify it stopped
time.Sleep(100 * time.Millisecond)
mu.Lock()
countAfterStop := callCount
mu.Unlock()
// Allow 1 in-flight execution
assert.LessOrEqual(t, countAfterStop, countAfterStart+1, "task should stop executing")
}
// TestNewLazyCacheUnit tests NewLazyCache creation
func TestNewLazyCacheUnit(t *testing.T) {
cache := NewLazyCache()
require.NotNil(t, cache)
// Test basic operations
cache.Set("test-key", "test-value", time.Minute)
val, found := cache.Get("test-key")
assert.True(t, found)
assert.Equal(t, "test-value", val)
}
// TestNewLazyCacheWithLoggerUnit tests NewLazyCacheWithLogger creation
func TestNewLazyCacheWithLoggerUnit(t *testing.T) {
logger := GetSingletonNoOpLogger()
cache := NewLazyCacheWithLogger(logger)
require.NotNil(t, cache)
// Test with multiple entries
for i := 0; i < 10; i++ {
key := "key-" + string(rune('0'+i))
cache.Set(key, i, time.Minute)
}
// Verify entries
for i := 0; i < 10; i++ {
key := "key-" + string(rune('0'+i))
val, found := cache.Get(key)
assert.True(t, found, "should find key %s", key)
assert.Equal(t, i, val, "should get correct value for key %s", key)
}
}
// TestNewLazyCacheWithLoggerNilUnit tests NewLazyCacheWithLogger with nil logger
func TestNewLazyCacheWithLoggerNilUnit(t *testing.T) {
cache := NewLazyCacheWithLogger(nil)
require.NotNil(t, cache)
// Should work with nil logger (uses no-op logger)
cache.Set("nil-test", "value", time.Minute)
val, found := cache.Get("nil-test")
assert.True(t, found)
assert.Equal(t, "value", val)
}
// TestCleanupIdleConnectionsUnit tests CleanupIdleConnections function
func TestCleanupIdleConnectionsUnit(t *testing.T) {
t.Run("basic cleanup cycle", func(t *testing.T) {
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableCompression: true,
},
}
stopChan := make(chan struct{})
// Start cleanup in background
go CleanupIdleConnections(client, 40*time.Millisecond, stopChan)
// Let it run a couple of cycles
time.Sleep(100 * time.Millisecond)
// Stop cleanup
close(stopChan)
// Wait for cleanup to finish
time.Sleep(50 * time.Millisecond)
})
t.Run("immediate stop", func(t *testing.T) {
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
},
}
stopChan := make(chan struct{})
// Start and immediately stop
go CleanupIdleConnections(client, 100*time.Millisecond, stopChan)
time.Sleep(10 * time.Millisecond)
close(stopChan)
// Wait for cleanup
time.Sleep(50 * time.Millisecond)
})
t.Run("nil transport", func(t *testing.T) {
client := &http.Client{
Transport: nil,
}
stopChan := make(chan struct{})
// Should handle gracefully
go CleanupIdleConnections(client, 40*time.Millisecond, stopChan)
time.Sleep(80 * time.Millisecond)
close(stopChan)
time.Sleep(50 * time.Millisecond)
})
}
// TestDefaultOptimizedConfigUnit tests DefaultOptimizedConfig function (already has 100% coverage)
func TestDefaultOptimizedConfigUnit(t *testing.T) {
config := DefaultOptimizedConfig()
require.NotNil(t, config)
assert.True(t, config.DelayBackgroundTasks)
assert.True(t, config.ReducedCleanupIntervals)
assert.True(t, config.AggressiveConnectionCleanup)
assert.True(t, config.MinimalCacheSize)
}