Wire the web-scraper as a third update fallback, add regression tests
AutoUpdateAndLearn.update_data() now falls back to LottoWebUpdater (lotto.de scraping) when the API chain (Lottoland/GitHub/lottoAPI) returns no new data, matching the pattern already in production on the Eurojackpot sibling project. Required installing beautifulsoup4 into the venv - its absence would have crashed the entire update script the moment the fallback import was added, not just the fallback path itself, so this needed verifying before wiring it in. Also adds tests/test_popularity_score.py (pytest): regression coverage for the popularity/EV score against known overplayed combinations (1-2-3-4-5-6, 5-10-15-20-25-30) and the Superzahl EV ordering. Verified against the pre-fix code (commit8c2089d) that these tests would actually have caught the sum-extremity bug fixed ine05f1f1- this had previously only been caught by manual spot-checking. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
73a6b43ec9
commit
ec7290ece5
4 files changed
+135
-14
No files matched your search
@@ -0,0 +1,84 @@
|
||||
"""
|
||||
Regressionstests für den Popularity/EV-Score.
|
||||
|
||||
Diese Tests bestehen aus bekannten, real dokumentierten Vielspieler-
|
||||
Kombinationen (siehe documentation/ARCHITECTURE.md Abschnitt 3) und stellen
|
||||
sicher, dass der Score sie konsistent NIEDRIGER bewertet als gestreute
|
||||
Kombinationen. Genau das hatte der Sum-Extremity-Bug (Commit e05f1f1)
|
||||
verletzt: eng geclusterte Zahlenfolgen wie 1-2-3-4-5-6 wurden durch ihre
|
||||
extreme (niedrige) Summe fälschlich AUFGEWERTET statt abgewertet, weil die
|
||||
Summen-Komponente unabhängig von der Muster-Erkennung lief. Ohne diesen Test
|
||||
wäre der Bug nur durch zufälliges manuelles Nachrechnen aufgefallen.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts", "generators"))
|
||||
|
||||
from ultimate_ai_ml_hybrid_generator import UltimateAIMLHybridGenerator as Generator
|
||||
|
||||
# Bekannte, historisch meistgespielte Kombinationen (siehe ARCHITECTURE.md).
|
||||
OVERPLAYED_COMBOS = [
|
||||
[1, 2, 3, 4, 5, 6],
|
||||
[5, 10, 15, 20, 25, 30],
|
||||
]
|
||||
|
||||
# Gestreute Kombinationen ohne erkennbares menschliches Auswahlmuster.
|
||||
SPREAD_COMBOS = [
|
||||
[3, 16, 22, 43, 48, 49], # echte Ziehung vom 29.07.2026
|
||||
[1, 13, 25, 37, 41, 49],
|
||||
]
|
||||
|
||||
|
||||
def test_overplayed_combos_score_low():
|
||||
for combo in OVERPLAYED_COMBOS:
|
||||
score = Generator._calculate_popularity_score(combo)
|
||||
assert score < 0.3, f"{combo} sollte als populär (niedriger Score) gelten, war {score:.3f}"
|
||||
|
||||
|
||||
def test_spread_combos_score_high():
|
||||
for combo in SPREAD_COMBOS:
|
||||
score = Generator._calculate_popularity_score(combo)
|
||||
assert score > 0.5, f"{combo} sollte als unpopulär (hoher Score) gelten, war {score:.3f}"
|
||||
|
||||
|
||||
def test_overplayed_never_outscores_spread():
|
||||
"""Regressionsschutz für den Sum-Extremity-Bug: eine bekannte
|
||||
Vielspieler-Kombination darf NIE höher bewerten als eine gestreute."""
|
||||
worst_overplayed = max(Generator._calculate_popularity_score(c) for c in OVERPLAYED_COMBOS)
|
||||
best_spread = min(Generator._calculate_popularity_score(c) for c in SPREAD_COMBOS)
|
||||
assert worst_overplayed < best_spread
|
||||
|
||||
|
||||
def test_arithmetic_progression_penalized_regardless_of_step():
|
||||
"""Nicht nur direkte Nachbarn (+1), auch andere Schrittweiten (hier +7)
|
||||
müssen als Muster erkannt werden."""
|
||||
step_one = Generator._calculate_popularity_score([10, 11, 12, 13, 14, 15])
|
||||
step_seven = Generator._calculate_popularity_score([2, 9, 16, 23, 30, 37])
|
||||
spread = Generator._calculate_popularity_score([2, 14, 23, 31, 40, 47])
|
||||
assert step_one < spread
|
||||
assert step_seven < spread
|
||||
|
||||
|
||||
def test_balanced_odd_even_split_scores_lower_than_extreme_split():
|
||||
"""Menschen bevorzugen 3-3-Splits, obwohl jede Kombination gleich
|
||||
wahrscheinlich ist - ein extremer Split (hier 6 gerade) sollte daher
|
||||
einen höheren Score bekommen als ein 3-3-Split mit sonst identischen
|
||||
Eigenschaften."""
|
||||
balanced = Generator._calculate_popularity_score([2, 3, 34, 35, 46, 47]) # 3 gerade, 3 ungerade
|
||||
extreme = Generator._calculate_popularity_score([2, 4, 34, 36, 46, 48]) # 6 gerade
|
||||
assert extreme > balanced
|
||||
|
||||
|
||||
def test_score_bounded_between_zero_and_one():
|
||||
for combo in OVERPLAYED_COMBOS + SPREAD_COMBOS:
|
||||
score = Generator._calculate_popularity_score(combo)
|
||||
assert 0.0 <= score <= 1.0
|
||||
|
||||
|
||||
def test_superzahl_covers_all_ten_digits_across_one_batch():
|
||||
"""Die EV-Rangfolge muss über einen 10er-Batch alle Ziffern 0-9 genau
|
||||
einmal abdecken (Regressionsschutz gegen die alte, historienbasierte
|
||||
Logik, die einzelne Ziffern - z.B. historisch häufige - bevorzugt hat)."""
|
||||
digits = {Generator._get_smart_superzahl(None, tip) for tip in range(1, 11)}
|
||||
assert digits == set(range(10))
|
||||
Reference in new issue
Block a user