Files
mission-control-v2/backend/tests/test_wartung.py
T
HitonabiandClaude Opus 5.5 589c14d5e9 phase2d: gemeinsames Ziel-Modell, KI-Box als erster Adapter, strukturierter Update-Verlauf
- 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>
2026-09-24 18:14:42 +02:00

90 lines
3.9 KiB
Python

"""Kleinigkeiten der Update-Seite (24.09.2026): Größe je Quantisierung, GitHub-Zwischenspeicher,
Hermes-Version auch dann, wenn der Abruf scheitert."""
import subprocess
import httpx
import pytest
from services import hf, maintenance
def test_quants_mit_groesse(monkeypatch):
baum = [
{"path": "Q8_0/M-Q8_0-00001-of-00002.gguf", "size": 50},
{"path": "Q8_0/M-Q8_0-00002-of-00002.gguf", "size": 50},
{"path": "M-Q4_K_M.gguf", "lfs": {"size": 30}},
{"path": "mmproj-F16.gguf", "size": 5},
{"path": "README.md", "size": 1},
]
aufrufe = []
monkeypatch.setattr(hf, "_tree", lambda repo: aufrufe.append(repo) or baum)
assert hf.list_quants("x/y") == [{"quant": "Q4_K_M", "total_bytes": 35}, {"quant": "Q8_0", "total_bytes": 105}]
assert aufrufe == ["x/y"] # ein Abruf für alle Stufen
class _Antwort:
def __init__(self, status: int, daten: object):
self.status_code = status
self._daten = daten
def raise_for_status(self):
if self.status_code >= 400:
raise httpx.HTTPStatusError("fehler", request=httpx.Request("GET", "https://x"),
response=httpx.Response(self.status_code))
def json(self):
return self._daten
def test_github_antworten_werden_gemerkt_fehler_nicht(monkeypatch):
monkeypatch.setattr(maintenance, "_github_cache", {})
antworten = [_Antwort(403, {"message": "API rate limit exceeded"}), _Antwort(200, {"tag_name": "v258"})]
aufrufe = []
monkeypatch.setattr(maintenance.httpx, "get", lambda url, **kw: aufrufe.append(url) or antworten.pop(0))
with pytest.raises(httpx.HTTPStatusError):
maintenance._github_json("repos/a/b/releases/latest")
assert maintenance._github_json("repos/a/b/releases/latest") == {"tag_name": "v258"}
assert maintenance._github_json("repos/a/b/releases/latest") == {"tag_name": "v258"}
assert len(aufrufe) == 2
def test_hermes_version_auch_wenn_der_abruf_haengt(monkeypatch, tmp_path):
(tmp_path / "pyproject.toml").write_text('[project]\nversion = "0.21.4"\n', encoding="utf-8")
monkeypatch.setattr(maintenance.system, "find_hermes_agent_git", lambda: {"path": str(tmp_path)})
def run(befehl, **kw):
if "fetch" in befehl:
raise subprocess.TimeoutExpired(befehl, 25)
return subprocess.CompletedProcess(befehl, 0, stdout="main\n", stderr="")
monkeypatch.setattr(maintenance.subprocess, "run", run)
info = maintenance.hermes_update_details()
assert info["installed_version"] == "0.21.4"
assert "error" in info
def test_update_per_knopf_landet_im_verlauf(monkeypatch):
"""Einzel-Updates und „Alle aktualisieren“ schreiben ins strukturierte Verlauf-Format; bei einer
gescheiterten Kette zeigt die letzte Teil-Marke, welcher Teil scheiterte."""
from services import jobengine, update_verlauf
zeilen: list[tuple] = []
monkeypatch.setattr(update_verlauf, "eintragen", lambda *a: zeilen.append(a))
job = {"id": "x", "state": "done", "returncode": 0, "started_at": 1790000000.0,
"abschluss_daten": {"bausteine": ["engine"]}}
maintenance._update_im_verlauf(job)
assert zeilen[-1][1:] == ("Update von Hand", "engine", "eingespielt", "Per Knopf eingespielt, Prüfung grün.")
zeilen.clear()
monkeypatch.setattr(jobengine, "protokoll", lambda job_id: [
"$ bash -c …", "════════ [1/3] Engine (llama.cpp) ════════", "ok",
"════════ [2/3] Router (llama-swap) ════════", "Stack-Check rot"])
maintenance._update_im_verlauf({**job, "state": "failed", "returncode": 1,
"abschluss_daten": {"bausteine": ["engine", "swap", "hermes"]}})
assert [(z[2], z[3]) for z in zeilen] == [
("engine", "eingespielt"), ("swap", "zurueckgerollt"), ("hermes", "offen")]
zeilen.clear()
maintenance._update_im_verlauf({**job, "abschluss_daten": {}})
assert zeilen == []