Welle 2 · Befundliste mit Ablauf: neu, gesehen, 24 h stumm; Puffer gegen Flackern
Jeder Hinweis hat jetzt einen Zustand (neu, gesehen, stumm bis …) mit Knöpfen in beiden Cockpits; das Homelab-Cockpit nutzt dieselbe Liste wie die KI-Box (Knöpfe gehen an den Homelab-Teil, neue Route). Stumm heißt: keine Telegram-Erinnerungen, unten in der Liste, keine Warnleuchte — verschärft er sich von gelb auf rot, gilt stumm nicht mehr. Ein Hinweis ist erst erledigt, wenn sein Befund drei Takte am Stück fehlt: Kurze Wackler bringen kein „Erledigt“ und gleich danach keinen neuen Alarm. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
45f11bc3dc
commit
995a0f34ee
@@ -0,0 +1,95 @@
|
||||
"""Befundliste mit Ablauf (Ausbauplan Welle 2, Punkt 10, 25.09.2026): Puffer gegen Flackern, Zustand je Hinweis
|
||||
(neu, gesehen, stumm bis …) und dass Kritisches trotz „stumm“ durchkommt."""
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from services import waechter
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wache(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(waechter, "STORE_PATH", tmp_path / "waechter.json")
|
||||
monkeypatch.setattr(waechter, "ZUSTAND_PATH", tmp_path / "zustand.json")
|
||||
monkeypatch.setattr(waechter, "_stand", {"hinweise": {}, "kandidaten": {}, "verlauf": [], "auto": {}, "stand": 0.0})
|
||||
monkeypatch.setattr(waechter, "_update_laeuft", lambda: False)
|
||||
monkeypatch.setattr(waechter, "FAIL_AFTER", 2)
|
||||
meldungen: list[str] = []
|
||||
monkeypatch.setattr(waechter, "_telegram", lambda betreff, text: meldungen.append(f"{betreff} {text}"))
|
||||
befund = {"stufe": "rot", "da": True}
|
||||
|
||||
def pruefung():
|
||||
if not befund["da"]:
|
||||
return []
|
||||
return [waechter.Befund(id="kern:engine", stufe=befund["stufe"], titel="Der Motor antwortet nicht", text="…",
|
||||
quelle="engine")]
|
||||
|
||||
monkeypatch.setattr(waechter, "PRUEFUNGEN", (pruefung,))
|
||||
return befund, meldungen
|
||||
|
||||
|
||||
def test_kurzer_wackler_bringt_kein_erledigt_und_keinen_neuen_alarm(wache):
|
||||
befund, meldungen = wache
|
||||
waechter.takt()
|
||||
waechter.takt()
|
||||
seit = waechter._stand["hinweise"]["kern:engine"]["seit"]
|
||||
assert len(meldungen) == 1
|
||||
befund["da"] = False
|
||||
waechter.takt() # einen Takt weg …
|
||||
befund["da"] = True
|
||||
waechter.takt() # … und wieder da: derselbe Hinweis, keine Meldung
|
||||
assert waechter._stand["hinweise"]["kern:engine"]["seit"] == seit and len(meldungen) == 1
|
||||
assert [v["art"] for v in waechter._stand["verlauf"]] == ["neu"]
|
||||
|
||||
|
||||
def test_zustand_neu_gesehen_stumm(wache):
|
||||
befund, meldungen = wache
|
||||
befund["stufe"] = "gelb"
|
||||
waechter.takt()
|
||||
waechter.takt()
|
||||
(h,) = waechter.lese_stand()["hinweise"]
|
||||
assert h["zustand"] == "neu" and "fehlt" not in h
|
||||
assert waechter.fuehre_aktion_aus("kern:engine", "gesehen") == {"ok": True, "text": "Als gesehen markiert."}
|
||||
assert waechter.lese_stand()["hinweise"][0]["zustand"] == "gesehen"
|
||||
antwort = waechter.fuehre_aktion_aus("kern:engine", "stumm")
|
||||
assert antwort["ok"] and antwort["text"].startswith("Stumm für 24 Stunden")
|
||||
h = waechter.lese_stand()["hinweise"][0]
|
||||
assert h["zustand"] == "stumm" and h["stumm_bis"] > h["zuletzt"]
|
||||
# Wird er rot, gilt stumm nicht mehr: Kritisches meldet sich trotzdem.
|
||||
befund["stufe"] = "rot"
|
||||
waechter.takt()
|
||||
assert waechter.lese_stand()["hinweise"][0]["zustand"] == "gesehen"
|
||||
assert len(meldungen) == 1 and "Der Motor antwortet nicht" in meldungen[0]
|
||||
assert waechter.fuehre_aktion_aus("kern:engine", "laut")["text"] == "Meldet sich wieder."
|
||||
|
||||
|
||||
def test_stumm_haelt_die_erinnerung_zurueck(wache, monkeypatch):
|
||||
_, meldungen = wache
|
||||
waechter.takt()
|
||||
waechter.takt()
|
||||
assert len(meldungen) == 1
|
||||
waechter.fuehre_aktion_aus("kern:engine", "stumm")
|
||||
monkeypatch.setattr(waechter, "REMIND_S", 0) # jede Erinnerung wäre fällig …
|
||||
waechter.takt()
|
||||
assert len(meldungen) == 1 # … aber stumm
|
||||
waechter.fuehre_aktion_aus("kern:engine", "laut")
|
||||
waechter.takt()
|
||||
assert len(meldungen) == 2 and "Immer noch" in meldungen[1]
|
||||
|
||||
|
||||
def test_stumme_stehen_unten_und_der_knopf_geht_auch_im_homelab(wache, monkeypatch):
|
||||
from routers import homelab
|
||||
befund, _ = wache
|
||||
befund["stufe"] = "gelb"
|
||||
waechter.takt()
|
||||
waechter.takt()
|
||||
waechter._stand["hinweise"]["zweiter"] = {"id": "zweiter", "stufe": "gelb", "titel": "B", "text": "", "quelle": "x",
|
||||
"seit": 1.0, "zuletzt": 2.0, "aktionen": []}
|
||||
waechter._speichere()
|
||||
waechter.fuehre_aktion_aus("zweiter", "stumm")
|
||||
assert [h["id"] for h in waechter.lese_stand()["hinweise"]] == ["kern:engine", "zweiter"]
|
||||
app = FastAPI()
|
||||
app.include_router(homelab.router)
|
||||
client = TestClient(app)
|
||||
assert client.post("/api/homelab/hinweise/kern:engine/aktion/stumm").json()["ok"] is True
|
||||
assert client.post("/api/homelab/hinweise/weg/aktion/stumm").status_code == 409
|
||||
@@ -156,7 +156,8 @@ def test_waechter_meldet_den_partner_erst_nach_geduld(monkeypatch, tmp_path):
|
||||
assert len(meldungen) == 1 and "Proxmox-PC (http://pve:9001) ist nicht erreichbar" in meldungen[0]
|
||||
|
||||
erreichbar[0] = True
|
||||
waechter.takt()
|
||||
for _ in range(waechter.ERLEDIGT_NACH): # Puffer gegen Flackern (seit 25.09.2026)
|
||||
waechter.takt()
|
||||
assert waechter._stand["hinweise"] == {}
|
||||
assert meldungen[-1] == "Erledigt: Homelab antwortet nicht."
|
||||
|
||||
|
||||
@@ -95,7 +95,11 @@ def test_befunde_werden_erst_nach_fail_after_takten_zum_hinweis(monkeypatch, tmp
|
||||
assert "kern:engine" in waechter._stand["hinweise"]
|
||||
assert meldungen == ["[Box-Problem]"]
|
||||
|
||||
# Seit 25.09.2026 mit Puffer: erst nach ERLEDIGT_NACH Takten ohne Befund ist der Hinweis erledigt.
|
||||
defekt[0] = False
|
||||
for _ in range(waechter.ERLEDIGT_NACH - 1):
|
||||
waechter.takt()
|
||||
assert "kern:engine" in waechter._stand["hinweise"] and meldungen == ["[Box-Problem]"]
|
||||
waechter.takt()
|
||||
assert waechter._stand["hinweise"] == {}
|
||||
assert meldungen == ["[Box-Problem]", "[Box wieder ok]"]
|
||||
|
||||
Reference in New Issue
Block a user