test: Add tests for metric threshold lookup functions
- normalizeMetricTimeThresholds: key normalization, invalid value filtering - getThresholdForMetric: all metric types, empty config, case sensitivity - getThresholdForMetricFromConfig: hysteresis application, all metric types Coverage: alerts 49.0% → 49.9%
This commit is contained in:
parent
be5a6eb97e
commit
4058e575f7
1 changed files with 394 additions and 0 deletions
|
|
@ -2148,3 +2148,397 @@ func TestParsePulseTags(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestNormalizeMetricTimeThresholds tests the normalizeMetricTimeThresholds function
|
||||
func TestNormalizeMetricTimeThresholds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input map[string]map[string]int
|
||||
want map[string]map[string]int
|
||||
}{
|
||||
{
|
||||
name: "nil input returns nil",
|
||||
input: nil,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "empty input returns nil",
|
||||
input: map[string]map[string]int{},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "valid input is normalized",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": 120},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": 120},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keys are lowercased",
|
||||
input: map[string]map[string]int{
|
||||
"GUEST": {"CPU": 60, "MEMORY": 120},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": 120},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "keys are trimmed",
|
||||
input: map[string]map[string]int{
|
||||
" guest ": {" cpu ": 60},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 60},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative delays are dropped",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": -1},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 60},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "zero delay is valid",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {"cpu": 0},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty type key is dropped",
|
||||
input: map[string]map[string]int{
|
||||
"": {"cpu": 60},
|
||||
"guest": {"memory": 120},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"memory": 120},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "whitespace-only type key is dropped",
|
||||
input: map[string]map[string]int{
|
||||
" ": {"cpu": 60},
|
||||
"guest": {"memory": 120},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"memory": 120},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty metric key is dropped",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {"": 60, "cpu": 120},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 120},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty metrics map is dropped",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {},
|
||||
"node": {"cpu": 60},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"node": {"cpu": 60},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all invalid results in nil",
|
||||
input: map[string]map[string]int{
|
||||
"": {"cpu": 60},
|
||||
"guest": {"": 60, "memory": -1},
|
||||
},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "multiple resource types",
|
||||
input: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": 120},
|
||||
"node": {"disk": 30},
|
||||
},
|
||||
want: map[string]map[string]int{
|
||||
"guest": {"cpu": 60, "memory": 120},
|
||||
"node": {"disk": 30},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := normalizeMetricTimeThresholds(tc.input)
|
||||
|
||||
if tc.want == nil {
|
||||
if result != nil {
|
||||
t.Errorf("normalizeMetricTimeThresholds() = %v, want nil", result)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Fatalf("normalizeMetricTimeThresholds() = nil, want %v", tc.want)
|
||||
}
|
||||
|
||||
// Check all expected keys exist with correct values
|
||||
for typeKey, metrics := range tc.want {
|
||||
resultMetrics, exists := result[typeKey]
|
||||
if !exists {
|
||||
t.Errorf("missing type key %q", typeKey)
|
||||
continue
|
||||
}
|
||||
for metricKey, delay := range metrics {
|
||||
resultDelay, exists := resultMetrics[metricKey]
|
||||
if !exists {
|
||||
t.Errorf("missing metric key %q for type %q", metricKey, typeKey)
|
||||
continue
|
||||
}
|
||||
if resultDelay != delay {
|
||||
t.Errorf("result[%q][%q] = %d, want %d", typeKey, metricKey, resultDelay, delay)
|
||||
}
|
||||
}
|
||||
// Check no extra metric keys
|
||||
if len(resultMetrics) != len(metrics) {
|
||||
t.Errorf("result[%q] has %d keys, want %d", typeKey, len(resultMetrics), len(metrics))
|
||||
}
|
||||
}
|
||||
// Check no extra type keys
|
||||
if len(result) != len(tc.want) {
|
||||
t.Errorf("result has %d type keys, want %d", len(result), len(tc.want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetThresholdForMetric tests the getThresholdForMetric function
|
||||
func TestGetThresholdForMetric(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cpuThreshold := &HysteresisThreshold{Trigger: 80, Clear: 70}
|
||||
memoryThreshold := &HysteresisThreshold{Trigger: 85, Clear: 75}
|
||||
diskThreshold := &HysteresisThreshold{Trigger: 90, Clear: 85}
|
||||
diskReadThreshold := &HysteresisThreshold{Trigger: 50, Clear: 40}
|
||||
diskWriteThreshold := &HysteresisThreshold{Trigger: 55, Clear: 45}
|
||||
networkInThreshold := &HysteresisThreshold{Trigger: 70, Clear: 60}
|
||||
networkOutThreshold := &HysteresisThreshold{Trigger: 75, Clear: 65}
|
||||
temperatureThreshold := &HysteresisThreshold{Trigger: 65, Clear: 55}
|
||||
usageThreshold := &HysteresisThreshold{Trigger: 88, Clear: 78}
|
||||
|
||||
config := ThresholdConfig{
|
||||
CPU: cpuThreshold,
|
||||
Memory: memoryThreshold,
|
||||
Disk: diskThreshold,
|
||||
DiskRead: diskReadThreshold,
|
||||
DiskWrite: diskWriteThreshold,
|
||||
NetworkIn: networkInThreshold,
|
||||
NetworkOut: networkOutThreshold,
|
||||
Temperature: temperatureThreshold,
|
||||
Usage: usageThreshold,
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
metricType string
|
||||
want *HysteresisThreshold
|
||||
}{
|
||||
{"cpu", "cpu", cpuThreshold},
|
||||
{"memory", "memory", memoryThreshold},
|
||||
{"disk", "disk", diskThreshold},
|
||||
{"diskRead", "diskRead", diskReadThreshold},
|
||||
{"diskWrite", "diskWrite", diskWriteThreshold},
|
||||
{"networkIn", "networkIn", networkInThreshold},
|
||||
{"networkOut", "networkOut", networkOutThreshold},
|
||||
{"temperature", "temperature", temperatureThreshold},
|
||||
{"usage", "usage", usageThreshold},
|
||||
{"unknown metric", "unknown", nil},
|
||||
{"empty metric", "", nil},
|
||||
{"case sensitive - CPU", "CPU", nil},
|
||||
{"case sensitive - Memory", "Memory", nil},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := getThresholdForMetric(config, tc.metricType)
|
||||
if result != tc.want {
|
||||
t.Errorf("getThresholdForMetric(%q) = %v, want %v", tc.metricType, result, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetThresholdForMetric_EmptyConfig tests getThresholdForMetric with empty config
|
||||
func TestGetThresholdForMetric_EmptyConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
config := ThresholdConfig{}
|
||||
|
||||
metricTypes := []string{"cpu", "memory", "disk", "diskRead", "diskWrite", "networkIn", "networkOut", "temperature", "usage"}
|
||||
|
||||
for _, metricType := range metricTypes {
|
||||
t.Run(metricType, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := getThresholdForMetric(config, metricType)
|
||||
if result != nil {
|
||||
t.Errorf("getThresholdForMetric(%q) with empty config = %v, want nil", metricType, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetThresholdForMetricFromConfig tests the getThresholdForMetricFromConfig function
|
||||
func TestGetThresholdForMetricFromConfig(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
config ThresholdConfig
|
||||
metricType string
|
||||
wantNil bool
|
||||
wantTrigger float64
|
||||
wantClear float64
|
||||
}{
|
||||
{
|
||||
name: "empty config returns nil",
|
||||
config: ThresholdConfig{},
|
||||
metricType: "cpu",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "cpu threshold returned",
|
||||
config: ThresholdConfig{
|
||||
CPU: &HysteresisThreshold{Trigger: 80, Clear: 70},
|
||||
},
|
||||
metricType: "cpu",
|
||||
wantTrigger: 80,
|
||||
wantClear: 70,
|
||||
},
|
||||
{
|
||||
name: "memory threshold returned",
|
||||
config: ThresholdConfig{
|
||||
Memory: &HysteresisThreshold{Trigger: 85, Clear: 75},
|
||||
},
|
||||
metricType: "memory",
|
||||
wantTrigger: 85,
|
||||
wantClear: 75,
|
||||
},
|
||||
{
|
||||
name: "disk threshold returned",
|
||||
config: ThresholdConfig{
|
||||
Disk: &HysteresisThreshold{Trigger: 90, Clear: 85},
|
||||
},
|
||||
metricType: "disk",
|
||||
wantTrigger: 90,
|
||||
wantClear: 85,
|
||||
},
|
||||
{
|
||||
name: "diskRead threshold returned",
|
||||
config: ThresholdConfig{
|
||||
DiskRead: &HysteresisThreshold{Trigger: 50, Clear: 40},
|
||||
},
|
||||
metricType: "diskRead",
|
||||
wantTrigger: 50,
|
||||
wantClear: 40,
|
||||
},
|
||||
{
|
||||
name: "diskWrite threshold returned",
|
||||
config: ThresholdConfig{
|
||||
DiskWrite: &HysteresisThreshold{Trigger: 55, Clear: 45},
|
||||
},
|
||||
metricType: "diskWrite",
|
||||
wantTrigger: 55,
|
||||
wantClear: 45,
|
||||
},
|
||||
{
|
||||
name: "networkIn threshold returned",
|
||||
config: ThresholdConfig{
|
||||
NetworkIn: &HysteresisThreshold{Trigger: 70, Clear: 60},
|
||||
},
|
||||
metricType: "networkIn",
|
||||
wantTrigger: 70,
|
||||
wantClear: 60,
|
||||
},
|
||||
{
|
||||
name: "networkOut threshold returned",
|
||||
config: ThresholdConfig{
|
||||
NetworkOut: &HysteresisThreshold{Trigger: 75, Clear: 65},
|
||||
},
|
||||
metricType: "networkOut",
|
||||
wantTrigger: 75,
|
||||
wantClear: 65,
|
||||
},
|
||||
{
|
||||
name: "temperature threshold returned",
|
||||
config: ThresholdConfig{
|
||||
Temperature: &HysteresisThreshold{Trigger: 65, Clear: 55},
|
||||
},
|
||||
metricType: "temperature",
|
||||
wantTrigger: 65,
|
||||
wantClear: 55,
|
||||
},
|
||||
{
|
||||
name: "usage threshold returned",
|
||||
config: ThresholdConfig{
|
||||
Usage: &HysteresisThreshold{Trigger: 88, Clear: 78},
|
||||
},
|
||||
metricType: "usage",
|
||||
wantTrigger: 88,
|
||||
wantClear: 78,
|
||||
},
|
||||
{
|
||||
name: "unknown metric returns nil",
|
||||
config: ThresholdConfig{
|
||||
CPU: &HysteresisThreshold{Trigger: 80, Clear: 70},
|
||||
},
|
||||
metricType: "unknown",
|
||||
wantNil: true,
|
||||
},
|
||||
{
|
||||
name: "threshold with zero clear gets default hysteresis",
|
||||
config: ThresholdConfig{
|
||||
CPU: &HysteresisThreshold{Trigger: 80, Clear: 0},
|
||||
},
|
||||
metricType: "cpu",
|
||||
wantTrigger: 80,
|
||||
wantClear: 75, // 80 - 5 default margin
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
result := getThresholdForMetricFromConfig(tc.config, tc.metricType)
|
||||
|
||||
if tc.wantNil {
|
||||
if result != nil {
|
||||
t.Errorf("getThresholdForMetricFromConfig() = %v, want nil", result)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
t.Fatalf("getThresholdForMetricFromConfig() = nil, want non-nil")
|
||||
}
|
||||
|
||||
if result.Trigger != tc.wantTrigger {
|
||||
t.Errorf("Trigger = %v, want %v", result.Trigger, tc.wantTrigger)
|
||||
}
|
||||
if result.Clear != tc.wantClear {
|
||||
t.Errorf("Clear = %v, want %v", result.Clear, tc.wantClear)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue