From a97a4a022c31f06c73454cc5c915a43363c07740 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 1 Dec 2025 15:42:35 +0000 Subject: [PATCH] test: Add comprehensive tests for shouldNotifyAfterCooldown function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cooldown disabled (0) allows notification - negative cooldown allows notification - first notification allowed when never notified - notification blocked during cooldown period - notification allowed after cooldown expires - boundary condition (exact cooldown time) - shouldNotifyAfterCooldown 42.9%→100% --- internal/alerts/alerts_test.go | 110 +++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/internal/alerts/alerts_test.go b/internal/alerts/alerts_test.go index 87b0003..2fda6cb 100644 --- a/internal/alerts/alerts_test.go +++ b/internal/alerts/alerts_test.go @@ -2802,3 +2802,113 @@ func TestApplyRelaxedGuestThresholds(t *testing.T) { } }) } + +func TestShouldNotifyAfterCooldown(t *testing.T) { + t.Parallel() + + t.Run("cooldown disabled allows notification", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = 0 + m.mu.Unlock() + + alert := &Alert{ + ID: "test-alert", + LastNotified: nil, + } + + if !m.shouldNotifyAfterCooldown(alert) { + t.Error("expected true when cooldown is 0") + } + }) + + t.Run("negative cooldown allows notification", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = -5 + m.mu.Unlock() + + now := time.Now() + alert := &Alert{ + ID: "test-alert", + LastNotified: &now, + } + + if !m.shouldNotifyAfterCooldown(alert) { + t.Error("expected true when cooldown is negative") + } + }) + + t.Run("first notification allowed when never notified", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = 30 // 30 minutes + m.mu.Unlock() + + alert := &Alert{ + ID: "test-alert", + LastNotified: nil, + } + + if !m.shouldNotifyAfterCooldown(alert) { + t.Error("expected true when alert has never been notified") + } + }) + + t.Run("notification blocked during cooldown period", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = 30 // 30 minutes + m.mu.Unlock() + + lastNotified := time.Now().Add(-10 * time.Minute) // Notified 10 minutes ago + alert := &Alert{ + ID: "test-alert", + LastNotified: &lastNotified, + } + + if m.shouldNotifyAfterCooldown(alert) { + t.Error("expected false when still in cooldown period") + } + }) + + t.Run("notification allowed after cooldown expires", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = 30 // 30 minutes + m.mu.Unlock() + + lastNotified := time.Now().Add(-45 * time.Minute) // Notified 45 minutes ago + alert := &Alert{ + ID: "test-alert", + LastNotified: &lastNotified, + } + + if !m.shouldNotifyAfterCooldown(alert) { + t.Error("expected true after cooldown period expires") + } + }) + + t.Run("notification allowed at exact cooldown boundary", func(t *testing.T) { + t.Parallel() + m := NewManager() + m.mu.Lock() + m.config.Schedule.Cooldown = 30 // 30 minutes + m.mu.Unlock() + + lastNotified := time.Now().Add(-30 * time.Minute) // Exactly 30 minutes ago + alert := &Alert{ + ID: "test-alert", + LastNotified: &lastNotified, + } + + if !m.shouldNotifyAfterCooldown(alert) { + t.Error("expected true at exact cooldown boundary (>=)") + } + }) +}