fix(tests): eliminate tarpit OOM from global asyncio.sleep mock

Two interacting bugs caused asyncio.sleep to be mocked globally,
letting tarpit_watcher_worker spin the event loop on a non-async
mock and accumulate _increment_mock_call records without bound:

1. test_ingester.py patched `decnet.web.ingester.asyncio.sleep` via
   the asyncio singleton — any code in the process using asyncio.sleep
   (including the tarpit worker) hit the fake_sleep side_effect.
   Fix: add `_sleep = asyncio.sleep` alias in ingester.py and patch
   `decnet.web.ingester._sleep` instead — scopes the mock to ingester.

2. test_api_startup_guards.py called `_run_lifespan_startup` without
   DECNET_CONTRACT_TEST=true, which started the real tarpit task in a
   manually-constructed event loop that the tests never cancelled.
   Fix: set DECNET_CONTRACT_TEST=true inside _run_lifespan_startup so
   the lifespan skips all background workers.
This commit is contained in:
2026-05-10 10:06:21 -04:00
parent a2c34cac02
commit ff51ce55e2
3 changed files with 32 additions and 22 deletions

View File

@@ -26,6 +26,7 @@ from decnet.web.db.repository import BaseRepository
logger = get_logger("api")
_INGEST_STATE_KEY = "ingest_worker_position"
_sleep = asyncio.sleep # module-level alias so tests patch here, not the global asyncio singleton
async def log_ingestion_worker(repo: BaseRepository) -> None:
@@ -73,7 +74,7 @@ async def _run_loop(
while True:
try:
if not _json_log_path.exists():
await asyncio.sleep(2)
await _sleep(2)
continue
_stat: os.stat_result = _json_log_path.stat()
@@ -84,7 +85,7 @@ async def _run_loop(
if _stat.st_size == _position:
# No new data
await asyncio.sleep(1)
await _sleep(1)
continue
# Accumulate parsed rows and the file offset they end at. We
@@ -149,9 +150,9 @@ async def _run_loop(
break # Exit worker — DB is gone or uninitialized
logger.error("ingest: error in worker: %s", _e)
await asyncio.sleep(5)
await _sleep(5)
await asyncio.sleep(1)
await _sleep(1)
async def _publish_batch(bus: Any, flushed: int, position: int) -> None: