test: Add tests for normalizeEndpointHost, taskExecutionTimeout, decrementPending

- normalizeEndpointHost: 89%→94.4% (19 cases covering schemes, ports, paths, edge cases)
- taskExecutionTimeout: 83%→100% (4 cases covering defaults and custom timeouts)
- decrementPending: 88%→100% (5 cases covering decrement logic and no-op scenarios)
This commit is contained in:
rcourtman 2025-12-01 18:42:57 +00:00
parent 6183727368
commit acb7f4cf3a
3 changed files with 184 additions and 7 deletions

View file

@ -17,20 +17,49 @@ func TestNormalizeEndpointHost(t *testing.T) {
t.Parallel()
cases := []struct {
name string
input string
want string
}{
{"", ""},
{" node.local ", "node.local"},
{"https://node.local:8006/", "node.local"},
{"node.local:8006", "node.local"},
{"node.local/path", "node.local"},
{"https://[2001:db8::1]:8006", "2001:db8::1"},
// Empty and whitespace
{"empty string", "", ""},
{"whitespace only", " ", ""},
{"whitespace with tabs", " \t ", ""},
// Full URLs with scheme
{"https URL with port and path", "https://example.com:8006/api", "example.com"},
{"http URL with path", "http://host/path", "host"},
{"https URL with trailing slash", "https://node.local:8006/", "node.local"},
// URLs without scheme
{"host with port", "example.com:8006", "example.com"},
{"host with port no scheme", "node.local:8006", "node.local"},
// Hostname only
{"hostname only", "example.com", "example.com"},
{"simple hostname", "node.local", "node.local"},
// IP addresses
{"IPv4 with port", "192.168.1.1:8006", "192.168.1.1"},
{"IPv4 only", "192.168.1.100", "192.168.1.100"},
{"IPv6 bracketed with port", "https://[2001:db8::1]:8006", "2001:db8::1"},
// Host with path (no scheme)
{"host with path no scheme", "node.local/path", "node.local"},
{"host with deep path", "server.example.com/api/v1/resource", "server.example.com"},
// Edge cases with just scheme prefix
{"just https prefix", "https://", ""},
{"just http prefix", "http://", ""},
// Whitespace trimming
{"whitespace around hostname", " node.local ", "node.local"},
{"whitespace around URL", " https://example.com:8006 ", "example.com"},
}
for _, tc := range cases {
tc := tc
t.Run(tc.input, func(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
if got := normalizeEndpointHost(tc.input); got != tc.want {
t.Fatalf("normalizeEndpointHost(%q) = %q, want %q", tc.input, got, tc.want)

View file

@ -1094,3 +1094,104 @@ func TestDecInFlight_DecrementsGauge(t *testing.T) {
t.Errorf("inflight{pve} = %v, want 0 after full decrement", got)
}
}
func TestDecrementPending_NilPollMetrics(t *testing.T) {
t.Parallel()
var pm *PollMetrics
// Should not panic
pm.decrementPending()
}
func TestDecrementPending_DecrementsWhenPositive(t *testing.T) {
t.Parallel()
pm := newFullTestPollMetrics(t)
// Set initial pending count
pm.ResetQueueDepth(5)
pm.decrementPending()
pm.mu.RLock()
gotPending := pm.pending
pm.mu.RUnlock()
if gotPending != 4 {
t.Errorf("pending = %v, want 4 after decrement from 5", gotPending)
}
}
func TestDecrementPending_DoesNotGoBelowZero(t *testing.T) {
t.Parallel()
pm := newFullTestPollMetrics(t)
// Start at 0 (default)
pm.decrementPending()
pm.mu.RLock()
gotPending := pm.pending
pm.mu.RUnlock()
if gotPending != 0 {
t.Errorf("pending = %v, want 0 (should not go negative)", gotPending)
}
// Also verify the gauge is 0
gotQueueDepth := getGaugeValue(pm.queueDepth)
if gotQueueDepth != 0 {
t.Errorf("queueDepth gauge = %v, want 0", gotQueueDepth)
}
}
func TestDecrementPending_UpdatesQueueDepthGauge(t *testing.T) {
t.Parallel()
pm := newFullTestPollMetrics(t)
pm.ResetQueueDepth(10)
pm.decrementPending()
gotQueueDepth := getGaugeValue(pm.queueDepth)
if gotQueueDepth != 9 {
t.Errorf("queueDepth gauge = %v, want 9", gotQueueDepth)
}
}
func TestDecrementPending_MultipleDecrements(t *testing.T) {
t.Parallel()
pm := newFullTestPollMetrics(t)
pm.ResetQueueDepth(5)
// Decrement 5 times
for i := 0; i < 5; i++ {
pm.decrementPending()
}
pm.mu.RLock()
gotPending := pm.pending
pm.mu.RUnlock()
if gotPending != 0 {
t.Errorf("pending = %v, want 0 after 5 decrements from 5", gotPending)
}
gotQueueDepth := getGaugeValue(pm.queueDepth)
if gotQueueDepth != 0 {
t.Errorf("queueDepth gauge = %v, want 0", gotQueueDepth)
}
// Decrement one more time - should stay at 0
pm.decrementPending()
pm.mu.RLock()
gotPending = pm.pending
pm.mu.RUnlock()
if gotPending != 0 {
t.Errorf("pending = %v, want 0 after extra decrement", gotPending)
}
}

View file

@ -65,4 +65,51 @@ func TestTaskExecutionTimeout(t *testing.T) {
t.Fatalf("taskExecutionTimeout() = %v, want %v", got, defaultTaskTimeout)
}
})
t.Run("nil Monitor returns defaultTaskTimeout", func(t *testing.T) {
var m *Monitor
got := m.taskExecutionTimeout(InstanceTypePVE)
if got != defaultTaskTimeout {
t.Fatalf("taskExecutionTimeout() = %v, want %v", got, defaultTaskTimeout)
}
})
t.Run("zero pollTimeout returns defaultTaskTimeout", func(t *testing.T) {
m := &Monitor{pollTimeout: 0}
got := m.taskExecutionTimeout(InstanceTypePVE)
if got != defaultTaskTimeout {
t.Fatalf("taskExecutionTimeout() = %v, want %v", got, defaultTaskTimeout)
}
})
t.Run("negative pollTimeout returns defaultTaskTimeout", func(t *testing.T) {
m := &Monitor{pollTimeout: -5 * time.Second}
got := m.taskExecutionTimeout(InstanceTypePVE)
if got != defaultTaskTimeout {
t.Fatalf("taskExecutionTimeout() = %v, want %v", got, defaultTaskTimeout)
}
})
t.Run("InstanceType parameter is ignored", func(t *testing.T) {
m := &Monitor{pollTimeout: 60 * time.Second}
// All instance types should return the same value
gotPVE := m.taskExecutionTimeout(InstanceTypePVE)
gotPBS := m.taskExecutionTimeout(InstanceTypePBS)
gotPMG := m.taskExecutionTimeout(InstanceTypePMG)
gotUnknown := m.taskExecutionTimeout(InstanceType("unknown"))
expected := 60 * time.Second
if gotPVE != expected {
t.Errorf("taskExecutionTimeout(PVE) = %v, want %v", gotPVE, expected)
}
if gotPBS != expected {
t.Errorf("taskExecutionTimeout(PBS) = %v, want %v", gotPBS, expected)
}
if gotPMG != expected {
t.Errorf("taskExecutionTimeout(PMG) = %v, want %v", gotPMG, expected)
}
if gotUnknown != expected {
t.Errorf("taskExecutionTimeout(unknown) = %v, want %v", gotUnknown, expected)
}
})
}