""" Root Test Configuration — Global Guards Against Environmental Pollution ======================================================================= This conftest protects ALL tests from the #1 cause of mass failure: Config() constructor calling argparse.parse_known_args() which reads sys.argv (pytest's arguments) and crashes with SystemExit: 2. Every test directory inherits these fixtures automatically. """ import sys import pytest def pytest_addoption(parser): parser.addoption("--live", action="store_true", default=False, help="Run live tests") @pytest.fixture(autouse=True) def _isolate_config_from_argparse(monkeypatch): """Prevent Config() from reading sys.argv during tests. Root cause: Config.__init__ calls self.parse_args() which calls self.parser.parse_known_args(). In pytest, sys.argv contains pytest flags like '--ignore=...' which argparse interprets as Config arguments, causing SystemExit: 2. Fix: Temporarily set sys.argv to a minimal list so argparse doesn't choke on pytest's arguments. """ monkeypatch.setattr(sys, "argv", ["test_runner"]) # ═══════════════════════════════════════════════════════ # Pytest Markers Registration # ═══════════════════════════════════════════════════════ def pytest_configure(config): config.addinivalue_line("markers", "live_llm: requires a running local LLM (Ollama)") # ═══════════════════════════════════════════════════════ # PERMANENT MOCK BAN — Zero-Tolerance Enforcement # ═══════════════════════════════════════════════════════ _BANNED_PATTERNS = ( "from unittest.mock", "from unittest import mock", "import unittest.mock", "from mock import", "import mock", "MagicMock(", "MagicMock)", "@patch(", "@patch\n", "patch.object(", ) def pytest_collect_file(parent, file_path): """Scan every collected .py test file for banned mock imports. This runs at COLLECTION TIME — before any test executes. If a banned pattern is found, the file is still collected but every test inside it will be marked as an error via pytest_collection_modifyitems below. """ if file_path.suffix == ".py" and file_path.name.startswith("test_"): try: content = file_path.read_text(encoding="utf-8") for pattern in _BANNED_PATTERNS: if pattern in content: # Store the violation on the config for later reporting if not hasattr(parent.config, "_mock_violations"): parent.config._mock_violations = {} parent.config._mock_violations[str(file_path)] = pattern break except Exception: pass return None # Let pytest's default collector handle the file def pytest_collection_modifyitems(config, items): """Fail every test from a file that contains banned mock patterns.""" violations = getattr(config, "_mock_violations", {}) if not violations: return for item in items: test_file = str(item.fspath) if test_file in violations: pattern = violations[test_file] item.add_marker( pytest.mark.xfail( reason=( f"🚨 MOCK BAN VIOLATION: File contains '{pattern}'. " f"unittest.mock is permanently banned. " f"Use monkeypatch + real fixtures instead." ), strict=True, raises=Exception, ) )