Files
Crussell/backend/mw/ratelimit_prod_test.go
T
popertotsandSisyphus 510828c924
CI / Go vulnerabilities (push) Successful in 1m10s
CI / Build & Vet (push) Successful in 1m39s
CI / Frontend build (gate) (push) Successful in 1m42s
CI / Frontend QC (audit) (push) Successful in 56s
CI / Frontend QC (typecheck) (push) Successful in 1m36s
CI / Frontend QC (lint) (push) Successful in 1m51s
CI / Tests (prod) (push) Has been cancelled
CI / Tests (dev) (push) Has been cancelled
CI / Race (prod) (push) Has been cancelled
CI / Race (dev) (push) Has been cancelled
chore: run go fix for Go 1.26 modernization
106 files: interface{}→any, strings.Split→SplitSeq, CutPrefix/Cut, strings.Builder, slices.Contains, remove redundant // +build directives, gofmt import ordering and indentation.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-07-09 17:25:23 +01:00

113 lines
2.8 KiB
Go

//go:build test && !dev
package mw
import (
"testing"
"time"
"crussell/clock"
)
// TestProgressiveRateLimiter_SingleRequest verifies no delay for first request.
func TestProgressiveRateLimiter_SingleRequest(t *testing.T) {
prl := NewProgressiveRateLimiter()
delay := prl.Check("192.168.1.1")
if delay != 0 {
t.Errorf("expected 0 delay for first request, got %d", delay)
}
}
// TestProgressiveRateLimiter_BurstAllowsPageLoad verifies 15 requests in 5s are OK.
func TestProgressiveRateLimiter_BurstAllowsPageLoad(t *testing.T) {
prl := NewProgressiveRateLimiter()
ip := "192.168.1.1"
for i := 0; i < 15; i++ {
delay := prl.Check(ip)
if delay != 0 {
t.Errorf("expected 0 delay for request %d (within burst), got %d", i+1, delay)
}
}
}
// TestProgressiveRateLimiter_ExcessBurstDelays verifies 35+ requests in 5s get delayed.
func TestProgressiveRateLimiter_ExcessBurstDelays(t *testing.T) {
prl := NewProgressiveRateLimiter()
ip := "192.168.1.1"
delayed := false
for i := 0; i < 35; i++ {
delay := prl.Check(ip)
if delay > 0 {
delayed = true
}
}
if !delayed {
t.Error("expected at least one delay after 35 burst requests")
}
}
// TestProgressiveRateLimiter_SustainedAllowsNormal verifies 30 requests
// spread over 60 seconds are not delayed.
func TestProgressiveRateLimiter_SustainedAllowsNormal(t *testing.T) {
prl := NewProgressiveRateLimiter()
ip := "192.168.1.2"
for i := 0; i < 30; i++ {
prl.mu.Lock()
state, exists := prl.requests[ip]
if !exists {
prl.requests[ip] = &ipProgressiveState{
timestamps: []time.Time{clock.Now().Add(-time.Duration(60-i*2) * time.Second)},
}
prl.mu.Unlock()
continue
}
state.timestamps = append(state.timestamps, clock.Now().Add(-time.Duration(60-i*2)*time.Second))
prl.mu.Unlock()
}
delay := prl.Check(ip)
if delay != 0 {
t.Errorf("expected 0 delay for 30 sustained requests over 60s, got %d", delay)
}
}
// TestProgressiveRateLimiter_ExcessSustainedDelays verifies 130+ requests
// in 60s triggers delay.
func TestProgressiveRateLimiter_ExcessSustainedDelays(t *testing.T) {
prl := NewProgressiveRateLimiter()
ip := "192.168.1.3"
now := clock.Now()
prl.mu.Lock()
state := &ipProgressiveState{timestamps: make([]time.Time, 130)}
for i := 0; i < 130; i++ {
state.timestamps[i] = now.Add(-time.Duration(60-i/3) * time.Second)
}
prl.requests[ip] = state
prl.mu.Unlock()
delay := prl.Check(ip)
if delay == 0 {
t.Error("expected delay > 0 for 130 sustained requests")
}
}
// TestProgressiveRateLimiter_DifferentIPs verifies rate limiter
// tracks IPs independently.
func TestProgressiveRateLimiter_DifferentIPs(t *testing.T) {
prl := NewProgressiveRateLimiter()
for i := 0; i < 40; i++ {
prl.Check("10.0.0.1")
}
delay := prl.Check("10.0.0.2")
if delay != 0 {
t.Errorf("expected 0 delay for separate IP, got %d", delay)
}
}