From 33c71fd6fec6342bc153da5ab9fb40b1052de61e Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Thu, 6 Feb 2025 23:38:22 +0000 Subject: [PATCH] Enhance test suite. --- helpers_test.go | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 helpers_test.go diff --git a/helpers_test.go b/helpers_test.go new file mode 100644 index 0000000..1c87c0c --- /dev/null +++ b/helpers_test.go @@ -0,0 +1,225 @@ +package traefikoidc + +import ( + "fmt" + "runtime" + "testing" + "time" +) + +func TestTokenBlacklistSizeLimit(t *testing.T) { + tb := NewTokenBlacklist() + + // Add tokens up to maxSize + for i := 0; i < 1000; i++ { + tb.Add(fmt.Sprintf("token%d", i), time.Now().Add(time.Hour)) + } + + // Verify size is at max + if len(tb.blacklist) != 1000 { + t.Errorf("Expected blacklist size to be 1000, got %d", len(tb.blacklist)) + } + + // Add one more token, should trigger cleanup/eviction + tb.Add("newtoken", time.Now().Add(time.Hour)) + + // Size should still be at max + if len(tb.blacklist) > 1000 { + t.Errorf("Blacklist exceeded max size: %d", len(tb.blacklist)) + } +} + +func TestTokenBlacklistExpiredCleanup(t *testing.T) { + tb := NewTokenBlacklist() + + // Add some expired tokens + for i := 0; i < 500; i++ { + tb.Add(fmt.Sprintf("expired%d", i), time.Now().Add(-time.Hour)) + } + + // Add some valid tokens + for i := 0; i < 500; i++ { + tb.Add(fmt.Sprintf("valid%d", i), time.Now().Add(time.Hour)) + } + + // Force cleanup + tb.Cleanup() + + // Only valid tokens should remain + if len(tb.blacklist) != 500 { + t.Errorf("Expected 500 valid tokens after cleanup, got %d", len(tb.blacklist)) + } + + // Verify only valid tokens remain + for token, expiry := range tb.blacklist { + if time.Now().After(expiry) { + t.Errorf("Found expired token after cleanup: %s", token) + } + } +} + +func TestTokenBlacklistOldestEviction(t *testing.T) { + tb := NewTokenBlacklist() + + // Add tokens at capacity with different expiration times + baseTime := time.Now() + oldestToken := "oldest" + + // Add oldest token first + tb.Add(oldestToken, baseTime.Add(time.Hour)) + + // Fill up to capacity with newer tokens + for i := 0; i < 999; i++ { + tb.Add(fmt.Sprintf("token%d", i), baseTime.Add(time.Hour*2)) + } + + // Add a new token that should evict the oldest + newToken := "newest" + tb.Add(newToken, baseTime.Add(time.Hour*3)) + + // Verify oldest token was evicted + if tb.IsBlacklisted(oldestToken) { + t.Error("Oldest token should have been evicted") + } + + // Verify newest token is present + if !tb.IsBlacklisted(newToken) { + t.Error("Newest token should be present") + } +} + +func TestTokenBlacklistMemoryUsage(t *testing.T) { + tb := NewTokenBlacklist() + iterations := 10000 + + // Force initial GC + runtime.GC() + + // Record initial memory stats + var m1, m2 runtime.MemStats + runtime.ReadMemStats(&m1) + + // Simulate heavy usage + for i := 0; i < iterations; i++ { + // Add new token + tb.Add(fmt.Sprintf("token%d", i), time.Now().Add(time.Hour)) + + // Periodically check blacklisted status + if i%100 == 0 { + tb.IsBlacklisted(fmt.Sprintf("token%d", i-50)) + } + + // Periodically cleanup + if i%1000 == 0 { + tb.Cleanup() + } + } + + // Force GC and wait for it to complete + runtime.GC() + time.Sleep(100 * time.Millisecond) + runtime.ReadMemStats(&m2) + + // Check memory growth (using HeapAlloc for more accurate measurement) + memoryGrowth := int64(m2.HeapAlloc - m1.HeapAlloc) + maxAllowedGrowth := int64(2 * 1024 * 1024) // 2MB max growth + + if memoryGrowth > maxAllowedGrowth { + t.Logf("Initial HeapAlloc: %d, Final HeapAlloc: %d", m1.HeapAlloc, m2.HeapAlloc) + t.Errorf("Excessive memory growth: %d bytes", memoryGrowth) + } + + // Verify size stayed within limits + if len(tb.blacklist) > tb.maxSize { + t.Errorf("Blacklist exceeded max size: %d", len(tb.blacklist)) + } +} + +func TestConcurrentTokenBlacklistOperations(t *testing.T) { + tb := NewTokenBlacklist() + iterations := 1000 + concurrency := 10 + done := make(chan bool) + + // Start multiple goroutines performing operations + for i := 0; i < concurrency; i++ { + go func(id int) { + for j := 0; j < iterations; j++ { + // Add tokens + token := fmt.Sprintf("token%d-%d", id, j) + tb.Add(token, time.Now().Add(time.Hour)) + + // Check blacklist status + tb.IsBlacklisted(token) + + // Periodic cleanup + if j%100 == 0 { + tb.Cleanup() + } + } + done <- true + }(i) + } + + // Wait for all goroutines to complete + for i := 0; i < concurrency; i++ { + <-done + } + + // Verify size constraints were maintained + if len(tb.blacklist) > tb.maxSize { + t.Errorf("Blacklist exceeded max size under concurrent operations: %d", len(tb.blacklist)) + } +} + +func TestTokenCacheMemoryUsage(t *testing.T) { + tc := NewTokenCache() + iterations := 10000 + + // Force initial GC + runtime.GC() + + // Record initial memory stats + var m1, m2 runtime.MemStats + runtime.ReadMemStats(&m1) + + // Simulate heavy cache usage + for i := 0; i < iterations; i++ { + claims := map[string]interface{}{ + "sub": fmt.Sprintf("user%d", i), + "exp": time.Now().Add(time.Hour).Unix(), + } + + // Add to cache + tc.Set(fmt.Sprintf("token%d", i), claims, time.Hour) + + // Periodically retrieve + if i%100 == 0 { + tc.Get(fmt.Sprintf("token%d", i-50)) + } + + // Periodically cleanup + if i%1000 == 0 { + tc.Cleanup() + } + } + + // Force GC and wait for it to complete + runtime.GC() + time.Sleep(100 * time.Millisecond) + runtime.ReadMemStats(&m2) + + // Check memory growth (using HeapAlloc for more accurate measurement) + memoryGrowth := int64(m2.HeapAlloc - m1.HeapAlloc) + maxAllowedGrowth := int64(2 * 1024 * 1024) // 2MB max growth + + if memoryGrowth > maxAllowedGrowth { + t.Logf("Initial HeapAlloc: %d, Final HeapAlloc: %d", m1.HeapAlloc, m2.HeapAlloc) + t.Errorf("Excessive cache memory growth: %d bytes", memoryGrowth) + } + + // Verify cache size stayed within limits + if len(tc.cache.items) > tc.cache.maxSize { + t.Errorf("Cache exceeded max size: %d", len(tc.cache.items)) + } +}