feat(alerts): Send test on a rule checks that its alerts reach someone (#5466)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m9s
CI / unit (push) Successful in 1m36s
CI / publish (push) Successful in 1m6s

#5466 "Send test: check a rule's delivery from the rule". A rule's page gets
a Send test button (operator role), and MCP gets send_test_alert. Each sends
a TEST alert for that rule now, by email to everyone who gets alerts and by
webhook. The alert carries the rule's name and condition, at the latest
reading of what it watches; a rule covering hosts uses its first host. The
result for each channel shows under the button, or "Nothing is set up to
send to". Nothing fires, no event is written, and the test is audited as
alert_rule.tested. An alert with no reading now says "has no reading yet"
instead of "is None".

Docs: alerting.md also gains the burst email from #5465 "One email per burst
of alerts".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 08:33:32 -04:00
co-authored by Claude Opus 5.5
parent 997aa50939
commit 5ef18c992c
13 changed files with 299 additions and 7 deletions
+15
View File
@@ -420,6 +420,21 @@ async def toggle_rule(rule_id: str):
return redirect(url_for("alerts.list_alerts"))
@alerts_bp.post("/rules/<rule_id>/test")
@require_role(UserRole.operator)
async def test_rule(rule_id: str):
"""Send test (#5466): a TEST notification for this rule, now, by every
channel its alerts use; the result shows under the button."""
try:
sent = await alert_svc.send_test(current_app, rule_id)
except LookupError:
return "Not found", 404
await log_audit(current_app, session.get("user_id"), session.get("username", ""),
"alert_rule.tested", entity_type="alert_rule", entity_id=sent["rule"],
detail={"rule_id": rule_id, "ok": sent["ok"], "lines": sent["lines"]})
return await render_template("alerts/_test_result.html", ok=sent["ok"], lines=sent["lines"])
@alerts_bp.post("/rules/<rule_id>/delete")
@require_role(UserRole.admin)
async def delete_rule(rule_id: str):