c3d9fbd8d3
Implementa fase 2 del issue 0016: - rate_limit_middleware: limita por IP (X-Forwarded-For > X-Real-IP > RemoteAddr) - rate_limiter_by_key: middleware configurable con keyFunc custom (API key, user ID...) - Cuando se rechaza responde 429 con HTTPError JSON y headers Retry-After + X-RateLimit-* - Tests con httptest.NewRecorder cubriendo: limite, burst, IPs independientes, XFF prioritario, recarga temporal, JSON body, headers IETF, GC stop idempotente, key vacia salta limit
44 lines
1.1 KiB
Go
44 lines
1.1 KiB
Go
package infra
|
|
|
|
import "testing"
|
|
|
|
func TestRateLimiterNew(t *testing.T) {
|
|
t.Run("crea limiter con rate y burst configurados", func(t *testing.T) {
|
|
rl := RateLimiterNew(10, 20)
|
|
if rl == nil {
|
|
t.Fatal("RateLimiterNew retorno nil")
|
|
}
|
|
if rl.rate != 10 {
|
|
t.Errorf("rate=%v, want 10", rl.rate)
|
|
}
|
|
if rl.burst != 20 {
|
|
t.Errorf("burst=%d, want 20", rl.burst)
|
|
}
|
|
})
|
|
|
|
t.Run("valores cero se sustituyen por 1", func(t *testing.T) {
|
|
rl := RateLimiterNew(0, 0)
|
|
if rl.rate != 1 {
|
|
t.Errorf("rate=%v, want 1 (default)", rl.rate)
|
|
}
|
|
if rl.burst != 1 {
|
|
t.Errorf("burst=%d, want 1 (default)", rl.burst)
|
|
}
|
|
|
|
rl2 := RateLimiterNew(-5, -10)
|
|
if rl2.rate != 1 || rl2.burst != 1 {
|
|
t.Errorf("valores negativos no se normalizaron a 1: rate=%v burst=%d", rl2.rate, rl2.burst)
|
|
}
|
|
})
|
|
|
|
t.Run("el mapa de clientes empieza vacio", func(t *testing.T) {
|
|
rl := RateLimiterNew(10, 20)
|
|
if rl.clients == nil {
|
|
t.Error("clients map es nil, deberia ser inicializado")
|
|
}
|
|
if len(rl.clients) != 0 {
|
|
t.Errorf("clients len=%d, want 0", len(rl.clients))
|
|
}
|
|
})
|
|
}
|