test: Add tests for polling interval, proxy success handlers, and failed instance removal
- effectivePVEPollingInterval: 80%→100% (6 cases for nil/clamping) - handleProxySuccess: 80%→100% (3 cases for nil client, reset) - handleProxyHostSuccess: →100% (6 cases for empty/whitespace/removal) - removeFailedPBSNode: 75%→100% (5 cases for removal, backups, health) - removeFailedPMGInstance: 75%→100% (5 cases for removal, backups, health)
This commit is contained in:
parent
acb7f4cf3a
commit
2d775e9409
2 changed files with 423 additions and 0 deletions
|
|
@ -1067,6 +1067,70 @@ func TestExtractSnapshotName(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestEffectivePVEPollingInterval(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
monitor *Monitor
|
||||
expected time.Duration
|
||||
}{
|
||||
{
|
||||
name: "nil monitor returns minInterval",
|
||||
monitor: nil,
|
||||
expected: 10 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "nil config returns minInterval",
|
||||
monitor: &Monitor{config: nil},
|
||||
expected: 10 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "zero PVEPollingInterval returns minInterval",
|
||||
monitor: &Monitor{
|
||||
config: &config.Config{
|
||||
PVEPollingInterval: 0,
|
||||
},
|
||||
},
|
||||
expected: 10 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "valid interval within range",
|
||||
monitor: &Monitor{
|
||||
config: &config.Config{
|
||||
PVEPollingInterval: 30 * time.Second,
|
||||
},
|
||||
},
|
||||
expected: 30 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "interval below minInterval clamped to 10s",
|
||||
monitor: &Monitor{
|
||||
config: &config.Config{
|
||||
PVEPollingInterval: 5 * time.Second,
|
||||
},
|
||||
},
|
||||
expected: 10 * time.Second,
|
||||
},
|
||||
{
|
||||
name: "interval above maxInterval clamped to 1h",
|
||||
monitor: &Monitor{
|
||||
config: &config.Config{
|
||||
PVEPollingInterval: 2 * time.Hour,
|
||||
},
|
||||
},
|
||||
expected: time.Hour,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.monitor.effectivePVEPollingInterval()
|
||||
if result != tt.expected {
|
||||
t.Errorf("expected %v, got %v", tt.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClampUint64ToInt64(t *testing.T) {
|
||||
t.Run("zero value returns 0", func(t *testing.T) {
|
||||
result := clampUint64ToInt64(0)
|
||||
|
|
@ -1103,3 +1167,213 @@ func TestClampUint64ToInt64(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemoveFailedPBSNode(t *testing.T) {
|
||||
t.Run("removes correct instance from PBSInstances", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePBSInstances([]models.PBSInstance{
|
||||
{Name: "pbs1", Host: "192.168.1.1"},
|
||||
{Name: "pbs2", Host: "192.168.1.2"},
|
||||
{Name: "pbs3", Host: "192.168.1.3"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPBSNode("pbs2")
|
||||
|
||||
if len(state.PBSInstances) != 2 {
|
||||
t.Fatalf("expected 2 instances, got %d", len(state.PBSInstances))
|
||||
}
|
||||
for _, inst := range state.PBSInstances {
|
||||
if inst.Name == "pbs2" {
|
||||
t.Error("expected pbs2 to be removed")
|
||||
}
|
||||
}
|
||||
// Verify other instances remain
|
||||
names := make(map[string]bool)
|
||||
for _, inst := range state.PBSInstances {
|
||||
names[inst.Name] = true
|
||||
}
|
||||
if !names["pbs1"] || !names["pbs3"] {
|
||||
t.Errorf("expected pbs1 and pbs3 to remain, got %v", names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("clears PBS backups for that instance", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePBSBackups("pbs1", []models.PBSBackup{
|
||||
{ID: "backup1", Instance: "pbs1"},
|
||||
{ID: "backup2", Instance: "pbs1"},
|
||||
})
|
||||
state.UpdatePBSBackups("pbs2", []models.PBSBackup{
|
||||
{ID: "backup3", Instance: "pbs2"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPBSNode("pbs1")
|
||||
|
||||
// Verify pbs1 backups are gone
|
||||
for _, backup := range state.PBSBackups {
|
||||
if backup.Instance == "pbs1" {
|
||||
t.Errorf("expected pbs1 backups to be cleared, found %s", backup.ID)
|
||||
}
|
||||
}
|
||||
// Verify pbs2 backups remain
|
||||
found := false
|
||||
for _, backup := range state.PBSBackups {
|
||||
if backup.Instance == "pbs2" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected pbs2 backups to remain")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sets connection health to false", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.SetConnectionHealth("pbs-pbs1", true)
|
||||
state.SetConnectionHealth("pbs-pbs2", true)
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPBSNode("pbs1")
|
||||
|
||||
if state.ConnectionHealth["pbs-pbs1"] != false {
|
||||
t.Error("expected pbs-pbs1 connection health to be false")
|
||||
}
|
||||
if state.ConnectionHealth["pbs-pbs2"] != true {
|
||||
t.Error("expected pbs-pbs2 connection health to remain true")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("handles empty instances list", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
m := &Monitor{state: state}
|
||||
|
||||
// Should not panic
|
||||
m.removeFailedPBSNode("nonexistent")
|
||||
|
||||
if len(state.PBSInstances) != 0 {
|
||||
t.Errorf("expected empty instances, got %d", len(state.PBSInstances))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("handles instance not found", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePBSInstances([]models.PBSInstance{
|
||||
{Name: "pbs1", Host: "192.168.1.1"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPBSNode("nonexistent")
|
||||
|
||||
if len(state.PBSInstances) != 1 {
|
||||
t.Errorf("expected 1 instance to remain, got %d", len(state.PBSInstances))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestRemoveFailedPMGInstance(t *testing.T) {
|
||||
t.Run("removes correct instance from PMGInstances", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePMGInstances([]models.PMGInstance{
|
||||
{Name: "pmg1", Host: "192.168.1.1"},
|
||||
{Name: "pmg2", Host: "192.168.1.2"},
|
||||
{Name: "pmg3", Host: "192.168.1.3"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPMGInstance("pmg2")
|
||||
|
||||
if len(state.PMGInstances) != 2 {
|
||||
t.Fatalf("expected 2 instances, got %d", len(state.PMGInstances))
|
||||
}
|
||||
for _, inst := range state.PMGInstances {
|
||||
if inst.Name == "pmg2" {
|
||||
t.Error("expected pmg2 to be removed")
|
||||
}
|
||||
}
|
||||
// Verify other instances remain
|
||||
names := make(map[string]bool)
|
||||
for _, inst := range state.PMGInstances {
|
||||
names[inst.Name] = true
|
||||
}
|
||||
if !names["pmg1"] || !names["pmg3"] {
|
||||
t.Errorf("expected pmg1 and pmg3 to remain, got %v", names)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("clears PMG backups for that instance", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePMGBackups("pmg1", []models.PMGBackup{
|
||||
{ID: "backup1", Instance: "pmg1"},
|
||||
{ID: "backup2", Instance: "pmg1"},
|
||||
})
|
||||
state.UpdatePMGBackups("pmg2", []models.PMGBackup{
|
||||
{ID: "backup3", Instance: "pmg2"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPMGInstance("pmg1")
|
||||
|
||||
// Verify pmg1 backups are gone
|
||||
for _, backup := range state.PMGBackups {
|
||||
if backup.Instance == "pmg1" {
|
||||
t.Errorf("expected pmg1 backups to be cleared, found %s", backup.ID)
|
||||
}
|
||||
}
|
||||
// Verify pmg2 backups remain
|
||||
found := false
|
||||
for _, backup := range state.PMGBackups {
|
||||
if backup.Instance == "pmg2" {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Error("expected pmg2 backups to remain")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("sets connection health to false", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.SetConnectionHealth("pmg-pmg1", true)
|
||||
state.SetConnectionHealth("pmg-pmg2", true)
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPMGInstance("pmg1")
|
||||
|
||||
if state.ConnectionHealth["pmg-pmg1"] != false {
|
||||
t.Error("expected pmg-pmg1 connection health to be false")
|
||||
}
|
||||
if state.ConnectionHealth["pmg-pmg2"] != true {
|
||||
t.Error("expected pmg-pmg2 connection health to remain true")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("handles empty instances list", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
m := &Monitor{state: state}
|
||||
|
||||
// Should not panic
|
||||
m.removeFailedPMGInstance("nonexistent")
|
||||
|
||||
if len(state.PMGInstances) != 0 {
|
||||
t.Errorf("expected empty instances, got %d", len(state.PMGInstances))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("handles instance not found", func(t *testing.T) {
|
||||
state := models.NewState()
|
||||
state.UpdatePMGInstances([]models.PMGInstance{
|
||||
{Name: "pmg1", Host: "192.168.1.1"},
|
||||
})
|
||||
|
||||
m := &Monitor{state: state}
|
||||
m.removeFailedPMGInstance("nonexistent")
|
||||
|
||||
if len(state.PMGInstances) != 1 {
|
||||
t.Errorf("expected 1 instance to remain, got %d", len(state.PMGInstances))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1486,6 +1486,155 @@ func TestShouldSkipProxyHost_TrimsWhitespace(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Tests for handleProxySuccess
|
||||
// =============================================================================
|
||||
|
||||
func TestHandleProxySuccess_NilProxyClient(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyClient: nil,
|
||||
proxyFailures: 5, // should remain unchanged
|
||||
}
|
||||
|
||||
tc.handleProxySuccess()
|
||||
|
||||
if tc.proxyFailures != 5 {
|
||||
t.Errorf("expected proxyFailures to remain 5 when proxyClient is nil, got %d", tc.proxyFailures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleProxySuccess_ResetsFailures(t *testing.T) {
|
||||
stub := &stubTemperatureProxy{}
|
||||
|
||||
tc := &TemperatureCollector{
|
||||
proxyClient: stub,
|
||||
proxyFailures: 3,
|
||||
}
|
||||
|
||||
tc.handleProxySuccess()
|
||||
|
||||
if tc.proxyFailures != 0 {
|
||||
t.Errorf("expected proxyFailures to be reset to 0, got %d", tc.proxyFailures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleProxySuccess_AlreadyZeroFailures(t *testing.T) {
|
||||
stub := &stubTemperatureProxy{}
|
||||
|
||||
tc := &TemperatureCollector{
|
||||
proxyClient: stub,
|
||||
proxyFailures: 0,
|
||||
}
|
||||
|
||||
tc.handleProxySuccess()
|
||||
|
||||
if tc.proxyFailures != 0 {
|
||||
t.Errorf("expected proxyFailures to remain 0, got %d", tc.proxyFailures)
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Tests for handleProxyHostSuccess
|
||||
// =============================================================================
|
||||
|
||||
func TestHandleProxyHostSuccess_EmptyHost(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: map[string]*proxyHostState{
|
||||
"192.168.1.100": {failures: 2, cooldownUntil: time.Now().Add(time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
tc.handleProxyHostSuccess("")
|
||||
|
||||
// Map should be unchanged
|
||||
tc.proxyMu.Lock()
|
||||
if len(tc.proxyHostStates) != 1 {
|
||||
t.Errorf("expected map to have 1 entry, got %d", len(tc.proxyHostStates))
|
||||
}
|
||||
tc.proxyMu.Unlock()
|
||||
}
|
||||
|
||||
func TestHandleProxyHostSuccess_WhitespaceOnlyHost(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: map[string]*proxyHostState{
|
||||
"192.168.1.100": {failures: 2, cooldownUntil: time.Now().Add(time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
tc.handleProxyHostSuccess(" ")
|
||||
|
||||
// Map should be unchanged
|
||||
tc.proxyMu.Lock()
|
||||
if len(tc.proxyHostStates) != 1 {
|
||||
t.Errorf("expected map to have 1 entry, got %d", len(tc.proxyHostStates))
|
||||
}
|
||||
tc.proxyMu.Unlock()
|
||||
}
|
||||
|
||||
func TestHandleProxyHostSuccess_RemovesHostFromMap(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: map[string]*proxyHostState{
|
||||
"192.168.1.100": {failures: 5, cooldownUntil: time.Now().Add(time.Minute)},
|
||||
"192.168.1.101": {failures: 3, cooldownUntil: time.Now().Add(time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
tc.handleProxyHostSuccess("192.168.1.100")
|
||||
|
||||
tc.proxyMu.Lock()
|
||||
defer tc.proxyMu.Unlock()
|
||||
|
||||
if _, exists := tc.proxyHostStates["192.168.1.100"]; exists {
|
||||
t.Error("expected host 192.168.1.100 to be removed from map")
|
||||
}
|
||||
if _, exists := tc.proxyHostStates["192.168.1.101"]; !exists {
|
||||
t.Error("expected host 192.168.1.101 to remain in map")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleProxyHostSuccess_HostNotInMap(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: map[string]*proxyHostState{
|
||||
"192.168.1.100": {failures: 2},
|
||||
},
|
||||
}
|
||||
|
||||
// Should not panic when host doesn't exist
|
||||
tc.handleProxyHostSuccess("192.168.1.200")
|
||||
|
||||
tc.proxyMu.Lock()
|
||||
if len(tc.proxyHostStates) != 1 {
|
||||
t.Errorf("expected map to have 1 entry, got %d", len(tc.proxyHostStates))
|
||||
}
|
||||
tc.proxyMu.Unlock()
|
||||
}
|
||||
|
||||
func TestHandleProxyHostSuccess_TrimsWhitespace(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: map[string]*proxyHostState{
|
||||
"192.168.1.100": {failures: 5, cooldownUntil: time.Now().Add(time.Minute)},
|
||||
},
|
||||
}
|
||||
|
||||
tc.handleProxyHostSuccess(" 192.168.1.100 ")
|
||||
|
||||
tc.proxyMu.Lock()
|
||||
defer tc.proxyMu.Unlock()
|
||||
|
||||
if _, exists := tc.proxyHostStates["192.168.1.100"]; exists {
|
||||
t.Error("expected host to be removed after trimming whitespace from input")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleProxyHostSuccess_NilMap(t *testing.T) {
|
||||
tc := &TemperatureCollector{
|
||||
proxyHostStates: nil,
|
||||
}
|
||||
|
||||
// Should not panic with nil map
|
||||
tc.handleProxyHostSuccess("192.168.1.100")
|
||||
}
|
||||
|
||||
// Helper functions for test setup
|
||||
|
||||
func intPtr(i int) *int {
|
||||
|
|
|
|||
Loading…
Reference in a new issue