Pulse/internal/monitoring/monitor_conversion_helpers_additional_test.go
rcourtman 53faa4e46a fix(unifiedresources): stop fabricating sightings and online stamps for never-seen resources
Ingest stamped every resource's SourceStatus "online" and replaced a zero
LastSeen with time.Now(), so a source that never delivered a resource
(synthesized offline PVE placeholders, never-polled instances) reported a
fresh sighting and a healthy source forever: markStaleLocked skips zero
LastSeen entries, so the stamp was permanently exempt from stale-marking.
That fabricated freshness fed monitor previous-state reads (the
resurrection loop mitigated in 8372a22c5), the frontend lastSeen display,
the monitored-systems ledger, and AI/agent context.

- ingest/mergeInto/IngestResources/presentation coalesce: stamp per-source
  delivery status from the actual sighting ("unknown" when the source has
  never delivered, "online" otherwise) and preserve zero LastSeen instead
  of substituting ingest time.
- replaceRegistry: run MarkStale over the fully assembled registry. The
  IngestSnapshot stale pass ran before record sources (TrueNAS, VMware)
  were ingested, leaving them stamped "online" at any age.
- monitorLastSeenUnix: report 0 for never-seen instead of time.Now(), so
  the frontend renders a dash rather than "just now".
- connected infrastructure: clamp zero LastSeen to 0 ms instead of the
  negative UnixMilli of the zero time.

preserveOrExpireNodes keeps requiring a real online sighting for grace;
the registry no longer manufactures the timestamps that made that guard
necessary, so the 8372a22c5 failure mode is impossible by construction.
2026-06-11 10:23:49 +01:00

220 lines
7.4 KiB
Go

package monitoring
import (
"testing"
"time"
unifiedresources "github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
)
func ptrF64Coverage(v float64) *float64 { return &v }
func TestMonitorFrontendNamesAdditional(t *testing.T) {
t.Run("frontend names use canonical display name", func(t *testing.T) {
name, display := monitorFrontendNames(unifiedresources.Resource{
ID: "node-1",
Name: "Node Friendly",
Proxmox: &unifiedresources.ProxmoxData{
NodeName: "pve-node-1",
},
}, "node")
if name != "Node Friendly" {
t.Fatalf("name = %q, want %q", name, "Node Friendly")
}
if display != "Node Friendly" {
t.Fatalf("display = %q, want %q", display, "Node Friendly")
}
})
t.Run("frontend names ignore source hostnames when a display name exists", func(t *testing.T) {
hostName, hostDisplay := monitorFrontendNames(unifiedresources.Resource{
ID: "host-1",
Name: "Friendly Host",
Agent: &unifiedresources.AgentData{
Hostname: "agent-host",
},
}, "agent")
if hostName != "Friendly Host" || hostDisplay != "Friendly Host" {
t.Fatalf("agent branch mismatch: name=%q display=%q", hostName, hostDisplay)
}
dockerName, dockerDisplay := monitorFrontendNames(unifiedresources.Resource{
ID: "docker-1",
Name: "Friendly Docker",
Docker: &unifiedresources.DockerData{
Hostname: "docker-host",
},
}, "docker-host")
if dockerName != "Friendly Docker" || dockerDisplay != "Friendly Docker" {
t.Fatalf("docker branch mismatch: name=%q display=%q", dockerName, dockerDisplay)
}
})
t.Run("falls back to id when display name is empty", func(t *testing.T) {
name, display := monitorFrontendNames(unifiedresources.Resource{
ID: "fallback-id",
Name: " ",
}, "unknown")
if name != "fallback-id" {
t.Fatalf("name = %q, want fallback id", name)
}
if display != "fallback-id" {
t.Fatalf("display = %q, want fallback id", display)
}
})
}
func TestMonitorTemperatureAndUptimeAdditional(t *testing.T) {
t.Run("temperature follows expected source precedence", func(t *testing.T) {
resource := unifiedresources.Resource{
Agent: &unifiedresources.AgentData{Temperature: ptrF64Coverage(50)},
Proxmox: &unifiedresources.ProxmoxData{Temperature: ptrF64Coverage(45)},
Docker: &unifiedresources.DockerData{Temperature: ptrF64Coverage(40)},
Kubernetes: &unifiedresources.K8sData{Temperature: ptrF64Coverage(35)},
}
got := monitorTemperature(resource)
if got == nil || *got != 50 {
t.Fatalf("temperature = %v, want 50 from agent", got)
}
resource.Agent = nil
got = monitorTemperature(resource)
if got == nil || *got != 45 {
t.Fatalf("temperature = %v, want 45 from proxmox", got)
}
resource.Proxmox = nil
got = monitorTemperature(resource)
if got == nil || *got != 40 {
t.Fatalf("temperature = %v, want 40 from docker", got)
}
resource.Docker = nil
got = monitorTemperature(resource)
if got == nil || *got != 35 {
t.Fatalf("temperature = %v, want 35 from kubernetes", got)
}
resource.Kubernetes = nil
if got := monitorTemperature(resource); got != nil {
t.Fatalf("temperature = %v, want nil when no source has temp", got)
}
})
t.Run("uptime walks sources and ignores zero values", func(t *testing.T) {
resource := unifiedresources.Resource{
Agent: &unifiedresources.AgentData{UptimeSeconds: 0},
Proxmox: &unifiedresources.ProxmoxData{Uptime: 0},
Docker: &unifiedresources.DockerData{UptimeSeconds: 0},
Kubernetes: &unifiedresources.K8sData{UptimeSeconds: 0},
PBS: &unifiedresources.PBSData{UptimeSeconds: 11},
PMG: &unifiedresources.PMGData{UptimeSeconds: 12},
}
got := monitorUptime(resource)
if got == nil || *got != 11 {
t.Fatalf("uptime = %v, want 11 from PBS", got)
}
resource.PBS.UptimeSeconds = 0
got = monitorUptime(resource)
if got == nil || *got != 12 {
t.Fatalf("uptime = %v, want 12 from PMG", got)
}
resource.PMG.UptimeSeconds = 0
if got := monitorUptime(resource); got != nil {
t.Fatalf("uptime = %v, want nil when all sources are zero", got)
}
})
}
func TestMonitorIdentityLabelsSourceTypeAndLastSeenAdditional(t *testing.T) {
t.Run("labels are copied and nil is preserved", func(t *testing.T) {
if got := monitorLabels(unifiedresources.Resource{}); got != nil {
t.Fatalf("monitorLabels() = %#v, want nil", got)
}
resource := unifiedresources.Resource{
Kubernetes: &unifiedresources.K8sData{
Labels: map[string]string{"env": "prod"},
},
}
labels := monitorLabels(resource)
labels["env"] = "dev"
if resource.Kubernetes.Labels["env"] != "prod" {
t.Fatalf("expected labels to be copied, source mutated to %q", resource.Kubernetes.Labels["env"])
}
})
t.Run("identity resolution trims values and uses fallbacks", func(t *testing.T) {
resource := unifiedresources.Resource{
Identity: unifiedresources.ResourceIdentity{
MachineID: " machine-1 ",
Hostnames: []string{"", " host-from-identity "},
IPAddresses: []string{" 10.0.0.1 ", "", "10.0.0.2"},
},
}
identity := monitorIdentity(resource, "fallback-name")
if identity == nil {
t.Fatal("expected non-nil identity")
}
if identity.Hostname != "host-from-identity" {
t.Fatalf("hostname = %q, want host-from-identity", identity.Hostname)
}
if identity.MachineID != "machine-1" {
t.Fatalf("machineID = %q, want machine-1", identity.MachineID)
}
if len(identity.IPs) != 2 || identity.IPs[0] != "10.0.0.1" || identity.IPs[1] != "10.0.0.2" {
t.Fatalf("ips = %#v, want trimmed non-empty entries", identity.IPs)
}
none := monitorIdentity(unifiedresources.Resource{}, "")
if none != nil {
t.Fatalf("expected nil identity for empty input, got %#v", none)
}
})
t.Run("source type and last seen branches", func(t *testing.T) {
if got := monitorSourceType([]unifiedresources.DataSource{unifiedresources.SourceAgent, unifiedresources.SourceDocker}); got != "hybrid" {
t.Fatalf("monitorSourceType(multi) = %q, want hybrid", got)
}
if got := monitorSourceType([]unifiedresources.DataSource{unifiedresources.SourceK8s}); got != "agent" {
t.Fatalf("monitorSourceType(agent) = %q, want agent", got)
}
if got := monitorSourceType([]unifiedresources.DataSource{unifiedresources.SourcePBS}); got != "api" {
t.Fatalf("monitorSourceType(api) = %q, want api", got)
}
if got := monitorSourceType(nil); got != "api" {
t.Fatalf("monitorSourceType(nil) = %q, want api", got)
}
now := time.Now().UTC().Add(-time.Second).Truncate(time.Millisecond)
if got := monitorLastSeenUnix(now); got != now.UnixMilli() {
t.Fatalf("monitorLastSeenUnix(non-zero) = %d, want %d", got, now.UnixMilli())
}
// A zero LastSeen means the resource has never been sighted; the
// payload must not fabricate a fresh timestamp for it.
if got := monitorLastSeenUnix(time.Time{}); got != 0 {
t.Fatalf("monitorLastSeenUnix(zero) = %d, want 0", got)
}
})
t.Run("identity prefers direct source hostnames", func(t *testing.T) {
identity := monitorIdentity(unifiedresources.Resource{
Agent: &unifiedresources.AgentData{Hostname: "agent-host"},
Docker: &unifiedresources.DockerData{Hostname: "docker-host"},
Proxmox: &unifiedresources.ProxmoxData{NodeName: "proxmox-node"},
Identity: unifiedresources.ResourceIdentity{MachineID: "m"},
}, "fallback")
if identity == nil {
t.Fatal("expected non-nil identity")
}
if identity.Hostname != "agent-host" {
t.Fatalf("hostname = %q, want agent-host", identity.Hostname)
}
})
}