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 ); } test('erkennt Z-Score-Anomalien korrekt', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); $feature = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 42.0, unit: 'count' ); $baseline = createTestBaselineSAD(); // Act $anomalies = $detector->detectAnomalies([$feature], $baseline); // Assert expect($anomalies)->toHaveCount(1); expect($anomalies[0])->toBeInstanceOf(AnomalyDetection::class); expect($anomalies[0]->type)->toBe(AnomalyType::STATISTICAL_ANOMALY); expect($anomalies[0]->featureType)->toBe(FeatureType::STRUCTURAL_PATTERN); expect($anomalies[0]->confidence->getValue())->toBeGreaterThan(50.0); // Z-Score sollte (42 - 10) / 5 = 6.4 sein, was deutlich über dem Schwellenwert liegt expect($anomalies[0]->anomalyScore)->toBeGreaterThan(0.7); }); test('ignoriert Werte innerhalb des normalen Bereichs', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); $feature = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 12.0, // Nahe am Mittelwert unit: 'count' ); $baseline = createTestBaselineSAD(); // Act $anomalies = $detector->detectAnomalies([$feature], $baseline); // Assert expect($anomalies)->toBeEmpty(); }); test('erkennt Ausreißer ohne Baseline', function () { // Arrange - braucht mehr Samples für IQR Outlier Detection $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.0, // Low threshold to allow detection even without zScore in Feature zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 20, enableOutlierDetection: true, enableTrendAnalysis: false, featureHistory: [ 'structural_pattern:test_feature' => [ 10, 12, 9, 11, 10, 13, 8, 11, 10, 12, 9, 11, 10, 12, 11, 10, 9, 13, 11, 10, 12, 10, 11, 9, 10 // 25 Samples für robuste IQR ], ] ); $feature = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 30.0, // Deutlicher Ausreißer (normal: 8-13) unit: 'count' // No zScore to avoid triggering Z-score detection ); // Act $anomalies = $detector->detectAnomalies([$feature], null); // Assert expect($anomalies)->not->toBeEmpty(); expect($anomalies[0]->type)->toBe(AnomalyType::OUTLIER_DETECTION); }); test('unterstützt verschiedene Verhaltenstypen', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); // Act $supportedTypes = $detector->getSupportedFeatureTypes(); // Assert expect($supportedTypes)->toBeArray(); expect($supportedTypes)->toContain(FeatureType::FREQUENCY); expect($supportedTypes)->toContain(FeatureType::STRUCTURAL_PATTERN); expect($supportedTypes)->toContain(FeatureType::STRUCTURAL_PATTERN); }); test('aktualisiert Modell mit neuen Daten', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); $feature1 = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 15.0, unit: 'count' ); $feature2 = new Feature( type: FeatureType::FREQUENCY, name: 'request_rate', value: 5.0, unit: 'requests/second' ); // Act - Keine Assertion möglich, da featureHistory private ist // Wir testen nur, dass keine Exception geworfen wird $detector->updateModel([$feature1, $feature2]); // Assert expect(true)->toBeTrue(); // Dummy assertion }); test('gibt Konfiguration korrekt zurück', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.75, zScoreThreshold: 2.5, extremeZScoreThreshold: 4.0, minSampleSize: 10, enableOutlierDetection: true, enableTrendAnalysis: false, featureHistory: [] ); // Act $config = $detector->getConfiguration(); // Assert expect($config)->toBeArray(); expect($config['enabled'])->toBeTrue(); expect($config['confidence_threshold'])->toBe(0.75); expect($config['z_score_threshold'])->toBe(2.5); expect($config['extreme_z_score_threshold'])->toBe(4.0); expect($config['min_sample_size'])->toBe(10); expect($config['enable_outlier_detection'])->toBeTrue(); expect($config['enable_trend_analysis'])->toBeFalse(); }); test('kann Analyse durchführen wenn aktiviert', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: true, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); $feature = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 42.0, unit: 'count' ); // Act & Assert expect($detector->isEnabled())->toBeTrue(); expect($detector->canAnalyze([$feature]))->toBeTrue(); }); test('gibt leere Ergebnisse zurück wenn deaktiviert', function () { // Arrange $detector = new StatisticalAnomalyDetector( enabled: false, confidenceThreshold: 0.5, zScoreThreshold: 2.0, extremeZScoreThreshold: 3.0, minSampleSize: 5, enableOutlierDetection: true, enableTrendAnalysis: true, featureHistory: [] ); $feature = new Feature( type: FeatureType::STRUCTURAL_PATTERN, name: 'test_feature', value: 42.0, unit: 'count' ); $baseline = createTestBaselineSAD(); // Act $anomalies = $detector->detectAnomalies([$feature], $baseline); // Assert expect($anomalies)->toBeEmpty(); });