fix(web/api): scope DB-retry sleep so tests don't starve background tasks
test_lifespan_db_retry patched decnet.web.api.asyncio.sleep to skip the DB-retry backoff. Problem: asyncio is a shared module — the patch leaks to every caller that looked up asyncio.sleep via `import asyncio`, including run_health_heartbeat's own sleep loop. That heartbeat task spawns inside the same lifespan; with its sleep mocked, the while-loop spins tight, starves cancellation, and leaves an orphan task that pytest-timeout eventually signals — surfacing as the 'Task exception was never retrieved' warnings the user saw when running the suite. Fix: give decnet.web.api a local binding `_retry_sleep = asyncio.sleep` for the DB-retry wait, and have the test patch that instead. Narrowly scoped, no impact on asyncio.sleep callers elsewhere. Test timing before: 12s with --timeout=10 (interrupted by signal). Test timing after: 0.58s. Full tests/web slice: 27s → 7.1s with the spurious warnings gone.
This commit is contained in:
@@ -148,7 +148,10 @@ class TestLifespan:
|
||||
mock_repo.initialize = _failing_init
|
||||
|
||||
with patch("decnet.web.api.repo", mock_repo):
|
||||
with patch("decnet.web.api.asyncio.sleep", new_callable=AsyncMock):
|
||||
# Patch only the local _retry_sleep binding — patching
|
||||
# `asyncio.sleep` globally would starve the heartbeat loop's
|
||||
# own sleep and leak the task past the test's lifetime.
|
||||
with patch("decnet.web.api._retry_sleep", new_callable=AsyncMock):
|
||||
with patch("decnet.web.api.log_ingestion_worker", return_value=asyncio.sleep(0)):
|
||||
with patch("decnet.web.api.log_collector_worker", return_value=asyncio.sleep(0)):
|
||||
with patch("decnet.web.api.attacker_profile_worker", return_value=asyncio.sleep(0)):
|
||||
|
||||
Reference in New Issue
Block a user