"""Test configuration for the backend test suite. Sets up sys.path and pre-mocks modules that would cause circular import issues when unit-testing lightweight config/registry code in isolation. """ import importlib.util import sys from pathlib import Path from types import SimpleNamespace from unittest.mock import MagicMock import pytest # Make 'app' and 'deerflow' importable from any working directory sys.path.insert(0, str(Path(__file__).parent.parent)) sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "scripts")) # Break the circular import chain that exists in production code: # deerflow.subagents.__init__ # -> .executor (SubagentExecutor, SubagentResult) # -> deerflow.agents.thread_state # -> deerflow.agents.__init__ # -> lead_agent.agent # -> subagent_limit_middleware # -> deerflow.subagents.executor <-- circular! # # By injecting a mock for deerflow.subagents.executor *before* any test module # triggers the import, __init__.py's "from .executor import ..." succeeds # immediately without running the real executor module. _executor_mock = MagicMock() _executor_mock.SubagentExecutor = MagicMock _executor_mock.SubagentResult = MagicMock _executor_mock.SubagentStatus = MagicMock _executor_mock.MAX_CONCURRENT_SUBAGENTS = 3 _executor_mock.get_background_task_result = MagicMock() sys.modules["deerflow.subagents.executor"] = _executor_mock @pytest.fixture() def provisioner_module(): """Load docker/provisioner/app.py as an importable test module. Shared by test_provisioner_kubeconfig and test_provisioner_pvc_volumes so that any change to the provisioner entry-point path or module name only needs to be updated in one place. """ repo_root = Path(__file__).resolve().parents[2] module_path = repo_root / "docker" / "provisioner" / "app.py" spec = importlib.util.spec_from_file_location("provisioner_app_test", module_path) assert spec is not None assert spec.loader is not None module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module # --------------------------------------------------------------------------- # Auto-set user context for every test unless marked no_auto_user # --------------------------------------------------------------------------- # # Repository methods read ``user_id`` from a contextvar by default # (see ``deerflow.runtime.user_context``). Without this fixture, every # pre-existing persistence test would raise RuntimeError because the # contextvar is unset. The fixture sets a default test user on every # test; tests that explicitly want to verify behaviour *without* a user # context should mark themselves ``@pytest.mark.no_auto_user``. @pytest.fixture(autouse=True) def _auto_user_context(request): """Inject a default ``test-user-autouse`` into the contextvar. Opt-out via ``@pytest.mark.no_auto_user``. Uses lazy import so that tests which don't touch the persistence layer never pay the cost of importing runtime.user_context. """ if request.node.get_closest_marker("no_auto_user"): yield return try: from deerflow.runtime.user_context import ( reset_current_user, set_current_user, ) except ImportError: yield return user = SimpleNamespace(id="test-user-autouse", email="test@local") token = set_current_user(user) try: yield finally: reset_current_user(token)