mirror of
https://github.com/syntrex-lab/gomcp.git
synced 2026-05-15 06:12:37 +02:00
Release prep: 54 engines, self-hosted signatures, i18n, dashboard updates
This commit is contained in:
parent
694e32be26
commit
41cbfd6e0a
178 changed files with 36008 additions and 399 deletions
101
internal/domain/soc/anomaly_test.go
Normal file
101
internal/domain/soc/anomaly_test.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package soc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAnomalyDetector_NoAlertDuringWarmup(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
// First 10 observations are warmup — should never alert
|
||||
for i := 0; i < 10; i++ {
|
||||
alert := d.Observe("cpu", 50.0)
|
||||
if alert != nil {
|
||||
t.Fatalf("should not alert during warmup, got alert at observation %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_NormalValues(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
// Build baseline with consistent values
|
||||
for i := 0; i < 20; i++ {
|
||||
d.Observe("rps", 100.0+float64(i%3)) // values: 100, 101, 102
|
||||
}
|
||||
|
||||
// Normal value should not trigger
|
||||
alert := d.Observe("rps", 103.0)
|
||||
if alert != nil {
|
||||
t.Fatal("normal value should not trigger anomaly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_ExtremeValue(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
// Build tight baseline
|
||||
for i := 0; i < 30; i++ {
|
||||
d.Observe("latency_ms", 10.0)
|
||||
}
|
||||
|
||||
// Extreme spike should trigger
|
||||
alert := d.Observe("latency_ms", 1000.0)
|
||||
if alert == nil {
|
||||
t.Fatal("extreme value should trigger anomaly")
|
||||
}
|
||||
if alert.Severity != "CRITICAL" {
|
||||
t.Fatalf("extreme deviation should be CRITICAL, got %s", alert.Severity)
|
||||
}
|
||||
if alert.ZScore < 3.0 {
|
||||
t.Fatalf("Z-score should be >= 3.0, got %f", alert.ZScore)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_CustomThreshold(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
d.SetThreshold(2.0) // More sensitive
|
||||
|
||||
for i := 0; i < 30; i++ {
|
||||
d.Observe("mem", 50.0)
|
||||
}
|
||||
|
||||
// Moderate deviation should trigger with lower threshold
|
||||
alert := d.Observe("mem", 80.0)
|
||||
if alert == nil {
|
||||
t.Fatal("moderate deviation should trigger with Z=2.0 threshold")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_Baselines(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
d.Observe("metric_a", 10.0)
|
||||
d.Observe("metric_b", 20.0)
|
||||
|
||||
baselines := d.Baselines()
|
||||
if len(baselines) != 2 {
|
||||
t.Fatalf("expected 2 baselines, got %d", len(baselines))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_Alerts(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
for i := 0; i < 30; i++ {
|
||||
d.Observe("test", 10.0)
|
||||
}
|
||||
d.Observe("test", 10000.0) // trigger alert
|
||||
|
||||
alerts := d.Alerts(10)
|
||||
if len(alerts) != 1 {
|
||||
t.Fatalf("expected 1 alert, got %d", len(alerts))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnomalyDetector_Stats(t *testing.T) {
|
||||
d := NewAnomalyDetector()
|
||||
d.Observe("x", 1.0)
|
||||
stats := d.Stats()
|
||||
if stats["metrics_tracked"].(int) != 1 {
|
||||
t.Fatal("should track 1 metric")
|
||||
}
|
||||
if stats["z_threshold"].(float64) != 3.0 {
|
||||
t.Fatal("default threshold should be 3.0")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue