Files
FabledSteward/tests/plugins/host_agent/test_scheduler.py
T
bvandeusenandClaude Opus 5.5 a9bf4b7f16
CI / lint (push) Successful in 2s
CI / integration (push) Failing after 1m26s
CI / unit (push) Successful in 1m31s
CI / publish (push) Skipped
fix(alerts): Steward's own outage no longer reads as every agent going silent (#5441)
After Steward or its database was down, every agent's age and every
series' last sample were old, so all agent-offline rules fired and all
rules went no-data at once, clearing a minute later. With per-account
alert email that would have mailed everyone a dozen notices per restart.

A periodic job now ticks a Watch (steward/core/watch.py) once it has
reached the database; the Watch says since when runs have been unbroken
(process start, or the end of a gap over 120 s). seconds_since_seen and
the no-data sweep count silence from no earlier than that, so a source
must stay quiet while Steward is watching before it counts as silent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:45:47 -04:00

53 lines
1.9 KiB
Python

"""Pure-function tests for the host_agent seconds_since_seen reconciler."""
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from plugins.host_agent.scheduler import seconds_since_seen
@dataclass
class FakeReg:
host_id: str
last_seen_at: datetime | None
created_at: datetime | None
NOW = datetime(2026, 4, 15, 12, 0, 0, tzinfo=timezone.utc)
NAMES = {"h1": "nas", "h2": "web", "h3": "db"}
def test_seconds_since_each_agent_last_reported():
regs = [FakeReg("h1", NOW - timedelta(seconds=30), NOW - timedelta(days=9)),
FakeReg("h2", NOW - timedelta(minutes=10), NOW - timedelta(days=9))]
assert seconds_since_seen(regs, NAMES, NOW) == [("nas", 30.0), ("web", 600.0)]
def test_an_agent_that_never_reported_counts_from_registration():
regs = [FakeReg("h3", None, NOW - timedelta(minutes=5))]
assert seconds_since_seen(regs, NAMES, NOW) == [("db", 300.0)]
def test_a_registration_without_its_host_is_skipped():
regs = [FakeReg("gone", NOW, NOW)]
assert seconds_since_seen(regs, NAMES, NOW) == []
def test_a_clock_ahead_of_ours_reads_as_just_seen():
regs = [FakeReg("h1", NOW + timedelta(seconds=5), NOW)]
assert seconds_since_seen(regs, NAMES, NOW) == [("nas", 0.0)]
def test_empty_input():
assert seconds_since_seen([], NAMES, NOW) == []
def test_steward_s_own_outage_is_not_the_agent_s_silence():
"""#5441: agents that last reported 10 minutes ago, while Steward has only
been back for a minute, are a minute old; an agent heard since then keeps
its own age."""
back = NOW - timedelta(minutes=1)
regs = [FakeReg("h1", NOW - timedelta(minutes=10), NOW - timedelta(days=9)),
FakeReg("h2", NOW - timedelta(seconds=20), NOW - timedelta(days=9)),
FakeReg("h3", None, NOW - timedelta(days=9))]
assert seconds_since_seen(regs, NAMES, NOW, back) == [("nas", 60.0), ("web", 20.0), ("db", 60.0)]