Files
planer2/backend/tests/test_ledger.py
2026-07-29 15:19:41 +02:00

166 lines
5.8 KiB
Python

"""Das Gedächtnis der Stilprüfung: was gilt weiter, was wird neu gefragt.
Die Prüfung fragt ein Modell. Dieselbe Frage kippt bei Grenzfällen auch bei
Temperatur 0 — vorher meldete Runde 1 einen Befund, Runde 2 keinen, Runde 3 wieder
einen. Hier steht, unter welchen Bedingungen ein Urteil stehen bleibt.
"""
import json
import pytest
import agents
import features
import ledger
import projekte
@pytest.fixture
def projekt(datenordner):
projekte.anlegen("Test")
return "Test"
def karte(projekt, titel, *funktionen):
"""funktionen sind (name, regel) oder (name, regel, [testschritte])."""
text = f"<!-- titel -->\n{titel}\n"
for eintrag in funktionen:
name, regel, schritte = (*eintrag, [])[:3]
text += f"<!-- funktion -->\n{name}\n<!-- regeln -->\n- {regel}\n"
if schritte:
text += "<!-- testen -->\n" + "".join(f"- {s}\n" for s in schritte)
return features.anlegen(projekt, text)
class Zaehler:
"""Ersetzt das Modell und zählt mit, wie oft überhaupt gefragt wurde."""
def __init__(self, antwort):
self.antwort = antwort
self.rufe = 0
async def __call__(self, system, nachricht, temperatur=0.0):
self.rufe += 1
wert = self.antwort
return wert(nachricht, self.rufe) if callable(wert) else wert
# ---------------------------------------------------------------- Datei
def test_leeres_gedaechtnis_hat_alle_faecher(projekt):
stand = ledger.lade(projekt)
assert set(stand) == set(ledger.FAECHER)
assert all(v == {} for v in stand.values())
def test_kaputte_datei_wird_wie_leer_behandelt(projekt):
(projekte.wurzel(projekt) / ledger.DATEI).write_text("{kein json")
assert ledger.lade(projekt)["belege"] == {}
def test_gemerktes_ueberlebt_den_lauf(projekt):
stand = ledger.lade(projekt)
stand["belege"][ledger.schluessel("a", "Tun")] = {"abdruck": "x", "datei": "a.py",
"beleg": "def tun():"}
ledger.sichere(projekt, stand)
assert ledger.lade(projekt)["belege"] == stand["belege"]
def test_schluessel_bleibt_zerlegbar(projekt):
"""Titel dürfen alles enthalten — auch das, was als Trennzeichen taugen würde."""
fies = 'Klick auf "x", dann | und \\ und ["a","b"]'
assert ledger.teile(ledger.schluessel("karte", fies)) == ["karte", fies]
def test_gedaechtnis_ist_kein_feature(projekt):
karte(projekt, "Eins")
ledger.sichere(projekt, ledger.lade(projekt))
assert [f["sektionen"]["titel"] for f in features.liste(projekt)] == ["Eins"]
# ---------------------------------------------------------------- Vision
VISION = "<!-- titel -->\nVision\n<!-- art -->\nvision\n<!-- ziel -->\nSo soll es werden\n"
def test_vision_ist_eine_eigene_kartenart(projekt):
f = features.anlegen(projekt, VISION)
assert f["sektionen"]["art"] == "vision"
assert f["sektionen"]["ziel"] == "So soll es werden"
def test_unbekannte_art_bleibt_kern(projekt):
f = features.anlegen(projekt, "<!-- titel -->\nX\n<!-- art -->\nwichtig\n")
assert f["sektionen"]["art"] == "kern"
def test_keine_pruefung_fasst_die_vision_an(projekt):
"""Die Vision ist die Vorlage, nicht der Prüfling."""
karte(projekt, "Eins", ("Etwas starten", "Knopf"))
features.anlegen(projekt, VISION)
assert len(features.liste(projekt)) == 2
titel = [f["sektionen"]["titel"] for f in features.pruefbar(projekt)]
assert titel == ["Eins"]
# ---------------------------------------------------------------- Prüfung 1
async def test_stilurteil_wird_gemerkt_und_nicht_neu_gefragt(projekt, monkeypatch):
"""176 Anfragen je Lauf brachten den Anbieter zum Drosseln. Unveränderter
Ausschnitt bei unverändertem Katalog heißt: das Urteil von vorhin gilt."""
import pruefung
karte(projekt, "Eins", ("Etwas starten", "Knopf"))
modell = Zaehler('{"felder": {}}')
monkeypatch.setattr(agents, "frage", modell)
liste, arbeite, abschluss = await pruefung._stil_aufgaben(projekt)
for a in liste:
assert await arbeite(a, None) == []
abschluss()
erste = modell.rufe
assert erste >= len(pruefung.TEMPERATUREN)
liste, arbeite, abschluss = await pruefung._stil_aufgaben(projekt)
for a in liste:
assert await arbeite(a, None) == []
assert modell.rufe == erste # keine einzige neue Anfrage
async def test_geaenderter_text_wird_neu_gefragt(projekt, monkeypatch):
import pruefung
karte(projekt, "Eins", ("Etwas starten", "Knopf"))
modell = Zaehler('{"felder": {}}')
monkeypatch.setattr(agents, "frage", modell)
liste, arbeite, abschluss = await pruefung._stil_aufgaben(projekt)
for a in liste:
await arbeite(a, None)
abschluss()
vorher = modell.rufe
datei = projekte.wurzel(projekt) / f"{features.liste(projekt)[0]['id']}.md"
datei.write_text(datei.read_text().replace("- Knopf", "- Ganz anderer Text"))
liste, arbeite, _ = await pruefung._stil_aufgaben(projekt)
for a in liste:
await arbeite(a, None)
assert modell.rufe > vorher
async def test_gemessener_verstoss_kommt_nie_aus_dem_gedaechtnis(projekt, monkeypatch):
"""Sonst bliebe eine neu hinzugekommene Messregel an alten Karten wirkungslos."""
import pruefung
karte(projekt, "Eins", ("Etwas starten", "Auch Passwort prüfen."))
modell = Zaehler('{"felder": {"regeln": {"neu": "- Passwort prüfen",'
' "grund": "Füllwort", "regel": "x"}}}')
monkeypatch.setattr(agents, "frage", modell)
for lauf in range(2):
liste, arbeite, abschluss = await pruefung._stil_aufgaben(projekt)
gefunden = [p for a in liste for p in await arbeite(a, None)]
abschluss()
assert [p["vorschlag"] for p in gefunden] == ["Passwort prüfen"], lauf
assert modell.rufe > len(pruefung.TEMPERATUREN) # zweimal wirklich gefragt