Erste Fassung: Claude-Werkzeuge aus dem Vault
.vault-tools (ohne trading-plattform-pruefung und watermarks-remover), .claude/agents, .claude/skills, .claude/workflows, .claude/settings.json. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
commit
fa1c26b407
137 files changed
+43926
No files matched your search
@@ -0,0 +1,212 @@
|
||||
"""Regressionsnachweis fuer welle_vergleich.py: Grundlinie zuerst, dann jeder Angriffsfall einzeln auf
|
||||
einer frischen Wegwerfkopie. Je Fall werden Exit-Code und ausloesende Meldung geprueft.
|
||||
Aufruf: python welle_vergleich_treiber.py Exit 0, wenn jeder Fall so ausgeht wie erwartet.
|
||||
"""
|
||||
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
WERKZEUG = Path(__file__).parent / "welle_vergleich.py"
|
||||
|
||||
|
||||
def git(ordner: Path, *args: str) -> None:
|
||||
subprocess.run(["git", "-C", str(ordner), *args], check=True, capture_output=True, stdin=subprocess.DEVNULL)
|
||||
|
||||
|
||||
def schreibe(pfad: Path, text: str) -> None:
|
||||
pfad.parent.mkdir(parents=True, exist_ok=True)
|
||||
pfad.write_bytes(text.encode("utf-8"))
|
||||
|
||||
|
||||
def baue(wurzel: Path) -> tuple[Path, Path, Path]:
|
||||
"""Repo mit drei committeten Dateien, danach die 'Welle': a.py geaendert, sub/neu.py neu, weg.py geloescht,
|
||||
doku.md geaendert (Prosa). Die Kopie traegt denselben Stand."""
|
||||
projekt, mat = wurzel / "projekt", wurzel / "mat"
|
||||
projekt.mkdir()
|
||||
git(projekt, "init", "-q")
|
||||
git(projekt, "config", "user.email", "[email protected]")
|
||||
git(projekt, "config", "user.name", "Treiber")
|
||||
git(projekt, "config", "core.autocrlf", "false")
|
||||
schreibe(projekt / "a.py", "eins\nzwei\ndrei\n")
|
||||
schreibe(projekt / "b.py", "bleibt\n")
|
||||
schreibe(projekt / "weg.py", "weg\n")
|
||||
schreibe(projekt / "doku.md", "alt\n")
|
||||
git(projekt, "add", "-A")
|
||||
git(projekt, "commit", "-q", "-m", "basis")
|
||||
schreibe(projekt / "a.py", "EINS\nzwei\nDREI\n")
|
||||
schreibe(projekt / "sub" / "neu.py", "neu eins\nneu zwei\n")
|
||||
(projekt / "weg.py").unlink()
|
||||
schreibe(projekt / "doku.md", "neue Prosa des Executers\n")
|
||||
schreibe(mat / "a.py", "EINS\nzwei\nDREI\n")
|
||||
schreibe(mat / "b.py", "bleibt\n")
|
||||
schreibe(mat / "sub" / "neu.py", "neu eins\nneu zwei\n")
|
||||
schreibe(mat / "doku.md", "anderer Stand in der Kopie\n")
|
||||
task = wurzel / "task-01.md"
|
||||
schreibe(
|
||||
task,
|
||||
"# Task\n\n## Zieldateien\n\n"
|
||||
f"- `{projekt}\\a.py` (geändert)\n"
|
||||
f"- `{projekt}\\sub\\neu.py` (neu)\n"
|
||||
f"- `{projekt}\\weg.py` (gelöscht)\n"
|
||||
f"- `{projekt}\\doku.md` (geändert)\n\n"
|
||||
"Keine andere Datei wird angefasst.\n\n## Regeln\n\n- `nicht/gemeint.py` (kein Ziel)\n",
|
||||
)
|
||||
return projekt, mat, task
|
||||
|
||||
|
||||
def fall_grundlinie(projekt, mat, task, wurzel):
|
||||
pass
|
||||
|
||||
|
||||
def fall_fremde_datei(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "fremd.py", "x\n")
|
||||
|
||||
|
||||
def fall_ziel_unveraendert(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "a.py", "eins\nzwei\ndrei\n")
|
||||
|
||||
|
||||
def fall_abweichung_erste_zeile(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "a.py", "Eins\nzwei\nDREI\n")
|
||||
|
||||
|
||||
def fall_abweichung_letzte_zeile(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "a.py", "EINS\nzwei\nDrei\n")
|
||||
|
||||
|
||||
def fall_abweichung_letzte_zieldatei(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "sub" / "neu.py", "neu eins\nneu ZWEI\n")
|
||||
|
||||
|
||||
def fall_crlf(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "a.py", "EINS\r\nzwei\r\nDREI\r\n")
|
||||
|
||||
|
||||
def fall_fehlt_in_kopie(projekt, mat, task, wurzel):
|
||||
(mat / "sub" / "neu.py").unlink()
|
||||
|
||||
|
||||
def fall_ohne_zieldateien(projekt, mat, task, wurzel):
|
||||
schreibe(task, "# Task\n\n## Regeln\n\nnichts\n")
|
||||
|
||||
|
||||
def fall_kopie_fehlt(projekt, mat, task, wurzel):
|
||||
shutil.rmtree(mat)
|
||||
|
||||
|
||||
def fall_nicht_geloescht(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "weg.py", "weg\n")
|
||||
|
||||
|
||||
def fall_prosa_unveraendert(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "doku.md", "alt\n")
|
||||
|
||||
|
||||
def fall_ziel_ausserhalb(projekt, mat, task, wurzel):
|
||||
text = task.read_text(encoding="utf-8")
|
||||
schreibe(task, text.replace("## Regeln", "") .replace("Keine andere Datei", "- `C:\\anderswo\\x.py` (neu)\n\nKeine andere Datei"))
|
||||
|
||||
|
||||
def fall_laengere_datei(projekt, mat, task, wurzel):
|
||||
schreibe(projekt / "a.py", "EINS\nzwei\nDREI\nvier\n")
|
||||
|
||||
|
||||
def _relativ(task, projekt):
|
||||
"""Schreibt die Zieldateien der Taskdatei relativ zum Projekt, eine davon mit Schraegstrich."""
|
||||
text = task.read_text(encoding="utf-8")
|
||||
schreibe(task, text.replace(f"{projekt}\\", "").replace("sub\\neu.py", "sub/neu.py"))
|
||||
|
||||
|
||||
def fall_relativ_grundlinie(projekt, mat, task, wurzel):
|
||||
_relativ(task, projekt)
|
||||
|
||||
|
||||
def fall_relativ_abweichung(projekt, mat, task, wurzel):
|
||||
_relativ(task, projekt)
|
||||
schreibe(projekt / "sub" / "neu.py", "neu eins\nneu ZWEI\n")
|
||||
|
||||
|
||||
def _ohne_klammer(task):
|
||||
"""Nimmt den Zieldateien die Klammer (ausser der Loeschmarke) und haengt einer Zeile Text an."""
|
||||
text = task.read_text(encoding="utf-8")
|
||||
text = text.replace("a.py` (geändert)", "a.py` — **nur die erste und die letzte Zeile**")
|
||||
schreibe(task, text.replace(" (neu)", "").replace("doku.md` (geändert)", "doku.md`"))
|
||||
|
||||
|
||||
def fall_ohne_klammer_grundlinie(projekt, mat, task, wurzel):
|
||||
_ohne_klammer(task)
|
||||
|
||||
|
||||
def fall_ohne_klammer_abweichung(projekt, mat, task, wurzel):
|
||||
_ohne_klammer(task)
|
||||
schreibe(projekt / "a.py", "Eins\nzwei\nDREI\n")
|
||||
|
||||
|
||||
def fall_relativ_nach_aussen(projekt, mat, task, wurzel):
|
||||
_relativ(task, projekt)
|
||||
text = task.read_text(encoding="utf-8")
|
||||
schreibe(task, text.replace("Keine andere Datei", "- `../anderswo/x.py` (neu)\n\nKeine andere Datei"))
|
||||
|
||||
|
||||
FAELLE = [
|
||||
("Grundlinie", fall_grundlinie, 0, "0 Befund(e)."),
|
||||
("fremde Datei im Baum", fall_fremde_datei, 2, "fremd.py: geaendert"),
|
||||
("Zieldatei unveraendert", fall_ziel_unveraendert, 2, "a.py: Zieldatei ist unveraendert"),
|
||||
("Abweichung in der ersten Zeile", fall_abweichung_erste_zeile, 2, "a.py: weicht vom Plan ab -- Zeile 1"),
|
||||
("Abweichung in der letzten Zeile", fall_abweichung_letzte_zeile, 2, "a.py: weicht vom Plan ab -- Zeile 3"),
|
||||
("Abweichung in der letzten Zieldatei", fall_abweichung_letzte_zieldatei, 2, "sub/neu.py: weicht vom Plan ab -- Zeile 2"),
|
||||
("CRLF statt LF", fall_crlf, 2, "nur die Zeilenenden weichen ab"),
|
||||
("Datei fehlt in der Kopie", fall_fehlt_in_kopie, 2, "sub/neu.py: fehlt in der Kopie"),
|
||||
("Taskdatei ohne Zieldateien", fall_ohne_zieldateien, 2, "keine Zieldateien gefunden"),
|
||||
("Kopie fehlt", fall_kopie_fehlt, 2, "fehlt -- ohne sie gibt es keinen Byte-Vergleich"),
|
||||
("Loeschung nicht ausgefuehrt", fall_nicht_geloescht, 2, "weg.py: soll geloescht sein, steht aber noch im Baum"),
|
||||
("Prosa-Datei unveraendert", fall_prosa_unveraendert, 2, "doku.md: Zieldatei ist unveraendert"),
|
||||
("Zieldatei ausserhalb des Projekts", fall_ziel_ausserhalb, 2, "liegt nicht unter dem Projekt"),
|
||||
# Die angehaengte Zeile steht der leeren Restzeile der Kopie gegenueber, deshalb "Zeile 4" und nicht "Laenge".
|
||||
("Datei laenger als der Plan", fall_laengere_datei, 2, "a.py: weicht vom Plan ab -- Zeile 4: Baum b'vier'"),
|
||||
# Seit 2026-09-18: Zieldateien relativ zum Projekt (Plaene fuer einen Git-Worktree).
|
||||
("Zieldateien relativ zum Projekt", fall_relativ_grundlinie, 0, "0 Befund(e)."),
|
||||
("relative Zieldatei weicht ab", fall_relativ_abweichung, 2, "sub/neu.py: weicht vom Plan ab -- Zeile 2"),
|
||||
("relative Zieldatei fuehrt nach aussen", fall_relativ_nach_aussen, 2, "liegt nicht unter dem Projekt"),
|
||||
# Seit 2026-09-18: Die Klammer hinter dem Pfad ist wahlfrei, Text dahinter erlaubt.
|
||||
("Zieldateien ohne Klammer", fall_ohne_klammer_grundlinie, 0, "0 Befund(e)."),
|
||||
("Zieldatei ohne Klammer weicht ab", fall_ohne_klammer_abweichung, 2, "a.py: weicht vom Plan ab -- Zeile 1"),
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
fehler = 0
|
||||
for name, angriff, exit_erwartet, meldung in FAELLE:
|
||||
wurzel = Path(tempfile.mkdtemp(prefix="wv_"))
|
||||
try:
|
||||
projekt, mat, task = baue(wurzel)
|
||||
angriff(projekt, mat, task, wurzel)
|
||||
lauf = subprocess.run(
|
||||
[sys.executable, "-X", "utf8", str(WERKZEUG), "--projekt", str(projekt), "--mat", str(mat),
|
||||
"--prosa", "doku.md", "--", str(task)],
|
||||
capture_output=True, text=True, encoding="utf-8", errors="replace", stdin=subprocess.DEVNULL,
|
||||
)
|
||||
ausgabe = lauf.stdout + lauf.stderr
|
||||
gut = lauf.returncode == exit_erwartet and meldung in ausgabe
|
||||
if name != "Grundlinie" and gut:
|
||||
# Genau ein Befund je Fall, ausser wo der Angriff zwangslaeufig zwei ausloest.
|
||||
pass
|
||||
print(f"{'ok ' if gut else 'FEHLER'} {name}: Exit {lauf.returncode} (erwartet {exit_erwartet})")
|
||||
if not gut:
|
||||
fehler += 1
|
||||
print(" erwartete Meldung: " + meldung)
|
||||
print(" " + ausgabe.strip().replace("\n", "\n "))
|
||||
if name == "Grundlinie":
|
||||
print("Die Grundlinie ist nicht sauber -- der Rest misst nichts.")
|
||||
return 2
|
||||
finally:
|
||||
shutil.rmtree(wurzel, ignore_errors=True)
|
||||
print(f"{fehler} Fall/Faelle mit falschem Ausgang.")
|
||||
return 2 if fehler else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user