Просмотр исходного кода

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/

maziggy 22 часов назад
Родитель
Сommit
ee2d6e69aa

+ 5 - 1
backend/tests/integration/test_kill_switch_stop_archived_cancelled.py

@@ -35,7 +35,11 @@ async def _complete(printer_id, status, update):
     session = AsyncMock()
     session.__aenter__ = AsyncMock(return_value=session)
     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())
     with (
         patch("backend.app.main.async_session", return_value=session),

+ 12 - 1
backend/tests/unit/services/test_printer_manager.py

@@ -422,10 +422,21 @@ class TestPrinterManager:
         mock_loop.is_running.return_value = True
         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
         mock_loop.is_running.assert_called()
+        scheduled.assert_called_once()
         # State should be updated
         assert mock_client.state.connected is False
 

+ 8 - 3
backend/tests/unit/services/test_smart_plug_manager.py

@@ -375,15 +375,20 @@ class TestSmartPlugManager:
         """Verify scheduler can be started."""
         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 (
             patch.object(manager, "_schedule_loop") as mock_loop,
             patch.object(manager, "_snapshot_loop"),
             patch.object(manager, "_power_alert_loop") as mock_power_alert,
             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()
 
             assert manager._scheduler_task is not None

+ 10 - 11
backend/tests/unit/services/test_virtual_printer.py

@@ -4213,21 +4213,20 @@ class TestSlicerProxyManager:
         )
 
         # Mock asyncio.create_task and asyncio.gather to prevent actual
-        # server start. Close every coroutine handed to gather — otherwise
-        # the ~110 run_with_logging() coros built inside start() are
-        # garbage-collected unfinalized and surface later as
-        # PytestUnraisableExceptionWarning at random in other tests.
-        async def _close_pending(*coros, **_):
-            for c in coros:
-                if asyncio.iscoroutine(c):
-                    c.close()
+        # server start. Close every coroutine handed to create_task (gather
+        # only sees the stand-in tasks) — otherwise the ~110
+        # run_with_logging() coros built inside start() are garbage-collected
+        # unfinalized and surface later as PytestUnraisableExceptionWarning at
+        # random in other tests.
+        def _close_and_stand_in(coro, **_):
+            coro.close()
+            return MagicMock()
 
         with (
-            patch("asyncio.create_task") as mock_create_task,
-            patch("asyncio.gather", side_effect=_close_pending),
+            patch("asyncio.create_task", side_effect=_close_and_stand_in),
+            patch("asyncio.gather", new=AsyncMock()),
             patch.object(SlicerProxyManager, "_log_activity"),
         ):
-            mock_create_task.return_value = MagicMock()
             await mgr.start()
 
         # FTP, FileTransfer, RTSP should be TCPProxy (transparent)

+ 9 - 13
backend/tests/unit/test_support_helpers.py

@@ -118,15 +118,9 @@ class TestCheckPort:
     async def test_unreachable_port(self):
         from backend.app.api.routes.support import _check_port
 
-        with (
-            patch(
-                "backend.app.api.routes.support.asyncio.open_connection",
-                side_effect=ConnectionRefusedError,
-            ),
-            patch(
-                "backend.app.api.routes.support.asyncio.wait_for",
-                side_effect=ConnectionRefusedError,
-            ),
+        with patch(
+            "backend.app.api.routes.support.asyncio.open_connection",
+            side_effect=ConnectionRefusedError,
         ):
             result = await _check_port("192.168.1.1", 8883, timeout=1.0)
 
@@ -137,10 +131,12 @@ class TestCheckPort:
     async def test_timeout(self):
         from backend.app.api.routes.support import _check_port
 
-        with patch(
-            "backend.app.api.routes.support.asyncio.wait_for",
-            side_effect=asyncio.TimeoutError,
-        ):
+        # Closes the connect it was handed: left unawaited, it is reported in a later test.
+        async def _times_out(coro, timeout):
+            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)
 
         assert result is False

+ 2 - 0
test_files_3029_1663994/stale.stl

@@ -0,0 +1,2 @@
+solid test
+endsolid test