fix(06-07): make limiter admission atomic
- Replace split store checks with one mutex-guarded Attempt operation - Use domainless trusted-client keys for anonymous inline throttles - Preserve fixed-window headers, expiry, stacking, and principal isolation
This commit is contained in:
@@ -12,20 +12,20 @@ import (
|
||||
"git.golem15.com/golem15/summercms/compass"
|
||||
)
|
||||
|
||||
// Gap (b): MemoryStore's sweep goroutine (purge), not TooManyAttempts' lazy
|
||||
// expiry. Construct with a short sweep and assert the internal map drops
|
||||
// an expired entry without calling TooManyAttempts.
|
||||
// Gap (b): MemoryStore's sweep goroutine (purge), not Attempt's lazy expiry.
|
||||
// Construct with a short sweep and assert the internal map drops
|
||||
// an expired entry without a later Attempt.
|
||||
|
||||
func TestMemoryStoreSweepRemovesExpiredEntry(t *testing.T) {
|
||||
s := NewMemoryStore(15 * time.Millisecond)
|
||||
t.Cleanup(func() { close(s.stop) })
|
||||
|
||||
s.Hit("k", 25*time.Millisecond)
|
||||
s.Attempt("k", 1, 25*time.Millisecond)
|
||||
s.mu.Lock()
|
||||
n := len(s.entries)
|
||||
s.mu.Unlock()
|
||||
if n != 1 {
|
||||
t.Fatalf("after Hit, entries = %d", n)
|
||||
t.Fatalf("after Attempt, entries = %d", n)
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(200 * time.Millisecond)
|
||||
@@ -41,18 +41,20 @@ func TestMemoryStoreSweepRemovesExpiredEntry(t *testing.T) {
|
||||
t.Fatalf("sweep did not drop expired entry, count=%d", n)
|
||||
}
|
||||
|
||||
func TestMemoryStoreAvailableInExpiredAndMissing(t *testing.T) {
|
||||
func TestMemoryStoreAttemptRetryAfterAndExpiry(t *testing.T) {
|
||||
s := NewMemoryStore(0)
|
||||
if d := s.AvailableIn("missing"); d != 0 {
|
||||
t.Fatalf("missing AvailableIn = %s", d)
|
||||
allowed, attempts, retryAfter := s.Attempt("k", 1, 20*time.Millisecond)
|
||||
if !allowed || attempts != 1 || retryAfter <= 0 {
|
||||
t.Fatalf("first attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter)
|
||||
}
|
||||
s.Hit("k", 20*time.Millisecond)
|
||||
if d := s.AvailableIn("k"); d <= 0 {
|
||||
t.Fatalf("live AvailableIn = %s", d)
|
||||
allowed, attempts, retryAfter = s.Attempt("k", 1, 20*time.Millisecond)
|
||||
if allowed || attempts != 1 || retryAfter <= 0 {
|
||||
t.Fatalf("denied attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter)
|
||||
}
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
if d := s.AvailableIn("k"); d != 0 {
|
||||
t.Fatalf("expired AvailableIn = %s", d)
|
||||
allowed, attempts, retryAfter = s.Attempt("k", 1, 20*time.Millisecond)
|
||||
if !allowed || attempts != 1 || retryAfter <= 0 {
|
||||
t.Fatalf("expired attempt = allowed %v, attempts %d, retryAfter %s", allowed, attempts, retryAfter)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user