Complete alert event log governance proof

Bind monitor-side event-log bootstrap to its canonical boundary and exercise the new event infrastructure through the registry-recognized alerts, API, monitoring, and agent-lifecycle proof files.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot] 2026-08-26 21:32:10 +01:00
parent e88583866a
commit 18bf3e1a8d
4 changed files with 53 additions and 0 deletions

View file

@ -32,6 +32,11 @@ monitor restart.
Monitor construction also applies the persisted grouping enabled flag, window,
and node/guest keys as one notification-manager policy, so restart behavior is
identical to a live alert-configuration save.
Monitor construction enables the alerts-owned persistent event log once for
each tenant alert manager. This is bootstrap wiring only: monitoring does not
own event types, retention, query semantics, or lifecycle/notification truth,
and an event-store startup or append failure must not interrupt polling, state
assembly, alert evaluation, or notification delivery.
Monitoring also owns the distinction between Proxmox VM power state and QEMU
guest-agent reachability: fresh or never-healthy VMs with an enabled but
unavailable guest agent stay `not-running`, while only VMs with recent healthy

View file

@ -12,6 +12,7 @@ import (
"testing"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/alerts/eventlog"
alertspecs "github.com/rcourtman/pulse-go-rewrite/internal/alerts/specs"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/rcourtman/pulse-go-rewrite/internal/proxmoxidentity"
@ -31,6 +32,23 @@ func ptrTime(t time.Time) *time.Time {
return &tt
}
func TestAlertEventLogDoesNotInventFiringFromActiveStorage(t *testing.T) {
manager := newEventLogManager(t)
addEventLogAlert(manager, &Alert{
ID: "restored-alert",
Type: "cpu",
Level: AlertLevelWarning,
ResourceID: "node-1",
ResourceName: "node-1",
StartTime: time.Now().Add(-10 * time.Minute),
})
events := queryAlertEvents(t, manager, eventlog.Filter{AlertID: "restored-alert"})
if len(events) != 0 {
t.Fatalf("active-storage insertion recorded events = %+v, want none", events)
}
}
func TestGuestAlertIncludesTagsForNotificationRouting(t *testing.T) {
m := newTestManager(t)
m.ClearActiveAlerts()

View file

@ -8,6 +8,7 @@ import (
"testing"
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
"github.com/rcourtman/pulse-go-rewrite/internal/alerts/eventlog"
)
func TestAlertsEndpoints(t *testing.T) {
@ -285,6 +286,26 @@ func TestAlertsEndpoints(t *testing.T) {
}
})
t.Run("GetAlertEvents", func(t *testing.T) {
res, err := http.Get(srv.server.URL + "/api/alerts/events")
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
t.Fatalf("status code = %d, want %d", res.StatusCode, http.StatusOK)
}
var events []eventlog.Event
if err := json.NewDecoder(res.Body).Decode(&events); err != nil {
t.Fatalf("decode alert events: %v", err)
}
if events == nil {
t.Fatal("alert events decoded as nil, want an empty JSON array")
}
})
// 5. Get alert history
t.Run("GetAlertHistory", func(t *testing.T) {
res, err := http.Get(srv.server.URL + "/api/alerts/history")

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/alerts"
"github.com/rcourtman/pulse-go-rewrite/internal/alerts/eventlog"
"github.com/rcourtman/pulse-go-rewrite/internal/config"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
@ -16,6 +17,10 @@ import (
func newHostRemovalLifecycleMonitor(t *testing.T, dataPath string) *Monitor {
t.Helper()
events, err := eventlog.OpenInMemory()
if err != nil {
t.Fatalf("open alert event log: %v", err)
}
monitor := &Monitor{
state: models.NewState(),
alertManager: alerts.NewManagerWithDataDir(dataPath),
@ -25,6 +30,10 @@ func newHostRemovalLifecycleMonitor(t *testing.T, dataPath string) *Monitor {
config: &config.Config{DataPath: dataPath},
hostContinuityStore: config.NewHostContinuityStore(dataPath, nil),
}
// Production monitor construction enables the alerts-owned event log.
// Exercise agent removal and re-enrollment with the same adjacent runtime
// enabled so it cannot accidentally become lifecycle authority.
monitor.alertManager.SetEventLog(events)
t.Cleanup(func() { monitor.alertManager.Stop() })
return monitor
}