mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-10-14 04:07:46 +00:00
fixup! Further pursue of perfection.
This commit is contained in:
@@ -0,0 +1,495 @@
|
||||
package traefikoidc
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test the core OptimizedCache functionality that's not covered
|
||||
func TestOptimizedCacheBasic(t *testing.T) {
|
||||
cache := NewOptimizedCache()
|
||||
if cache == nil {
|
||||
t.Fatal("NewOptimizedCache returned nil")
|
||||
}
|
||||
|
||||
// Basic set and get
|
||||
cache.Set("test_key", "test_value", 5*time.Minute)
|
||||
if val, found := cache.Get("test_key"); !found || val != "test_value" {
|
||||
t.Errorf("Failed to get cached value: %v, %v", val, found)
|
||||
}
|
||||
|
||||
// Delete
|
||||
cache.Delete("test_key")
|
||||
if _, found := cache.Get("test_key"); found {
|
||||
t.Error("Value should be deleted")
|
||||
}
|
||||
|
||||
// Test with config
|
||||
logger := NewLogger("debug")
|
||||
cache2 := NewOptimizedCacheWithConfig(50, 10, logger)
|
||||
if cache2 == nil {
|
||||
t.Fatal("NewOptimizedCacheWithConfig returned nil")
|
||||
}
|
||||
|
||||
// Test cleanup
|
||||
cache2.Set("expire", "value", 100*time.Millisecond)
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
cache2.Cleanup()
|
||||
if _, found := cache2.Get("expire"); found {
|
||||
t.Error("Expired item should be cleaned up")
|
||||
}
|
||||
|
||||
// Test close
|
||||
cache2.Close()
|
||||
}
|
||||
|
||||
// Test UnifiedCache basic functionality
|
||||
func TestUnifiedCacheBasic(t *testing.T) {
|
||||
config := DefaultUnifiedCacheConfig()
|
||||
if config.MaxSize <= 0 {
|
||||
t.Error("Default config should have positive MaxSize")
|
||||
}
|
||||
|
||||
cache := NewUnifiedCache(config)
|
||||
if cache == nil {
|
||||
t.Fatal("NewUnifiedCache returned nil")
|
||||
}
|
||||
|
||||
// Basic operations
|
||||
cache.Set("key1", "value1", 5*time.Minute)
|
||||
if val, found := cache.Get("key1"); !found || val != "value1" {
|
||||
t.Errorf("Failed to get cached value: %v", val)
|
||||
}
|
||||
|
||||
cache.Delete("key1")
|
||||
if _, found := cache.Get("key1"); found {
|
||||
t.Error("Value should be deleted")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
cache.Set("expired", "value", 1*time.Nanosecond)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cache.Cleanup()
|
||||
|
||||
// GetMetrics
|
||||
metrics := cache.GetMetrics()
|
||||
if metrics == nil {
|
||||
t.Error("GetMetrics should not return nil")
|
||||
}
|
||||
|
||||
cache.Close()
|
||||
}
|
||||
|
||||
// Test memory pool functionality
|
||||
func TestMemoryPoolBasic(t *testing.T) {
|
||||
manager := NewMemoryPoolManager()
|
||||
if manager == nil {
|
||||
t.Fatal("NewMemoryPoolManager returned nil")
|
||||
}
|
||||
|
||||
// Test compression buffer
|
||||
buf := manager.GetCompressionBuffer()
|
||||
if buf == nil {
|
||||
t.Fatal("GetCompressionBuffer returned nil")
|
||||
}
|
||||
buf.WriteString("test")
|
||||
manager.PutCompressionBuffer(buf)
|
||||
|
||||
// Test JWT parsing buffer
|
||||
jwtBuf := manager.GetJWTParsingBuffer()
|
||||
if jwtBuf == nil {
|
||||
t.Fatal("GetJWTParsingBuffer returned nil")
|
||||
}
|
||||
manager.PutJWTParsingBuffer(jwtBuf)
|
||||
|
||||
// Test HTTP response buffer
|
||||
httpBuf := manager.GetHTTPResponseBuffer()
|
||||
if httpBuf == nil {
|
||||
t.Fatal("GetHTTPResponseBuffer returned nil")
|
||||
}
|
||||
manager.PutHTTPResponseBuffer(httpBuf)
|
||||
|
||||
// Test string builder
|
||||
sb := manager.GetStringBuilder()
|
||||
if sb == nil {
|
||||
t.Fatal("GetStringBuilder returned nil")
|
||||
}
|
||||
manager.PutStringBuilder(sb)
|
||||
}
|
||||
|
||||
// Test global memory pools
|
||||
func TestGlobalPools(t *testing.T) {
|
||||
pools := GetGlobalMemoryPools()
|
||||
if pools == nil {
|
||||
t.Fatal("GetGlobalMemoryPools returned nil")
|
||||
}
|
||||
|
||||
// Should be singleton
|
||||
pools2 := GetGlobalMemoryPools()
|
||||
if pools != pools2 {
|
||||
t.Error("GetGlobalMemoryPools should return singleton")
|
||||
}
|
||||
|
||||
// Cleanup should not panic
|
||||
CleanupGlobalMemoryPools()
|
||||
}
|
||||
|
||||
// Test TokenCompressionPool
|
||||
func TestTokenCompression(t *testing.T) {
|
||||
pool := NewTokenCompressionPool()
|
||||
if pool == nil {
|
||||
t.Fatal("NewTokenCompressionPool returned nil")
|
||||
}
|
||||
|
||||
compBuf := pool.GetCompressionBuffer()
|
||||
if compBuf == nil {
|
||||
t.Fatal("GetCompressionBuffer returned nil")
|
||||
}
|
||||
pool.PutCompressionBuffer(compBuf)
|
||||
|
||||
decompBuf := pool.GetDecompressionBuffer()
|
||||
if decompBuf == nil {
|
||||
t.Fatal("GetDecompressionBuffer returned nil")
|
||||
}
|
||||
pool.PutDecompressionBuffer(decompBuf)
|
||||
}
|
||||
|
||||
// Test error recovery base functionality
|
||||
func TestErrorRecoveryBasic(t *testing.T) {
|
||||
logger := NewLogger("debug")
|
||||
|
||||
// Test BaseRecoveryMechanism
|
||||
base := NewBaseRecoveryMechanism("test", logger)
|
||||
base.RecordRequest()
|
||||
base.RecordSuccess()
|
||||
base.RecordFailure()
|
||||
|
||||
metrics := base.GetBaseMetrics()
|
||||
if metrics == nil {
|
||||
t.Fatal("GetBaseMetrics returned nil")
|
||||
}
|
||||
|
||||
// Test CircuitBreaker
|
||||
cbConfig := CircuitBreakerConfig{
|
||||
MaxFailures: 3,
|
||||
ResetTimeout: 100 * time.Millisecond,
|
||||
}
|
||||
cb := NewCircuitBreaker(cbConfig, logger)
|
||||
|
||||
if cb.GetState() != CircuitBreakerClosed {
|
||||
t.Error("Initial state should be closed")
|
||||
}
|
||||
|
||||
// Trip the circuit
|
||||
for i := 0; i < 3; i++ {
|
||||
cb.Execute(func() error {
|
||||
return fmt.Errorf("error")
|
||||
})
|
||||
}
|
||||
|
||||
if cb.GetState() != CircuitBreakerOpen {
|
||||
t.Error("Circuit should be open after max failures")
|
||||
}
|
||||
|
||||
cb.Reset()
|
||||
if cb.GetState() != CircuitBreakerClosed {
|
||||
t.Error("Circuit should be closed after reset")
|
||||
}
|
||||
|
||||
// Test RetryExecutor
|
||||
retryConfig := RetryConfig{
|
||||
MaxAttempts: 3,
|
||||
InitialDelay: 10 * time.Millisecond,
|
||||
}
|
||||
re := NewRetryExecutor(retryConfig, logger)
|
||||
|
||||
if re != nil && !re.IsAvailable() {
|
||||
t.Error("RetryExecutor should be available")
|
||||
}
|
||||
}
|
||||
|
||||
// Test profiling components
|
||||
func TestProfilingBasic(t *testing.T) {
|
||||
logger := NewLogger("debug")
|
||||
|
||||
// Test ProfilingManager
|
||||
pm := NewProfilingManager(logger)
|
||||
if pm == nil {
|
||||
t.Fatal("NewProfilingManager returned nil")
|
||||
}
|
||||
|
||||
// Take snapshot - may fail if profiling is disabled
|
||||
snapshot, _ := pm.TakeSnapshot()
|
||||
if snapshot != nil {
|
||||
t.Log("Snapshot taken successfully")
|
||||
}
|
||||
|
||||
// Test global singletons
|
||||
globalPM := GetGlobalProfilingManager()
|
||||
if globalPM == nil {
|
||||
t.Fatal("GetGlobalProfilingManager returned nil")
|
||||
}
|
||||
|
||||
globalMTO := GetGlobalTestOrchestrator()
|
||||
if globalMTO == nil {
|
||||
t.Fatal("GetGlobalTestOrchestrator returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
// Test cache strategies
|
||||
func TestCacheStrategies(t *testing.T) {
|
||||
// Test LRU strategy
|
||||
lru := NewLRUStrategy(10)
|
||||
if lru.Name() != "LRU" {
|
||||
t.Errorf("Expected LRU name, got %s", lru.Name())
|
||||
}
|
||||
|
||||
// Test doubly linked list
|
||||
list := NewDoublyLinkedList()
|
||||
node1 := list.PushBack("key1")
|
||||
if node1 == nil || node1.Key != "key1" {
|
||||
t.Error("PushBack failed")
|
||||
}
|
||||
|
||||
list.PushBack("key2")
|
||||
list.PushBack("key3")
|
||||
|
||||
list.MoveToBack(node1)
|
||||
|
||||
popped := list.PopFront()
|
||||
if popped == nil || popped.Key != "key2" {
|
||||
t.Errorf("Expected key2, got %v", popped)
|
||||
}
|
||||
}
|
||||
|
||||
// Test CacheAdapter
|
||||
func TestCacheAdapterBasic(t *testing.T) {
|
||||
config := DefaultUnifiedCacheConfig()
|
||||
unified := NewUnifiedCache(config)
|
||||
adapter := NewCacheAdapter(unified)
|
||||
|
||||
adapter.Set("key", "value", 5*time.Minute)
|
||||
if val, found := adapter.Get("key"); !found || val != "value" {
|
||||
t.Errorf("CacheAdapter get failed: %v", val)
|
||||
}
|
||||
|
||||
adapter.Delete("key")
|
||||
adapter.Cleanup()
|
||||
adapter.Close()
|
||||
}
|
||||
|
||||
// Test JWT validation edge cases
|
||||
func TestJWTValidationCases(t *testing.T) {
|
||||
validator := NewTokenValidator(NewLogger("debug"))
|
||||
|
||||
// Test valid JWT
|
||||
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256","typ":"JWT"}`))
|
||||
claims := map[string]interface{}{
|
||||
"sub": "user123",
|
||||
"exp": time.Now().Add(1 * time.Hour).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
}
|
||||
claimsJSON, _ := json.Marshal(claims)
|
||||
payload := base64.RawURLEncoding.EncodeToString(claimsJSON)
|
||||
signature := base64.RawURLEncoding.EncodeToString([]byte("fake-signature"))
|
||||
|
||||
token := header + "." + payload + "." + signature
|
||||
|
||||
result := validator.ValidateToken(token, true)
|
||||
if !result.Valid {
|
||||
t.Errorf("Expected valid token, got error: %v", result.Error)
|
||||
}
|
||||
|
||||
// Test expired JWT
|
||||
expiredClaims := map[string]interface{}{
|
||||
"sub": "user123",
|
||||
"exp": time.Now().Add(-1 * time.Hour).Unix(),
|
||||
}
|
||||
expiredJSON, _ := json.Marshal(expiredClaims)
|
||||
expiredPayload := base64.RawURLEncoding.EncodeToString(expiredJSON)
|
||||
expiredToken := header + "." + expiredPayload + "." + signature
|
||||
|
||||
expiredResult := validator.ValidateToken(expiredToken, true)
|
||||
if expiredResult.Valid {
|
||||
t.Error("Expired token should be invalid")
|
||||
}
|
||||
|
||||
// Test opaque token
|
||||
opaqueToken := "abcdefghijklmnopqrstuvwxyz1234567890"
|
||||
opaqueResult := validator.ValidateToken(opaqueToken, false)
|
||||
if !opaqueResult.Valid {
|
||||
t.Errorf("Valid opaque token rejected: %v", opaqueResult.Error)
|
||||
}
|
||||
|
||||
// Test ExtractClaims
|
||||
extractedClaims, err := validator.ExtractClaims(token)
|
||||
if err != nil || extractedClaims["sub"] != "user123" {
|
||||
t.Errorf("ExtractClaims failed: %v", err)
|
||||
}
|
||||
|
||||
// Test CompareTokens
|
||||
if !validator.CompareTokens("token123", "token123") {
|
||||
t.Error("Identical tokens should match")
|
||||
}
|
||||
if validator.CompareTokens("token123", "token456") {
|
||||
t.Error("Different tokens should not match")
|
||||
}
|
||||
|
||||
// Test ValidateTokenSize
|
||||
if err := validator.ValidateTokenSize("small", 100); err != nil {
|
||||
t.Errorf("Small token should be valid: %v", err)
|
||||
}
|
||||
if err := validator.ValidateTokenSize(strings.Repeat("a", 200), 100); err == nil {
|
||||
t.Error("Large token should be invalid")
|
||||
}
|
||||
}
|
||||
|
||||
// Test replay cache
|
||||
func TestReplayCacheBasic(t *testing.T) {
|
||||
// Test getReplayCacheStats
|
||||
size, maxSize := getReplayCacheStats()
|
||||
if size < 0 || maxSize <= 0 {
|
||||
t.Errorf("Invalid replay cache stats: size=%d, maxSize=%d", size, maxSize)
|
||||
}
|
||||
|
||||
// Test cleanupReplayCache (should not panic)
|
||||
cleanupReplayCache()
|
||||
}
|
||||
|
||||
// Test audience verification
|
||||
func TestAudienceVerify(t *testing.T) {
|
||||
// Exact match
|
||||
claims1 := map[string]interface{}{
|
||||
"aud": "my-client",
|
||||
}
|
||||
if err := verifyAudience(claims1, "my-client"); err != nil {
|
||||
t.Errorf("Exact match should succeed: %v", err)
|
||||
}
|
||||
|
||||
// Array contains
|
||||
claims2 := map[string]interface{}{
|
||||
"aud": []interface{}{"client1", "my-client", "client2"},
|
||||
}
|
||||
if err := verifyAudience(claims2, "my-client"); err != nil {
|
||||
t.Errorf("Array contains should succeed: %v", err)
|
||||
}
|
||||
|
||||
// Mismatch
|
||||
claims3 := map[string]interface{}{
|
||||
"aud": "other-client",
|
||||
}
|
||||
if err := verifyAudience(claims3, "my-client"); err == nil {
|
||||
t.Error("Mismatch should fail")
|
||||
}
|
||||
|
||||
// Missing
|
||||
claims4 := map[string]interface{}{}
|
||||
if err := verifyAudience(claims4, "my-client"); err == nil {
|
||||
t.Error("Missing aud should fail")
|
||||
}
|
||||
}
|
||||
|
||||
// Test helper functions
|
||||
func TestHelpers(t *testing.T) {
|
||||
// Test nonce generation
|
||||
nonce1, err := generateNonce()
|
||||
if err != nil {
|
||||
t.Fatalf("generateNonce failed: %v", err)
|
||||
}
|
||||
if len(nonce1) == 0 {
|
||||
t.Error("Nonce should not be empty")
|
||||
}
|
||||
|
||||
nonce2, _ := generateNonce()
|
||||
if nonce1 == nonce2 {
|
||||
t.Error("Nonces should be unique")
|
||||
}
|
||||
|
||||
// Test code verifier generation
|
||||
verifier1, err := generateCodeVerifier()
|
||||
if err != nil {
|
||||
t.Fatalf("generateCodeVerifier failed: %v", err)
|
||||
}
|
||||
if len(verifier1) == 0 {
|
||||
t.Error("Code verifier should not be empty")
|
||||
}
|
||||
|
||||
verifier2, _ := generateCodeVerifier()
|
||||
if verifier1 == verifier2 {
|
||||
t.Error("Code verifiers should be unique")
|
||||
}
|
||||
}
|
||||
|
||||
// Test HTTP client factory
|
||||
func TestHTTPClientCreation(t *testing.T) {
|
||||
// Default client
|
||||
client1 := CreateDefaultHTTPClient()
|
||||
if client1 == nil {
|
||||
t.Fatal("CreateDefaultHTTPClient returned nil")
|
||||
}
|
||||
if client1.Timeout != 30*time.Second {
|
||||
t.Errorf("Expected 30s timeout, got %v", client1.Timeout)
|
||||
}
|
||||
|
||||
// Token client
|
||||
client2 := CreateTokenHTTPClient()
|
||||
if client2 == nil {
|
||||
t.Fatal("CreateTokenHTTPClient returned nil")
|
||||
}
|
||||
if client2.Timeout != 10*time.Second {
|
||||
t.Errorf("Expected 10s timeout, got %v", client2.Timeout)
|
||||
}
|
||||
|
||||
// Custom config client
|
||||
config := HTTPClientConfig{
|
||||
Timeout: 5 * time.Second,
|
||||
DisableCompression: true,
|
||||
DisableKeepAlives: true,
|
||||
}
|
||||
client3 := CreateHTTPClientWithConfig(config)
|
||||
if client3 == nil {
|
||||
t.Fatal("CreateHTTPClientWithConfig returned nil")
|
||||
}
|
||||
if client3.Timeout != 5*time.Second {
|
||||
t.Errorf("Expected 5s timeout, got %v", client3.Timeout)
|
||||
}
|
||||
}
|
||||
|
||||
// Test metadata cache
|
||||
func TestMetadataCaching(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
logger := NewLogger("debug")
|
||||
cache := NewMetadataCacheWithLogger(&wg, logger)
|
||||
if cache == nil {
|
||||
t.Fatal("NewMetadataCache returned nil")
|
||||
}
|
||||
// Basic test that cache was created
|
||||
}
|
||||
|
||||
// Test JWT parsing
|
||||
func TestJWTParsing(t *testing.T) {
|
||||
header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"RS256","typ":"JWT"}`))
|
||||
claims := map[string]interface{}{
|
||||
"sub": "user123",
|
||||
"exp": time.Now().Add(1 * time.Hour).Unix(),
|
||||
}
|
||||
claimsJSON, _ := json.Marshal(claims)
|
||||
payload := base64.RawURLEncoding.EncodeToString(claimsJSON)
|
||||
signature := base64.RawURLEncoding.EncodeToString([]byte("signature"))
|
||||
|
||||
token := header + "." + payload + "." + signature
|
||||
|
||||
parsedJWT, err := parseJWT(token)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse JWT: %v", err)
|
||||
}
|
||||
|
||||
if parsedJWT.Claims["sub"] != "user123" {
|
||||
t.Errorf("Expected sub=user123, got %v", parsedJWT.Claims["sub"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user