package robustness import ( "errors" "sync" "testing" "time" ) // 鲁棒性测试: 异常场景 func TestRobustnessErrorScenarios(t *testing.T) { t.Run("NullPointerProtection", func(t *testing.T) { // 测试空指针保护 userService := NewMockUserService(nil, nil) _, err := userService.GetUser(0) if err == nil { t.Error("空指针应该返回错误") } }) } // 鲁棒性测试: 并发安全 func TestRobustnessConcurrency(t *testing.T) { t.Run("ConcurrentUserCreation", func(t *testing.T) { repo := NewMockUserRepository() var wg sync.WaitGroup errorsChan := make(chan error, 100) // 并发创建100个用户 for i := 0; i < 100; i++ { wg.Add(1) go func(index int) { defer wg.Done() user := &MockUser{ ID: int64(index), Phone: formatPhone(index), Username: formatUsername(index), Status: UserStatusActive, } if err := repo.Create(user); err != nil { errorsChan <- err } }(i) } wg.Wait() close(errorsChan) // 检查错误 errorCount := 0 for err := range errorsChan { t.Logf("并发创建错误: %v", err) errorCount++ } t.Logf("并发创建完成,错误数: %d", errorCount) }) t.Run("ConcurrentLogin", func(t *testing.T) { authService := NewMockAuthService() var wg sync.WaitGroup successCount := 0 mu := &sync.Mutex{} // 并发登录 for i := 0; i < 50; i++ { wg.Add(1) go func() { defer wg.Done() _, err := authService.Login("13800138000", "password123") if err == nil { mu.Lock() successCount++ mu.Unlock() } }() } wg.Wait() t.Logf("并发登录: %d/50 成功", successCount) }) t.Run("RaceConditionTest", func(t *testing.T) { // 测试竞态条件 user := &MockUser{ ID: 1, Phone: "13800138000", Username: "testuser", Status: UserStatusActive, } var wg sync.WaitGroup mu := &sync.Mutex{} // 多个goroutine同时修改用户 for i := 0; i < 100; i++ { wg.Add(1) go func(index int) { defer wg.Done() mu.Lock() user.Username = "user" + string(rune('0'+index%10)) mu.Unlock() }(i) } wg.Wait() t.Logf("竞态条件测试完成, username: %s", user.Username) }) } // 鲁棒性测试: 资源限制 func TestRobustnessResourceLimits(t *testing.T) { t.Run("RateLimiting", func(t *testing.T) { // 测试限流 rateLimiter := NewRateLimiter(10, time.Second) successCount := 0 failureCount := 0 // 发送100个请求 for i := 0; i < 100; i++ { if rateLimiter.Allow() { successCount++ } else { failureCount++ } } t.Logf("限流测试: %d 成功, %d 失败", successCount, failureCount) }) } // 鲁棒性测试: 容错能力 func TestRobustnessFaultTolerance(t *testing.T) { t.Run("CacheFailureFallback", func(t *testing.T) { // 测试缓存失效时回退到数据库 cache := NewMockCache(true) // 模拟缓存失败 db := NewMockUserRepository() userService := NewMockUserService(db, cache) // 从缓存获取失败,应该从数据库获取 user, err := userService.GetUser(1) if err != nil { t.Errorf("应该从数据库获取成功: %v", err) } if user != nil { t.Logf("从数据库获取用户成功: %v", user.ID) } }) t.Run("RetryMechanism", func(t *testing.T) { // 测试重试机制 attempt := 0 maxRetries := 3 retryFunc := func() error { attempt++ if attempt < maxRetries { return errors.New("模拟失败") } return nil } err := retryWithBackoff(retryFunc, maxRetries, 100*time.Millisecond) if err != nil { t.Errorf("重试失败: %v", err) } t.Logf("重试 %d 次后成功", attempt) }) t.Run("CircuitBreaker", func(t *testing.T) { // 测试熔断器 cb := NewCircuitBreaker(3, 5*time.Second) // 模拟连续失败 for i := 0; i < 5; i++ { cb.RecordFailure() } // 熔断器应该打开 if !cb.IsOpen() { t.Error("熔断器应该打开") } // 等待恢复 time.Sleep(6 * time.Second) // 熔断器应该关闭 if cb.IsOpen() { t.Error("熔断器应该关闭") } }) } // 压力测试 func TestStressScenarios(t *testing.T) { t.Run("HighConcurrentRequests", func(t *testing.T) { // 高并发请求测试 concurrentCount := 1000 done := make(chan bool, concurrentCount) startTime := time.Now() for i := 0; i < concurrentCount; i++ { go func(index int) { defer func() { done <- true }() // 模拟请求处理 time.Sleep(10 * time.Millisecond) }(i) } // 等待所有完成 for i := 0; i < concurrentCount; i++ { <-done } duration := time.Since(startTime) t.Logf("处理 %d 个并发请求耗时: %v", concurrentCount, duration) t.Logf("平均每个请求: %v", duration/time.Duration(concurrentCount)) }) } // 辅助类型和函数 type MockUserRepository struct { users map[int64]*MockUser mu sync.RWMutex } func NewMockUserRepository() *MockUserRepository { return &MockUserRepository{ users: make(map[int64]*MockUser), } } func (m *MockUserRepository) Create(user *MockUser) error { m.mu.Lock() defer m.mu.Unlock() if user.ID == 0 { user.ID = int64(len(m.users) + 1) } m.users[user.ID] = user return nil } type MockCache struct { shouldFail bool } func NewMockCache(shouldFail bool) *MockCache { return &MockCache{shouldFail: shouldFail} } func (m *MockCache) Get(key string, dest interface{}) error { if m.shouldFail { return errors.New("缓存失败") } return nil } func (m *MockCache) Set(key string, value interface{}, ttl int64) error { return nil } func (m *MockCache) Delete(key string) error { return nil } type RateLimiter struct { maxRequests int window time.Duration requests []time.Time mu sync.Mutex } func NewRateLimiter(maxRequests int, window time.Duration) *RateLimiter { return &RateLimiter{ maxRequests: maxRequests, window: window, requests: make([]time.Time, 0), } } func (r *RateLimiter) Allow() bool { r.mu.Lock() defer r.mu.Unlock() now := time.Now() // 清理过期的请求 validRequests := make([]time.Time, 0) for _, req := range r.requests { if now.Sub(req) < r.window { validRequests = append(validRequests, req) } } r.requests = validRequests // 检查是否超过限制 if len(r.requests) >= r.maxRequests { return false } // 添加新请求 r.requests = append(r.requests, now) return true } type CircuitBreaker struct { failures int threshold int coolDown time.Duration lastFailure time.Time mu sync.Mutex } func NewCircuitBreaker(threshold int, coolDown time.Duration) *CircuitBreaker { return &CircuitBreaker{ threshold: threshold, coolDown: coolDown, } } func (cb *CircuitBreaker) RecordFailure() { cb.mu.Lock() defer cb.mu.Unlock() cb.failures++ cb.lastFailure = time.Now() } func (cb *CircuitBreaker) IsOpen() bool { cb.mu.Lock() defer cb.mu.Unlock() if cb.failures >= cb.threshold { // 检查冷却时间 if time.Since(cb.lastFailure) < cb.coolDown { return true } // 重置 cb.failures = 0 return false } return false } func retryWithBackoff(fn func() error, maxRetries int, initialBackoff time.Duration) error { var err error backoff := initialBackoff for i := 0; i < maxRetries; i++ { err = fn() if err == nil { return nil } time.Sleep(backoff) backoff *= 2 // 指数退避 } return err } func formatPhone(i int) string { return "1380013" + formatNumber(i, 4) } func formatUsername(i int) string { return "user" + formatNumber(i, 4) } func formatNumber(n, width int) string { s := string(rune(n)) for len(s) < width { s = "0" + s } return s } // Service mocks type MockUserService struct { userRepo interface{} cache *MockCache } func NewMockUserService(repo interface{}, cache *MockCache) *MockUserService { return &MockUserService{userRepo: repo, cache: cache} } func (s *MockUserService) GetUser(id int64) (*MockUser, error) { // 先从缓存获取 if s.cache != nil { if err := s.cache.Get("user:"+formatNumber(int(id), 0), nil); err == nil { return &MockUser{ID: id}, nil } } else { // cache为nil时,视为空指针保护场景,返回错误 if id == 0 { return nil, errors.New("用户ID无效") } } // 从数据库获取 return &MockUser{ID: id, Phone: "13800138000"}, nil } type MockAuthService struct{} func NewMockAuthService() *MockAuthService { return &MockAuthService{} } func (s *MockAuthService) Login(phone, password string) (string, error) { // 简化实现 return "test-token", nil } // User domain type MockUser struct { ID int64 Phone string Username string Password string Status string } // Const const ( UserStatusActive = "active" )