Files
checkbot/tests/test_formatter_host_short.py
patrickandClaude Sonnet 5.5 347f55cf7c
test / pytest (3.11) (push) Canceled after 0s
test / pytest (3.13) (push) Canceled after 0s
feat: Phase 6c baseline, ack, host alarms, outage notice, send limit, privacy mode, UI link
Includes atomic migrations, per-rule reminder semantics, first-run helper script
and the note on the undecided UI chat idea.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-06 03:06:23 +01:00

96 lines
4.1 KiB
Python

from datetime import datetime
from app.alerts import Analysis, build_user_prompt, format_message
from app.models import (HOST_SERVICE_NAME, KIND_FLAPPING, KIND_PROBLEM, KIND_RECOVERY, KIND_REMINDER, Event,
display_state, severity)
NOW = int(datetime(2026, 11, 6, 20, 0).timestamp())
SINCE = int(datetime(2026, 11, 6, 18, 5).timestamp())
HOUR = 3600
def host_event(kind=KIND_PROBLEM, state=2, **kwargs):
return Event(ts=NOW, host="srv1", service=HOST_SERVICE_NAME, kind=kind, state=state, output="no route", **kwargs)
def service_event(kind=KIND_PROBLEM, state=2, **kwargs):
return Event(ts=NOW, host="srv1", service="zpool", kind=kind, state=state, output="kaputt", **kwargs)
def test_display_state_names_hosts_and_services():
assert [display_state(HOST_SERVICE_NAME, s) for s in (0, 2, 3)] == ["UP", "DOWN", "UNREACH"]
assert [display_state("cpu", s) for s in (0, 1, 2, 3)] == ["OK", "WARN", "CRIT", "UNKNOWN"]
def test_severity_orders_crit_before_unknown_before_warn():
assert severity(2) > severity(3) > severity(1) > severity(0)
def test_host_down_line():
text = format_message([host_event(since_ts=SINCE)])
assert "<b>srv1</b>" in text and "<b>Host DOWN</b>, seit 18:05: no route" in text
assert "Host" in text and "zpool" not in text
def test_host_unreach_line_and_transition():
assert "<b>Host UNREACH</b>: no route" in format_message([host_event(state=3)])
text = format_message([host_event(state=3, previous_state=2, previous_duration=HOUR, since_ts=SINCE)])
assert "<b>Host DOWN → UNREACH</b>, seit 18:05 (vorher DOWN seit 1 h): no route" in text
def test_host_recovery_text():
event = host_event(kind=KIND_RECOVERY, state=0, previous_state=2, problem_since_ts=NOW - (3 * HOUR + 12 * 60))
assert "<b>Host wieder UP</b> nach 3 h 12 min (zuletzt DOWN)" in format_message([event])
def test_host_recovery_without_delta():
assert "<b>Host wieder UP</b>" in format_message([host_event(kind=KIND_RECOVERY, state=0)])
def test_host_reminder_line():
text = format_message([host_event(kind=KIND_REMINDER, since_ts=NOW - 4 * HOUR)])
assert "<b>Host DOWN</b>: weiter DOWN seit 4 h: no route" in text
def test_host_output_is_escaped():
event = Event(ts=NOW, host="<h>", service=HOST_SERVICE_NAME, kind=KIND_PROBLEM, state=2,
output="<script>x</script>")
text = format_message([event])
assert "<script>" not in text and "&lt;script&gt;" in text and "<h>" not in text
def test_user_prompt_for_host_names_status_down():
prompt = build_user_prompt(host_event(), [])
assert "Status: DOWN" in prompt and f"Service: {HOST_SERVICE_NAME}" in prompt
def test_short_mode_drops_output_but_keeps_delta():
event = service_event(previous_state=1, previous_duration=2 * HOUR, since_ts=SINCE)
text = format_message([event], mode="kurz")
assert "<b>WARN → CRIT</b> zpool, seit 18:05 (vorher WARN seit 2 h)" in text
assert "kaputt" not in text and ": " not in text.split("\n")[1]
def test_short_mode_keeps_recovery_and_reminder_text_without_output():
recovery = service_event(kind=KIND_RECOVERY, state=0, previous_state=2, problem_since_ts=NOW - HOUR)
assert "<b>OK</b> zpool: wieder OK nach 1 h (zuletzt CRIT)" in format_message([recovery], mode="kurz")
reminder = service_event(kind=KIND_REMINDER, since_ts=NOW - 4 * HOUR)
text = format_message([reminder], mode="kurz")
assert "weiter CRIT seit 4 h" in text and "kaputt" not in text
def test_short_mode_flapping_and_host_without_output():
assert "kaputt" not in format_message([service_event(kind=KIND_FLAPPING)], mode="kurz")
text = format_message([host_event(since_ts=SINCE)], mode="kurz")
assert "<b>Host DOWN</b>, seit 18:05" in text and "no route" not in text
def test_short_mode_never_contains_ai_text():
event = service_event()
assert "Analyse" not in format_message([event], {0: Analysis(text="geheim")}, mode="kurz")
assert "geheim" in format_message([event], {0: Analysis(text="geheim")})
def test_full_mode_is_default_and_unchanged():
assert format_message([service_event()]) == format_message([service_event()], mode="voll")