Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
176 lines
4.8 KiB
Go
176 lines
4.8 KiB
Go
package security
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEnhancedRateLimiter(t *testing.T) {
|
|
|
|
config := &RateLimiterConfig{
|
|
IPRequestsPerSecond: 5,
|
|
IPBurstSize: 10,
|
|
GlobalRequestsPerSecond: 10000, // Set high global limit
|
|
GlobalBurstSize: 20000, // Set high global burst
|
|
UserRequestsPerSecond: 1000, // Set high user limit
|
|
UserBurstSize: 2000, // Set high user burst
|
|
SlidingWindowEnabled: false, // Disabled for testing basic burst logic
|
|
SlidingWindowSize: time.Minute,
|
|
SlidingWindowPrecision: time.Second,
|
|
AdaptiveEnabled: false, // Disabled for testing basic burst logic
|
|
AdaptiveAdjustInterval: 100 * time.Millisecond,
|
|
SystemLoadThreshold: 80.0,
|
|
BypassDetectionEnabled: true,
|
|
BypassThreshold: 3,
|
|
CleanupInterval: time.Minute,
|
|
BucketTTL: time.Hour,
|
|
}
|
|
|
|
rl := NewEnhancedRateLimiter(config)
|
|
defer rl.Stop()
|
|
|
|
ctx := context.Background()
|
|
headers := make(map[string]string)
|
|
|
|
// Test basic rate limiting
|
|
for i := 0; i < 3; i++ {
|
|
result := rl.CheckRateLimitEnhanced(ctx, "127.0.0.1", "test-user", "TestAgent", "test", headers)
|
|
if !result.Allowed {
|
|
t.Errorf("Request %d should be allowed, but got: %s - %s", i+1, result.ReasonCode, result.Message)
|
|
}
|
|
}
|
|
|
|
// Test burst capacity (should allow up to burst size)
|
|
// We already made 3 requests, so we can make 7 more before hitting the limit
|
|
for i := 0; i < 7; i++ {
|
|
result := rl.CheckRateLimitEnhanced(ctx, "127.0.0.1", "test-user", "TestAgent", "test", headers)
|
|
if !result.Allowed {
|
|
t.Errorf("Request %d should be allowed within burst, but got: %s - %s", i+4, result.ReasonCode, result.Message)
|
|
}
|
|
}
|
|
|
|
// Now we should exceed the burst limit and be rate limited
|
|
for i := 0; i < 5; i++ {
|
|
result := rl.CheckRateLimitEnhanced(ctx, "127.0.0.1", "test-user", "TestAgent", "test", headers)
|
|
if result.Allowed {
|
|
t.Errorf("Request %d should be rate limited (exceeded burst)", i+11)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSlidingWindow(t *testing.T) {
|
|
window := NewSlidingWindow(5, time.Minute, time.Second)
|
|
|
|
// Test within limit
|
|
for i := 0; i < 5; i++ {
|
|
if !window.IsAllowed() {
|
|
t.Errorf("Request %d should be allowed", i+1)
|
|
}
|
|
}
|
|
|
|
// Test exceeding limit
|
|
if window.IsAllowed() {
|
|
t.Error("Request should be denied after exceeding limit")
|
|
}
|
|
}
|
|
|
|
func TestBypassDetection(t *testing.T) {
|
|
detector := NewBypassDetector(3, time.Hour, time.Minute)
|
|
headers := make(map[string]string)
|
|
|
|
// Test normal behavior
|
|
result := detector.DetectBypass("127.0.0.1", "TestAgent", headers, false)
|
|
if result.BypassDetected {
|
|
t.Error("Normal behavior should not trigger bypass detection")
|
|
}
|
|
|
|
// Test bypass pattern (multiple rate limit hits)
|
|
for i := 0; i < 25; i++ { // Increased to trigger MEDIUM severity
|
|
result = detector.DetectBypass("127.0.0.1", "TestAgent", headers, true)
|
|
}
|
|
|
|
if !result.BypassDetected {
|
|
t.Error("Multiple rate limit hits should trigger bypass detection")
|
|
}
|
|
|
|
if result.Severity != "MEDIUM" && result.Severity != "HIGH" {
|
|
t.Errorf("Expected MEDIUM or HIGH severity, got %s", result.Severity)
|
|
}
|
|
}
|
|
|
|
func TestSystemLoadMonitor(t *testing.T) {
|
|
monitor := NewSystemLoadMonitor(100 * time.Millisecond)
|
|
defer monitor.Stop()
|
|
|
|
// Allow some time for monitoring to start
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
cpu, memory, load, goroutines := monitor.GetCurrentLoad()
|
|
|
|
if cpu < 0 || cpu > 100 {
|
|
t.Errorf("CPU usage should be between 0-100, got %f", cpu)
|
|
}
|
|
|
|
if memory < 0 || memory > 100 {
|
|
t.Errorf("Memory usage should be between 0-100, got %f", memory)
|
|
}
|
|
|
|
if load < 0 {
|
|
t.Errorf("Load average should be positive, got %f", load)
|
|
}
|
|
|
|
if goroutines <= 0 {
|
|
t.Errorf("Goroutine count should be positive, got %d", goroutines)
|
|
}
|
|
}
|
|
|
|
func TestEnhancedMetrics(t *testing.T) {
|
|
config := &RateLimiterConfig{
|
|
IPRequestsPerSecond: 10,
|
|
SlidingWindowEnabled: true,
|
|
AdaptiveEnabled: true,
|
|
AdaptiveAdjustInterval: 100 * time.Millisecond,
|
|
BypassDetectionEnabled: true,
|
|
CleanupInterval: time.Second,
|
|
BypassThreshold: 5,
|
|
BypassDetectionWindow: time.Minute,
|
|
BypassAlertCooldown: time.Minute,
|
|
}
|
|
|
|
rl := NewEnhancedRateLimiter(config)
|
|
defer rl.Stop()
|
|
|
|
metrics := rl.GetEnhancedMetrics()
|
|
|
|
// Check that all expected metrics are present
|
|
expectedKeys := []string{
|
|
"sliding_window_enabled",
|
|
"adaptive_enabled",
|
|
"bypass_detection_enabled",
|
|
"system_cpu_usage",
|
|
"system_memory_usage",
|
|
"system_load_average",
|
|
"system_goroutines",
|
|
}
|
|
|
|
for _, key := range expectedKeys {
|
|
if _, exists := metrics[key]; !exists {
|
|
t.Errorf("Expected metric %s not found", key)
|
|
}
|
|
}
|
|
|
|
// Verify boolean flags
|
|
if metrics["sliding_window_enabled"] != true {
|
|
t.Error("sliding_window_enabled should be true")
|
|
}
|
|
|
|
if metrics["adaptive_enabled"] != true {
|
|
t.Error("adaptive_enabled should be true")
|
|
}
|
|
|
|
if metrics["bypass_detection_enabled"] != true {
|
|
t.Error("bypass_detection_enabled should be true")
|
|
}
|
|
}
|