Files
nezha_domains/model/alertrule_security_test.go
T

124 lines
3.0 KiB
Go

package model
import (
"math"
"testing"
"time"
)
func TestRuleSnapshotIgnoresEmptyMetricSamples(t *testing.T) {
tests := []struct {
name string
ruleType string
hostState *HostState
}{
{
name: "GPU list is empty",
ruleType: "gpu_max",
hostState: &HostState{},
},
{
name: "all temperature samples are filtered",
ruleType: "temperature_max",
hostState: &HostState{Temperatures: []SensorTemperature{
{Name: "coretemp", Temperature: 0},
}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
server := &Server{Common: Common{ID: 1}, State: test.hostState, Host: &Host{}}
rule := &Rule{Type: test.ruleType, Max: 1, Duration: 3, Cover: RuleCoverAll}
defer func() {
if recovered := recover(); recovered != nil {
t.Fatalf("Snapshot panicked for missing metric data: %v", recovered)
}
}()
if passed := rule.Snapshot(nil, server, nil); !passed {
t.Fatal("missing metric data must be ignored instead of treated as a failed threshold")
}
})
}
}
func TestAlertRuleCheckRejectsOverflowingPersistedDuration(t *testing.T) {
rule := &AlertRule{Rules: []*Rule{{
Type: "offline",
Duration: math.MaxUint64,
Cover: RuleCoverAll,
}}}
defer func() {
if recovered := recover(); recovered != nil {
t.Fatalf("Check panicked after uint64-to-int overflow: %v", recovered)
}
}()
duration, passed := rule.Check([][]bool{{false}})
if duration != 0 || !passed {
t.Fatalf("invalid persisted duration must be ignored safely, got duration=%d passed=%v", duration, passed)
}
if window := rule.RetentionWindow(); window != 0 {
t.Fatalf("invalid persisted duration must not create a retention window, got %d", window)
}
}
func TestAlertRulePersistedDataSafety(t *testing.T) {
cycleStart := time.Now().Add(-time.Hour)
tests := []struct {
name string
rule *AlertRule
want bool
}{
{
name: "normal rule",
rule: &AlertRule{Rules: []*Rule{{
Type: "cpu", Duration: 3, Cover: RuleCoverAll,
}}},
want: true,
},
{
name: "normal cycle rule",
rule: &AlertRule{Rules: []*Rule{{
Type: "transfer_in_cycle", CycleStart: &cycleStart, CycleInterval: 1, Cover: RuleCoverAll,
}}},
want: true,
},
{
name: "unknown type",
rule: &AlertRule{Rules: []*Rule{{
Type: "attacker_controlled", Duration: 3, Cover: RuleCoverAll,
}}},
},
{
name: "overflowing duration",
rule: &AlertRule{Rules: []*Rule{{
Type: "offline", Duration: math.MaxUint64, Cover: RuleCoverAll,
}}},
},
{
name: "cycle without start",
rule: &AlertRule{Rules: []*Rule{{
Type: "transfer_in_cycle", CycleInterval: 1, Cover: RuleCoverAll,
}}},
},
{
name: "nil rule",
rule: &AlertRule{Rules: []*Rule{nil}},
},
{
name: "empty rule list",
rule: &AlertRule{},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if got := test.rule.IsSafeToEvaluate(); got != test.want {
t.Fatalf("IsSafeToEvaluate()=%v, want %v", got, test.want)
}
})
}
}