Files
mission-control-v2/backend/tests/test_lucy_logs.py
T
HitonabiandClaude Opus 5.5 12cefb427a Welle 3 · Lucys Freiheiten je Aktionsart, Rückfragen mit „ja 7“, Lucy untersucht rote Hinweise, Wochenbericht
- services/lucy_autonomie.py: Stufe je Aktionsart (nur lesen, erst fragen, selbst handeln), Start „mehr Freiheit“;
  Meldung danach (POST /api/lucy/gehandelt)
- services/rueckfragen.py: nummerierte Rückfragen, nur Knöpfe der Oberfläche (ERLAUBT), „ja“ führt genau die
  gespeicherte Aktion aus, 24 h gültig, hinfällig wenn der Hinweis weg ist, Nachprüfung danach
- notify.sh -k: Antworttasten (Antworttastatur über den Direktweg, ohne Eingriff in Hermes)
- services/ermittlung.py: Wächter meldet rote Hinweise an Lucy; Knopf wird Rückfrage, Lucy sieht über den
  api_server nach und schickt Ursache und Weg als eine Nachricht
- services/wochenbericht.py + mc2-wochenbericht.timer (So 07:30): feste Sätze, Absatz vom Modell, offene
  App-Updates als Rückfragen
- MCP: Werkzeuge fragen die Freiheiten (auf_bitte), neu: freiheiten, rueckfragen, rueckfrage_beantworten,
  sicherheitsupdates_einspielen, snapshot_zurueckspielen, snapshots, geraet_logs, dienst_logs
- Ausführer-Aktion journal (nur lesend), POST /api/homelab/ziele/{id}/sicherheitsupdates
- Läufe des Homelabs mit flock (Oberfläche und Steward schreiben beide)
- Skill homelab-orchestrator, SOUL-Abschnitt als Kopie in deploy/nur-box

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 19:12:50 +02:00

76 lines
3.8 KiB
Python

"""Lucy liest Protokolle, wenn sie einen Hinweis untersucht (Ausbauplan Welle 3, Punkt 14, 25.09.2026): die Aktion
journal des Ausführers (nur lesend) und GET /api/homelab/ziele/{id}/journal; dazu der Knopf „nur Sicherheitsupdates“
(POST /api/homelab/ziele/{id}/sicherheitsupdates), den ihre Werkzeuge und die Rückfragen nutzen."""
import importlib.util
from pathlib import Path
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from services.homelab import inventar, journal, kanal, updates
AUSFUEHRER = Path(__file__).resolve().parents[2] / "deploy" / "homelab" / "ausfuehrer.py"
@pytest.fixture(scope="module")
def a():
spec = importlib.util.spec_from_file_location("ausfuehrer_journal", AUSFUEHRER)
modul = importlib.util.module_from_spec(spec)
spec.loader.exec_module(modul)
return modul
def test_ausfuehrer_liest_nur(a, monkeypatch):
befehle = []
monkeypatch.setattr(a, "_gast_pruefen", lambda vmid: ("lxc", vmid))
monkeypatch.setattr(a, "_im_gast", lambda vmid, befehl, zeitlimit=30: befehle.append((vmid, befehl)) or (0, "x" * 20000))
monkeypatch.setattr(a, "NUR_LESEN", True) # geht auch im Nur-Lesen-Modus
antwort = a.ausfuehren({"aktion": "journal", "parameter": {"vmid": 104, "zeilen": 50}})
assert antwort["code"] == 0 and len(antwort["text"]) == 12000
vmid, befehl = befehle[0]
assert vmid == 104 and "journalctl -p warning -n 50 --no-pager -o short-iso --since -24h" in befehl
assert "tail -n 50 /var/log/messages" in befehl
for falsch in ({"vmid": 104, "zeilen": "80; reboot"}, {"vmid": 104, "zeilen": 5000}):
abgelehnt = a.ausfuehren({"aktion": "journal", "parameter": falsch})
assert abgelehnt["code"] == 2 and "zeilen" in abgelehnt["text"]
monkeypatch.setattr(a, "_gast_pruefen", lambda vmid: ("qemu", vmid))
assert "nur von Containern" in a.ausfuehren({"aktion": "journal", "parameter": {"vmid": 106}})["text"]
@pytest.fixture
def gaeste(monkeypatch):
liste = {"ct-104": {"vmid": 104, "art": "lxc", "erlaubt": True}, "vm-106": {"vmid": 106, "art": "qemu", "erlaubt": True},
"ct-120": {"vmid": 120, "art": "lxc", "erlaubt": False}}
monkeypatch.setattr(inventar, "gast", lambda zid: liste.get(zid))
auftraege = []
ergebnis = {"status": "fertig", "text": "2026-09-25T03:12:00 gitea[311]: database is locked\n"}
monkeypatch.setattr(kanal, "anlegen", lambda aktion, parameter=None: auftraege.append((aktion, parameter)) or "a1")
monkeypatch.setattr(kanal, "warten", lambda aid, zeitlimit_s, takt_s=2.0: ergebnis)
return auftraege, ergebnis
def test_journal_ueber_den_ausfuehrer(gaeste):
auftraege, ergebnis = gaeste
assert journal.lesen("ct-104", 5000) == {"ok": True, "text": "2026-09-25T03:12:00 gitea[311]: database is locked"}
assert auftraege == [("journal", {"vmid": 104, "zeilen": 300})]
assert "nur von Containern" in journal.lesen("vm-106")["detail"]
assert "nicht freigegeben" in journal.lesen("ct-120")["detail"]
assert "nicht im Bericht" in journal.lesen("ct-999")["detail"]
ergebnis.update(status="fehler", text="Abgelehnt: Gast 104 hat kein erlaubtes Etikett")
assert journal.lesen("ct-104")["detail"].startswith("Abgelehnt")
def test_schnittstellen(gaeste, monkeypatch):
from routers import homelab
gestartet = []
monkeypatch.setattr(updates, "starten", lambda zid, baustein, sammellauf=None: gestartet.append((zid, baustein))
or {"ok": True, "lauf": "l9"})
app = FastAPI()
app.include_router(homelab.router)
c = TestClient(app)
assert c.get("/api/homelab/ziele/ct-104/journal").json()["ok"]
assert c.get("/api/homelab/ziele/vm-106/journal").status_code == 409
assert c.post("/api/homelab/ziele/ct-104/sicherheitsupdates").json() == {"ok": True, "lauf": "l9"}
assert gestartet == [("ct-104", "sicherheit")]