160 lines
6.5 KiB
Python
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"]
|