154 lines
3.7 KiB
Go
154 lines
3.7 KiB
Go
package notifications
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
|
|
)
|
|
|
|
func flushPending(n *NotificationManager) {
|
|
n.mu.Lock()
|
|
if n.groupTimer != nil {
|
|
n.groupTimer.Stop()
|
|
n.groupTimer = nil
|
|
}
|
|
n.mu.Unlock()
|
|
n.sendGroupedAlerts()
|
|
}
|
|
|
|
func TestNotificationCooldownAllowsNewAlertInstance(t *testing.T) {
|
|
nm := NewNotificationManager("")
|
|
nm.SetCooldown(1) // 1 minute cooldown
|
|
nm.SetGroupingWindow(3600) // keep timer from firing immediately
|
|
|
|
alertStart := time.Now().Add(-time.Minute)
|
|
alertA := &alerts.Alert{
|
|
ID: "vm-100-memory",
|
|
Type: "memory",
|
|
Level: alerts.AlertLevelWarning,
|
|
StartTime: alertStart,
|
|
}
|
|
|
|
nm.SendAlert(alertA)
|
|
flushPending(nm)
|
|
|
|
nm.mu.RLock()
|
|
firstRecord, ok := nm.lastNotified[alertA.ID]
|
|
nm.mu.RUnlock()
|
|
if !ok {
|
|
t.Fatalf("first notification not recorded")
|
|
}
|
|
|
|
nm.SendAlert(alertA)
|
|
|
|
nm.mu.RLock()
|
|
pendingAfter := len(nm.pendingAlerts)
|
|
nm.mu.RUnlock()
|
|
if pendingAfter != 0 {
|
|
t.Fatalf("cooldown alert should not be queued, found %d pending", pendingAfter)
|
|
}
|
|
|
|
alertRestart := &alerts.Alert{
|
|
ID: "vm-100-memory",
|
|
Type: "memory",
|
|
Level: alerts.AlertLevelWarning,
|
|
StartTime: alertStart.Add(time.Minute),
|
|
}
|
|
|
|
nm.SendAlert(alertRestart)
|
|
flushPending(nm)
|
|
|
|
nm.mu.RLock()
|
|
recordAfter := nm.lastNotified[alertRestart.ID]
|
|
nm.mu.RUnlock()
|
|
|
|
if !recordAfter.alertStart.Equal(alertRestart.StartTime) {
|
|
t.Fatalf("expected alertStart %v, got %v", alertRestart.StartTime, recordAfter.alertStart)
|
|
}
|
|
if !recordAfter.lastSent.After(firstRecord.lastSent) {
|
|
t.Fatalf("lastSent was not updated for new alert instance")
|
|
}
|
|
}
|
|
|
|
func TestCancelAlertRemovesPending(t *testing.T) {
|
|
nm := NewNotificationManager("")
|
|
nm.SetGroupingWindow(120)
|
|
|
|
alertA := &alerts.Alert{
|
|
ID: "vm-100-disk",
|
|
Type: "disk",
|
|
Level: alerts.AlertLevelWarning,
|
|
StartTime: time.Now(),
|
|
}
|
|
alertB := &alerts.Alert{
|
|
ID: "vm-101-disk",
|
|
Type: "disk",
|
|
Level: alerts.AlertLevelWarning,
|
|
StartTime: time.Now(),
|
|
}
|
|
|
|
nm.SendAlert(alertA)
|
|
nm.SendAlert(alertB)
|
|
|
|
nm.CancelAlert(alertA.ID)
|
|
|
|
nm.mu.RLock()
|
|
remaining := make([]string, 0, len(nm.pendingAlerts))
|
|
for _, pending := range nm.pendingAlerts {
|
|
if pending != nil {
|
|
remaining = append(remaining, pending.ID)
|
|
}
|
|
}
|
|
groupTimerActive := nm.groupTimer != nil
|
|
nm.mu.RUnlock()
|
|
|
|
if len(remaining) != 1 || remaining[0] != alertB.ID {
|
|
t.Fatalf("expected only %s to remain pending, got %v", alertB.ID, remaining)
|
|
}
|
|
if !groupTimerActive {
|
|
t.Fatalf("expected grouping timer to remain active while other alerts pending")
|
|
}
|
|
|
|
nm.CancelAlert(alertB.ID)
|
|
|
|
nm.mu.RLock()
|
|
if len(nm.pendingAlerts) != 0 {
|
|
nm.mu.RUnlock()
|
|
t.Fatalf("expected no pending alerts after cancelling all, found %d", len(nm.pendingAlerts))
|
|
}
|
|
timerStopped := nm.groupTimer == nil
|
|
nm.mu.RUnlock()
|
|
|
|
if !timerStopped {
|
|
t.Fatalf("expected grouping timer to be cleared when no alerts remain")
|
|
}
|
|
}
|
|
|
|
func TestConvertWebhookCustomFields(t *testing.T) {
|
|
if result := convertWebhookCustomFields(nil); result != nil {
|
|
t.Fatalf("expected nil for empty input, got %#v", result)
|
|
}
|
|
|
|
original := map[string]string{
|
|
"app_token": "abc123",
|
|
"user_token": "user456",
|
|
}
|
|
|
|
converted := convertWebhookCustomFields(original)
|
|
if len(converted) != len(original) {
|
|
t.Fatalf("expected %d keys, got %d", len(original), len(converted))
|
|
}
|
|
|
|
for key, value := range original {
|
|
if got, ok := converted[key]; !ok || got != value {
|
|
t.Fatalf("expected %s=%s, got %v (present=%v)", key, value, got, ok)
|
|
}
|
|
}
|
|
|
|
// Mutate original map and ensure converted copy remains unchanged
|
|
original["extra"] = "new-value"
|
|
if _, ok := converted["extra"]; ok {
|
|
t.Fatalf("expected converted map to be independent of original mutations")
|
|
}
|
|
}
|