test: Add tests for Cleanup and convertLegacyThreshold
- Cleanup: 16 test cases covering auto-acknowledge, TTL cleanup, rate limit entries, suppressions, pending alerts, flapping history, Docker restart tracking, PMG anomaly trackers, quarantine history - convertLegacyThreshold: 5 test cases covering nil/zero/negative input, default margin, custom margin Coverage: Cleanup 0%→97.6%, convertLegacyThreshold 0%→83.3% Package coverage: 65.4%→67.6%
This commit is contained in:
parent
24c44c8e5a
commit
1aa5eeb0cb
1 changed files with 424 additions and 0 deletions
|
|
@ -7828,3 +7828,427 @@ func TestCheckGuestPoweredOff(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCleanup(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("auto-acknowledges old alerts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
oldTime := time.Now().Add(-3 * time.Hour)
|
||||||
|
m.mu.Lock()
|
||||||
|
m.config.AutoAcknowledgeAfterHours = 2
|
||||||
|
m.activeAlerts["old-alert"] = &Alert{
|
||||||
|
ID: "old-alert",
|
||||||
|
StartTime: oldTime,
|
||||||
|
Acknowledged: false,
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
alert := m.activeAlerts["old-alert"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if alert == nil {
|
||||||
|
t.Fatal("expected alert to exist")
|
||||||
|
}
|
||||||
|
if !alert.Acknowledged {
|
||||||
|
t.Error("expected alert to be auto-acknowledged")
|
||||||
|
}
|
||||||
|
if alert.AckUser != "system-auto" {
|
||||||
|
t.Errorf("expected AckUser 'system-auto', got %s", alert.AckUser)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("removes old acknowledged alerts by TTL", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
oldAckTime := time.Now().Add(-10 * 24 * time.Hour) // 10 days ago
|
||||||
|
m.mu.Lock()
|
||||||
|
m.config.MaxAcknowledgedAgeDays = 7
|
||||||
|
m.activeAlerts["ack-alert"] = &Alert{
|
||||||
|
ID: "ack-alert",
|
||||||
|
Acknowledged: true,
|
||||||
|
AckTime: &oldAckTime,
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.activeAlerts["ack-alert"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected acknowledged alert to be removed by TTL")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("removes old unacknowledged alerts by TTL", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
oldTime := time.Now().Add(-40 * 24 * time.Hour) // 40 days ago
|
||||||
|
m.mu.Lock()
|
||||||
|
m.config.MaxAlertAgeDays = 30
|
||||||
|
m.config.AutoAcknowledgeAfterHours = 0 // Disable auto-acknowledge to test TTL
|
||||||
|
m.activeAlerts["old-unack-alert"] = &Alert{
|
||||||
|
ID: "old-unack-alert",
|
||||||
|
StartTime: oldTime,
|
||||||
|
Acknowledged: false,
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.activeAlerts["old-unack-alert"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected old unacknowledged alert to be removed by TTL")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("removes acknowledged alerts by maxAge fallback", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
oldAckTime := time.Now().Add(-2 * time.Hour)
|
||||||
|
m.mu.Lock()
|
||||||
|
m.activeAlerts["ack-fallback"] = &Alert{
|
||||||
|
ID: "ack-fallback",
|
||||||
|
Acknowledged: true,
|
||||||
|
AckTime: &oldAckTime,
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.activeAlerts["ack-fallback"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected acknowledged alert to be removed by maxAge fallback")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up old recent alerts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
oldTime := time.Now().Add(-10 * time.Minute)
|
||||||
|
m.mu.Lock()
|
||||||
|
m.recentAlerts["recent-old"] = &Alert{
|
||||||
|
ID: "recent-old",
|
||||||
|
StartTime: oldTime,
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.recentAlerts["recent-old"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected old recent alert to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up expired suppressions", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.suppressedUntil["suppressed-alert"] = time.Now().Add(-1 * time.Hour)
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.suppressedUntil["suppressed-alert"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected expired suppression to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up old rate limit entries", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.alertRateLimit["rate-limited"] = []time.Time{
|
||||||
|
time.Now().Add(-2 * time.Hour), // Old, should be removed
|
||||||
|
time.Now().Add(-30 * time.Minute), // Recent, should remain
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
times := m.alertRateLimit["rate-limited"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if len(times) != 1 {
|
||||||
|
t.Errorf("expected 1 recent time, got %d", len(times))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("removes empty rate limit entries", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.alertRateLimit["all-old"] = []time.Time{
|
||||||
|
time.Now().Add(-2 * time.Hour),
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.alertRateLimit["all-old"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected empty rate limit entry to be removed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up old recently resolved alerts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.resolvedMutex.Lock()
|
||||||
|
m.recentlyResolved["old-resolved"] = &ResolvedAlert{
|
||||||
|
Alert: &Alert{ID: "old-resolved"},
|
||||||
|
ResolvedTime: time.Now().Add(-10 * time.Minute),
|
||||||
|
}
|
||||||
|
m.resolvedMutex.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.resolvedMutex.Lock()
|
||||||
|
_, exists := m.recentlyResolved["old-resolved"]
|
||||||
|
m.resolvedMutex.Unlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected old recently resolved alert to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up stale pending alerts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.pendingAlerts["stale-pending"] = time.Now().Add(-15 * time.Minute)
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.pendingAlerts["stale-pending"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected stale pending alert to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up flapping history for inactive alerts", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.flappingHistory["inactive-alert"] = []time.Time{
|
||||||
|
time.Now().Add(-30 * time.Minute),
|
||||||
|
}
|
||||||
|
m.flappingActive["inactive-alert"] = true
|
||||||
|
// No active alert, no suppression
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, historyExists := m.flappingHistory["inactive-alert"]
|
||||||
|
_, activeExists := m.flappingActive["inactive-alert"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if historyExists {
|
||||||
|
t.Error("expected flapping history to be cleaned up")
|
||||||
|
}
|
||||||
|
if activeExists {
|
||||||
|
t.Error("expected flapping active flag to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up stale Docker restart tracking", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.dockerRestartTracking["stale-container"] = &dockerRestartRecord{
|
||||||
|
lastChecked: time.Now().Add(-25 * time.Hour),
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.dockerRestartTracking["stale-container"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected stale Docker restart tracking to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up stale PMG anomaly trackers", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.pmgAnomalyTrackers["stale-pmg"] = &pmgAnomalyTracker{
|
||||||
|
LastSampleTime: time.Now().Add(-25 * time.Hour),
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.pmgAnomalyTrackers["stale-pmg"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected stale PMG anomaly tracker to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up empty PMG quarantine history", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.pmgQuarantineHistory["empty-pmg"] = []pmgQuarantineSnapshot{}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.pmgQuarantineHistory["empty-pmg"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected empty PMG quarantine history to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("cleans up stale PMG quarantine history", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.pmgQuarantineHistory["stale-pmg"] = []pmgQuarantineSnapshot{
|
||||||
|
{Timestamp: time.Now().Add(-8 * 24 * time.Hour)},
|
||||||
|
}
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
m.Cleanup(1 * time.Hour)
|
||||||
|
|
||||||
|
m.mu.RLock()
|
||||||
|
_, exists := m.pmgQuarantineHistory["stale-pmg"]
|
||||||
|
m.mu.RUnlock()
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
t.Error("expected stale PMG quarantine history to be cleaned up")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertLegacyThreshold(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("nil input returns nil", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
result := m.convertLegacyThreshold(nil)
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("expected nil result for nil input")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("zero value returns nil", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
zero := 0.0
|
||||||
|
result := m.convertLegacyThreshold(&zero)
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("expected nil result for zero value")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("negative value returns nil", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
neg := -5.0
|
||||||
|
result := m.convertLegacyThreshold(&neg)
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("expected nil result for negative value")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("positive value with default margin", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
threshold := 80.0
|
||||||
|
result := m.convertLegacyThreshold(&threshold)
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
t.Fatal("expected non-nil result")
|
||||||
|
}
|
||||||
|
if result.Trigger != 80.0 {
|
||||||
|
t.Errorf("expected trigger 80.0, got %f", result.Trigger)
|
||||||
|
}
|
||||||
|
if result.Clear != 75.0 { // 80 - 5 (default margin)
|
||||||
|
t.Errorf("expected clear 75.0, got %f", result.Clear)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("positive value with custom margin", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
m := NewManager()
|
||||||
|
|
||||||
|
m.mu.Lock()
|
||||||
|
m.config.HysteresisMargin = 10.0
|
||||||
|
m.mu.Unlock()
|
||||||
|
|
||||||
|
threshold := 80.0
|
||||||
|
result := m.convertLegacyThreshold(&threshold)
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
t.Fatal("expected non-nil result")
|
||||||
|
}
|
||||||
|
if result.Trigger != 80.0 {
|
||||||
|
t.Errorf("expected trigger 80.0, got %f", result.Trigger)
|
||||||
|
}
|
||||||
|
if result.Clear != 70.0 { // 80 - 10 (custom margin)
|
||||||
|
t.Errorf("expected clear 70.0, got %f", result.Clear)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue