[FIX] test_foerderer.py: CSV-Exporttests an die verpackten Separator-Zeilen anpassen

Commit e2f0384 (vor dieser Session) liess EXPORTCSV/EXPORTSIVAS auch die in
VF_n-Blockdefinitionen verpackten Separator_SP-Sensoren als eigene Zeile
exportieren (TeileArt "ILS 2.0 Separator"). tests/test_foerderer.py wurde
seitdem nicht angepasst und nahm weiter an, JEDE Zeile sei eine Vario-
Strecke - drei Tests schlugen daher fehl, sobald die CSV tatsaechlich
vorlag (39 statt 13 Zeilen: 13 Vario- + 26 Separator-Zeilen).

Neu NEBEN_TEILEARTEN = {"ILS 2.0 Separator"} plus zwei Filter-Helfer
(_vario_rows/_separator_rows). test_vario_teileart, test_merkmale_felder
und test_anzahl_csv_zeilen_entspricht_gebaut pruefen jetzt nur noch die
Vario-Zeilen (verifiziert: 13 Vario- + 26 Separator-Zeilen in der aktuellen
CSV, Summe stimmt).

Statt die Separator-Zeilen nur zu ignorieren, zwei neue, eigenstaendige
Tests:
- test_teileart_bekannt: jede Zeile ist Vario-Strecke ODER eine bekannte
  Neben-TeileArt - faengt eine dritte, unerwartete Art, die die beiden
  Filter sonst stillschweigend durchliessen.
- test_separator_merkmale_felder: die Separator-Merkmale haben ihr eigenes,
  kleineres Pflichtfeld-Schema (Zuordnung, ARTINR) statt der Vario-Felder.
- test_separator_zuordnung_konsistent: der eigentliche Beweis, dass
  csv:sep-proxies-erzeugen die Sensoren dem RICHTIGEN Wrapper zuordnet -
  jede Separator-Zuordnung zeigt auf eine Vario-TeileId derselben Datei,
  und je Vario-Zeile stimmt die Anzahl ihrer Separatoren mit dem eigenen
  Merkmal Anzahl_Separator ueberein (test_export_ids.py prueft nur, dass
  die Zuordnung ueberhaupt auf eine bekannte ID zeigt, nicht auf die
  richtige). Gegengeprobt: eine verfaelschte Zuordnung wird erkannt.

Verifiziert: alle 27 Tests in test_foerderer.py gruen (vorher 3 FAILED),
Gesamtsuite 188 passed / 0 failed / 19 skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-03 15:50:41 +02:00
parent 545fa33bc6
commit 23e11c16c6
+98 -8
View File
@@ -7,7 +7,10 @@ Prueft:
1. Status-Werte und Vollstaendigkeit (foerderer_results.json)
2. Mathematische Invarianten (Laengen, Winkel, Richtung)
3. Geometrisch unmoegliche Faelle
4. CSV-Export-Struktur (foerderer_tests_export.csv, aus TEST_EXPORT_ALL)
4. CSV-Export-Struktur (foerderer_tests_export.csv, aus TEST_EXPORT_ALL) -
seit csv:sep-proxies-erzeugen (Commit e2f0384) enthaelt die CSV neben
den Vario-Zeilen auch eine Zeile je verpacktem Separator-Sensor; siehe
NEBEN_TEILEARTEN und TestFoerdererExportCSV._vario_rows/_separator_rows.
5. Sivas-Export-Struktur (foerderer_tests_sivas.csv, aus TEST_EXPORT_ALL)
"""
@@ -17,6 +20,15 @@ import os
import pytest
# TeileArt-Werte, die einen SENSOR beschreiben statt einer Vario-Strecke -
# csv:sep-proxies-erzeugen (export.lsp, Commit e2f0384) erfasst seither auch
# Separator_SP-Symbole, die als Sub-INSERT in der VF_n-Blockdefinition
# verpackt sind, und exportiert je Fund eine eigene Zeile. Foerderer kennt
# nur diese eine Sensorart (kein Scanner); die vollstaendige Liste steht in
# test_export_ids.py:SENSOR_TEILEARTEN.
NEBEN_TEILEARTEN = {"ILS 2.0 Separator"}
# ---------------------------------------------------------------------------
# Hilfsfunktionen
# ---------------------------------------------------------------------------
@@ -211,6 +223,16 @@ class TestFoerdererExportCSV:
return None
return row[i]
def _vario_rows(self):
"""Nur die eigentlichen Foerderer-Zeilen, ohne die mitexportierten
Separator-Sensoren (siehe NEBEN_TEILEARTEN)."""
return [row for row in self.rows
if self._cell(row, "TeileArt") not in NEBEN_TEILEARTEN]
def _separator_rows(self):
return [row for row in self.rows
if self._cell(row, "TeileArt") == "ILS 2.0 Separator"]
def test_header_felder(self):
"""CSV-Header muss die erwarteten Spalten enthalten (Reihenfolge/Zusatz-
spalten wie Insertpoint/K1-K4/Warnungen egal - per Name geprueft)."""
@@ -227,9 +249,10 @@ class TestFoerdererExportCSV:
assert len(self.rows) > 0, "foerderer_tests_export.csv enthaelt keine Daten"
def test_vario_teileart(self):
"""Alle Eintraege muessen TeileArt 'ILS 2.0 Strecke' haben (VarioFoerderer
"""Jede Vario-Zeile (ohne die mitexportierten Separator-Sensoren, siehe
NEBEN_TEILEARTEN) muss TeileArt 'ILS 2.0 Strecke' haben (VarioFoerderer
ist ein Streckentyp, siehe export_csv.py:process_blocks)."""
for i, row in enumerate(self.rows):
for i, row in enumerate(self._vario_rows()):
teileart = self._cell(row, "TeileArt")
if teileart is None:
continue
@@ -237,6 +260,19 @@ class TestFoerdererExportCSV:
f"Zeile {i + 2}: TeileArt='{teileart}', erwartet 'ILS 2.0 Strecke'"
)
def test_teileart_bekannt(self):
"""Jede Zeile ist entweder eine Vario-Strecke oder eine der bekannten
Neben-TeileArten - keine unerwartete dritte Art (die die beiden obigen
Filter sonst stillschweigend durchliesse)."""
bekannt = {"ILS 2.0 Strecke"} | NEBEN_TEILEARTEN
for i, row in enumerate(self.rows):
teileart = self._cell(row, "TeileArt")
if teileart is None:
continue
assert teileart.strip('"') in bekannt, (
f"Zeile {i + 2}: unbekannte TeileArt '{teileart}'"
)
def test_anzahl_spalte_ist_eins(self):
"""Anzahl-Spalte muss fuer alle Zeilen '1' sein."""
for i, row in enumerate(self.rows):
@@ -259,10 +295,27 @@ class TestFoerdererExportCSV:
pytest.fail(f"Zeile {i + 2}: Merkmale ist kein valides JSON: {e}")
def test_merkmale_felder(self):
"""Merkmale muessen alle Pflichtfelder enthalten."""
"""Merkmale der Vario-Zeilen muessen alle Pflichtfelder enthalten. Die
mitexportierten Separator-Sensoren haben ein eigenes, kleineres Schema
(siehe test_separator_merkmale_felder) und sind hier ausgenommen."""
required = {"Typ", "Winkel", "DeltaH_mm", "DeltaL_mm",
"L_VF_m", "Hoehe_Von_mm", "Hoehe_Bis_mm"}
for i, row in enumerate(self.rows):
for i, row in enumerate(self._vario_rows()):
zelle = self._cell(row, "Merkmale")
if zelle is None:
continue
merkmale = json.loads(zelle)
missing = required - set(merkmale.keys())
assert not missing, (
f"Zeile {i + 2}: Fehlende Merkmale-Felder: {missing}"
)
def test_separator_merkmale_felder(self):
"""Merkmale der mitexportierten Separator-Sensoren muessen die
Zuordnung zu ihrem Vario-Wrapper tragen (siehe
test_separator_zuordnung_konsistent) sowie ARTINR."""
required = {"Zuordnung", "ARTINR"}
for i, row in enumerate(self._separator_rows()):
zelle = self._cell(row, "Merkmale")
if zelle is None:
continue
@@ -273,14 +326,51 @@ class TestFoerdererExportCSV:
)
def test_anzahl_csv_zeilen_entspricht_gebaut(self):
"""CSV-Zeilenanzahl muss Anzahl GEBAUT-Testfaellen entsprechen."""
"""Anzahl der Vario-Zeilen (ohne die mitexportierten Separatoren) muss
der Anzahl GEBAUT-Testfaellen entsprechen."""
results = _load_foerderer_results()
anzahl_gebaut = sum(1 for r in results if r["status"] == "GEBAUT")
assert len(self.rows) == anzahl_gebaut, (
f"CSV hat {len(self.rows)} Zeilen, "
vario_rows = self._vario_rows()
assert len(vario_rows) == anzahl_gebaut, (
f"CSV hat {len(vario_rows)} Vario-Zeilen, "
f"erwartet {anzahl_gebaut} (Anzahl GEBAUT-Faelle)"
)
def test_separator_zuordnung_konsistent(self):
"""Jeder Separator zeigt (ueber Merkmale.Zuordnung) auf die TeileId
eines Vario-Eintrags DERSELBEN Datei, und je Vario-Zeile stimmt die
Anzahl seiner Separatoren mit ihrem eigenen Merkmal Anzahl_Separator
ueberein. Das ist der eigentliche Beweis, dass
csv:sep-proxies-erzeugen die verpackten Sensoren dem richtigen
Wrapper zuordnet - test_export_ids.py::test_sensor_zuordnung_existiert
prueft nur, dass die Zuordnung ueberhaupt auf eine bekannte ID zeigt."""
vario_rows = self._vario_rows()
vario_ids = {self._cell(row, "TeileId") for row in vario_rows}
separator_je_id = {}
for row in self._separator_rows():
zelle = self._cell(row, "Merkmale")
if zelle is None:
continue
zuordnung = json.loads(zelle).get("Zuordnung")
assert zuordnung in vario_ids, (
f'Separator {self._cell(row, "TeileId")}: Zuordnung '
f"'{zuordnung}' zeigt auf keine Vario-Zeile dieser Datei"
)
separator_je_id[zuordnung] = separator_je_id.get(zuordnung, 0) + 1
for row in vario_rows:
tid = self._cell(row, "TeileId")
zelle = self._cell(row, "Merkmale")
if zelle is None:
continue
soll = int(json.loads(zelle).get("Anzahl_Separator", 0))
ist = separator_je_id.get(tid, 0)
assert soll == ist, (
f"TeileId {tid}: Anzahl_Separator={soll}, aber {ist} "
f"Separator-Zeile(n) mit Zuordnung darauf gefunden"
)
# ---------------------------------------------------------------------------
# Klasse 4: Sivas-Export-Pruefung (foerderer_tests_sivas.csv)