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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
dd23f3fb4c
commit
12cefb427a
@@ -0,0 +1,75 @@
|
||||
"""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")]
|
||||
Reference in New Issue
Block a user