61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLoginLocksSweepExpiredKeepsLocked(t *testing.T) {
|
|
locks := NewLoginLocks()
|
|
now := time.Date(2026, 9, 30, 12, 0, 0, 0, time.UTC)
|
|
locks.SetClock(func() time.Time { return now })
|
|
|
|
for i := 0; i < 10000; i++ {
|
|
if locked, _ := locks.Fail(LockKey{Kind: LockRegisterIP, IP: fmt.Sprintf("2001:db8::%d", i)}); locked {
|
|
t.Fatalf("unexpected lock at i=%d", i)
|
|
}
|
|
}
|
|
if n := locks.entryCount(); n < 10000 {
|
|
t.Fatalf("want 10000 entries before sweep, got %d", n)
|
|
}
|
|
|
|
now = now.Add(4 * time.Minute)
|
|
keep := LockKey{Kind: LockRegisterIP, IP: "keep-locked"}
|
|
for i := 0; i < 10; i++ {
|
|
locks.Fail(keep)
|
|
}
|
|
if locked, _ := locks.Check(keep); !locked {
|
|
t.Fatal("keep-locked should be locked")
|
|
}
|
|
|
|
now = now.Add(2 * time.Minute) // 10k 已过 5 分钟窗口;keep 仍锁定至 +5min
|
|
if locked, _ := locks.Check(LockKey{Kind: LockRegisterIP, IP: "trigger-sweep"}); locked {
|
|
t.Fatal("trigger must not lock")
|
|
}
|
|
if n := locks.entryCount(); n != 1 {
|
|
t.Fatalf("want only locked entry after sweep, got %d", n)
|
|
}
|
|
if locked, _ := locks.Check(keep); !locked {
|
|
t.Fatal("locked entry must remain")
|
|
}
|
|
}
|
|
|
|
func TestLoginLocksCapDoesNotGrow(t *testing.T) {
|
|
locks := NewLoginLocks()
|
|
locks.maxEntries = 64
|
|
now := time.Date(2026, 9, 30, 15, 0, 0, 0, time.UTC)
|
|
locks.SetClock(func() time.Time { return now })
|
|
|
|
for i := 0; i < 200; i++ {
|
|
locks.Fail(LockKey{Kind: LockRegisterIP, IP: fmt.Sprintf("ip-%d", i)})
|
|
now = now.Add(time.Millisecond)
|
|
if n := locks.entryCount(); n > 64 {
|
|
t.Fatalf("entries=%d exceeded cap at i=%d", n, i)
|
|
}
|
|
}
|
|
if n := locks.entryCount(); n != 64 {
|
|
t.Fatalf("entries=%d want 64", n)
|
|
}
|
|
}
|