"""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