fix(tests): patch _get_session_service directly to eliminate parallel-worker race condition #9213

Merged
HAL9000 merged 1 commits from fix/flaky-session-tell-tests into master 2026-04-28 06:59:24 +00:00
3 changed files with 42 additions and 4 deletions
+13
View File
@@ -713,6 +713,19 @@ def after_scenario(context, scenario):
except ImportError:
pass
# Reset session CLI module-level service singleton so that no stale
# service instance leaks into the next scenario. Without this reset,
# a scenario that sets _service to a real container-constructed instance
# would leave it cached; a subsequent scenario that patches _service
# via unittest.mock.patch would restore to the stale real instance on
# cleanup, causing the next scenario to hit the real container and fail.
try:
from cleveragents.cli.commands.session import _reset_session_service
_reset_session_service()
except ImportError:
pass
# Clean up service instances
for attr in ["context_service", "plan_service", "project_service"]:
if hasattr(context, attr):
@@ -101,7 +101,20 @@ def _mock_service() -> MagicMock:
def _patch_service(context, svc):
patcher = patch("cleveragents.cli.commands.session._service", svc)
"""Patch _get_session_service to return *svc* deterministically.
Patching the function directly (rather than the module-level ``_service``
attribute) prevents a race condition in parallel Behave workers: if a
concurrent worker's cleanup resets ``_service`` to ``None`` while this
scenario's CLI command is executing, ``_get_session_service()`` would fall
through to the real DI container and raise, producing a spurious exit
code 1. Patching the function itself short-circuits the cache check
entirely and is immune to cross-worker ``_service`` mutations.
"""
patcher = patch(
"cleveragents.cli.commands.session._get_session_service",
return_value=svc,
)
patcher.start()
context._scvbst_cleanups.append(patcher.stop)
@@ -72,12 +72,24 @@ def _mock_service() -> MagicMock:
return svc
# ---- helpers to patch the module-level _service ---------------------------
# ---- helpers to patch the module-level service accessor -------------------
def _patch_service(context, svc):
"""Patch session._service and register cleanup."""
patcher = patch("cleveragents.cli.commands.session._service", svc)
"""Patch _get_session_service to return *svc* deterministically.
Patching the function directly (rather than the module-level ``_service``
attribute) prevents a race condition in parallel Behave workers: if a
concurrent worker's cleanup resets ``_service`` to ``None`` while this
scenario's CLI command is executing, ``_get_session_service()`` would fall
through to the real DI container and raise, producing a spurious exit
code 1. Patching the function itself short-circuits the cache check
entirely and is immune to cross-worker ``_service`` mutations.
"""
patcher = patch(
"cleveragents.cli.commands.session._get_session_service",
return_value=svc,
)
patcher.start()
context._cleanup_handlers.append(patcher.stop)