package auth import ( "testing" "time" ) func TestRateLimiterAllow(t *testing.T) { r := NewRateLimiter() if !r.Allow("user1") { t.Error("fresh user should be allowed") } } func TestRateLimiterLockAfterMaxAttempts(t *testing.T) { r := NewRateLimiter() id := "user2" for i := 0; i < maxAttempts-1; i++ { locked := r.Record(id) if locked { t.Fatalf("should not be locked before max attempts (attempt %d)", i+1) } if !r.Allow(id) { t.Fatalf("should be allowed before lock (attempt %d)", i+1) } } locked := r.Record(id) if !locked { t.Error("should be locked after max attempts") } if r.Allow(id) { t.Error("locked user should not be allowed") } } func TestRateLimiterReset(t *testing.T) { r := NewRateLimiter() id := "user3" for i := 0; i < maxAttempts; i++ { r.Record(id) //nolint } if r.Allow(id) { t.Error("should be locked before reset") } r.Reset(id) if !r.Allow(id) { t.Error("should be allowed after reset") } } func TestRateLimiterWindowReset(t *testing.T) { r := NewRateLimiter() id := "user4" // Simulate 4 attempts within window then manually expire the window for i := 0; i < maxAttempts-1; i++ { r.Record(id) //nolint } // Manually expire the window r.mu.Lock() r.entries[id].firstAt = time.Now().Add(-windowMinutes*time.Minute - time.Second) r.mu.Unlock() // Next attempt should reset the window, not lock locked := r.Record(id) if locked { t.Error("should not lock after window expired") } } func TestRateLimiterRemainingLock(t *testing.T) { r := NewRateLimiter() id := "user5" if r.RemainingLock(id) != 0 { t.Error("no lock expected for fresh user") } for i := 0; i < maxAttempts; i++ { r.Record(id) //nolint } if r.RemainingLock(id) == 0 { t.Error("lock expected after max attempts") } }