feat(alerts): alert history page — transitions, deliveries, mutes and actions (#5066)
#5066 "Alert history page". /alerts/history lists every alert transition in a range (1h–30d, the shared range picker), newest first, all rules or one (?rule=). Each row: from → to, the value against the rule's condition ("no data" for a silent one), email and webhook sent or failed with the error, "muted · <window>" (and "notified when the window ended") taken out of the event note, other notes, and a link to the Ansible run an action started. Range and rule changes swap only the rows (HTMX). Viewer role. The Alerts page links each rule's history and the whole history. The query moves to alert_svc.history, which MCP get_alert_history now uses too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
"""How the alert history page reads one AlertEvent (#5066): the maintenance
|
||||
mute and its release come out of the note as their own fields, a silent
|
||||
transition reads "no data", and only attempted deliveries are listed."""
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from steward.alerts.routes import _history_row
|
||||
from steward.core.alerts import MUTED, RELEASED
|
||||
from steward.models.alerts import AlertOperator
|
||||
|
||||
RULE = SimpleNamespace(operator=AlertOperator(">"), threshold=90.0)
|
||||
|
||||
|
||||
def _event(**kw):
|
||||
base = dict(transitioned_at=datetime(2026, 10, 6, tzinfo=timezone.utc), from_state="inactive",
|
||||
to_state="firing", metric_value=97.5, note=None, email_sent=None, email_error=None,
|
||||
webhook_sent=None, webhook_error=None, ansible_run_id=None)
|
||||
return SimpleNamespace(**{**base, **kw})
|
||||
|
||||
|
||||
@pytest.mark.parametrize("note, notes, muted_by, released", [
|
||||
(None, [], None, False),
|
||||
("rule enabled", ["rule enabled"], None, False),
|
||||
(f"{MUTED} patch night", [], "patch night", False),
|
||||
(f"no sample for 18m; {MUTED} patch night; {RELEASED}", ["no sample for 18m"], "patch night", True),
|
||||
(MUTED, [], "a maintenance window", False),
|
||||
])
|
||||
def test_notes_split_into_mute_release_and_the_rest(note, notes, muted_by, released):
|
||||
row = _history_row(_event(note=note), RULE)
|
||||
assert (row["notes"], row["muted_by"], row["released"]) == (notes, muted_by, released)
|
||||
|
||||
|
||||
def test_value_and_condition():
|
||||
assert _history_row(_event(), RULE)["value"] == "97.5"
|
||||
assert _history_row(_event(metric_value=None), RULE)["value"] == "no data"
|
||||
assert _history_row(_event(), RULE)["condition"] == "> 90"
|
||||
|
||||
|
||||
def test_only_attempted_deliveries_are_listed():
|
||||
row = _history_row(_event(email_sent=False, email_error="timed out", webhook_sent=None), RULE)
|
||||
assert row["deliveries"] == [("email", False, "timed out")]
|
||||
row = _history_row(_event(email_sent=True, webhook_sent=True), RULE)
|
||||
assert [(label, sent) for label, sent, _ in row["deliveries"]] == [("email", True), ("webhook", True)]
|
||||
assert _history_row(_event(), RULE)["deliveries"] == []
|
||||
Reference in New Issue
Block a user