mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-22 03:49:18 +02:00
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:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
solid test
|
||||||
|
endsolid test
|
||||||
Reference in New Issue
Block a user