style: Apply gofmt to 37 files
Standardize code formatting across test files and monitor.go. No functional changes.
This commit is contained in:
parent
ec6133afd8
commit
72af7bed5e
37 changed files with 251 additions and 253 deletions
|
|
@ -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,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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},
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)",
|
||||||
|
|
|
||||||
|
|
@ -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] {
|
||||||
|
|
|
||||||
|
|
@ -71,4 +71,3 @@ func TestNormalisePlatform(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
|
|
@ -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)",
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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},
|
||||||
|
|
|
||||||
|
|
@ -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"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 := `{
|
||||||
|
|
|
||||||
|
|
@ -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},
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue