test: close the coroutines that mocked create_task, wait_for, run_coroutine_threadsafe, spawn_background_task and AsyncMock sessions left unawaited (reported as unraisable warnings in unrelated later tests); the Spoolman recovery tests archive under their own tmp_path instead of the shared archive/

This commit is contained in:
maziggy
2026-10-21 13:18:54 +02:00
parent c1ea484a5a
commit ee2d6e69aa
6 changed files with 46 additions and 29 deletions
@@ -35,7 +35,11 @@ async def _complete(printer_id, status, update):
session = AsyncMock() session = AsyncMock()
session.__aenter__ = AsyncMock(return_value=session) session.__aenter__ = AsyncMock(return_value=session)
session.__aexit__ = AsyncMock(return_value=False) session.__aexit__ = AsyncMock(return_value=False)
session.execute = AsyncMock(return_value=MagicMock()) # Plain mocks for what the reads return: an AsyncMock's results are
# AsyncMocks too, so a plain call on one (completed_at.astimezone(),
# .first()) leaves an unawaited coroutine, reported in a later test.
for read in ("execute", "get", "scalar", "scalars"):
setattr(session, read, AsyncMock(return_value=MagicMock()))
tasks_before = set(asyncio.all_tasks()) tasks_before = set(asyncio.all_tasks())
with ( with (
patch("backend.app.main.async_session", return_value=session), patch("backend.app.main.async_session", return_value=session),
@@ -422,10 +422,21 @@ class TestPrinterManager:
mock_loop.is_running.return_value = True mock_loop.is_running.return_value = True
manager._loop = mock_loop manager._loop = mock_loop
manager.mark_printer_offline(1) # The stand-in loop never runs the callback, so the scheduled
# coroutine is closed here instead of being reported in a later test.
def _close_and_stand_in(coro, loop):
coro.close()
return MagicMock()
with patch(
"backend.app.services.printer_manager.asyncio.run_coroutine_threadsafe",
side_effect=_close_and_stand_in,
) as scheduled:
manager.mark_printer_offline(1)
# Callback should be scheduled via run_coroutine_threadsafe # Callback should be scheduled via run_coroutine_threadsafe
mock_loop.is_running.assert_called() mock_loop.is_running.assert_called()
scheduled.assert_called_once()
# State should be updated # State should be updated
assert mock_client.state.connected is False assert mock_client.state.connected is False
@@ -375,15 +375,20 @@ class TestSmartPlugManager:
"""Verify scheduler can be started.""" """Verify scheduler can be started."""
assert manager._scheduler_task is None assert manager._scheduler_task is None
# Mock _schedule_loop to return a mock coroutine to avoid unawaited coroutine warning # The patched loops are AsyncMocks, so each call still builds a
# coroutine: the stand-in create_task closes it, or it is reported
# unawaited in a later test.
def _close_and_stand_in(coro, **_):
coro.close()
return MagicMock()
with ( with (
patch.object(manager, "_schedule_loop") as mock_loop, patch.object(manager, "_schedule_loop") as mock_loop,
patch.object(manager, "_snapshot_loop"), patch.object(manager, "_snapshot_loop"),
patch.object(manager, "_power_alert_loop") as mock_power_alert, patch.object(manager, "_power_alert_loop") as mock_power_alert,
patch.object(manager, "_relay_energy_loop") as mock_relay_energy, patch.object(manager, "_relay_energy_loop") as mock_relay_energy,
patch("asyncio.create_task") as mock_create, patch("asyncio.create_task", side_effect=_close_and_stand_in),
): ):
mock_create.return_value = MagicMock()
manager.start_scheduler() manager.start_scheduler()
assert manager._scheduler_task is not None assert manager._scheduler_task is not None
@@ -4213,21 +4213,20 @@ class TestSlicerProxyManager:
) )
# Mock asyncio.create_task and asyncio.gather to prevent actual # Mock asyncio.create_task and asyncio.gather to prevent actual
# server start. Close every coroutine handed to gather — otherwise # server start. Close every coroutine handed to create_task (gather
# the ~110 run_with_logging() coros built inside start() are # only sees the stand-in tasks) — otherwise the ~110
# garbage-collected unfinalized and surface later as # run_with_logging() coros built inside start() are garbage-collected
# PytestUnraisableExceptionWarning at random in other tests. # unfinalized and surface later as PytestUnraisableExceptionWarning at
async def _close_pending(*coros, **_): # random in other tests.
for c in coros: def _close_and_stand_in(coro, **_):
if asyncio.iscoroutine(c): coro.close()
c.close() return MagicMock()
with ( with (
patch("asyncio.create_task") as mock_create_task, patch("asyncio.create_task", side_effect=_close_and_stand_in),
patch("asyncio.gather", side_effect=_close_pending), patch("asyncio.gather", new=AsyncMock()),
patch.object(SlicerProxyManager, "_log_activity"), patch.object(SlicerProxyManager, "_log_activity"),
): ):
mock_create_task.return_value = MagicMock()
await mgr.start() await mgr.start()
# FTP, FileTransfer, RTSP should be TCPProxy (transparent) # FTP, FileTransfer, RTSP should be TCPProxy (transparent)
+9 -13
View File
@@ -118,15 +118,9 @@ class TestCheckPort:
async def test_unreachable_port(self): async def test_unreachable_port(self):
from backend.app.api.routes.support import _check_port from backend.app.api.routes.support import _check_port
with ( with patch(
patch( "backend.app.api.routes.support.asyncio.open_connection",
"backend.app.api.routes.support.asyncio.open_connection", side_effect=ConnectionRefusedError,
side_effect=ConnectionRefusedError,
),
patch(
"backend.app.api.routes.support.asyncio.wait_for",
side_effect=ConnectionRefusedError,
),
): ):
result = await _check_port("192.168.1.1", 8883, timeout=1.0) result = await _check_port("192.168.1.1", 8883, timeout=1.0)
@@ -137,10 +131,12 @@ class TestCheckPort:
async def test_timeout(self): async def test_timeout(self):
from backend.app.api.routes.support import _check_port from backend.app.api.routes.support import _check_port
with patch( # Closes the connect it was handed: left unawaited, it is reported in a later test.
"backend.app.api.routes.support.asyncio.wait_for", async def _times_out(coro, timeout):
side_effect=asyncio.TimeoutError, coro.close()
): raise TimeoutError
with patch("backend.app.api.routes.support.asyncio.wait_for", side_effect=_times_out):
result = await _check_port("192.168.1.1", 8883, timeout=0.1) result = await _check_port("192.168.1.1", 8883, timeout=0.1)
assert result is False assert result is False
+2
View File
@@ -0,0 +1,2 @@
solid test
endsolid test