Public source validation / validate (push) Failing after 3m8s
101 lines
4.0 KiB
Go
101 lines
4.0 KiB
Go
package alert
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func signal(id, rule, evaluation string, state State, labels map[string]string) Signal {
|
|
return Signal{InstanceID: id, RuleID: rule, RuleVersionID: "version-1", Severity: SeverityDegraded, State: state, EvaluationKey: evaluation, ObservedAt: time.Date(2026, time.January, 1, 12, 0, 0, 0, time.UTC), Labels: labels, GroupBy: []string{"host", "application"}, SuppressWhen: []string{"host.unreachable", "dns.failure", "source.unavailable"}}
|
|
}
|
|
|
|
func TestBuildFingerprintIsStableAndIncludesRuleBehavior(t *testing.T) {
|
|
first, err := BuildFingerprint("rule-1", "version-1", "entity-1", map[string]string{"application": "media", "host": "pulse"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := BuildFingerprint("rule-1", "version-1", "entity-1", map[string]string{"host": "pulse", "application": "media"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if first != second || len(first) != 64 {
|
|
t.Fatalf("fingerprint instability: %q %q", first, second)
|
|
}
|
|
changedVersion, err := BuildFingerprint("rule-1", "version-2", "entity-1", map[string]string{"host": "pulse", "application": "media"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if changedVersion == first {
|
|
t.Fatal("rule version did not affect fingerprint")
|
|
}
|
|
tooMany := make(map[string]string, MaxFingerprintLabels+1)
|
|
for i := 0; i <= MaxFingerprintLabels; i++ {
|
|
tooMany["label"+string(rune('a'+i))] = "value"
|
|
}
|
|
if _, err := BuildFingerprint("rule-1", "version-1", "entity-1", tooMany); !errors.Is(err, ErrInvalidAlertIdentity) {
|
|
t.Fatalf("too many labels error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGroupAndDeduplicateSignalsAreDeterministic(t *testing.T) {
|
|
inputs := []Signal{
|
|
signal("instance-b", "rule-1", "slot-1", StateFiring, map[string]string{"host": "pulse", "application": "media"}),
|
|
signal("instance-a", "rule-1", "slot-1", StateFiring, map[string]string{"host": "pulse", "application": "media"}),
|
|
signal("instance-a", "rule-1", "slot-1", StateFiring, map[string]string{"host": "pulse", "application": "media"}),
|
|
signal("instance-c", "rule-1", "slot-1", StateFiring, map[string]string{"host": "other", "application": "media"}),
|
|
}
|
|
deduplicated, err := DeduplicateSignals(inputs)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(deduplicated) != 3 {
|
|
t.Fatalf("deduplicated signals = %d, want 3", len(deduplicated))
|
|
}
|
|
groups, err := GroupSignals(deduplicated)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(groups) != 2 || len(groups[0].Signals) != 1 || len(groups[1].Signals) != 2 {
|
|
t.Fatalf("unexpected groups: %#v", groups)
|
|
}
|
|
if groups[0].Key > groups[1].Key {
|
|
t.Fatal("groups are not sorted")
|
|
}
|
|
}
|
|
|
|
func TestSuppressionScenariosRemainInspectablyBounded(t *testing.T) {
|
|
base := signal("instance-1", "rule-service", "slot-1", StateFiring, map[string]string{"host": "pulse", "application": "web"})
|
|
tests := []struct {
|
|
name string
|
|
cause Cause
|
|
want bool
|
|
reason string
|
|
}{
|
|
{name: "host outage", cause: Cause{Key: "host.unreachable", State: StateFiring, Confirmed: true}, want: true, reason: "dependency_failure"},
|
|
{name: "dns outage", cause: Cause{Key: "dns.failure", State: StateAcknowledged, Confidence: .9}, want: true, reason: "dependency_failure"},
|
|
{name: "source outage", cause: Cause{Key: "source.unavailable", State: StateUnknown, Confirmed: true}, want: true, reason: "source_outage"},
|
|
{name: "low confidence", cause: Cause{Key: "host.unreachable", State: StateFiring, Confidence: .5}, want: false, reason: "no_active_suppression_cause"},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
decision, err := EvaluateSuppression(base, []Cause{test.cause})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decision.Suppressed != test.want || decision.Reason != test.reason {
|
|
t.Fatalf("decision = %#v, want suppressed=%v reason=%s", decision, test.want, test.reason)
|
|
}
|
|
})
|
|
}
|
|
resolved := base
|
|
resolved.State = StateResolved
|
|
decision, err := EvaluateSuppression(resolved, []Cause{{Key: "host.unreachable", State: StateFiring, Confirmed: true}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decision.Suppressed {
|
|
t.Fatal("resolved alert was suppressed")
|
|
}
|
|
}
|