package middleware_test import ( "context" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "sneak.berlin/go/webhooker/internal/config" "sneak.berlin/go/webhooker/internal/middleware" ) func TestLoginRateLimit_AllowsGET(t *testing.T) { t.Parallel() m, _ := testMiddleware(t, config.EnvironmentDev) var callCount int handler := m.LoginRateLimit()(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { callCount++ w.WriteHeader(http.StatusOK) }, )) // GET requests should never be rate-limited for i := range 20 { req := httptest.NewRequestWithContext( context.Background(), http.MethodGet, "/pages/login", nil, ) req.RemoteAddr = "192.168.1.1:12345" w := httptest.NewRecorder() handler.ServeHTTP(w, req) assert.Equal( t, http.StatusOK, w.Code, "GET request %d should pass", i, ) } assert.Equal(t, 20, callCount) } func TestLoginRateLimit_LimitsPOST(t *testing.T) { t.Parallel() m, _ := testMiddleware(t, config.EnvironmentDev) var callCount int handler := m.LoginRateLimit()(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { callCount++ w.WriteHeader(http.StatusOK) }, )) // First loginRateLimit POST requests should succeed for i := range middleware.LoginRateLimitConst { req := httptest.NewRequestWithContext( context.Background(), http.MethodPost, "/pages/login", nil, ) req.RemoteAddr = "10.0.0.1:12345" w := httptest.NewRecorder() handler.ServeHTTP(w, req) assert.Equal( t, http.StatusOK, w.Code, "POST request %d should pass", i, ) } // Next POST should be rate-limited req := httptest.NewRequestWithContext( context.Background(), http.MethodPost, "/pages/login", nil, ) req.RemoteAddr = "10.0.0.1:12345" w := httptest.NewRecorder() handler.ServeHTTP(w, req) assert.Equal( t, http.StatusTooManyRequests, w.Code, "POST after limit should be 429", ) assert.Equal(t, middleware.LoginRateLimitConst, callCount) } func TestLoginRateLimit_IndependentPerIP(t *testing.T) { t.Parallel() m, _ := testMiddleware(t, config.EnvironmentDev) handler := m.LoginRateLimit()(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }, )) // Exhaust limit for IP1 for range middleware.LoginRateLimitConst { req := httptest.NewRequestWithContext( context.Background(), http.MethodPost, "/pages/login", nil, ) req.RemoteAddr = "1.2.3.4:12345" w := httptest.NewRecorder() handler.ServeHTTP(w, req) } // IP1 should be rate-limited req := httptest.NewRequestWithContext( context.Background(), http.MethodPost, "/pages/login", nil, ) req.RemoteAddr = "1.2.3.4:12345" w := httptest.NewRecorder() handler.ServeHTTP(w, req) assert.Equal(t, http.StatusTooManyRequests, w.Code) // IP2 should still be allowed req2 := httptest.NewRequestWithContext( context.Background(), http.MethodPost, "/pages/login", nil, ) req2.RemoteAddr = "5.6.7.8:12345" w2 := httptest.NewRecorder() handler.ServeHTTP(w2, req2) assert.Equal( t, http.StatusOK, w2.Code, "different IP should not be affected", ) }