fix(tests): patch _get_session_service directly to eliminate parallel-worker race condition
CI / benchmark-publish (push) Failing after 43s
CI / lint (push) Successful in 52s
CI / quality (push) Successful in 1m0s
CI / typecheck (push) Successful in 1m21s
CI / security (push) Successful in 1m33s
CI / helm (push) Successful in 26s
CI / push-validation (push) Successful in 25s
CI / build (push) Successful in 37s
CI / integration_tests (push) Successful in 3m46s
CI / unit_tests (push) Successful in 4m31s
CI / e2e_tests (push) Successful in 3m47s
CI / docker (push) Successful in 1m32s
CI / coverage (push) Successful in 11m25s
CI / status-check (push) Successful in 5s
CI / benchmark-publish (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 11m39s
CI / build (pull_request) Successful in 1m17s
CI / unit_tests (pull_request) Successful in 6m34s
CI / e2e_tests (pull_request) Successful in 4m47s
CI / status-check (pull_request) Waiting to run
CI / push-validation (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 1m55s
CI / quality (pull_request) Successful in 1m56s
CI / integration_tests (pull_request) Successful in 5m36s
CI / helm (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 1m29s
CI / security (pull_request) Successful in 1m56s
CI / docker (pull_request) Successful in 1m35s
CI / benchmark-publish (push) Failing after 43s
CI / lint (push) Successful in 52s
CI / quality (push) Successful in 1m0s
CI / typecheck (push) Successful in 1m21s
CI / security (push) Successful in 1m33s
CI / helm (push) Successful in 26s
CI / push-validation (push) Successful in 25s
CI / build (push) Successful in 37s
CI / integration_tests (push) Successful in 3m46s
CI / unit_tests (push) Successful in 4m31s
CI / e2e_tests (push) Successful in 3m47s
CI / docker (push) Successful in 1m32s
CI / coverage (push) Successful in 11m25s
CI / status-check (push) Successful in 5s
CI / benchmark-publish (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 11m39s
CI / build (pull_request) Successful in 1m17s
CI / unit_tests (pull_request) Successful in 6m34s
CI / e2e_tests (pull_request) Successful in 4m47s
CI / status-check (pull_request) Waiting to run
CI / push-validation (pull_request) Successful in 29s
CI / typecheck (pull_request) Successful in 1m55s
CI / quality (pull_request) Successful in 1m56s
CI / integration_tests (pull_request) Successful in 5m36s
CI / helm (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 1m29s
CI / security (pull_request) Successful in 1m56s
CI / docker (pull_request) Successful in 1m35s
The root cause was a race condition in parallel Behave workers caused by mutating a module-level singleton (_service) in cleveragents.cli.commands.session. Concurrent cleanup in one worker could reset _service to None while another worker was still using it, leading to intermittent tell command test failures and exit code 1. The fix patches the _get_session_service function directly in the affected test steps (session_cli_coverage_boost_steps.py and session_cli_uncovered_branches_steps.py) to avoid mutating the module-level _service, and adds a reset call in features/environment.py's after_scenario to ensure the singleton is cleared between scenarios, preventing stale service instances from leaking across tests. Closes #9121
This commit was merged in pull request #9213.
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user