feat(hands,jobs): drill days skip the beat and ack carries the alert decision

This commit is contained in:
hh committed 2026-09-26 01:52:53 +00:00
1 parent 47c31ac356
commit a39e0b27af
4 files changed
+135 -7

No files matched your search

+60
View File
@@ -10,6 +10,7 @@ from beaver_agent.hands.heartbeat import (
Watch,
alarm_text,
beat_line,
drill,
every_from_cron,
look,
parse_beat,
@@ -198,6 +199,39 @@ def test_alarm_not_repeated_within_a_day(tmp_path):
assert look(w, [], now=NOW, res=None, full=True).alarm == "silence"
def test_ack_says_what_the_instrument_decided(tmp_path):
# Цепь жива - решать нечего
alive = state(tmp_path)
assert "alert=none" in look(alive, [hb(NOW)], now=NOW, res=None, full=True).ack
# Порог пройден, тревога взята в этом прогоне
loud = state(tmp_path, seen=NOW - timedelta(hours=40), every=timedelta(hours=24))
fired = look(loud, [], now=NOW, res=None, full=True)
assert fired.alarm == "silence" and "alert=fired" in fired.ack
# Порог пройден, но тревога уходила меньше суток назад - повтор подавлен
loud.alerted = NOW - timedelta(hours=5)
held = look(loud, [], now=NOW, res=None, full=True)
assert held.alarm is None and "alert=held" in held.ack
# Кольцо так и не вооружилось дольше недели - это тоже выстрел
late = state(tmp_path, unarmed_since=NOW - timedelta(days=8))
assert "alert=fired" in look(late, [], now=NOW, res=None, full=True).ack
# Показания нет (окно не дочитано) - не `held`, а `none`: подавлять нечего
unread = state(tmp_path, seen=NOW - timedelta(hours=40), every=timedelta(hours=24))
assert "alert=none" in look(unread, [], now=NOW, res=None, full=False).ack
def test_drill_flag_is_a_list_of_local_dates(tmp_path):
flag = tmp_path / "сторож-учения.txt"
assert not drill(flag, now=NOW, tz=WARSAW) # файла нет - отметка уходит
flag.write_text("\n \n", encoding="utf-8")
assert not drill(flag, now=NOW, tz=WARSAW) # пустой - тоже
flag.write_text("2026-09-28\n2026-09-29\n", encoding="utf-8")
assert drill(flag, now=datetime(2026, 9, 28, 15, 12, tzinfo=UTC), tz=WARSAW)
assert not drill(flag, now=datetime(2026, 9, 27, 15, 12, tzinfo=UTC), tz=WARSAW)
# Дата локальная в зоне планировщика: 22:30 UTC - это уже 29-е в Варшаве
assert drill(flag, now=datetime(2026, 9, 28, 22, 30, tzinfo=UTC), tz=WARSAW)
assert not drill(flag, now=datetime(2026, 9, 29, 22, 30, tzinfo=UTC), tz=WARSAW)
def test_alarm_text_names_three_causes_and_suspects_the_channel(tmp_path):
w = state(tmp_path, seen=NOW - timedelta(hours=40), every=timedelta(hours=24))
report = look(w, [], now=NOW, res=timedelta(hours=24), full=True)
@@ -318,6 +352,32 @@ async def test_beat_job_sends_period_from_its_own_cron(monkeypatch):
assert "every=unknown src=none" in odd.said[0]
async def test_beat_job_keeps_silent_on_a_drill_day(tmp_path, monkeypatch, caplog):
from beaver_agent.jobs import heartbeat as job
flag = tmp_path / "сторож-учения.txt"
monkeypatch.setattr(job, "DRILL_FILE", flag)
room = FakeRoom([], nick="beaver")
monkeypatch.setattr(job, "VIBEGRAM_HB", room)
today = datetime.now(WARSAW).strftime("%Y-%m-%d")
flag.write_text(f"2026-01-01\n{today}\n", encoding="utf-8")
with caplog.at_level(logging.INFO):
await job.beat(cast("Any", FakeRun(cron="12 17 * * *")))
assert not room.said # молчание в комнате и есть учения
assert "пропущена по флагу" in caplog.text
# Не наш день - отметка уходит как обычно
flag.write_text("2026-01-01\n", encoding="utf-8")
await job.beat(cast("Any", FakeRun(cron="12 17 * * *")))
assert len(room.said) == 1 and room.said[0].startswith("hb beaver at=")
# Файла нет - тоже обычный прогон
flag.unlink()
await job.beat(cast("Any", FakeRun(cron="12 17 * * *")))
assert len(room.said) == 2
async def test_watch_job_keeps_quiet_when_the_room_is_unreadable(tmp_path, monkeypatch):
from beaver_agent.jobs import heartbeat as job