package auth import ( "sync" "testing" "time" ) func TestStateManager_Store(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 10 * time.Minute, } sm.Store("test-state") sm.mu.RLock() _, exists := sm.states["test-state"] sm.mu.RUnlock() if !exists { t.Error("Store() did not store the state") } } func TestStateManager_Validate(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 10 * time.Minute, } // Test validating existing state sm.Store("valid-state") if !sm.Validate("valid-state") { t.Error("Validate() returned false for valid state") } // Test validating non-existent state if sm.Validate("non-existent-state") { t.Error("Validate() returned true for non-existent state") } } func TestStateManager_Validate_Expired(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 1 * time.Millisecond, } // Store a state sm.Store("expired-state") // Manually set to expired sm.mu.Lock() sm.states["expired-state"] = time.Now().Add(-2 * time.Hour) sm.mu.Unlock() // Wait for ttl to pass time.Sleep(10 * time.Millisecond) // Should return false for expired state if sm.Validate("expired-state") { t.Error("Validate() should return false for expired state") } } func TestStateManager_Delete(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 10 * time.Minute, } sm.Store("state-to-delete") sm.Delete("state-to-delete") sm.mu.RLock() _, exists := sm.states["state-to-delete"] sm.mu.RUnlock() if exists { t.Error("Delete() did not remove the state") } } func TestStateManager_Cleanup(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 10 * time.Minute, } // Add some states sm.Store("valid-state") // Manually add expired states (stored time + ttl should be before now) sm.mu.Lock() sm.states["expired-state-1"] = time.Now().Add(-20 * time.Minute) // 10 min + 10 min ttl = 20 min ago expired sm.states["expired-state-2"] = time.Now().Add(-15 * time.Minute) // 5 min after ttl expired sm.mu.Unlock() sm.Cleanup() sm.mu.RLock() defer sm.mu.RUnlock() // Valid state should remain if _, exists := sm.states["valid-state"]; !exists { t.Error("Cleanup() removed valid state") } // Expired states should be removed if _, exists := sm.states["expired-state-1"]; exists { t.Error("Cleanup() did not remove expired-state-1") } if _, exists := sm.states["expired-state-2"]; exists { t.Error("Cleanup() did not remove expired-state-2") } } func TestStateManager_StartCleanupRoutine(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 1 * time.Millisecond, } stop := make(chan struct{}) sm.StartCleanupRoutine(stop) // Add an expired state sm.mu.Lock() sm.states["to-cleanup"] = time.Now().Add(-1 * time.Hour) sm.mu.Unlock() // Wait for cleanup to run (5 minute ticker, but we'll just verify the routine started) // We'll stop it immediately for testing close(stop) // Give goroutine time to exit time.Sleep(100 * time.Millisecond) } func TestStartCleanupRoutineWithManager(t *testing.T) { // Reset for test cleanupRoutineManager = nil // Start the routine StartCleanupRoutineWithManager() if cleanupRoutineManager == nil { t.Error("StartCleanupRoutineWithManager() did not initialize manager") } // Starting again should be no-op StartCleanupRoutineWithManager() // Stop the routine StopCleanupRoutine() if cleanupRoutineManager != nil { t.Error("StopCleanupRoutine() did not clean up manager") } } func TestStopCleanupRoutine_NilManager(t *testing.T) { // Ensure manager is nil cleanupRoutineManager = nil // Should not panic StopCleanupRoutine() } func TestGetStateManager(t *testing.T) { sm := GetStateManager() if sm == nil { t.Error("GetStateManager() returned nil") } // Should return same instance sm2 := GetStateManager() if sm != sm2 { t.Error("GetStateManager() should return same instance") } } func TestStateManager_ConcurrentAccess(t *testing.T) { sm := &StateManager{ states: make(map[string]time.Time), ttl: 10 * time.Minute, } var wg sync.WaitGroup numOps := 100 // Concurrent stores for i := 0; i < numOps; i++ { wg.Add(1) go func(i int) { defer wg.Done() sm.Store(string(rune(i))) }(i) } // Concurrent validates for i := 0; i < numOps; i++ { wg.Add(1) go func(i int) { defer wg.Done() sm.Validate(string(rune(i))) }(i) } wg.Wait() }