115 lines
4.6 KiB
Python
115 lines
4.6 KiB
Python
"""Viewer-Service: Parser, Drift, Doku-Ordnung, Projektliste."""
|
|
from pathlib import Path
|
|
|
|
import config
|
|
import projekte
|
|
import viewer
|
|
|
|
MINI = Path(__file__).parent / "fixtures" / "mini_repo"
|
|
|
|
|
|
def test_lese_einheiten_mit_entscheidungen():
|
|
text = """# Einheiten: a.py
|
|
stand: abc
|
|
|
|
## a.py::eins
|
|
beschreibung: Macht eins.
|
|
input: x
|
|
output: y
|
|
entscheidungen:
|
|
- Erste Entscheidung.
|
|
beleg: "code hier"
|
|
- Zweite mit Umbruch
|
|
und Fortsetzung.
|
|
kanten:
|
|
- ruft-auf: a.py::zwei
|
|
|
|
## b.py
|
|
beschreibung: Datei-Einheit.
|
|
"""
|
|
e = viewer.lese_einheiten(text)
|
|
eins = e["a.py::eins"]
|
|
assert eins["entscheidungen"] == ["Erste Entscheidung.", "Zweite mit Umbruch und Fortsetzung."]
|
|
assert eins["kanten"] == [{"typ": "ruft-auf", "ziel": "a.py::zwei"}]
|
|
assert (eins["input"], eins["output"]) == ("x", "y")
|
|
assert e["b.py"]["beschreibung"] == "Datei-Einheit."
|
|
|
|
|
|
def test_lese_kern_und_pruefung():
|
|
kern = viewer.lese_kern("# Kern: demo\nZwei Sätze.\n\ntragende-einheiten: a.py::x, b.py::y\n")
|
|
assert kern["titel"] == "demo" and kern["tragende_einheiten"] == ["a.py::x", "b.py::y"]
|
|
pr = viewer.lese_pruefung("# Prüfung: demo\ntests: make test\nlint: (keins vorhanden)\n"
|
|
"start: make dev\nhinweise:\n- Vorsicht.\n")
|
|
assert pr["tests"] == "make test" and pr["hinweise"] == ["Vorsicht."]
|
|
|
|
|
|
def test_lese_features_architektur_flows():
|
|
b = viewer.lese_features("# Bereich: rechnen\nbeschreibung: Rechnet.\n\n"
|
|
"## Feature: Addieren [kern]\nbeschreibung: Summen.\n\n"
|
|
"### Kann addieren\neinheiten: a.py::eins\n")
|
|
assert b["features"][0]["flag"] == "kern"
|
|
assert b["features"][0]["teilfeatures"][0]["einheiten"] == ["a.py::eins"]
|
|
a = viewer.lese_architektur("# Architektur: demo\n\n## Komponente: kernstück\n"
|
|
"beschreibung: Trägt.\neinheiten: a.py::eins\n"
|
|
"beziehungen:\n- nutzt: anzeige\n")
|
|
assert a[0]["beziehungen"] == [{"typ": "nutzt", "ziel": "anzeige"}]
|
|
f = viewer.lese_flows("# Flows: demo\n\n## Flow: ablauf\n1. Schritt eins — a.py::eins\n"
|
|
"2. Schritt ohne Referenz\n")
|
|
assert f[0]["schritte"][0]["einheiten"] == ["a.py::eins"]
|
|
assert f[0]["schritte"][1]["einheiten"] == []
|
|
|
|
|
|
def test_code_extrahieren():
|
|
assert "return a + b" in viewer.code_extrahieren(MINI, "rechner.py::addiere")
|
|
assert viewer.code_extrahieren(MINI, "rechner.py::BASIS").startswith("BASIS = 10")
|
|
assert viewer.code_extrahieren(MINI, "rechner.py::gibtsnicht") == ""
|
|
assert viewer.code_extrahieren(MINI, "wegdatei.py::x") == ""
|
|
assert "def merke" in viewer.code_extrahieren(MINI, "rechner.py::Speicher.merke")
|
|
|
|
|
|
async def test_lade_projekt_und_drift(gescanntes_repo):
|
|
wurzel, git = gescanntes_repo
|
|
projekt = viewer.lade_projekt(wurzel)
|
|
assert "rechner.py::addiere" in projekt["einheiten"]
|
|
assert projekt["kern"]["titel"] == "mini"
|
|
assert projekt["staende"]["rechner.py"]
|
|
assert viewer.drift(wurzel, projekt["staende"])["veraltet"] == []
|
|
|
|
(wurzel / "rechner.py").write_text((wurzel / "rechner.py").read_text() + "\nX2 = 2\n")
|
|
git("add", "-A")
|
|
git("commit", "-m", "aenderung")
|
|
d = viewer.drift(wurzel, projekt["staende"])
|
|
assert d["veraltet"] == ["rechner.py"] # anzeige.py unverändert
|
|
|
|
|
|
def test_doku_ordnung_mit_zyklus():
|
|
projekt = {
|
|
"bereiche": [
|
|
{"name": "oben", "features": [{"teilfeatures": [{"einheiten": ["a.py::x"]}]}]},
|
|
{"name": "unten", "features": [{"teilfeatures": [{"einheiten": ["b.py::y"]}]}]},
|
|
],
|
|
"einheiten": {
|
|
"a.py::x": {"kanten": [{"typ": "ruft-auf", "ziel": "b.py::y"}]},
|
|
"b.py::y": {"kanten": []},
|
|
},
|
|
}
|
|
assert viewer.doku_ordnung(projekt) == ["unten", "oben"] # Fundament zuerst
|
|
# Zyklus: beide nutzen einander → deterministisch, beide enthalten
|
|
projekt["einheiten"]["b.py::y"]["kanten"] = [{"typ": "nutzt", "ziel": "a.py::x"}]
|
|
ordnung = viewer.doku_ordnung(projekt)
|
|
assert sorted(ordnung) == ["oben", "unten"] and len(ordnung) == 2
|
|
|
|
|
|
def test_projektliste(tmp_path, monkeypatch):
|
|
echt = tmp_path / "echt"
|
|
echt.mkdir()
|
|
(tmp_path / "kopie").mkdir()
|
|
datei = tmp_path / "projekte.txt"
|
|
datei.write_text(f"# Kommentar\n{echt}\n{tmp_path}/weg\n{tmp_path}/kopie\n{echt}\n")
|
|
monkeypatch.setattr(config, "PROJEKTE_DATEI", datei)
|
|
liste = projekte.lade()
|
|
assert [p.get("fehler") is None for p in liste] == [True, False, True, False]
|
|
assert "existiert nicht" in liste[1]["fehler"]
|
|
assert "Namenskollision" in liste[3]["fehler"]
|
|
assert projekte.finde("echt") == echt and projekte.finde("weg") is None
|