- Add new test files for auth, service, and handler modules - Improve test organization and coverage - Refactor code for better maintainability - Add captcha, settings, stats, and theme handler tests - Add auth module tests (CAS, OAuth, password, SSO, state) - Add service layer tests for auth, export, permissions, roles - All Go tests pass (exit code 0) - All frontend tests pass (325 tests in 59 files)
214 lines
4.4 KiB
Go
214 lines
4.4 KiB
Go
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()
|
|
}
|