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") } }