Files
OAMC-Auswertung/tests/test_wertung_engine.py
T
linusandClaude Sonnet 5 03f6fdb0d9 Erstumsetzung: OAMC Turnierauswertung (AP 0–7, 9)
* Datenmodell + Alembic-Initialmigration (Plan §4), Seed Saison 2026
* Wertungs-Engine (Plan §3.3), regelbasiert je Kategorie/Saison
  - Abnahmetest gegen die Ergebnisliste 2026-05-31
* Meisterschaftsberechnung mit Streichresultaten + Tie-Break
  (PLATZHALTER-Punktetabelle, O-1 offen)
* Ingest-API (idempotent, dry_run) inkl. Excel-Leser und Fahrer-Matching/Merge
* Zeitmessungs-Endpoint (Batch, idempotent, unzugeordnet/unplausibel) + SSE-Anzeige
  - Mess-Pi-Client bewusst NICHT enthalten (docs/zeitmessung-endpoint.md)
* Frontend: öffentliche Seiten + internes Backend (Jinja2/HTMX, kein Build)
* Auslieferung: Docker (multi-arch) + docker-compose sowie Debian-Paket/APT-Repo
* 57 Tests grün gegen echte PostgreSQL-Testdatenbank

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 18:49:00 +02:00

96 lines
3.0 KiB
Python

"""Einheitstests der Wertungsformeln (Plan §3.3)."""
from decimal import Decimal
from oamc.models.enums import Kategorie, StartStatus
from oamc.wertung.engine import WertungsEingabe, platziere, regel_fuer
def _e(sid, kat, zeit=None, fehler=None, status=StartStatus.GESTARTET, **kw):
return WertungsEingabe(
start_id=sid,
kategorie=kat,
status=status,
summe_zeit=Decimal(str(zeit)) if zeit is not None else None,
summe_fehler=fehler,
**kw,
)
def test_erwachsene_gesamt_ist_zeit_plus_fehler():
regel = regel_fuer(Kategorie.STAMM)
assert regel.gesamt(Decimal("105.43"), 0) == Decimal("105.43")
assert regel.gesamt(Decimal("103.26"), 10) == Decimal("113.26")
assert regel.gesamt(Decimal("65.71"), 1210) == Decimal("1275.71")
def test_jugend_gesamt_ist_nur_fehler():
regel = regel_fuer(Kategorie.JUGEND)
assert regel.gesamt(Decimal("136.88"), 0) == Decimal("0.00")
assert regel.gesamt(Decimal("148.42"), 60) == Decimal("60.00")
def test_jugend_gleichstand_wird_ueber_zeit_aufgeloest():
res = platziere(
[
_e(1, Kategorie.JUGEND, 144.46, 0),
_e(2, Kategorie.JUGEND, 136.88, 0),
_e(3, Kategorie.JUGEND, 140.40, 0),
]
)
platz = {r.start_id: r.platzierung for r in res}
assert platz == {2: 1, 3: 2, 1: 3} # schnellste Zeit vorn
def test_erwachsene_platzierung_aufsteigend_nach_gesamt():
res = platziere(
[
_e(1, Kategorie.STAMM, 130.33, 0), # 130.33
_e(2, Kategorie.STAMM, 105.43, 0), # 105.43
_e(3, Kategorie.STAMM, 103.26, 10), # 113.26
]
)
platz = {r.start_id: r.platzierung for r in res}
assert platz == {2: 1, 3: 2, 1: 3}
def test_dnf_dns_dsq_werden_nicht_klassiert():
res = platziere(
[
_e(1, Kategorie.STAMM, 100.0, 0),
_e(2, Kategorie.STAMM, 90.0, 0, status=StartStatus.DNF),
_e(3, Kategorie.STAMM, None, None, status=StartStatus.DNS),
]
)
by_id = {r.start_id: r for r in res}
assert by_id[1].platzierung == 1
assert by_id[2].platzierung is None and not by_id[2].gewertet
assert by_id[3].platzierung is None
def test_unvollstaendiges_ergebnis_bleibt_vorlaeufig_ohne_platz():
res = platziere(
[
_e(1, Kategorie.STAMM, 100.0, 0, fehler_vollstaendig=False),
_e(2, Kategorie.STAMM, 110.0, 0),
]
)
by_id = {r.start_id: r for r in res}
assert by_id[1].platzierung is None
assert by_id[1].gesamt == Decimal("100.00") # vorlaeufige Gesamtanzeige
assert "vorlaeufig" in by_id[1].hinweis
assert by_id[2].platzierung == 1
def test_echter_gleichstand_teilt_platz_und_laesst_luecke():
# identische Zeit UND Fehler -> selber Platz, danach Luecke
res = platziere(
[
_e(1, Kategorie.STAMM, 100.00, 0),
_e(2, Kategorie.STAMM, 100.00, 0),
_e(3, Kategorie.STAMM, 105.00, 0),
]
)
platz = sorted(r.platzierung for r in res)
assert platz == [1, 1, 3]