clock = new SystemClock(); // Create DDoS config for testing $this->config = new DDoSConfig( enabled: true, volumetricThreshold: 100.0, // Lower threshold for faster testing distributedThreshold: 0.8, analysisWindow: Duration::fromMinutes(5), trustedIps: ['127.0.0.1', '::1'], exemptPaths: ['/health', '/ping'] ); // Mock the engine (we'll need to set up proper DI for real tests) $this->engine = $this->createMockEngine(); }); describe('DDoS Protection Engine', function () { it('allows normal requests through', function () { $request = createTestRequest('192.168.1.100', 'GET', '/api/users'); $assessment = $this->engine->assessRequest($request); expect($assessment->threatLevel)->toBe(ThreatLevel::LOW); expect($assessment->shouldBlock)->toBeFalse(); expect($assessment->threatScore)->toBeLessThan(0.3); }); it('detects high volume attacks from single IP', function () { $attackerIp = '10.0.0.1'; $assessment = null; // Simulate rapid requests from same IP for ($i = 1; $i <= 20; $i++) { $request = createTestRequest($attackerIp, 'GET', "/page{$i}"); $assessment = $this->engine->assessRequest($request); if ($assessment->shouldBlock) { break; } } expect($assessment->threatLevel)->toBeIn([ThreatLevel::HIGH, ThreatLevel::CRITICAL]); expect($assessment->shouldBlock)->toBeTrue(); expect($assessment->threatScore)->toBeGreaterThan(0.7); }); it('detects distributed attacks', function () { $attackIps = ['1.2.3.4', '5.6.7.8', '9.10.11.12', '13.14.15.16']; $assessments = []; foreach ($attackIps as $ip) { for ($i = 1; $i <= 15; $i++) { $request = createTestRequest($ip, 'POST', '/api/login'); $assessments[] = $this->engine->assessRequest($request); } } // Should detect distributed pattern $lastAssessment = end($assessments); expect($lastAssessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]); expect($lastAssessment->threatScore)->toBeGreaterThan(0.4); }); it('blocks suspicious bot traffic', function () { $request = createTestRequest('192.168.1.200', 'GET', '/sensitive-data', [ 'User-Agent' => 'BadBot/1.0 (automated scraper)', ]); $assessment = $this->engine->assessRequest($request); expect($assessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]); expect($assessment->threatScore)->toBeGreaterThan(0.5); }); it('allows trusted IPs through', function () { $request = createTestRequest('127.0.0.1', 'GET', '/admin/sensitive'); $assessment = $this->engine->assessRequest($request); expect($assessment->threatLevel)->toBe(ThreatLevel::LOW); expect($assessment->shouldBlock)->toBeFalse(); }); it('bypasses exempt paths', function () { $request = createTestRequest('10.0.0.1', 'GET', '/health'); $assessment = $this->engine->assessRequest($request); expect($assessment->threatLevel)->toBe(ThreatLevel::LOW); expect($assessment->shouldBlock)->toBeFalse(); }); it('escalates threat level with repeated attacks', function () { $attackerIp = '192.168.1.50'; $assessments = []; // First wave - should be low threat for ($i = 1; $i <= 5; $i++) { $request = createTestRequest($attackerIp, 'GET', "/api/data{$i}"); $assessments[] = $this->engine->assessRequest($request); } expect($assessments[0]->threatLevel)->toBe(ThreatLevel::LOW); // Second wave - should escalate for ($i = 6; $i <= 25; $i++) { $request = createTestRequest($attackerIp, 'GET', "/api/data{$i}"); $assessments[] = $this->engine->assessRequest($request); } $lastAssessment = end($assessments); expect($lastAssessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH, ThreatLevel::CRITICAL]); expect($lastAssessment->threatScore)->toBeGreaterThan($assessments[0]->threatScore); }); it('handles malformed requests gracefully', function () { $request = createTestRequest('invalid-ip', 'INVALID_METHOD', ''); $assessment = $this->engine->assessRequest($request); // Should not crash and should treat as suspicious expect($assessment)->not()->toBeNull(); expect($assessment->threatLevel)->toBeIn([ThreatLevel::MEDIUM, ThreatLevel::HIGH]); }); it('provides detailed assessment information', function () { $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $assessment = $this->engine->assessRequest($request); expect($assessment)->toHaveProperties([ 'threatLevel', 'threatScore', 'shouldBlock', 'detectedPatterns', 'responseRecommendation', ]); expect($assessment->threatScore)->toBeBetween(0.0, 1.0); }); }); describe('DDoS Configuration', function () { it('respects custom thresholds', function () { $strictConfig = new DDoSConfig( volumetricThreshold: 10.0, // Very low threshold highThreatThreshold: 0.3 // Lower threshold for high threat ); $engine = createEngineWithConfig($strictConfig); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); // With strict config, even normal requests might be flagged $assessment = $engine->assessRequest($request); expect($assessment)->not()->toBeNull(); }); it('can be disabled', function () { $disabledConfig = new DDoSConfig(enabled: false); $engine = createEngineWithConfig($disabledConfig); $request = createTestRequest('10.0.0.1', 'GET', '/api/test'); $assessment = $engine->assessRequest($request); expect($assessment->threatLevel)->toBe(ThreatLevel::LOW); expect($assessment->shouldBlock)->toBeFalse(); }); }); describe('Performance', function () { it('completes assessment within reasonable time', function () { $start = microtime(true); $request = createTestRequest('192.168.1.100', 'GET', '/api/test'); $assessment = $this->engine->assessRequest($request); $duration = microtime(true) - $start; expect($duration)->toBeLessThan(0.1); // Should complete within 100ms expect($assessment)->not()->toBeNull(); }); it('handles high request volume efficiently', function () { $start = microtime(true); // Process 100 requests for ($i = 1; $i <= 100; $i++) { $ip = '192.168.1.' . ($i % 254 + 1); $request = createTestRequest($ip, 'GET', "/api/test{$i}"); $this->engine->assessRequest($request); } $duration = microtime(true) - $start; $avgPerRequest = $duration / 100; expect($avgPerRequest)->toBeLessThan(0.01); // Average <10ms per request }); }); // Helper functions are now in Helpers/TestHelpers.php