mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-07-10 00:14:38 +00:00
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 in8372a22c5), 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 the8372a22c5failure mode is impossible by construction.
220 lines
7.4 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|