Files
sps_skel/tests/test_scl_skeleton_golden.py
T
Michael StanglandClaude Opus 5.5 25ab362c0a scl_templates: Aufruf-Template FB_ILS_MTRO_2Sep2Swi (ohne Referenz-Aufrufstelle)
Mubea wird damit vollstaendig aus Templates erzeugt. Der FB existiert noch
nicht in der ILSLib; die Schnittstelle ist aus 2Sep1Swi (Separatoren) und
1Sep2Swi (Weichen) angenommen und steht im Template-Kopf - mit den
SPS-Kollegen abzustimmen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 23:13:02 +01:00

136 lines
5.5 KiB
Python

# -*- coding: utf-8 -*-
"""Golden-File-Tests fuer scl_skeleton.py - die komplette FB_Main-Ausgabe.
Schritt 1 aus doc/SCL_Templates.md (§8.3, §11): die heutige Ausgabe wird
festgeschrieben, bevor der Generator auf Templates umgebaut wird. Solange kein
Template eingeschaltet ist, muss jede SCL byte-gleich zur Referenz bleiben.
Eingaben: tests/fixtures/scl/<name>_tro.json - feste Kopien der TRO-JSON aus
tro_flow --json, damit nur scl_skeleton geprueft wird und nicht tro_flow.
Erwartung: tests/fixtures/scl/<name>_FB_Main.scl, Zeitstempel maskiert.
Eine gewollte Aenderung der Ausgabe (z. B. ein Template wird eingeschaltet):
Diff pruefen, dann die Referenzen neu schreiben mit
set SKEL_UPDATE_GOLDEN=1 & python -m pytest tests/test_scl_skeleton_golden.py
"""
import os
import re
import shutil
from pathlib import Path
import pytest
import scl_skeleton
from scl_skeleton import TEMPLATE_DIR, SclTemplates, load_document, render
FIXTURES = Path(__file__).resolve().parent / "fixtures" / "scl"
# mubea: alle fuenf dort vorkommenden Typen, auch 2Sep2Swi (ohne Template);
# HundM05: die Referenzanlage der TRO-Templates.
PROJEKTE = ["mubea", "HundM05"]
# Die einzige Zeile, die sich von Lauf zu Lauf aendert.
_ZEITSTEMPEL = re.compile(r"^// Erzeugt: .*$", re.MULTILINE)
_MASKE = "// Erzeugt: <maskiert>"
def _maskiert(text: str) -> str:
return _ZEITSTEMPEL.sub(_MASKE, text)
def _aktualisieren() -> bool:
return os.environ.get("SKEL_UPDATE_GOLDEN") == "1"
def _vergleichen(ist: str, referenz: Path) -> None:
if _aktualisieren():
referenz.write_text(ist, encoding="utf-8", newline="\n")
pytest.skip(f"Referenz neu geschrieben: {referenz.name}")
assert referenz.is_file(), (
f"{referenz.name} fehlt - mit SKEL_UPDATE_GOLDEN=1 erzeugen")
soll = referenz.read_text(encoding="utf-8")
if ist != soll:
# Erste abweichende Zeile nennen; der volle Diff ist bei ~900 Zeilen
# in der pytest-Ausgabe nicht lesbar.
ist_z, soll_z = ist.splitlines(), soll.splitlines()
for nr, (a, b) in enumerate(zip(ist_z, soll_z), start=1):
if a != b:
pytest.fail(f"{referenz.name} Zeile {nr}:\n soll: {b}\n ist : {a}")
pytest.fail(f"{referenz.name}: {len(ist_z)} statt {len(soll_z)} Zeilen")
@pytest.mark.parametrize("name", PROJEKTE)
def test_render_wie_referenz(name: str) -> None:
"""render() liefert byte-gleich die festgeschriebene SCL."""
quelle = f"{name}_tro.json"
document = load_document(FIXTURES / quelle)
_vergleichen(_maskiert(render(document, quelle)), FIXTURES / f"{name}_FB_Main.scl")
@pytest.mark.parametrize("name", PROJEKTE)
def test_render_mit_templates_wie_referenz(name: str) -> None:
"""--templates: TROs mit Template aus dem Template, der Rest generisch.
Eigene Referenz <name>_FB_Main_templates.scl. Eine Aenderung an einem
Template zeigt sich hier als Diff - pruefen, dann neu schreiben.
"""
quelle = f"{name}_tro.json"
document = load_document(FIXTURES / quelle)
text = render(document, quelle, templates=SclTemplates(TEMPLATE_DIR))
_vergleichen(_maskiert(text), FIXTURES / f"{name}_FB_Main_templates.scl")
@pytest.mark.parametrize("name", PROJEKTE)
def test_cli_schreibt_referenz(name: str, tmp_path: Path, monkeypatch) -> None:
"""Der CLI-Weg: Exit 0, Dateiname <name>_FB_Main.scl, Inhalt wie render().
Das ist der Vertrag, auf den sich ein Workflow verlaesst (doc/SCL_Templates.md §12).
"""
if _aktualisieren():
pytest.skip("Referenzen werden nur ueber test_render_wie_referenz geschrieben")
monkeypatch.setenv("SKEL_RESULTS", str(tmp_path))
exit_code = scl_skeleton.main(["--json", str(FIXTURES / f"{name}_tro.json")])
assert exit_code == 0
ausgabe = tmp_path / f"{name}_FB_Main.scl"
assert ausgabe.is_file()
soll = (FIXTURES / f"{name}_FB_Main.scl").read_text(encoding="utf-8")
assert _maskiert(ausgabe.read_text(encoding="utf-8")) == soll
def test_cli_templates_und_strict(tmp_path: Path, monkeypatch) -> None:
"""--templates schreibt die Template-Referenz; --strict bricht ab, solange
ein FB-Typ der Anlage kein Template hat (hier: Template-Ordner ohne 2Sep2Swi)."""
if _aktualisieren():
pytest.skip("Referenzen werden nur ueber die render-Tests geschrieben")
monkeypatch.setenv("SKEL_RESULTS", str(tmp_path))
json_file = str(FIXTURES / "mubea_tro.json")
assert scl_skeleton.main(["--json", json_file, "--templates"]) == 0
soll = (FIXTURES / "mubea_FB_Main_templates.scl").read_text(encoding="utf-8")
ist = (tmp_path / "mubea_FB_Main.scl").read_text(encoding="utf-8")
assert _maskiert(ist) == soll
(tmp_path / "mubea_FB_Main.scl").unlink()
ohne_2sep2swi = tmp_path / "templates"
shutil.copytree(scl_skeleton.TEMPLATE_DIR, ohne_2sep2swi)
(ohne_2sep2swi / "FB_ILS_MTRO_2Sep2Swi.scl.j2").unlink()
assert scl_skeleton.main(["--json", json_file, "--templates", str(ohne_2sep2swi),
"--strict"]) == 1
assert not (tmp_path / "mubea_FB_Main.scl").exists()
assert scl_skeleton.main(["--json", json_file, "--templates",
str(tmp_path / "gibt_es_nicht")]) == 1
def test_todo_marker_bleiben_zaehlbar() -> None:
"""Die Zahl der offenen Stellen ist eine Qualitaetsgroesse im Workflow.
Sie darf sich nur mit Absicht aendern (dann Referenz neu schreiben).
"""
for name in PROJEKTE:
text = (FIXTURES / f"{name}_FB_Main.scl").read_text(encoding="utf-8")
assert text.count(scl_skeleton.TODO) > 0, name