#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>
53 lines
2.1 KiB
Python
53 lines
2.1 KiB
Python
"""What a rule's Send test carries (#5466): the rule's own name and
|
|
condition, marked TEST, on its resource at the latest reading; a rule
|
|
covering hosts tests on its first host."""
|
|
from types import SimpleNamespace
|
|
|
|
from steward.alerts import service as alert_svc
|
|
from steward.models.alerts import AlertOperator
|
|
|
|
|
|
def _rule(**kw):
|
|
base = dict(id="r1", name="Hot", source_module="host_agent", resource_name="web01",
|
|
metric_name="cpu_pct", operator=AlertOperator.gt, threshold=90.0,
|
|
covers="resource", cover_group=None, parent_id=None)
|
|
return SimpleNamespace(**{**base, **kw})
|
|
|
|
|
|
class _Db:
|
|
"""Answers the one query test_alert makes: a covering rule's first host's rule."""
|
|
|
|
def __init__(self, child):
|
|
self.child = child
|
|
|
|
async def execute(self, stmt):
|
|
return SimpleNamespace(scalar_one_or_none=lambda: self.child)
|
|
|
|
|
|
def _readings(monkeypatch, values):
|
|
async def latest(db, rule):
|
|
return values.get(rule.resource_name)
|
|
monkeypatch.setattr(alert_svc, "latest_value", latest)
|
|
|
|
|
|
async def test_a_rule_tests_on_its_own_resource_at_its_latest_reading(monkeypatch):
|
|
_readings(monkeypatch, {"web01": 42.0})
|
|
got = await alert_svc.test_alert(_Db(None), _rule())
|
|
assert {k: got[k] for k in ("rule_name", "state", "resource", "value", "operator", "threshold")} == {
|
|
"rule_name": "Hot", "state": "TEST", "resource": "web01", "value": 42.0,
|
|
"operator": ">", "threshold": 90.0}
|
|
|
|
|
|
async def test_a_covering_rule_tests_on_its_first_host(monkeypatch):
|
|
_readings(monkeypatch, {"db01": 12.0})
|
|
parent = _rule(resource_name="", covers="group", cover_group="web")
|
|
got = await alert_svc.test_alert(_Db(_rule(id="c1", name="Hot: db01", resource_name="db01",
|
|
parent_id="r1")), parent)
|
|
assert (got["rule_name"], got["resource"], got["value"]) == ("Hot", "db01", 12.0)
|
|
|
|
|
|
async def test_a_covering_rule_with_no_hosts_yet_says_what_it_covers(monkeypatch):
|
|
_readings(monkeypatch, {})
|
|
got = await alert_svc.test_alert(_Db(None), _rule(resource_name="", covers="all_hosts"))
|
|
assert (got["resource"], got["value"]) == ("every host", None)
|