Referenzmodell Gewannenfestlegung für Konvertertests (#7)

- beispiele/referenz_gewannenfestlegung.md/.bpmn: aus dem ersten echten
  Einlese-Durchgang (input/Gewannenvermessung.md), als „Test“ markiert,
  fachlich nicht abgenommen. 4 Lanes, externer Pool, 14 Gateways,
  verschachtelte Varianten; Byte-Vergleich über pruefe_prozesse.py.
- tests/test_referenzmodelle.py: je Referenzmodell keine Lane „Nicht
  zugeordnet“, höchstens vier Kanten je Gateway, keine Kante durch eine
  Raute (Geometrie aus test_md_to_bpmn.py übernommen).
- Gefunden: Kante S12→S13 läuft durch die Raute von S13 – als
  xfail(strict=True) mit Verweis auf #8. AGENTS.md regelt, dass xfail nur
  für so erfasste Konverterfehler an Referenzmodellen zulässig ist.
- Anleitung, Fallstricke (zwei Einträge), STATUS.
- Tests: python -m pytest -q -> 82 bestanden, 1 xfail (#8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:18:07 +02:00
co-authored by Claude Opus 5.5
parent b64c38f71b
commit e62bc10b7b
7 changed files with 884 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
"""Strukturtests für die Referenzmodelle (.agents/skills/sn-bpmn/beispiele/referenz_*.md) – reines Python/pytest.
Die Referenzmodelle sind echte, größere Prozesse. Der Byte-Vergleich in test_pruefe_prozesse.py meldet jede
Änderung an der Ausgabe von md_to_bpmn.py; diese Tests prüfen zusätzlich, ob das Ergebnis danach noch den
Modellierregeln und den Grenzen von PICTURE genügt – auch wenn die .bpmn bewusst neu erzeugt wurde.
"""
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
SKILL = ROOT / ".agents" / "skills" / "sn-bpmn"
sys.path.insert(0, str(SKILL))
import md_to_bpmn as m # noqa: E402
from test_md_to_bpmn import _kante_kreuzt_gateway # noqa: E402 (dieselbe Geometrieprüfung wie dort)
REFERENZEN = sorted((SKILL / "beispiele").glob("referenz_*.md"))
GATEWAYS = ("exclusiveGateway", "inclusiveGateway", "parallelGateway", "eventBasedGateway")
# Bekannte, noch nicht behobene Konverterfehler je Referenzmodell. strict=True: Sobald der Fehler behoben ist,
# schlägt der Test als XPASS fehl – dann den Eintrag hier entfernen. Nie verwenden, um neue Fehler zu verstecken.
BEKANNTE_KANTEN_DURCH_GATEWAY = {
"referenz_gewannenfestlegung": "Issue #8: Kante S12→S13 (Ja) läuft senkrecht durch die Raute von S13",
}
def mit_bekannten_fehlern(bekannt):
return [pytest.param(md, id=md.stem, marks=pytest.mark.xfail(reason=bekannt[md.stem], strict=True))
if md.stem in bekannt else pytest.param(md, id=md.stem) for md in REFERENZEN]
def baue(md):
meta, steps, order, externals = m.parse_markdown(md.read_text(encoding="utf-8"))
return m.build_bpmn(meta, steps, order, externals)
def test_es_gibt_mindestens_ein_referenzmodell():
"""Prüft, dass die Strukturtests tatsächlich an einem Referenzmodell laufen."""
assert REFERENZEN
@pytest.mark.parametrize("md", REFERENZEN, ids=lambda p: p.stem)
def test_jedes_element_hat_eine_zustaendigkeit(md):
"""Prüft, dass im Referenzmodell kein Element in der Lane 'Nicht zugeordnet' landet."""
lanes = [lane.get("name") for lane in baue(md).iter(f"{{{m.NS['bpmn']}}}lane")]
assert lanes, "Referenzmodell ohne Lanes"
assert m.UNASSIGNED_LANE not in lanes
@pytest.mark.parametrize("md", REFERENZEN, ids=lambda p: p.stem)
def test_kein_gateway_hat_mehr_als_vier_kanten(md):
"""Prüft Modellierregel 4: kein Gateway im Referenzmodell braucht mehr als die vier Anschlusspunkte von PICTURE."""
defs = baue(md)
for tag in GATEWAYS:
for gateway in defs.iter(f"{{{m.NS['bpmn']}}}{tag}"):
kanten = gateway.findall(f"{{{m.NS['bpmn']}}}incoming") + gateway.findall(f"{{{m.NS['bpmn']}}}outgoing")
assert len(kanten) <= 4, f"{gateway.get('id')} hat {len(kanten)} Kanten"
@pytest.mark.parametrize("md", mit_bekannten_fehlern(BEKANNTE_KANTEN_DURCH_GATEWAY))
def test_keine_kante_fuehrt_durchs_innere_eines_gateways(md):
"""Prüft, dass im Referenzmodell keine Kante quer durch eine Raute läuft (Fallstrick Gateway-Kantenführung)."""
assert _kante_kreuzt_gateway(baue(md), m.NS) is None