package scheduler import ( "testing" ) func TestParsePingLatency(t *testing.T) { cases := []struct { name string out []byte want float64 }{ { name: "linux rtt format", out: []byte("PING host (1.2.3.4): 56 data bytes\n--- host ping statistics ---\n5 packets transmitted, 5 received, 0% packet loss\nrtt min/avg/max/mdev = 0.1/2.5/5.0/1.2 ms\n"), want: 2.5, }, { name: "macos round-trip format", out: []byte("PING host (1.2.3.4): 56 data bytes\n--- host ping statistics ---\n5 packets transmitted, 5 received, 0% packet loss\nround-trip min/avg/max/stddev = 0.1/2.5/5.0/1.2 ms\n"), want: 2.5, }, { name: "no match empty string", out: []byte(""), want: 0, }, { name: "malformed number in avg slot", out: []byte("rtt min/avg/max/mdev = 0.1/abc/5.0/1.2 ms\n"), want: 0, }, { name: "truly empty input nil", out: nil, want: 0, }, { name: "linux integer avg", out: []byte("rtt min/avg/max/mdev = 1/3/5/0.5 ms\n"), want: 3.0, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := parsePingLatency(tc.out) if got != tc.want { t.Fatalf("parsePingLatency(%q) = %v, want %v", tc.out, got, tc.want) } }) } } func TestAllowlistedScript(t *testing.T) { cases := []struct { name string in string want bool }{ {name: "cpu_check.sh", in: "cpu_check.sh", want: true}, {name: "disk_usage_check.sh", in: "disk_usage_check.sh", want: true}, {name: "hyphen allowed", in: "foo-bar.sh", want: true}, {name: "underscore allowed", in: "foo_bar.sh", want: true}, {name: "uppercase rejected", in: "Foo.sh", want: false}, {name: "bare .sh rejected (no name)", in: ".sh", want: false}, {name: "path traversal rejected", in: "../etc/passwd", want: false}, {name: "empty rejected", in: "", want: false}, // foo.sh.sh: regex ^[a-z][a-z0-9_-]+\.sh$ — the [a-z0-9_-]+ cannot // cross the first '.', so after matching "foo.sh" the trailing ".sh" // breaks the $ anchor → no match. {name: "doubled .sh.sh rejected", in: "foo.sh.sh", want: false}, {name: "wrong extension rejected", in: "foo.txt", want: false}, {name: "single char name rejected (needs 2+)", in: "a.sh", want: false}, {name: "two char name accepted", in: "ab.sh", want: true}, {name: "digit after first char", in: "a1.sh", want: true}, {name: "leading digit rejected", in: "1foo.sh", want: false}, {name: "dot in middle rejected", in: "foo.bar.sh", want: false}, {name: "space rejected", in: "foo bar.sh", want: false}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := allowlistedScript(tc.in) if got != tc.want { t.Fatalf("allowlistedScript(%q) = %v, want %v", tc.in, got, tc.want) } }) } } func TestEvaluateSeverity(t *testing.T) { cases := []struct { name string kind string signalKind string config []byte metrics map[string]float64 want string }{ { name: "empty config down signal", kind: "ping", signalKind: "down", config: nil, metrics: map[string]float64{}, want: "critical", }, { name: "empty config high_temp signal", kind: "ssh-script", signalKind: "high_temp", config: nil, metrics: map[string]float64{}, want: "warning", }, { name: "crit only exceeded", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"crit":90}}`), metrics: map[string]float64{"temp": 95}, want: "critical", }, { name: "warn only exceeded", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80}}`), metrics: map[string]float64{"temp": 85}, want: "warning", }, { name: "warn and crit, warn exceeded only", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"temp": 85}, want: "warning", }, { name: "warn and crit, crit exceeded", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"temp": 95}, want: "critical", }, { name: "metric below thresholds falls through to warning", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"temp": 70}, want: "warning", }, { name: "metric below thresholds falls through to down critical", kind: "ping", signalKind: "down", config: []byte(`{"ping_latency_ms":{"warn":100,"crit":200}}`), metrics: map[string]float64{"ping_latency_ms": 50}, want: "critical", }, { name: "crit zero skipped falls through", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"crit":0}}`), metrics: map[string]float64{"temp": 95}, want: "warning", }, { name: "metric not in thresholds ignored", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"cpu": 99}, want: "warning", }, { name: "equal to warn triggers warning", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"temp": 80}, want: "warning", }, { name: "equal to crit triggers critical", kind: "ssh-script", signalKind: "high_temp", config: []byte(`{"temp":{"warn":80,"crit":90}}`), metrics: map[string]float64{"temp": 90}, want: "critical", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := evaluateSeverity(tc.kind, tc.signalKind, tc.config, tc.metrics) if got != tc.want { t.Fatalf("evaluateSeverity(%q, %q, %s, %v) = %q, want %q", tc.kind, tc.signalKind, tc.config, tc.metrics, got, tc.want) } }) } }