pytest-Suite: 13 Tests in ~1 s, mutations-geprueft
Im Projekt sind ueber Monate dutzende Szenario-Tests entstanden ("mit 10
Szenarien getestet", "mit 18 synthetischen Szenarien") — alle als
Wegwerf-Skripte, keiner lief je ein zweites Mal. Bei einem System, das man vor
jeder Aenderung neu starten und live verifizieren muss, ist das der teuerste
Teil der Schleife.
Erfasst sind die beiden juengsten Pfade:
· _check_pending_fill (6 Faelle) — u.a. "fremde Position NICHT taggen" und
"derselbe Fill zaehlt nur einmal"
· Selbst-Kalibrierung (7 Faelle) — Episoden-Dedup, WARTEN re-armt, Auswertung
in beide Richtungen inkl. Broker-Offset, fehlende Bar bleibt offen,
Aggregation
Drei harte Regeln in tests/conftest.py: keine Live-DB (frische Temp-Datei je
Test), kein MT5 / kein laufender Server (gestubbt), kein Netz. Das Schema legen
die ECHTEN Konstruktoren an — HistoryLogger UND CandleLogger, denn candles_m1
gehoert nicht zum History-Schema. Ein handgebautes Test-Schema wuerde
irgendwann vom Produktivstand abweichen.
Warum Temp-DB statt Kopie der Live-DB: am 06.08. haben echte candles_m1-Zeilen
in einem vermeintlich leeren Fenster einen Test verfaelscht — zweimal sah es
nach einem Code-Fehler aus, es waren Testfehler.
MUTATIONS-GEPRUEFT, weil eine Suite die immer gruen ist wertlos waere: eine
absichtlich eingebaute "fremde Position wird doch getaggt"-Regression wird
gefangen. Nebenbefund: der Fill-Dedup ist DOPPELT gesichert (_pending_tagged
und der pop aus _pending_tickets) — nur eines zu entfernen faellt nicht auf,
beides zusammen bricht sofort zwei Tests. Gewollte Redundanz, kein
ungetesteter Zweig.
tests_rec_outcomes.py (Wegwerf-Fassung von gestern) und die verwaiste
test_pbreak.db entfernt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
79f3e8efe1
commit
9887e3d5da
@@ -0,0 +1,95 @@
|
||||
"""Gemeinsame Fixtures für die Szenario-Tests.
|
||||
|
||||
⚠⚠ DREI HARTE REGELN, damit diese Suite jederzeit gefahrlos läuft:
|
||||
1. **Nie die Live-DB anfassen.** Jeder Test bekommt eine frische Temp-Datei;
|
||||
`HistoryLogger.__init__` ruft `_init_db()` und legt das komplette Schema an.
|
||||
Die 15,7-MB-Live-DB zu kopieren wäre nicht nur langsam, sondern auch
|
||||
unzuverlässig — echte Zeilen haben mir am 06.08. einen Test verfälscht
|
||||
(echte `candles_m1` im vermeintlich leeren Fenster).
|
||||
2. **Kein MT5, kein laufender Server.** Alles, was den Broker berührt, wird
|
||||
gestubbt. Die Tests prüfen die LOGIK, nicht die Anbindung.
|
||||
3. **Keine Netzwerkzugriffe.**
|
||||
|
||||
Die Engine wird über `__new__` ohne `__init__` erzeugt: der echte Konstruktor
|
||||
baut MT5-Verbindung, Threads und Config auf. Für die geprüften Methoden reichen
|
||||
wenige Attribute — was fehlt, fällt im Test sofort als AttributeError auf und ist
|
||||
damit dokumentiert, statt still zu passieren.
|
||||
"""
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
from core.candle_logger import CandleLogger # noqa: E402
|
||||
from core.engine import TradingEngine # noqa: E402
|
||||
from core.history import HistoryLogger # noqa: E402
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db(tmp_path):
|
||||
"""Frische, vollständig angelegte Test-Datenbank.
|
||||
|
||||
⚠ `candles_m1` gehört NICHT zum `HistoryLogger`-Schema, sondern wird vom
|
||||
`CandleLogger` angelegt (`core/candle_logger.py`). Beide werden hier über
|
||||
ihre ECHTEN Konstruktoren aufgerufen — ein von Hand nachgebautes Test-Schema
|
||||
würde irgendwann vom Produktivstand abweichen, und der Test prüfte dann etwas
|
||||
anderes als das, was live läuft.
|
||||
"""
|
||||
p = tmp_path / "test.db"
|
||||
HistoryLogger(p) # legt trades, recommendations, rec_outcomes, …
|
||||
CandleLogger(str(p)) # legt candles_m1
|
||||
return p
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def history(db):
|
||||
return HistoryLogger(db)
|
||||
|
||||
|
||||
class FakeTrader:
|
||||
"""Minimaler Trader-Ersatz: liefert genau den Snapshot, den der Test setzt."""
|
||||
|
||||
def __init__(self):
|
||||
self.snap: dict = {}
|
||||
self.offset = 3 * 3600
|
||||
|
||||
def snapshot(self):
|
||||
return self.snap
|
||||
|
||||
def _broker_offset_s(self, sym=None):
|
||||
return self.offset
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def engine(history):
|
||||
"""Nackte Engine ohne `__init__` — nur die Attribute, die geprüft werden."""
|
||||
e = TradingEngine.__new__(TradingEngine)
|
||||
e.history = history
|
||||
e.trader = FakeTrader()
|
||||
e.data = types.SimpleNamespace(symbol="XTIUSD")
|
||||
e.cfg = {"telegram": {"enabled": "false"}}
|
||||
# Zustand der geprüften Pfade
|
||||
e._pending_tickets = {}
|
||||
e._pending_tagged = set()
|
||||
e._bot_open_ticket = None
|
||||
e._bot_open_source = None
|
||||
e._squeeze_entry_count = 0
|
||||
e._signal_entry_count = 0
|
||||
e._rec_epi_sig = None
|
||||
e._rec_eval_ts = 0.0
|
||||
return e
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_umleiten(db, monkeypatch):
|
||||
"""Leitet das modulweite `HISTORY_DB_FILE` der Engine auf die Test-DB um.
|
||||
|
||||
⚠ Nötig, weil `_evaluate_rec_outcomes` eine EIGENE sqlite3-Verbindung öffnet
|
||||
(bewusst — die Auswertung läuft außerhalb des HistoryLogger-Locks).
|
||||
"""
|
||||
import core.engine as CE
|
||||
monkeypatch.setattr(CE, "HISTORY_DB_FILE", str(db))
|
||||
return db
|
||||
Reference in New Issue
Block a user