7.5, 75 => 15.0, 90 => 18.0, 95 => 20.0, 99 => 22.0, ], sampleCount: 20, createdAt: $now, lastUpdated: $now, windowSize: Duration::fromMinutes(30), confidence: 0.8 ); } // Hilfsfunktion zum Erstellen von Testfeatures function createTestFeatures(): array { return [ new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_depth', value: 3.0, unit: 'count' ), new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_segments', value: 4.0, unit: 'count' ), new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_length', value: 25.0, unit: 'characters' ), new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'param_count', value: 2.0, unit: 'count' ), new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'param_length_avg', value: 8.0, unit: 'characters' ), ]; } test('erkennt Cluster-Abweichungen', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: true, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); // Viele normale Features für Clustering (20+ Datenpunkte) $features = []; for ($i = 0; $i < 20; $i++) { $features[] = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_length', value: 20.0 + rand(-5, 5), // Normal: 15-25 unit: 'characters' ); } // Anomales Feature mit deutlich abweichenden Werten $features[] = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_length', value: 150.0, // Deutlich höher als normal unit: 'characters' ); // Act $anomalies = $detector->detectAnomalies($features, null); // Assert - Clustering kann Anomalie erkennen oder nicht (abhängig von Algorithmus) // Test ist erfolgreich wenn keine Exception geworfen wird expect($anomalies)->toBeArray(); }); test('gruppiert Features nach Typ', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: true, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); // Features mit verschiedenen Typen $features = [ new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_feature', value: 10.0, unit: 'count' ), new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'param_feature', value: 5.0, unit: 'count' ), new Feature( type: FeatureType::FREQUENCY, name: 'freq_feature', value: 2.0, unit: 'requests/second' ), ]; // Wir können die private Methode nicht direkt testen, aber wir können testen, // dass der Detektor die Features analysieren kann // Act & Assert expect($detector->canAnalyze($features))->toBeTrue(); }); test('unterstützt verschiedene Verhaltenstypen', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: true, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); // Act $supportedTypes = $detector->getSupportedFeatureTypes(); // Assert expect($supportedTypes)->toBeArray(); expect($supportedTypes)->toContain(FeatureType::FREQUENCY); expect($supportedTypes)->toContain(FeatureType::STRUCTURAL_PATTERN); expect($supportedTypes)->toContain(FeatureType::BEHAVIORAL_PATTERN); expect($supportedTypes)->toContain(FeatureType::GEOGRAPHIC_DISTRIBUTION); }); test('erkennt Dichte-Anomalien wenn aktiviert', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: false, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); // Viele normale Features mit ähnlichen Werten für Dichte-Analyse $features = []; for ($i = 0; $i < 15; $i++) { $features[] = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_length', value: 20.0 + rand(-2, 2), // Dicht gruppiert: 18-22 unit: 'characters' ); } // Isoliertes Feature $features[] = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'path_length', value: 100.0, // Deutlich abseits der anderen unit: 'characters' ); // Act $anomalies = $detector->detectAnomalies($features, null); // Assert - Dichte-Analyse kann Anomalie erkennen oder nicht // Test ist erfolgreich wenn keine Exception geworfen wird expect($anomalies)->toBeArray(); }); test('aktualisiert Modell mit neuen Daten', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: true, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); $features = createTestFeatures(); // Act - Keine Assertion möglich, da interne Daten private sind // Wir testen nur, dass keine Exception geworfen wird $detector->updateModel($features); // Assert expect(true)->toBeTrue(); // Dummy assertion }); test('gibt Konfiguration korrekt zurück', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: true, confidenceThreshold: 0.75, maxClusters: 5, minClusterSize: 3, outlierThreshold: 0.9, maxIterations: 20, convergenceThreshold: 0.005, enableDensityAnalysis: true, enableGroupAnomalyDetection: false, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); // Act $config = $detector->getConfiguration(); // Assert expect($config)->toBeArray(); expect($config['enabled'])->toBeTrue(); expect($config['confidence_threshold'])->toBe(0.75); expect($config['max_clusters'])->toBe(5); expect($config['min_cluster_size'])->toBe(3); expect($config['outlier_threshold'])->toBe(0.9); expect($config['max_iterations'])->toBe(20); expect($config['enable_density_analysis'])->toBeTrue(); expect($config['enable_group_anomaly_detection'])->toBeFalse(); }); test('gibt leere Ergebnisse zurück wenn deaktiviert', function () { // Arrange $detector = new ClusteringAnomalyDetector(new SystemClock(), enabled: false, confidenceThreshold: 0.5, maxClusters: 3, minClusterSize: 2, outlierThreshold: 0.8, maxIterations: 10, convergenceThreshold: 0.01, enableDensityAnalysis: true, enableGroupAnomalyDetection: true, clusterCenters: [], clusterAssignments: [], featureVectors: [] ); $features = createTestFeatures(); // Act $anomalies = $detector->detectAnomalies($features, null); // Assert expect($anomalies)->toBeEmpty(); });