diff --git a/internal/monitoring/monitor_alert_handling_test.go b/internal/monitoring/monitor_alert_handling_test.go index 9d85fedba4..498bd35b8d 100644 --- a/internal/monitoring/monitor_alert_handling_test.go +++ b/internal/monitoring/monitor_alert_handling_test.go @@ -1466,39 +1466,3 @@ func TestMonitorDelayedPartialResolutionKeepsDestinationRecovery(t *testing.T) { t.Fatal("unannounced destination received a recovery") } } - -func TestSyncAlertsToStateCarriesLiveMetricStatusOfHeldAlert(t *testing.T) { - m := &Monitor{ - state: models.NewState(), - alertManager: alerts.NewManagerWithDataDir(t.TempDir()), - } - defer m.alertManager.Stop() - - cfg := m.alertManager.GetConfig() - cfg.TimeThresholds = map[string]int{"node": 0} - cfg.NodeDefaults.Temperature = &alerts.HysteresisThreshold{Trigger: 80, Clear: 75} - m.alertManager.UpdateConfig(cfg) - - node := models.Node{ - ID: "homelab-minipc", Name: "minipc", Instance: "homelab", Status: "online", - Temperature: &models.Temperature{Available: true, CPUPackage: 85}, - } - m.alertManager.CheckNode(node) - node.Temperature = &models.Temperature{Available: true, CPUPackage: 78} - m.alertManager.CheckNode(node) - - m.syncAlertsToState() - - var held *models.Alert - for i, alert := range m.state.GetSnapshot().ActiveAlerts { - if alert.Type == "temperature" { - held = &m.state.GetSnapshot().ActiveAlerts[i] - } - } - require.NotNil(t, held, "expected the temperature alert to stay open at 78°C") - require.Equal(t, 85.0, held.Value, "the legacy value keeps the last breach") - require.NotNil(t, held.MetricStatus, "websocket state must carry the live reading") - require.Equal(t, models.MetricAlertPhaseLatched, held.MetricStatus.Phase) - require.Equal(t, 78.0, held.MetricStatus.Value) - require.Equal(t, 75.0, held.MetricStatus.Recovery) -} diff --git a/internal/monitoring/monitor_alert_intent_test.go b/internal/monitoring/monitor_alert_intent_test.go index 6832ec92ce..1b3c0485e2 100644 --- a/internal/monitoring/monitor_alert_intent_test.go +++ b/internal/monitoring/monitor_alert_intent_test.go @@ -258,3 +258,47 @@ func TestResolveBackupIntentContextAcceptsSynthesizedJobGuestTask(t *testing.T) t.Fatalf("guest whose job section already finished should not carry intent: %+v", got) } } + +func TestSyncAlertsToStateCarriesLiveMetricStatusOfHeldAlert(t *testing.T) { + m := &Monitor{ + state: models.NewState(), + alertManager: alerts.NewManagerWithDataDir(t.TempDir()), + } + defer m.alertManager.Stop() + + cfg := m.alertManager.GetConfig() + cfg.TimeThresholds = map[string]int{"node": 0} + cfg.NodeDefaults.Temperature = &alerts.HysteresisThreshold{Trigger: 80, Clear: 75} + m.alertManager.UpdateConfig(cfg) + + node := models.Node{ + ID: "homelab-minipc", Name: "minipc", Instance: "homelab", Status: "online", + Temperature: &models.Temperature{Available: true, CPUPackage: 85}, + } + m.alertManager.CheckNode(node) + node.Temperature = &models.Temperature{Available: true, CPUPackage: 78} + m.alertManager.CheckNode(node) + + m.syncAlertsToState() + + var held *models.Alert + snapshot := m.state.GetSnapshot() + for i := range snapshot.ActiveAlerts { + if snapshot.ActiveAlerts[i].Type == "temperature" { + held = &snapshot.ActiveAlerts[i] + } + } + if held == nil { + t.Fatal("expected the temperature alert to stay open at 78°C") + } + if held.Value != 85 { + t.Fatalf("legacy value = %v, want the last breach 85", held.Value) + } + status := held.MetricStatus + if status == nil { + t.Fatal("websocket state must carry the live reading") + } + if status.Phase != models.MetricAlertPhaseLatched || status.Value != 78 || status.Recovery != 75 { + t.Fatalf("live status = %+v, want latched at 78 clearing at 75", status) + } +}