Procházet zdrojové kódy

test: test scripts run the build, type check and lint exits; finish query cancellations and release the app engine per test; refresh the type-check baseline

maziggy před 4 dny
rodič
revize
fa5f300a4d

+ 22 - 0
backend/tests/conftest.py

@@ -396,6 +396,27 @@ async def _reset_pg_test_schema(conn) -> None:
     await conn.execute(text(_PG_EMPTY_TABLES))
 
 
+async def _finish_query_cancellations() -> None:
+    """Wait for asyncpg to finish cancelling queries in this test's loop.
+
+    A test that cancels a task mid-query (to stop the background work a
+    handler started) awaits the task, but asyncpg cancels the query on the
+    server in a task of its own. If the test ends first, that task is still
+    pending when the loop closes and is reported as an unraisable
+    "Connection._cancel" coroutine in some later test.
+    """
+    current = asyncio.current_task()
+    pending = [
+        task
+        for task in asyncio.all_tasks()
+        if task is not current
+        and not task.done()
+        and getattr(task.get_coro(), "__qualname__", "") == "Connection._cancel"
+    ]
+    if pending:
+        await asyncio.wait(pending, timeout=5)
+
+
 async def _release_app_engine() -> None:
     """Close the app engine's pooled connections before this test's loop ends.
 
@@ -406,6 +427,7 @@ async def _release_app_engine() -> None:
     PostgreSQL that leaves asyncpg's cleanup unawaited, reported as an
     unraisable "Connection._cancel" coroutine in some later test.
     """
+    await _finish_query_cancellations()
     await _app_engine.dispose()
 
 

+ 38 - 0
backend/tests/unit/test_conftest_query_cancellations.py

@@ -0,0 +1,38 @@
+"""The test harness waits for asyncpg's query cancellations before it closes
+a test's engines.
+
+A test that cancels a task mid-query leaves asyncpg's own cancel task behind;
+if the test ends first, it is still pending when the loop closes and shows up
+as an unraisable "Connection._cancel" warning in some later test on PostgreSQL.
+"""
+
+import asyncio
+
+from backend.tests.conftest import _finish_query_cancellations
+
+
+class Connection:
+    """Stands in for asyncpg's: the helper recognises the task by this name."""
+
+    async def _cancel(self) -> None:
+        await asyncio.sleep(0.05)
+
+
+async def test_a_pending_cancellation_is_finished_first():
+    task = asyncio.get_running_loop().create_task(Connection()._cancel())
+
+    await _finish_query_cancellations()
+
+    assert task.done()
+
+
+async def test_other_tasks_are_left_alone():
+    async def unrelated() -> None:
+        await asyncio.sleep(10)
+
+    task = asyncio.get_running_loop().create_task(unrelated())
+    try:
+        await asyncio.wait_for(_finish_query_cancellations(), timeout=1)
+        assert not task.done()
+    finally:
+        task.cancel()