- kern/ziele.py: Ziel -> Bausteine mit Stand (neu/aktuell/unbekannt/festgehalten/wird-geprueft), Versionen und dem Knopf, der das Update anstoesst; gleiches Modell fuer Box und Homelab - services/box_updates.py: Update-Zwischenspeicher aus dem Router geholt, ki_box_ziel() als Box-Adapter; GET /api/ziele - Update-Verlauf strukturiert: autoupdate.sh und die Update-Knoepfe schreiben je Baustein eine JSON-Zeile (mc2-update-verlauf.jsonl); die Meldungstexte bleiben gleich, aeltere Laeufe kommen weiter aus dem Meldeprotokoll. Updates per Knopf erscheinen jetzt auch im Verlauf. - jobengine: Abschluss-Haken fuer jedes Ende (neben der Nacharbeit fuer den Erfolg) - Sonntags-Lauf setzt seinen Anlass; Hermes-Job aus dem Lauf schreibt nicht doppelt - Vertragstest Bash-Schreiber gegen Python-Leser (laeuft auf der Box mit jq) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68 lines
3.4 KiB
Python
68 lines
3.4 KiB
Python
"""Gemeinsames Ziel-Modell (Phase 2, 24.09.2026) und die KI-Box als erster Adapter."""
|
|
|
|
import pytest
|
|
from kern.ziele import BausteinStand, ZielStand
|
|
from services import box_updates, maintenance, system, update_verlauf
|
|
|
|
|
|
def test_als_dict_ordnet_nach_dringlichkeit_und_zaehlt():
|
|
ziel = ZielStand(id="ct-104", name="Gitea", art="container", instanz="homelab", bausteine=[
|
|
BausteinStand(id="os", name="Betriebssystem", zustand="aktuell"),
|
|
BausteinStand(id="app", name="Gitea", zustand="neu", kurz="1.27.2 → 1.27.3"),
|
|
BausteinStand(id="docker", name="Docker", zustand="unbekannt", grund="nicht erreichbar"),
|
|
])
|
|
daten = ziel.als_dict()
|
|
assert [b["id"] for b in daten["bausteine"]] == ["docker", "app", "os"]
|
|
assert daten["zaehlung"] == {"aktuell": 1, "neu": 1, "unbekannt": 1} and daten["offen"] == 1
|
|
|
|
|
|
@pytest.fixture
|
|
def box(monkeypatch):
|
|
monkeypatch.setattr(maintenance, "_installed_engine_build", lambda: 11160)
|
|
monkeypatch.setattr(maintenance, "_installed_swap_version", lambda: 257)
|
|
monkeypatch.setattr(maintenance, "_latest_engine_asset_release", lambda: {"tag_name": "b11172"})
|
|
monkeypatch.setattr(maintenance, "_github_json", lambda pfad, timeout=8: {"tag_name": "v258"})
|
|
monkeypatch.setattr(maintenance, "_hermes_version", lambda pfad: "0.21.4")
|
|
monkeypatch.setattr(system, "find_hermes_agent_git", lambda: {"path": "/x", "hash": "abc1234"})
|
|
monkeypatch.setattr(update_verlauf, "festgehalten", list)
|
|
monkeypatch.setattr(box_updates, "gelesen", lambda: True)
|
|
|
|
def setzen(daten: dict) -> None:
|
|
monkeypatch.setattr(box_updates, "gecacht", lambda: daten)
|
|
|
|
return setzen
|
|
|
|
|
|
def _nach_id(ziel: ZielStand) -> dict[str, BausteinStand]:
|
|
return {b.id: b for b in ziel.bausteine}
|
|
|
|
|
|
def test_ki_box_neu_aktuell_und_knoepfe(box):
|
|
box({"os": 10, "engine": 1, "swap": 0, "pruef_fehler": {},
|
|
"components": [{"key": "hermes_agent", "update": True, "behind": 743, "current": "abc1234"}]})
|
|
b = _nach_id(box_updates.ki_box_ziel())
|
|
assert (b["os"].zustand, b["os"].kurz) == ("neu", "10 Pakete")
|
|
assert (b["engine"].installiert, b["engine"].verfuegbar) == ("b11160", "b11172")
|
|
assert (b["swap"].zustand, b["swap"].installiert, b["swap"].verfuegbar) == ("aktuell", "v257", None)
|
|
assert (b["hermes"].installiert, b["hermes"].verfuegbar) == ("0.21.4", "+743 Änderungen")
|
|
assert b["engine"].aktion.pfad == "/api/maintenance/engine-update"
|
|
assert b["swap"].aktion is None
|
|
|
|
|
|
def test_ki_box_unbekannt_und_festgehalten_ohne_knopf(box, monkeypatch):
|
|
monkeypatch.setattr(update_verlauf, "festgehalten", lambda: [
|
|
{"baustein": "engine", "name": "Motor", "version": "10819", "seit": "13.09.", "grund": "Check rot"}])
|
|
box({"os": 0, "engine": 1, "swap": 0, "pruef_fehler": {"swap": "GitHub nicht erreichbar"}, "components": []})
|
|
b = _nach_id(box_updates.ki_box_ziel())
|
|
assert b["engine"].zustand == "festgehalten" and b["engine"].aktion is None
|
|
assert b["engine"].grund == "Seit 13.09. auf 10819: Check rot"
|
|
assert (b["swap"].zustand, b["swap"].grund) == ("unbekannt", "GitHub nicht erreichbar")
|
|
assert b["os"].zustand == "aktuell"
|
|
|
|
|
|
def test_ki_box_vor_der_ersten_pruefung(box, monkeypatch):
|
|
monkeypatch.setattr(box_updates, "gelesen", lambda: False)
|
|
box({})
|
|
ziel = box_updates.ki_box_ziel()
|
|
assert {b.zustand for b in ziel.bausteine} == {"wird-geprueft"} and len(ziel.bausteine) == 4
|