103 lines
3.7 KiB
Python
103 lines
3.7 KiB
Python
"""Re-Scan über git-Diff: nur berührte Dateien werden neu gescannt, Löschungen räumen
|
|
auf, sauberer Tree ist Pflicht. Läuft komplett in der Fake-Welt."""
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import fake_agents
|
|
import kanban
|
|
import repo as repo_mod
|
|
|
|
MINI = Path(__file__).parent / "fixtures" / "mini_repo"
|
|
|
|
|
|
def _git(wurzel, *args):
|
|
subprocess.run(["git", "-C", str(wurzel), *args], check=True, capture_output=True,
|
|
env={"PATH": "/usr/bin:/bin", "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t",
|
|
"GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t",
|
|
"HOME": str(wurzel)})
|
|
|
|
|
|
def _repo(tmp_path: Path) -> Path:
|
|
ziel = tmp_path / "repo"
|
|
shutil.copytree(MINI, ziel)
|
|
_git(ziel, "init", "-b", "main")
|
|
_git(ziel, "add", "-A")
|
|
_git(ziel, "commit", "-m", "start")
|
|
return ziel
|
|
|
|
|
|
@pytest.fixture
|
|
def cli(monkeypatch):
|
|
import cli as cli_mod
|
|
monkeypatch.setattr(kanban, "RETRY_BACKOFF", 0.05)
|
|
return cli_mod
|
|
|
|
|
|
async def test_scan_dann_rescan_nur_geaenderte(cli, fake_welt, testdb, tmp_path):
|
|
fake_agents.standard_antworten(fake_welt)
|
|
wurzel = _repo(tmp_path)
|
|
assert await cli._scan(wurzel) == 0
|
|
stand1 = repo_mod.head_hash(wurzel)
|
|
assert f"stand: {stand1}" in (wurzel / ".planer" / "einheiten" / "rechner.py.md").read_text()
|
|
|
|
_git(wurzel, "add", "-A")
|
|
_git(wurzel, "commit", "-m", "scan-artefakte")
|
|
(wurzel / "rechner.py").write_text((wurzel / "rechner.py").read_text() + "\n\ndef neu():\n return 1\n")
|
|
_git(wurzel, "add", "-A")
|
|
_git(wurzel, "commit", "-m", "neue funktion")
|
|
stand2 = repo_mod.head_hash(wurzel)
|
|
|
|
fake_welt.calls.clear()
|
|
assert await cli._rescan(wurzel) == 0
|
|
# Nur rechner.py wurde neu gescannt …
|
|
assert all("anzeige" not in k for k in fake_welt.calls if "-schneiden-" in k)
|
|
rechner = (wurzel / ".planer" / "einheiten" / "rechner.py.md").read_text()
|
|
anzeige = (wurzel / ".planer" / "einheiten" / "anzeige.py.md").read_text()
|
|
assert f"stand: {stand2}" in rechner and "## rechner.py::neu" in rechner
|
|
assert f"stand: {stand1}" in anzeige # unverändert übernommen
|
|
# … aber die Sichten wurden neu aggregiert und referenzieren auch den Bestand
|
|
assert any("-sichten-" in k for k in fake_welt.calls)
|
|
kern = (wurzel / ".planer" / "kern.md").read_text()
|
|
assert kern.startswith("# Kern:")
|
|
|
|
|
|
async def test_rescan_ohne_aenderung_ruft_kein_llm(cli, fake_welt, testdb, tmp_path):
|
|
fake_agents.standard_antworten(fake_welt)
|
|
wurzel = _repo(tmp_path)
|
|
assert await cli._scan(wurzel) == 0
|
|
_git(wurzel, "add", "-A")
|
|
_git(wurzel, "commit", "-m", "scan-artefakte")
|
|
fake_welt.calls.clear()
|
|
assert await cli._rescan(wurzel) == 0
|
|
assert fake_welt.calls == []
|
|
|
|
|
|
async def test_rescan_geloeschte_datei_raeumt_auf(cli, fake_welt, testdb, tmp_path):
|
|
fake_agents.standard_antworten(fake_welt)
|
|
wurzel = _repo(tmp_path)
|
|
assert await cli._scan(wurzel) == 0
|
|
_git(wurzel, "add", "-A")
|
|
_git(wurzel, "commit", "-m", "scan-artefakte")
|
|
(wurzel / "anzeige.py").unlink()
|
|
_git(wurzel, "add", "-A")
|
|
_git(wurzel, "commit", "-m", "anzeige weg")
|
|
assert await cli._rescan(wurzel) == 0
|
|
assert not (wurzel / ".planer" / "einheiten" / "anzeige.py.md").exists()
|
|
|
|
|
|
async def test_unsauberer_tree_bricht_ab(cli, fake_welt, testdb, tmp_path):
|
|
wurzel = _repo(tmp_path)
|
|
(wurzel / "schmutz.py").write_text("X = 1\n")
|
|
with pytest.raises(SystemExit):
|
|
await cli._scan(wurzel)
|
|
|
|
|
|
async def test_kein_git_repo_bricht_ab(cli, fake_welt, testdb, tmp_path):
|
|
kein_repo = tmp_path / "kein-repo"
|
|
kein_repo.mkdir()
|
|
with pytest.raises(SystemExit):
|
|
await cli._scan(kein_repo)
|