rdev/internal/auth/middleware_bench_test.go
jordan 72d16929ca feat: Implement hexagonal architecture with services, webhooks, queue, and telemetry
Major refactoring to hexagonal (ports & adapters) architecture:

- Add service layer (apikey_service, project_service) for business logic
- Add webhook system with dispatcher and delivery tracking
- Add command queue with priority-based processing
- Add rate limiting with sliding window algorithm
- Add audit logging for command execution
- Add OpenTelemetry integration (traces, metrics, spans)
- Add circuit breaker for fault tolerance
- Add cached repository wrapper for performance
- Add comprehensive validation package
- Add Kubernetes client integration for pod management
- Add database migrations (allowed_ips, audit_log, rate_limiting, queue, webhooks)
- Add network policy and PodDisruptionBudget for k8s
- Remove legacy executor and projects/registry packages
- Untrack secrets.yaml (now managed via envault)
- Add coverage.out to .gitignore
- Add e2e test infrastructure with docker-compose
- Add comprehensive documentation (API, architecture, operations, plans)
- Add golangci-lint config and pre-commit hook

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 19:57:46 -07:00

294 lines
7.3 KiB
Go

package auth
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
)
// BenchmarkAuthMiddleware benchmarks the authentication middleware overhead.
func BenchmarkAuthMiddleware(b *testing.B) {
// Create a mock API key
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute, ScopeProjectsRead},
CreatedAt: time.Now(),
}
// Simple handler that just writes OK
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Wrap with a mock middleware that simulates auth without DB
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Simulate auth header parsing
key := r.Header.Get(HeaderAPIKey)
if key == "" {
auth := r.Header.Get("Authorization")
if len(auth) > 7 && auth[:7] == "Bearer " {
key = auth[7:]
}
}
if key == "" {
w.WriteHeader(http.StatusUnauthorized)
return
}
// Simulate key validation (without DB)
if key != "valid-key" {
w.WriteHeader(http.StatusUnauthorized)
return
}
// Add key to context
ctx := context.WithValue(r.Context(), contextKeyAPIKey, apiKey)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
wrappedHandler := middleware(handler)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest("GET", "/projects", nil)
req.Header.Set(HeaderAPIKey, "valid-key")
rec := httptest.NewRecorder()
wrappedHandler.ServeHTTP(rec, req)
}
}
// BenchmarkAuthMiddleware_Bearer benchmarks auth with Bearer token.
func BenchmarkAuthMiddleware_Bearer(b *testing.B) {
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute, ScopeProjectsRead},
CreatedAt: time.Now(),
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := r.Header.Get(HeaderAPIKey)
if key == "" {
auth := r.Header.Get("Authorization")
if len(auth) > 7 && auth[:7] == "Bearer " {
key = auth[7:]
}
}
if key == "" || key != "valid-key" {
w.WriteHeader(http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), contextKeyAPIKey, apiKey)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
wrappedHandler := middleware(handler)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest("GET", "/projects", nil)
req.Header.Set("Authorization", "Bearer valid-key")
rec := httptest.NewRecorder()
wrappedHandler.ServeHTTP(rec, req)
}
}
// BenchmarkRequireScope benchmarks the scope checking middleware.
func BenchmarkRequireScope(b *testing.B) {
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute, ScopeProjectsRead},
CreatedAt: time.Now(),
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
scopeMiddleware := RequireScope(ScopeProjectsExecute)
wrappedHandler := scopeMiddleware(handler)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest("POST", "/projects/test/claude", nil)
// Pre-set the API key in context
ctx := context.WithValue(req.Context(), contextKeyAPIKey, apiKey)
req = req.WithContext(ctx)
rec := httptest.NewRecorder()
wrappedHandler.ServeHTTP(rec, req)
}
}
// BenchmarkGetClientIP benchmarks IP extraction from requests.
func BenchmarkGetClientIP(b *testing.B) {
req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "192.168.1.100:12345"
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = getClientIP(req)
}
}
// BenchmarkGetClientIP_XForwardedFor benchmarks IP extraction with X-Forwarded-For.
func BenchmarkGetClientIP_XForwardedFor(b *testing.B) {
req := httptest.NewRequest("GET", "/", nil)
req.Header.Set("X-Forwarded-For", "10.0.0.1, 192.168.1.1, 172.16.0.1")
req.RemoteAddr = "127.0.0.1:12345"
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = getClientIP(req)
}
}
// BenchmarkGetClientIP_IPv6 benchmarks IP extraction for IPv6.
func BenchmarkGetClientIP_IPv6(b *testing.B) {
req := httptest.NewRequest("GET", "/", nil)
req.RemoteAddr = "[2001:db8::1]:12345"
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = getClientIP(req)
}
}
// BenchmarkIPAllowlistCheck benchmarks the IP allowlist checking.
func BenchmarkIPAllowlistCheck(b *testing.B) {
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute},
AllowedIPs: []string{"192.168.1.0/24", "10.0.0.0/8", "172.16.0.0/12"},
CreatedAt: time.Now(),
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = apiKey.IsIPAllowed("192.168.1.100")
}
}
// BenchmarkIPAllowlistCheck_NoAllowlist benchmarks IP check when no allowlist configured.
func BenchmarkIPAllowlistCheck_NoAllowlist(b *testing.B) {
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute},
CreatedAt: time.Now(),
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_ = apiKey.IsIPAllowed("192.168.1.100")
}
}
// BenchmarkHealthEndpointSkip benchmarks the path-skip logic for health endpoints.
func BenchmarkHealthEndpointSkip(b *testing.B) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
// Simulate the skip check in middleware
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
if path == "/health" || path == "/ready" || path == "/metrics" {
next.ServeHTTP(w, r)
return
}
// Would do auth here
next.ServeHTTP(w, r)
})
}
wrappedHandler := middleware(handler)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
req := httptest.NewRequest("GET", "/health", nil)
rec := httptest.NewRecorder()
wrappedHandler.ServeHTTP(rec, req)
}
}
// BenchmarkConcurrentAuth benchmarks concurrent auth middleware calls.
func BenchmarkConcurrentAuth(b *testing.B) {
apiKey := &APIKey{
ID: "test-key-id",
Name: "benchmark-key",
KeyPrefix: "rdev",
Scopes: []Scope{ScopeProjectsExecute, ScopeProjectsRead},
CreatedAt: time.Now(),
}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
key := r.Header.Get(HeaderAPIKey)
if key == "" || key != "valid-key" {
w.WriteHeader(http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), contextKeyAPIKey, apiKey)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
wrappedHandler := middleware(handler)
b.ResetTimer()
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
req := httptest.NewRequest("GET", "/projects", nil)
req.Header.Set(HeaderAPIKey, "valid-key")
rec := httptest.NewRecorder()
wrappedHandler.ServeHTTP(rec, req)
}
})
}