test: add comprehensive test coverage and improve code quality
- 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)
This commit is contained in:
213
internal/auth/state_test.go
Normal file
213
internal/auth/state_test.go
Normal file
@@ -0,0 +1,213 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user