style: Apply gofmt to 37 files

Standardize code formatting across test files and monitor.go.
No functional changes.
This commit is contained in:
rcourtman 2025-12-02 17:21:48 +00:00
parent ec6133afd8
commit 72af7bed5e
37 changed files with 251 additions and 253 deletions

View file

@ -45,8 +45,8 @@ other_key: value`,
expected: []string{"node1", "node2", "node3"}, expected: []string{"node1", "node2", "node3"},
}, },
{ {
name: "list format single item", name: "list format single item",
input: `- single-node`, input: `- single-node`,
expected: []string{"single-node"}, expected: []string{"single-node"},
}, },
@ -94,8 +94,8 @@ allowed_nodes:
expected: []string{"node1", "node2"}, expected: []string{"node1", "node2"},
}, },
{ {
name: "map format allowed_nodes not a list", name: "map format allowed_nodes not a list",
input: `allowed_nodes: not-a-list`, input: `allowed_nodes: not-a-list`,
expected: nil, expected: nil,
}, },

View file

@ -8399,7 +8399,7 @@ func TestCleanup(t *testing.T) {
m.mu.Lock() m.mu.Lock()
m.alertRateLimit["rate-limited"] = []time.Time{ m.alertRateLimit["rate-limited"] = []time.Time{
time.Now().Add(-2 * time.Hour), // Old, should be removed time.Now().Add(-2 * time.Hour), // Old, should be removed
time.Now().Add(-30 * time.Minute), // Recent, should remain time.Now().Add(-30 * time.Minute), // Recent, should remain
} }
m.mu.Unlock() m.mu.Unlock()
@ -14443,9 +14443,9 @@ func TestCheckPMGQuarantineBacklog(t *testing.T) {
} }
thresholds := PMGThresholdConfig{ thresholds := PMGThresholdConfig{
QuarantineSpamWarn: 2000, QuarantineSpamWarn: 2000,
QuarantineSpamCritical: 5000, QuarantineSpamCritical: 5000,
QuarantineVirusWarn: 2000, QuarantineVirusWarn: 2000,
QuarantineVirusCritical: 5000, QuarantineVirusCritical: 5000,
} }
@ -14482,9 +14482,9 @@ func TestCheckPMGQuarantineBacklog(t *testing.T) {
} }
thresholds := PMGThresholdConfig{ thresholds := PMGThresholdConfig{
QuarantineSpamWarn: 2000, QuarantineSpamWarn: 2000,
QuarantineSpamCritical: 5000, QuarantineSpamCritical: 5000,
QuarantineVirusWarn: 2000, QuarantineVirusWarn: 2000,
QuarantineVirusCritical: 5000, QuarantineVirusCritical: 5000,
} }

View file

@ -11,17 +11,17 @@ func TestEvaluateVMCondition(t *testing.T) {
m := NewManager() m := NewManager()
testVM := models.VM{ testVM := models.VM{
ID: "test-vm-1", ID: "test-vm-1",
VMID: 100, VMID: 100,
Name: "test-vm", Name: "test-vm",
Node: "node1", Node: "node1",
Status: "running", Status: "running",
CPU: 0.50, // 50% CPU: 0.50, // 50%
Memory: models.Memory{Usage: 75.0}, Memory: models.Memory{Usage: 75.0},
Disk: models.Disk{Usage: 60.0}, Disk: models.Disk{Usage: 60.0},
DiskRead: 100 * 1024 * 1024, // 100 MB/s in bytes DiskRead: 100 * 1024 * 1024, // 100 MB/s in bytes
DiskWrite: 50 * 1024 * 1024, // 50 MB/s in bytes DiskWrite: 50 * 1024 * 1024, // 50 MB/s in bytes
NetworkIn: 200 * 1024 * 1024, // 200 MB/s in bytes NetworkIn: 200 * 1024 * 1024, // 200 MB/s in bytes
NetworkOut: 150 * 1024 * 1024, // 150 MB/s in bytes NetworkOut: 150 * 1024 * 1024, // 150 MB/s in bytes
} }
@ -958,12 +958,12 @@ func TestMetricOperators(t *testing.T) {
} }
tests := []struct { tests := []struct {
name string name string
field string field string
operator string operator string
value float64 value float64
vmValue float64 // expected processed value vmValue float64 // expected processed value
want bool want bool
}{ }{
// Greater than // Greater than
{"GT true", "cpu", ">", 40.0, 50.0, true}, {"GT true", "cpu", ">", 40.0, 50.0, true},

View file

@ -311,8 +311,8 @@ func TestCleanOldEntries(t *testing.T) {
now := time.Now() now := time.Now()
hm.history = []HistoryEntry{ hm.history = []HistoryEntry{
{Alert: Alert{ID: "old1"}, Timestamp: now.AddDate(0, 0, -40)}, // 40 days old - should be removed {Alert: Alert{ID: "old1"}, Timestamp: now.AddDate(0, 0, -40)}, // 40 days old - should be removed
{Alert: Alert{ID: "old2"}, Timestamp: now.AddDate(0, 0, -35)}, // 35 days old - should be removed {Alert: Alert{ID: "old2"}, Timestamp: now.AddDate(0, 0, -35)}, // 35 days old - should be removed
{Alert: Alert{ID: "recent1"}, Timestamp: now.AddDate(0, 0, -25)}, // 25 days old - should stay {Alert: Alert{ID: "recent1"}, Timestamp: now.AddDate(0, 0, -25)}, // 25 days old - should stay
{Alert: Alert{ID: "recent2"}, Timestamp: now.AddDate(0, 0, -1)}, // 1 day old - should stay {Alert: Alert{ID: "recent2"}, Timestamp: now.AddDate(0, 0, -1)}, // 1 day old - should stay
} }

View file

@ -1636,7 +1636,7 @@ func TestEnsureValidHysteresis(t *testing.T) {
threshold: &HysteresisThreshold{Trigger: 0, Clear: 0}, threshold: &HysteresisThreshold{Trigger: 0, Clear: 0},
metricName: "disabled", metricName: "disabled",
wantTrigger: 0, wantTrigger: 0,
wantClear: 0, // 0 - 5 = -5, clamped to 0 (same value, but fix attempted) wantClear: 0, // 0 - 5 = -5, clamped to 0 (same value, but fix attempted)
expectChange: false, // Result same as input, even though fix was attempted expectChange: false, // Result same as input, even though fix was attempted
}, },
{ {

View file

@ -104,10 +104,10 @@ func TestDetectProxy(t *testing.T) {
func TestIsConnectionSecure(t *testing.T) { func TestIsConnectionSecure(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
useTLS bool useTLS bool
headers map[string]string headers map[string]string
want bool want bool
}{ }{
{ {
name: "plain HTTP no headers", name: "plain HTTP no headers",
@ -158,7 +158,7 @@ func TestIsConnectionSecure(t *testing.T) {
want: true, want: true,
}, },
{ {
name: "both X-Forwarded-Proto and Forwarded present", name: "both X-Forwarded-Proto and Forwarded present",
useTLS: false, useTLS: false,
headers: map[string]string{ headers: map[string]string{
"X-Forwarded-Proto": "https", "X-Forwarded-Proto": "https",

View file

@ -106,9 +106,9 @@ func TestExtractHostAndPort(t *testing.T) {
wantPort: "8006", wantPort: "8006",
}, },
{ {
name: "IPv6 with brackets no port returns error", name: "IPv6 with brackets no port returns error",
hostStr: "[2001:db8::1]", hostStr: "[2001:db8::1]",
wantErr: true, // net.SplitHostPort fails on bracketed IPv6 without port wantErr: true, // net.SplitHostPort fails on bracketed IPv6 without port
}, },
{ {
name: "IPv6 with scheme brackets and port", name: "IPv6 with scheme brackets and port",
@ -171,14 +171,14 @@ func TestExtractHostAndPort(t *testing.T) {
// Error cases // Error cases
{ {
name: "malformed brackets", name: "malformed brackets",
hostStr: "[2001:db8::1:8006", hostStr: "[2001:db8::1:8006",
wantErr: true, wantErr: true,
}, },
{ {
name: "brackets without closing with port attempt", name: "brackets without closing with port attempt",
hostStr: "[invalid:8006", hostStr: "[invalid:8006",
wantErr: true, wantErr: true,
}, },
} }
@ -307,8 +307,8 @@ func TestDefaultPortForNodeType(t *testing.T) {
{"docker node returns empty", "docker", ""}, {"docker node returns empty", "docker", ""},
{"unknown type returns empty", "unknown", ""}, {"unknown type returns empty", "unknown", ""},
{"empty type returns empty", "", ""}, {"empty type returns empty", "", ""},
{"uppercase PVE returns empty", "PVE", ""}, // case sensitive {"uppercase PVE returns empty", "PVE", ""}, // case sensitive
{"mixed case returns empty", "Pve", ""}, // case sensitive {"mixed case returns empty", "Pve", ""}, // case sensitive
{"similar but wrong returns empty", "pvee", ""}, // exact match only {"similar but wrong returns empty", "pvee", ""}, // exact match only
} }

View file

@ -145,10 +145,10 @@ func TestNormalizePVEUser(t *testing.T) {
func TestShouldSkipClusterAutoDetection(t *testing.T) { func TestShouldSkipClusterAutoDetection(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
host string host string
vmName string vmName string
want bool want bool
}{ }{
// Empty host cases // Empty host cases
{ {

View file

@ -418,10 +418,10 @@ func TestMatchInstanceNameByHost(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
cfg *config.Config cfg *config.Config
host string host string
expected string expected string
}{ }{
{ {
name: "nil config", name: "nil config",

View file

@ -315,11 +315,11 @@ func TestGetRateLimiterForEndpoint_PriorityOrder(t *testing.T) {
InitializeRateLimiters() InitializeRateLimiters()
tests := []struct { tests := []struct {
name string name string
path string path string
method string method string
wantLimiterNm string wantLimiterNm string
reason string reason string
}{ }{
{ {
name: "recovery takes priority over security status", name: "recovery takes priority over security status",

View file

@ -912,12 +912,12 @@ func TestResolvePublicURL_ConfiguredPublicURL(t *testing.T) {
func TestResolvePublicURL_FromRequest(t *testing.T) { func TestResolvePublicURL_FromRequest(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
host string host string
useTLS bool useTLS bool
xForwardProto string xForwardProto string
frontendPort int frontendPort int
want string want string
}{ }{
{ {
name: "HTTP request with host header", name: "HTTP request with host header",

View file

@ -7,11 +7,11 @@ import (
func TestGetClientIP(t *testing.T) { func TestGetClientIP(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
xff string // X-Forwarded-For header xff string // X-Forwarded-For header
xri string // X-Real-IP header xri string // X-Real-IP header
remoteAddr string // Request.RemoteAddr remoteAddr string // Request.RemoteAddr
expectedIP string expectedIP string
}{ }{
// X-Forwarded-For takes priority // X-Forwarded-For takes priority
{ {

View file

@ -82,16 +82,16 @@ func TestCreateProxmoxConfigFromFields(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
host string host string
user string user string
password string password string
tokenName string tokenName string
tokenValue string tokenValue string
fingerprint string fingerprint string
verifySSL bool verifySSL bool
expectedUser string expectedUser string
expectedHost string expectedHost string
}{ }{
{ {
name: "user without realm gets @pam appended (password auth)", name: "user without realm gets @pam appended (password auth)",

View file

@ -187,8 +187,8 @@ func TestCalculateCPUPercent(t *testing.T) {
stats: containertypes.StatsResponse{ stats: containertypes.StatsResponse{
CPUStats: containertypes.CPUStats{ CPUStats: containertypes.CPUStats{
CPUUsage: containertypes.CPUUsage{ CPUUsage: containertypes.CPUUsage{
TotalUsage: 200000000, TotalUsage: 200000000,
PercpuUsage: []uint64{1, 2, 3, 4}, // 4 CPUs PercpuUsage: []uint64{1, 2, 3, 4}, // 4 CPUs
}, },
SystemUsage: 2000000000, SystemUsage: 2000000000,
OnlineCPUs: 0, OnlineCPUs: 0,
@ -1018,10 +1018,10 @@ func TestDetermineSelfUpdateArch(t *testing.T) {
// Should be one of the known values or empty (if architecture unknown) // Should be one of the known values or empty (if architecture unknown)
validArches := map[string]bool{ validArches := map[string]bool{
"": true, "": true,
"linux-amd64": true, "linux-amd64": true,
"linux-arm64": true, "linux-arm64": true,
"linux-armv7": true, "linux-armv7": true,
} }
if !validArches[got] { if !validArches[got] {

View file

@ -71,4 +71,3 @@ func TestNormalisePlatform(t *testing.T) {
}) })
} }
} }

View file

@ -72,8 +72,8 @@ func TestUpsertDockerHost(t *testing.T) {
// Test PendingUninstall flag preservation // Test PendingUninstall flag preservation
state.SetDockerHostPendingUninstall("host-1", true) state.SetDockerHostPendingUninstall("host-1", true)
host1Reset2 := DockerHost{ host1Reset2 := DockerHost{
ID: "host-1", ID: "host-1",
Hostname: "docker-host-1", Hostname: "docker-host-1",
PendingUninstall: false, PendingUninstall: false,
} }
state.UpsertDockerHost(host1Reset2) state.UpsertDockerHost(host1Reset2)

View file

@ -278,7 +278,7 @@ func TestParseContainerConfigNetworks(t *testing.T) {
{ {
name: "no net keys", name: "no net keys",
input: map[string]interface{}{ input: map[string]interface{}{
"cores": 2, "cores": 2,
"memory": 2048, "memory": 2048,
}, },
want: nil, want: nil,

View file

@ -782,14 +782,14 @@ func TestResolveDockerHostIdentifier(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
name string name string
report agentsdocker.Report report agentsdocker.Report
tokenRecord *config.APITokenRecord tokenRecord *config.APITokenRecord
hosts []models.DockerHost hosts []models.DockerHost
expectMatch bool expectMatch bool
expectFallbacks int expectFallbacks int
expectedID string expectedID string
checkIDFormat bool checkIDFormat bool
}{ }{
{ {
name: "existing host match", name: "existing host match",

View file

@ -816,7 +816,7 @@ func TestTryReserveGuestMetadataFetch(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataHoldDuration: 15 * time.Second, guestMetadataHoldDuration: 15 * time.Second,
} }
now := time.Now() now := time.Now()
@ -885,7 +885,7 @@ func TestTryReserveGuestMetadataFetch(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataHoldDuration: 0, // zero triggers default guestMetadataHoldDuration: 0, // zero triggers default
} }
now := time.Now() now := time.Now()
@ -905,7 +905,7 @@ func TestTryReserveGuestMetadataFetch(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataHoldDuration: -5 * time.Second, // negative triggers default guestMetadataHoldDuration: -5 * time.Second, // negative triggers default
} }
now := time.Now() now := time.Now()
@ -1016,10 +1016,10 @@ func TestScheduleNextGuestMetadataFetch(t *testing.T) {
rng := newDeterministicRng(42) rng := newDeterministicRng(42)
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataMinRefresh: 1 * time.Minute, guestMetadataMinRefresh: 1 * time.Minute,
guestMetadataRefreshJitter: 30 * time.Second, guestMetadataRefreshJitter: 30 * time.Second,
rng: rng, rng: rng,
} }
now := time.Now() now := time.Now()
@ -1038,10 +1038,10 @@ func TestScheduleNextGuestMetadataFetch(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataMinRefresh: 1 * time.Minute, guestMetadataMinRefresh: 1 * time.Minute,
guestMetadataRefreshJitter: 30 * time.Second, // jitter configured but rng is nil guestMetadataRefreshJitter: 30 * time.Second, // jitter configured but rng is nil
rng: nil, rng: nil,
} }
now := time.Now() now := time.Now()
@ -1060,10 +1060,10 @@ func TestScheduleNextGuestMetadataFetch(t *testing.T) {
rng := newDeterministicRng(42) rng := newDeterministicRng(42)
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataMinRefresh: 1 * time.Minute, guestMetadataMinRefresh: 1 * time.Minute,
guestMetadataRefreshJitter: 0, // zero jitter guestMetadataRefreshJitter: 0, // zero jitter
rng: rng, rng: rng,
} }
now := time.Now() now := time.Now()
@ -1082,10 +1082,10 @@ func TestScheduleNextGuestMetadataFetch(t *testing.T) {
rng := newDeterministicRng(42) rng := newDeterministicRng(42)
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataMinRefresh: 1 * time.Minute, guestMetadataMinRefresh: 1 * time.Minute,
guestMetadataRefreshJitter: -10 * time.Second, // negative jitter guestMetadataRefreshJitter: -10 * time.Second, // negative jitter
rng: rng, rng: rng,
} }
now := time.Now() now := time.Now()
@ -1135,7 +1135,7 @@ func TestDeferGuestMetadataRetry(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataRetryBackoff: 0, // zero triggers default guestMetadataRetryBackoff: 0, // zero triggers default
} }
now := time.Now() now := time.Now()
@ -1157,7 +1157,7 @@ func TestDeferGuestMetadataRetry(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataRetryBackoff: -10 * time.Second, // negative triggers default guestMetadataRetryBackoff: -10 * time.Second, // negative triggers default
} }
now := time.Now() now := time.Now()
@ -1175,7 +1175,7 @@ func TestDeferGuestMetadataRetry(t *testing.T) {
t.Parallel() t.Parallel()
m := &Monitor{ m := &Monitor{
guestMetadataLimiter: make(map[string]time.Time), guestMetadataLimiter: make(map[string]time.Time),
guestMetadataRetryBackoff: 45 * time.Second, guestMetadataRetryBackoff: 45 * time.Second,
} }
now := time.Now() now := time.Now()

View file

@ -27,21 +27,21 @@ func TestMergeNVMeTempsIntoDisks(t *testing.T) {
expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
}, },
{ {
name: "nil temperature returns disks unchanged", name: "nil temperature returns disks unchanged",
disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
nodes: []models.Node{{Name: "node1", Temperature: nil}}, nodes: []models.Node{{Name: "node1", Temperature: nil}},
expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
}, },
{ {
name: "temperature available false returns disks unchanged", name: "temperature available false returns disks unchanged",
disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
nodes: []models.Node{{Name: "node1", Temperature: &models.Temperature{Available: false}}}, nodes: []models.Node{{Name: "node1", Temperature: &models.Temperature{Available: false}}},
expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
}, },
{ {
name: "no SMART or NVMe temps returns disks unchanged", name: "no SMART or NVMe temps returns disks unchanged",
disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, disks: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
nodes: []models.Node{{Name: "node1", Temperature: &models.Temperature{Available: true, SMART: nil, NVMe: nil}}}, nodes: []models.Node{{Name: "node1", Temperature: &models.Temperature{Available: true, SMART: nil, NVMe: nil}}},
expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}}, expected: []models.PhysicalDisk{{Node: "node1", DevPath: "/dev/sda", Temperature: 0}},
}, },
{ {

View file

@ -113,7 +113,7 @@ func TestAppendMetric(t *testing.T) {
maxDataPoints: 100, maxDataPoints: 100,
retentionTime: 30 * time.Minute, retentionTime: 30 * time.Minute,
existing: []MetricPoint{ existing: []MetricPoint{
{Value: 10.0, Timestamp: now.Add(-2 * time.Hour)}, // beyond retention {Value: 10.0, Timestamp: now.Add(-2 * time.Hour)}, // beyond retention
{Value: 20.0, Timestamp: now.Add(-90 * time.Minute)}, // beyond retention {Value: 20.0, Timestamp: now.Add(-90 * time.Minute)}, // beyond retention
{Value: 30.0, Timestamp: now.Add(-20 * time.Minute)}, // within retention {Value: 30.0, Timestamp: now.Add(-20 * time.Minute)}, // within retention
}, },
@ -1111,12 +1111,12 @@ func TestRetentionTimeEnforced(t *testing.T) {
mh := NewMetricsHistory(100, retentionTime) mh := NewMetricsHistory(100, retentionTime)
// Add points at various times // Add points at various times
mh.AddGuestMetric("vm-100", "cpu", 10.0, now.Add(-2*time.Hour)) // beyond retention mh.AddGuestMetric("vm-100", "cpu", 10.0, now.Add(-2*time.Hour)) // beyond retention
mh.AddGuestMetric("vm-100", "cpu", 20.0, now.Add(-1*time.Hour)) // beyond retention mh.AddGuestMetric("vm-100", "cpu", 20.0, now.Add(-1*time.Hour)) // beyond retention
mh.AddGuestMetric("vm-100", "cpu", 30.0, now.Add(-45*time.Minute)) // beyond retention mh.AddGuestMetric("vm-100", "cpu", 30.0, now.Add(-45*time.Minute)) // beyond retention
mh.AddGuestMetric("vm-100", "cpu", 40.0, now.Add(-20*time.Minute)) // within retention mh.AddGuestMetric("vm-100", "cpu", 40.0, now.Add(-20*time.Minute)) // within retention
mh.AddGuestMetric("vm-100", "cpu", 50.0, now.Add(-10*time.Minute)) // within retention mh.AddGuestMetric("vm-100", "cpu", 50.0, now.Add(-10*time.Minute)) // within retention
mh.AddGuestMetric("vm-100", "cpu", 60.0, now) // within retention mh.AddGuestMetric("vm-100", "cpu", 60.0, now) // within retention
// appendMetric removes old points on each add // appendMetric removes old points on each add
result := mh.GetGuestMetrics("vm-100", "cpu", time.Hour) result := mh.GetGuestMetrics("vm-100", "cpu", time.Hour)

View file

@ -792,8 +792,8 @@ func TestRecordResult_LabelsSanitized(t *testing.T) {
pm := newFullTestPollMetrics(t) pm := newFullTestPollMetrics(t)
pm.RecordResult(PollResult{ pm.RecordResult(PollResult{
InstanceType: "", // Should become "unknown" InstanceType: "", // Should become "unknown"
InstanceName: " ", // Should become "unknown" InstanceName: " ", // Should become "unknown"
StartTime: time.Now().Add(-time.Second), StartTime: time.Now().Add(-time.Second),
EndTime: time.Now(), EndTime: time.Now(),
Success: true, Success: true,

View file

@ -875,8 +875,8 @@ const (
hostMinimumHealthWindow = 30 * time.Second hostMinimumHealthWindow = 30 * time.Second
hostMaximumHealthWindow = 10 * time.Minute hostMaximumHealthWindow = 10 * time.Minute
nodeOfflineGracePeriod = 60 * time.Second // Grace period before marking Proxmox nodes offline nodeOfflineGracePeriod = 60 * time.Second // Grace period before marking Proxmox nodes offline
nodeRRDCacheTTL = 30 * time.Second nodeRRDCacheTTL = 30 * time.Second
nodeRRDRequestTimeout = 2 * time.Second nodeRRDRequestTimeout = 2 * time.Second
) )
type taskOutcome struct { type taskOutcome struct {

View file

@ -96,8 +96,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "cycle-based: pollCounter=1 returns true", name: "cycle-based: pollCounter=1 returns true",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: 10, BackupPollingCycles: 10,
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 1, pollCounter: 1,
@ -112,8 +112,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "cycle-based: pollCounter divisible by cycles returns true", name: "cycle-based: pollCounter divisible by cycles returns true",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: 5, BackupPollingCycles: 5,
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 15, // 15 % 5 == 0 pollCounter: 15, // 15 % 5 == 0
@ -128,8 +128,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "cycle-based: pollCounter not divisible returns false", name: "cycle-based: pollCounter not divisible returns false",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: 5, BackupPollingCycles: 5,
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 7, // 7 % 5 == 2, remaining = 3 pollCounter: 7, // 7 % 5 == 2, remaining = 3
@ -144,8 +144,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "default cycles (10) when BackupPollingCycles is 0", name: "default cycles (10) when BackupPollingCycles is 0",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: 0, // should default to 10 BackupPollingCycles: 0, // should default to 10
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 10, // 10 % 10 == 0 pollCounter: 10, // 10 % 10 == 0
@ -160,8 +160,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "default cycles (10) when BackupPollingCycles is negative", name: "default cycles (10) when BackupPollingCycles is negative",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: -5, // should default to 10 BackupPollingCycles: -5, // should default to 10
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 20, // 20 % 10 == 0 pollCounter: 20, // 20 % 10 == 0
@ -176,8 +176,8 @@ func TestShouldRunBackupPoll(t *testing.T) {
name: "default cycles (10) not divisible returns false with correct remaining", name: "default cycles (10) not divisible returns false with correct remaining",
monitor: &Monitor{ monitor: &Monitor{
config: &config.Config{ config: &config.Config{
EnableBackupPolling: true, EnableBackupPolling: true,
BackupPollingCycles: 0, // defaults to 10 BackupPollingCycles: 0, // defaults to 10
BackupPollingInterval: 0, BackupPollingInterval: 0,
}, },
pollCounter: 3, // 3 % 10 == 3, remaining = 7 pollCounter: 3, // 3 % 10 == 3, remaining = 7

View file

@ -96,9 +96,9 @@ func TestRecordTaskResult_NilMonitor(t *testing.T) {
func TestRecordTaskResult_Success(t *testing.T) { func TestRecordTaskResult_Success(t *testing.T) {
m := &Monitor{ m := &Monitor{
pollStatusMap: make(map[string]*pollStatus), pollStatusMap: make(map[string]*pollStatus),
failureCounts: make(map[string]int), failureCounts: make(map[string]int),
lastOutcome: make(map[string]taskOutcome), lastOutcome: make(map[string]taskOutcome),
circuitBreakers: make(map[string]*circuitBreaker), circuitBreakers: make(map[string]*circuitBreaker),
} }
@ -133,9 +133,9 @@ func TestRecordTaskResult_Success(t *testing.T) {
func TestRecordTaskResult_Failure(t *testing.T) { func TestRecordTaskResult_Failure(t *testing.T) {
m := &Monitor{ m := &Monitor{
pollStatusMap: make(map[string]*pollStatus), pollStatusMap: make(map[string]*pollStatus),
failureCounts: make(map[string]int), failureCounts: make(map[string]int),
lastOutcome: make(map[string]taskOutcome), lastOutcome: make(map[string]taskOutcome),
circuitBreakers: make(map[string]*circuitBreaker), circuitBreakers: make(map[string]*circuitBreaker),
} }
@ -178,9 +178,9 @@ func TestRecordTaskResult_Failure(t *testing.T) {
func TestRecordTaskResult_ConsecutiveFailures(t *testing.T) { func TestRecordTaskResult_ConsecutiveFailures(t *testing.T) {
m := &Monitor{ m := &Monitor{
pollStatusMap: make(map[string]*pollStatus), pollStatusMap: make(map[string]*pollStatus),
failureCounts: make(map[string]int), failureCounts: make(map[string]int),
lastOutcome: make(map[string]taskOutcome), lastOutcome: make(map[string]taskOutcome),
circuitBreakers: make(map[string]*circuitBreaker), circuitBreakers: make(map[string]*circuitBreaker),
} }
@ -207,9 +207,9 @@ func TestRecordTaskResult_ConsecutiveFailures(t *testing.T) {
func TestRecordTaskResult_SuccessResetsFailures(t *testing.T) { func TestRecordTaskResult_SuccessResetsFailures(t *testing.T) {
m := &Monitor{ m := &Monitor{
pollStatusMap: make(map[string]*pollStatus), pollStatusMap: make(map[string]*pollStatus),
failureCounts: make(map[string]int), failureCounts: make(map[string]int),
lastOutcome: make(map[string]taskOutcome), lastOutcome: make(map[string]taskOutcome),
circuitBreakers: make(map[string]*circuitBreaker), circuitBreakers: make(map[string]*circuitBreaker),
} }
@ -242,9 +242,9 @@ func TestRecordTaskResult_SuccessResetsFailures(t *testing.T) {
func TestRecordTaskResult_NilMaps(t *testing.T) { func TestRecordTaskResult_NilMaps(t *testing.T) {
// Monitor with nil internal maps - should not panic // Monitor with nil internal maps - should not panic
m := &Monitor{ m := &Monitor{
pollStatusMap: make(map[string]*pollStatus), pollStatusMap: make(map[string]*pollStatus),
failureCounts: nil, // nil failureCounts: nil, // nil
lastOutcome: nil, // nil lastOutcome: nil, // nil
circuitBreakers: make(map[string]*circuitBreaker), circuitBreakers: make(map[string]*circuitBreaker),
} }
@ -711,10 +711,10 @@ func TestRescheduleTask_PMGInstance(t *testing.T) {
func TestRescheduleTask_AdaptivePollingMaxIntervalLimit(t *testing.T) { func TestRescheduleTask_AdaptivePollingMaxIntervalLimit(t *testing.T) {
cfg := &config.Config{ cfg := &config.Config{
PVEPollingInterval: 30 * time.Second, PVEPollingInterval: 30 * time.Second,
AdaptivePollingEnabled: true, AdaptivePollingEnabled: true,
AdaptivePollingMaxInterval: 10 * time.Second, // <= 15s enables capping AdaptivePollingMaxInterval: 10 * time.Second, // <= 15s enables capping
AdaptivePollingBaseInterval: 5 * time.Second, AdaptivePollingBaseInterval: 5 * time.Second,
} }
m := &Monitor{ m := &Monitor{

View file

@ -2289,23 +2289,23 @@ func TestEnsureBreaker(t *testing.T) {
wantExisting bool wantExisting bool
}{ }{
{ {
name: "nil circuitBreakers map gets initialized", name: "nil circuitBreakers map gets initialized",
circuitBreakers: nil, circuitBreakers: nil,
key: "test-key", key: "test-key",
wantRetryInterval: 5 * time.Second, wantRetryInterval: 5 * time.Second,
wantMaxDelay: 5 * time.Minute, wantMaxDelay: 5 * time.Minute,
wantHalfOpenWindow: 30 * time.Second, wantHalfOpenWindow: 30 * time.Second,
wantExisting: false, wantExisting: false,
}, },
{ {
name: "existing breaker for key is returned", name: "existing breaker for key is returned",
circuitBreakers: map[string]*circuitBreaker{}, circuitBreakers: map[string]*circuitBreaker{},
existingBreaker: newCircuitBreaker(3, 10*time.Second, 10*time.Minute, 60*time.Second), existingBreaker: newCircuitBreaker(3, 10*time.Second, 10*time.Minute, 60*time.Second),
key: "existing-key", key: "existing-key",
wantRetryInterval: 10 * time.Second, wantRetryInterval: 10 * time.Second,
wantMaxDelay: 10 * time.Minute, wantMaxDelay: 10 * time.Minute,
wantHalfOpenWindow: 60 * time.Second, wantHalfOpenWindow: 60 * time.Second,
wantExisting: true, wantExisting: true,
}, },
{ {
name: "new breaker with default values (all config fields zero)", name: "new breaker with default values (all config fields zero)",

View file

@ -142,7 +142,7 @@ func TestConvertPBSSnapshots(t *testing.T) {
Files: []interface{}{ Files: []interface{}{
map[string]interface{}{"filename": "valid.raw"}, map[string]interface{}{"filename": "valid.raw"},
map[string]interface{}{"name": "invalid.raw"}, // Wrong field name map[string]interface{}{"name": "invalid.raw"}, // Wrong field name
map[string]interface{}{}, // Empty object map[string]interface{}{}, // Empty object
}, },
}, },
} }

View file

@ -464,10 +464,10 @@ func TestCalculateRates_DoesNotUpdatePreviousOnStaleData(t *testing.T) {
// Second call with stale data (same values, different timestamp) // Second call with stale data (same values, different timestamp)
metrics2 := types.IOMetrics{ metrics2 := types.IOMetrics{
DiskRead: 1000, // same DiskRead: 1000, // same
DiskWrite: 2000, // same DiskWrite: 2000, // same
NetworkIn: 3000, // same NetworkIn: 3000, // same
NetworkOut: 4000, // same NetworkOut: 4000, // same
Timestamp: baseTime.Add(10 * time.Second), // different time Timestamp: baseTime.Add(10 * time.Second), // different time
} }
rt.CalculateRates("vm-100", metrics2) rt.CalculateRates("vm-100", metrics2)

View file

@ -849,8 +849,8 @@ func TestAdaptiveIntervalSelector_JitterDeterministic(t *testing.T) {
name: "jitter within bounds", name: "jitter within bounds",
seed: 12345, seed: 12345,
score: 0.5, score: 0.5,
wantMin: 19 * time.Second, // slightly below min due to jitter wantMin: 19 * time.Second, // slightly below min due to jitter
wantMax: 41 * time.Second, // slightly above base due to jitter wantMax: 41 * time.Second, // slightly above base due to jitter
iterations: 50, iterations: 50,
}, },
{ {
@ -905,11 +905,11 @@ func TestAdaptiveIntervalSelector_JitterDeterministic(t *testing.T) {
// TestAdaptiveIntervalSelector_BoundaryConditions tests edge cases // TestAdaptiveIntervalSelector_BoundaryConditions tests edge cases
func TestAdaptiveIntervalSelector_BoundaryConditions(t *testing.T) { func TestAdaptiveIntervalSelector_BoundaryConditions(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
cfg SchedulerConfig cfg SchedulerConfig
req IntervalRequest req IntervalRequest
wantMin time.Duration wantMin time.Duration
wantMax time.Duration wantMax time.Duration
}{ }{
{ {
name: "zero intervals default to min", name: "zero intervals default to min",
@ -1696,7 +1696,7 @@ func TestAdaptiveIntervalSelector_TargetBelowMinClamp(t *testing.T) {
// This hits the code path but with corrected values // This hits the code path but with corrected values
req := IntervalRequest{ req := IntervalRequest{
BaseInterval: cfg.BaseInterval, BaseInterval: cfg.BaseInterval,
MinInterval: -5 * time.Second, // negative min MinInterval: -5 * time.Second, // negative min
MaxInterval: -10 * time.Second, // negative max < negative min MaxInterval: -10 * time.Second, // negative max < negative min
StalenessScore: 0.5, StalenessScore: 0.5,
InstanceKey: "test-target-below-min", InstanceKey: "test-target-below-min",

View file

@ -452,7 +452,6 @@ func TestParseRPiTemperature(t *testing.T) {
} }
} }
func TestParseSensorsJSON_PiPartialSensors(t *testing.T) { func TestParseSensorsJSON_PiPartialSensors(t *testing.T) {
collector := &TemperatureCollector{} collector := &TemperatureCollector{}
@ -1096,19 +1095,19 @@ func TestExtractCoreNumber(t *testing.T) {
{"Core 10", 10}, {"Core 10", 10},
{"Core 99", 99}, {"Core 99", 99},
{"Core 127", 127}, {"Core 127", 127},
{"Core", 0}, // missing number {"Core", 0}, // missing number
{"Core ", 0}, // trailing space, no number {"Core ", 0}, // trailing space, no number
{"core 5", 5}, // lowercase {"core 5", 5}, // lowercase
{"CORE 7", 7}, // uppercase {"CORE 7", 7}, // uppercase
{"Core 12", 12}, // extra space (Fields handles this) {"Core 12", 12}, // extra space (Fields handles this)
{"", 0}, // empty string {"", 0}, // empty string
{" ", 0}, // whitespace only {" ", 0}, // whitespace only
{"Core abc", 0}, // non-numeric {"Core abc", 0}, // non-numeric
{"Package id 0", 0}, // last part is "0" {"Package id 0", 0}, // last part is "0"
{"temp1", 0}, // no spaces {"temp1", 0}, // no spaces
{"Core 1000", 1000}, // large core number {"Core 1000", 1000}, // large core number
{"Prefix Core 5", 5}, // core not at start {"Prefix Core 5", 5}, // core not at start
{"Core 0 extra", 0}, // text after number - "extra" is last field {"Core 0 extra", 0}, // text after number - "extra" is last field
} }
for _, tt := range tests { for _, tt := range tests {

View file

@ -322,10 +322,10 @@ func TestSendEmailOnce_BuildsMultipartMessage(t *testing.T) {
func TestTestConnection_TLSRouting(t *testing.T) { func TestTestConnection_TLSRouting(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
tls bool tls bool
port int port int
wantTLS bool wantTLS bool
}{ }{
{"TLS true uses TLS dial", true, 587, true}, {"TLS true uses TLS dial", true, 587, true},
{"Port 465 uses TLS dial", false, 465, true}, {"Port 465 uses TLS dial", false, 465, true},

View file

@ -312,9 +312,9 @@ func TestResolvedTimeFromAlerts(t *testing.T) {
fixedTimeStr := fixedTime.Format(time.RFC3339) fixedTimeStr := fixedTime.Format(time.RFC3339)
tests := []struct { tests := []struct {
name string name string
alerts []*alerts.Alert alerts []*alerts.Alert
checkFn func(*testing.T, time.Time) checkFn func(*testing.T, time.Time)
}{ }{
{ {
name: "nil slice - returns current time", name: "nil slice - returns current time",
@ -679,9 +679,9 @@ func TestCopyWebhookConfigs(t *testing.T) {
name: "multiple webhooks with all fields", name: "multiple webhooks with all fields",
webhooks: []WebhookConfig{ webhooks: []WebhookConfig{
{ {
Enabled: true, Enabled: true,
URL: "https://discord.com/api/webhooks/123", URL: "https://discord.com/api/webhooks/123",
Headers: map[string]string{"X-Custom": "value"}, Headers: map[string]string{"X-Custom": "value"},
CustomFields: map[string]string{"key": "val"}, CustomFields: map[string]string{"key": "val"},
}, },
{ {

View file

@ -373,13 +373,13 @@ func TestVersionPatternValidation(t *testing.T) {
{"master", false}, {"master", false},
{"main", false}, {"main", false},
{"HEAD", false}, {"HEAD", false},
{"1.0.0-", false}, // trailing dash {"1.0.0-", false}, // trailing dash
{"1.0.0+", false}, // trailing plus {"1.0.0+", false}, // trailing plus
{"-1.0.0", false}, // leading dash {"-1.0.0", false}, // leading dash
{"v-1.0.0", false}, // invalid prefix {"v-1.0.0", false}, // invalid prefix
{"V1.0.0", false}, // uppercase V not allowed {"V1.0.0", false}, // uppercase V not allowed
{"1.0.0--alpha", true}, // double dash allowed (not a security risk) {"1.0.0--alpha", true}, // double dash allowed (not a security risk)
{"1.0.0++build", false}, // double plus {"1.0.0++build", false}, // double plus
{"1.0.0-alpha_1", false}, // underscore not allowed in prerelease {"1.0.0-alpha_1", false}, // underscore not allowed in prerelease
{"1.0.0+build_1", false}, // underscore not allowed in build metadata {"1.0.0+build_1", false}, // underscore not allowed in build metadata
} }

View file

@ -77,9 +77,9 @@ func TestParseVersion(t *testing.T) {
wantMajor: 4, wantMinor: 24, wantPatch: 0, wantMajor: 4, wantMinor: 24, wantPatch: 0,
}, },
{ {
name: "with prerelease", name: "with prerelease",
input: "4.24.0-rc.3", input: "4.24.0-rc.3",
wantMajor: 4, wantMinor: 24, wantPatch: 0, wantMajor: 4, wantMinor: 24, wantPatch: 0,
wantPrerel: "rc.3", wantPrerel: "rc.3",
}, },
{ {
@ -89,9 +89,9 @@ func TestParseVersion(t *testing.T) {
wantBuild: "build.123", wantBuild: "build.123",
}, },
{ {
name: "with prerelease and build", name: "with prerelease and build",
input: "4.24.0-rc.3+build.123", input: "4.24.0-rc.3+build.123",
wantMajor: 4, wantMinor: 24, wantPatch: 0, wantMajor: 4, wantMinor: 24, wantPatch: 0,
wantPrerel: "rc.3", wantPrerel: "rc.3",
wantBuild: "build.123", wantBuild: "build.123",
}, },

View file

@ -440,12 +440,12 @@ func TestFlexibleTypes_InStruct(t *testing.T) {
func TestFlexibleTypes_PMGResponsePatterns(t *testing.T) { func TestFlexibleTypes_PMGResponsePatterns(t *testing.T) {
// Simulates real PMG API response patterns observed in production // Simulates real PMG API response patterns observed in production
type MailStats struct { type MailStats struct {
Count flexibleFloat `json:"count"` Count flexibleFloat `json:"count"`
CountIn flexibleFloat `json:"count_in"` CountIn flexibleFloat `json:"count_in"`
SpamCount flexibleFloat `json:"spamcount_in"` SpamCount flexibleFloat `json:"spamcount_in"`
BouncesIn flexibleFloat `json:"bounces_in"` BouncesIn flexibleFloat `json:"bounces_in"`
BytesIn flexibleFloat `json:"bytes_in"` BytesIn flexibleFloat `json:"bytes_in"`
AvgTime flexibleFloat `json:"avptime"` AvgTime flexibleFloat `json:"avptime"`
} }
input := `{ input := `{

View file

@ -145,10 +145,10 @@ func TestExtractStatusCode(t *testing.T) {
func TestIsTransientRateLimitError(t *testing.T) { func TestIsTransientRateLimitError(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
err error err error
wantTransient bool wantTransient bool
wantCode int wantCode int
}{ }{
{"nil error", nil, false, 0}, {"nil error", nil, false, 0},
{"generic error", errors.New("connection refused"), false, 0}, {"generic error", errors.New("connection refused"), false, 0},

View file

@ -359,10 +359,10 @@ func TestParseReplicationTime(t *testing.T) {
func TestParseDurationSeconds(t *testing.T) { func TestParseDurationSeconds(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
input interface{} input interface{}
wantSecs int wantSecs int
wantHuman string wantHuman string
}{ }{
// nil // nil
{"nil", nil, 0, ""}, {"nil", nil, 0, ""},
@ -526,17 +526,17 @@ func TestCopyFloat(t *testing.T) {
func TestParseReplicationJob(t *testing.T) { func TestParseReplicationJob(t *testing.T) {
entry := map[string]json.RawMessage{ entry := map[string]json.RawMessage{
"id": json.RawMessage(`"100-0"`), "id": json.RawMessage(`"100-0"`),
"guest": json.RawMessage(`100`), "guest": json.RawMessage(`100`),
"source": json.RawMessage(`"node1"`), "source": json.RawMessage(`"node1"`),
"target": json.RawMessage(`"node2"`), "target": json.RawMessage(`"node2"`),
"schedule": json.RawMessage(`"*/15"`), "schedule": json.RawMessage(`"*/15"`),
"type": json.RawMessage(`"local"`), "type": json.RawMessage(`"local"`),
"state": json.RawMessage(`"ok"`), "state": json.RawMessage(`"ok"`),
"enabled": json.RawMessage(`1`), "enabled": json.RawMessage(`1`),
"last_sync": json.RawMessage(`1736936400`), "last_sync": json.RawMessage(`1736936400`),
"fail_count": json.RawMessage(`0`), "fail_count": json.RawMessage(`0`),
"rate": json.RawMessage(`10.5`), "rate": json.RawMessage(`10.5`),
} }
job := parseReplicationJob(entry) job := parseReplicationJob(entry)