feat(hands,jobs): drill days skip the beat and ack carries the alert decision
This commit is contained in:
1 parent
47c31ac356
commit
a39e0b27af
4 files changed
+135
-7
No files matched your search
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user