Files
planer/backend/tests/test_gates.py
2026-07-22 16:12:23 +02:00

160 lines
6.5 KiB
Python

"""Gates gegen das mini_repo-Fixture: gültiges Fragment passiert, jede Manipulation
wird mit genau dem erwarteten Fehler gefangen."""
from pathlib import Path
import gates
MINI = Path(__file__).parent / "fixtures" / "mini_repo"
GUT = '''# Einheiten: rechner.py
stand: UNBEKANNT
## rechner.py::addiere
beschreibung: Addiert zwei Zahlen.
input: zwei Zahlen a, b
output: die Summe
entscheidungen:
- Reine Funktion ohne Zustand.
beleg: "return a + b"
kanten:
- wird-genutzt-von: anzeige.py::zeige
## rechner.py::Speicher
beschreibung: Merkt sich das letzte Ergebnis.
input: Werte über merke()
output: letzter Wert in self.letztes
entscheidungen:
- Startet leer.
beleg: "self.letztes = None"
kanten:
- nutzt: rechner.py::addiere
'''
def test_gutes_fragment_passiert():
fehler, warnungen, einheiten = gates.pruefe_fragment(GUT, MINI, "rechner.py")
assert fehler == []
assert [e["id"] for e in einheiten] == ["rechner.py::addiere", "rechner.py::Speicher"]
def test_falsches_zitat():
kaputt = GUT.replace('beleg: "return a + b"', 'beleg: "return a - b"')
fehler, _, _ = gates.pruefe_fragment(kaputt, MINI, "rechner.py")
assert any("Zitat nicht wörtlich" in f for f in fehler)
def test_erfundener_anker():
kaputt = GUT.replace("## rechner.py::addiere", "## rechner.py::subtrahiere")
fehler, _, _ = gates.pruefe_fragment(kaputt, MINI, "rechner.py")
assert any("Symbol 'subtrahiere' nicht in" in f for f in fehler)
def test_kaputte_kante():
kaputt = GUT.replace("- nutzt: rechner.py::addiere",
"- nutzt: rechner.py::addiere (wichtig!)")
fehler, _, _ = gates.pruefe_fragment(kaputt, MINI, "rechner.py")
assert any("Kante unlesbar" in f for f in fehler)
def test_unbekannter_kanten_typ():
kaputt = GUT.replace("- nutzt:", "- verwendet:")
fehler, _, _ = gates.pruefe_fragment(kaputt, MINI, "rechner.py")
assert any("unbekannter Kanten-Typ" in f for f in fehler)
def test_fremde_datei_abgelehnt():
fehler, _, _ = gates.pruefe_fragment(GUT, MINI, "anzeige.py")
assert any("gehört nicht zur gescannten Datei" in f for f in fehler)
def test_leere_antwort():
fehler, _, _ = gates.pruefe_fragment("", MINI, "rechner.py")
assert any("keine Einheit geparst" in f for f in fehler)
def test_escaping_normalisierung():
frag = GUT.replace('beleg: "return a + b"', 'beleg: "return f\\"Ergebnis: {addiere(a, b)}\\""')
frag = frag.replace("## rechner.py::addiere", "## anzeige.py::zeige").replace(
"# Einheiten: rechner.py", "# Einheiten: anzeige.py")
# nur den umgebauten zeige-Block prüfen
block = frag.split("\n\n")[1]
fehler, _, _ = gates.pruefe_fragment(block, MINI, "anzeige.py")
assert not any("Zitat" in f for f in fehler)
def test_fehlende_symbole():
_, _, einheiten = gates.pruefe_fragment(GUT, MINI, "rechner.py")
fehlt = gates.fehlende_symbole(["addiere", "skaliere", "Speicher", "BASIS"], einheiten, [])
assert fehlt == ["skaliere", "BASIS"]
fehlt2 = gates.fehlende_symbole(["skaliere"], einheiten, ["rechner.py::skaliere"])
assert fehlt2 == []
def test_repariere_kanten():
"""Erfundene Kanten-Ziele werden gestrichen (Datei erfunden, Symbol erfunden,
Prefix fehlt) — gültige Kanten und alle anderen Zeilen bleiben unangetastet."""
kaputt = GUT.replace(
"- nutzt: rechner.py::addiere",
"- nutzt: rechner.py::addiere\n- ruft-auf: erfunden.py::x\n"
"- nutzt: rechner.py::gibtsnicht\n- löst-aus: nur_symbol")
bereinigt, gestrichen = gates.repariere_kanten(kaputt, MINI, "rechner.py")
assert len(gestrichen) == 3
assert "- nutzt: rechner.py::addiere" in bereinigt
assert "erfunden.py" not in bereinigt and "gibtsnicht" not in bereinigt
fehler, _, _ = gates.pruefe_fragment(bereinigt, MINI, "rechner.py")
assert fehler == []
def test_repariere_belege_schnappt_fast_richtige_zitate():
"""Ein-Zeichen-Abweichung (`=` statt Original) wird auf die echte Quellzeile
geschnappt; frei erfundene Zitate bleiben stehen und fallen im Gate durch."""
kaputt = GUT.replace('beleg: "return a + b"', 'beleg: "return a+b"')
repariert, korrigiert = gates.repariere_belege(kaputt, MINI, "rechner.py")
assert len(korrigiert) == 1
fehler, _, _ = gates.pruefe_fragment(repariert, MINI, "rechner.py")
assert fehler == []
erfunden = GUT.replace('beleg: "return a + b"', 'beleg: "raise TotalErfunden(xyz)"')
repariert2, korrigiert2 = gates.repariere_belege(erfunden, MINI, "rechner.py")
assert korrigiert2 == []
fehler2, _, _ = gates.pruefe_fragment(repariert2, MINI, "rechner.py")
assert any("Zitat nicht wörtlich" in f for f in fehler2)
def test_qualifizierte_methode_als_symbol():
frag = GUT.replace("## rechner.py::Speicher", "## rechner.py::Speicher.merke")
fehler, _, _ = gates.pruefe_fragment(frag, MINI, "rechner.py")
assert not any("Speicher.merke" in f for f in fehler)
def test_beleg_mit_anfuehrungszeichen():
"""Belege mit eingebetteten Quotes/Regex (Befund 17) parsen und passieren."""
frag = ('## anzeige.py::zeige\nbeschreibung: Formatiert.\ninput: a, b\noutput: Text\n'
'entscheidungen:\n- Nutzt einen f-String.\n'
' beleg: "return f"Ergebnis: {addiere(a, b)}""\nkanten:\n')
fehler, _, _ = gates.pruefe_fragment(frag, MINI, "anzeige.py")
assert fehler == []
def test_datei_einheit_ohne_symbole(tmp_path):
"""Skript ohne def/class: Datei-Einheit `## pfad` (Befund 18)."""
(tmp_path / "skript.py").write_text('import sys\nprint(sys.argv)\n')
frag = ('## skript.py\nbeschreibung: Gibt Argumente aus.\ninput: argv\noutput: stdout\n'
'entscheidungen:\n- Direkter Druck ohne Formatierung.\n beleg: "print(sys.argv)"\nkanten:\n')
fehler, _, einheiten = gates.pruefe_fragment(frag, tmp_path, "skript.py")
assert fehler == [] and einheiten[0]["id"] == "skript.py"
def test_referenz_korrektur_falscher_prefix():
"""Symbol existiert eindeutig unter anderer Datei → ID wird korrigiert (Befund 19)."""
sicht = "### Kann reparieren\neinheiten: falsch.py::_fix, kaputt.py::gibtsnirgends\n"
text, gestrichen = gates.repariere_referenzen(sicht, {"richtig.py::_fix", "a.py::x"})
assert "richtig.py::_fix" in text
assert gestrichen == ["kaputt.py::gibtsnirgends"]
def test_referenz_integritaet():
sicht = "### Kann rechnen\neinheiten: rechner.py::addiere, rechner.py::gibtsnicht\n"
fehler = gates.pruefe_referenzen(sicht, {"rechner.py::addiere"})
assert fehler == ["unbekannte Einheiten-Referenz: rechner.py::gibtsnicht"]