| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 |
- package controller
- import (
- "testing"
- "time"
- )
- func TestLoginLimiterBlocksAfterConfiguredFailures(t *testing.T) {
- now := time.Date(2026, 5, 6, 12, 0, 0, 0, time.UTC)
- limiter := newLoginLimiter(5, 5*time.Minute, 15*time.Minute)
- limiter.now = func() time.Time { return now }
- for i := 0; i < 4; i++ {
- if _, blocked := limiter.registerFailure("192.0.2.10", "Admin"); blocked {
- t.Fatalf("failure %d should not block yet", i+1)
- }
- if _, ok := limiter.allow("192.0.2.10", "admin"); !ok {
- t.Fatalf("failure %d should still allow login attempts", i+1)
- }
- }
- blockedUntil, blocked := limiter.registerFailure("192.0.2.10", "ADMIN")
- if !blocked {
- t.Fatal("fifth failure should start cooldown")
- }
- if want := now.Add(15 * time.Minute); !blockedUntil.Equal(want) {
- t.Fatalf("blocked until %s, want %s", blockedUntil, want)
- }
- if _, ok := limiter.allow("192.0.2.10", "admin"); ok {
- t.Fatal("login should be blocked during cooldown")
- }
- now = blockedUntil
- if _, ok := limiter.allow("192.0.2.10", "admin"); !ok {
- t.Fatal("login should be allowed after cooldown")
- }
- }
- func TestLoginLimiterPrunesOldFailuresAndResetsOnSuccess(t *testing.T) {
- now := time.Date(2026, 5, 6, 12, 0, 0, 0, time.UTC)
- limiter := newLoginLimiter(5, 5*time.Minute, 15*time.Minute)
- limiter.now = func() time.Time { return now }
- for i := 0; i < 4; i++ {
- limiter.registerFailure("192.0.2.10", "admin")
- }
- now = now.Add(6 * time.Minute)
- if _, blocked := limiter.registerFailure("192.0.2.10", "admin"); blocked {
- t.Fatal("old failures should be pruned outside the rolling window")
- }
- limiter.registerSuccess("192.0.2.10", "admin")
- for i := 0; i < 4; i++ {
- if _, blocked := limiter.registerFailure("192.0.2.10", "admin"); blocked {
- t.Fatalf("success should reset previous failures; failure %d blocked", i+1)
- }
- }
- }
- func TestLoginLimiterSeparatesIPAndUsername(t *testing.T) {
- now := time.Date(2026, 5, 6, 12, 0, 0, 0, time.UTC)
- limiter := newLoginLimiter(5, 5*time.Minute, 15*time.Minute)
- limiter.now = func() time.Time { return now }
- for i := 0; i < 5; i++ {
- limiter.registerFailure("192.0.2.10", "admin")
- }
- if _, ok := limiter.allow("192.0.2.11", "admin"); !ok {
- t.Fatal("different IP should not be blocked")
- }
- if _, ok := limiter.allow("192.0.2.10", "other-admin"); !ok {
- t.Fatal("different username should not be blocked")
- }
- }
|