import sys from pathlib import Path import pytest sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) import database # noqa: E402 @pytest.fixture async def testdb(tmp_path, monkeypatch): """Fresh sqlite file per test; resets the module-global connection.""" monkeypatch.setattr(database, "DB_PATH", tmp_path / "test.db") database._db = None await database.init_db() yield database await database.close_db() @pytest.fixture async def fake_welt(testdb, tmp_path, monkeypatch): """E2E ohne LLM: run_agent überall durch die Fake-Welt ersetzt, Tempo-Bremsen raus. Alle echten Schichten (_race, Quorum, Panels, Producer, QA-Gate) laufen mit.""" import agents import blocks import board_inventory as bi import guide import kanban import pipeline import qa import repair from fake_agents import Welt welt = Welt() async def fake_run_agent(agent_key, prompt, timeout, provider="claude", role="fast", capabilities="none", lane="batch", scope=None, on_line=None, label=""): return welt.respond(agent_key, prompt, capabilities) for mod in (agents, pipeline, blocks, guide, repair): monkeypatch.setattr(mod, "run_agent", fake_run_agent) # Tempo: grace/poll/backoff bremsen echte Läufe, nicht den Fake monkeypatch.setattr(blocks, "CONSENSUS_GRACE", 0) monkeypatch.setattr(bi, "_QA_GATE_POLL", 0.05) monkeypatch.setattr(kanban, "RETRY_BACKOFF", 0.05) monkeypatch.setattr(qa, "QA_DIR", tmp_path / "qa") import guide_board monkeypatch.setattr(guide_board, "READABILITY_ACTIVE", False) # kein Modell-Load im Test import asyncio as _aio monkeypatch.setattr(bi, "_ingest_lock", _aio.Lock()) # Modul-Lock klebt sonst am Vortest-Loop class _FakeEmb: # identischer Text → cos 1.0, sonst 0.0 (deterministisch, ohne Modell) @staticmethod def available(): return True @staticmethod def embed_sims(texts): import numpy as np uniq = {t: k for k, t in enumerate(dict.fromkeys(texts))} arr = np.zeros((len(texts), max(len(uniq), 1))) for r, t in enumerate(texts): arr[r, uniq[t]] = 1.0 return arr @ arr.T @staticmethod def embed(texts): import numpy as np uniq = {t: k for k, t in enumerate(dict.fromkeys(texts))} arr = np.zeros((len(texts), max(len(uniq), 1))) for r, t in enumerate(texts): arr[r, uniq[t]] = 1.0 return arr import board_artefacts as ba for mod in (blocks, ba, qa): monkeypatch.setattr(mod, "embedding", _FakeEmb) async def emb_ok(flow): # Board-1-Vektorpfade aus (wie board_env) — Judge-Wellen reichen return False monkeypatch.setattr(bi, "_emb_ok", emb_ok) return welt