"""Global pytest fixtures. test(conftest): mute Telegram in ALL tests to stop prod leakage. Background: a pytest run on prod was sending REAL Telegram messages to Slava, because some tests (e.g. test_webhook_dedup advancing a stage) reach notify_stage_change -> send_telegram, which reads the live .env telegram_bot_token/chat_id and actually POSTs to Telegram. This autouse fixture stubs send_telegram to a no-op for every test: - "src.notifications.send_telegram" is the SOURCE. All the notify_* helpers in notifications.py call the module-global send_telegram, and every other module that does a *local* `from .notifications import send_telegram` inside a function resolves it live at call time -> covered by patching the source. - "src.stage_engine.send_telegram" is patched too, because stage_engine binds send_telegram as a MODULE-LEVEL name (from .notifications import send_telegram at import), so a patch of the source alone would not intercept its 3 direct calls. webhooks/plane and launcher import it locally inside functions, so the source patch already covers them; they are patched defensively with raising=False anyway in case that ever changes. raising=False so a module that doesn't (yet) expose the name never breaks setup. """ import pytest @pytest.fixture(autouse=True) def _no_telegram(monkeypatch): _noop = lambda *a, **k: None # noqa: E731 # Source of truth (covers notifications.notify_* and all local re-imports). monkeypatch.setattr("src.notifications.send_telegram", _noop, raising=False) # Module-level binding in stage_engine (and defensive coverage elsewhere). monkeypatch.setattr("src.stage_engine.send_telegram", _noop, raising=False) monkeypatch.setattr("src.webhooks.plane.send_telegram", _noop, raising=False) monkeypatch.setattr("src.agents.launcher.send_telegram", _noop, raising=False) monkeypatch.setattr("src.queue_worker.send_telegram", _noop, raising=False) yield @pytest.fixture(autouse=True) def _reset_webhook_secrets(monkeypatch): """Isolate settings singleton between test files (CI cross-file isolation). settings is a process-wide Pydantic singleton read once at import. Different test modules set env variables differently at import-time, so those values leak across files when pytest collects them together (as CI does). 1. webhook secrets: reset to "" so HMAC is disabled by default. Tests that intentionally test the 401 path (test_webhook_dedup.py:268,278) re-apply their own monkeypatch AFTER this autouse fixture runs, which overrides the reset for the duration of that one test only. 2. db_path: reset to the value from ORCH_DB_PATH env var (last written by the last imported test module). Without this, test_webhook_dedup.py (imported first, alphabetically) seeds settings.db_path = dedup.db, while test_webhooks.py's setup_db fixture tries to remove test_orchestrator.db, leaving the DB dirty across tests that share a branch name and causing get_task_by_repo_branch() to return a stale row with the wrong stage. Per-test monkeypatches in test_webhook_dedup.setup_db override this reset. """ import os from src.webhooks import gitea as gitea_mod from src.webhooks import plane as plane_mod from src import db as db_mod monkeypatch.setattr(gitea_mod.settings, "gitea_webhook_secret", "", raising=False) monkeypatch.setattr(plane_mod.settings, "plane_webhook_secret", "", raising=False) db_path_env = os.environ.get("ORCH_DB_PATH", "") if db_path_env: monkeypatch.setattr(db_mod.settings, "db_path", db_path_env, raising=False) yield