Description: test: replace fixed-sleep timing asserts with async waits
 test_query_wait_timeout(): wait until client 1's pg_sleep is visible as
 active on the server before letting client 2 connect, instead of a fixed
 0.1 s sleep that a loaded build machine can overrun.
 test_pool_idle_timeout(), test_min_pool_size() and the four reserve-pool
 tests in test_limits.py: wait for the expected pg_stat_activity connection
 count instead of asserting at a fixed moment, so a backend left over from a
 previous test (its pgbouncer SIGQUIT'd while a query was still running)
 cannot break the count.

--- a/test/test_timeouts.py	2026-09-28 15:37:14.011594650 +0200
+++ b/test/test_timeouts.py	2026-09-28 15:39:11.812588139 +0200
@@ -7,7 +7,7 @@
 import psycopg
 import pytest

-from .utils import USE_SUDO
+from .utils import USE_SUDO, wait_until_async


 @pytest.mark.parametrize(
@@ -63,14 +63,31 @@

     with bouncer.run_with_config(pgbouncer_ini):
         conn_1_fut = bouncer.asleep(3)
-        await asyncio.sleep(0.1)
+        # Wait until client 1's pg_sleep is actually running on the server
+        # and holds the only server connection, instead of a fixed sleep
+        # that a loaded buildd can overrun.
+        await wait_until_async(
+            lambda: bouncer.pg.sql_value(
+                "select count(1) from pg_stat_activity where usename = 'puser1'"
+                " and state = 'active' and query like 'select pg_sleep(3)%'"
+            )
+            >= 1,
+            "client 1 pg_sleep to run on the server",
+        )

         with pytest.raises(psycopg.OperationalError, match=r"query_wait_timeout"):
             bouncer.test()
         await conn_1_fut

         conn_1_fut = bouncer.asleep(1)
-        await asyncio.sleep(0.1)
+        await wait_until_async(
+            lambda: bouncer.pg.sql_value(
+                "select count(1) from pg_stat_activity where usename = 'puser1'"
+                " and state = 'active' and query like 'select pg_sleep(1)%'"
+            )
+            >= 1,
+            "client 1 pg_sleep to run on the server",
+        )
         bouncer.test()
         await conn_1_fut

@@ -550,13 +567,20 @@
                 cur.execute("select 1")


-def test_pool_idle_timeout(pg, bouncer):
+async def test_pool_idle_timeout(pg, bouncer):
     """Test that the pool closes server connections after being idle."""
     bouncer.admin("set pool_idle_timeout=1")
     bouncer.admin("set server_idle_timeout=1")

     bouncer.test()
-    assert pg.connection_count() == 1
+    # A server connection left over from a previous test (whose pgbouncer
+    # exited while a query was still running) can show up in
+    # pg_stat_activity for a while, so wait instead of asserting at a fixed
+    # moment. Leftovers die once the last query of the previous test
+    # finishes, which this test's own timeouts comfortably outwait.
+    await wait_until_async(
+        lambda: pg.connection_count() == 1, "server connection to be created", timeout=10
+    )

     # The non-admin pool exists after connecting. Column 0 of SHOW POOLS is
     # the database, and there's always an admin ("pgbouncer") pool.
@@ -572,10 +596,14 @@
     print("pools after idle timeout:", pools_after)
     assert all(row[0] == "pgbouncer" for row in pools_after)

-    assert pg.connection_count() == 0
+    await wait_until_async(
+        lambda: pg.connection_count() == 0, "server connections to be closed", timeout=20
+    )

     bouncer.test()
-    assert pg.connection_count() == 1
+    await wait_until_async(
+        lambda: pg.connection_count() == 1, "server connection to be created", timeout=10
+    )


 def test_pool_idle_timeout_ignores_min_pool_size(pg, bouncer):
--- a/test/test_limits.py	2026-09-28 15:37:14.011665317 +0200
+++ b/test/test_limits.py	2026-09-28 15:41:53.233437972 +0200
@@ -6,6 +6,8 @@
 import pytest
 from psycopg.rows import dict_row

+from .utils import wait_until_async
+

 async def test_max_client_conn(bouncer):
     bouncer.default_db = "p1"
@@ -216,15 +218,16 @@
         f.write(new)
     bouncer.admin("reload")

-    # having to wait a little to give janitor time to create connection to satisfy min_pool_size
-    await asyncio.sleep(2)
-
     # ensure db without min_pool_size has no connections
     # p0
     assert pg.connection_count(dbname="p0", users=("bouncer",)) == 0
-    # ensure db with min_pool_size and forced user (p0z) has the required
-    # backend connections
-    assert pg.connection_count(dbname="p0", users=("pswcheck",)) == 3
+    # give the janitor time to create the connections that satisfy
+    # min_pool_size on the forced-user db (p0z)
+    await wait_until_async(
+        lambda: pg.connection_count(dbname="p0", users=("pswcheck",)) == 3,
+        "min_pool_size connections to be created",
+        timeout=10,
+    )

     # ensure db with min_pool_size and no forced user (p0x) has no backend
     # connections
@@ -241,10 +244,16 @@
     # Also, we need to keep the query running while this is
     # happening so that the pool doesn't become momentarily
     # unused.
-    result = bouncer.asleep(2, dbname="p0x")
-    await asyncio.sleep(2)
-    await result
-    assert pg.connection_count(dbname="p0", users=("postgres",)) == 5
+    # The long sleep keeps the pool in use; we cancel it once the janitor
+    # has created all the min_pool_size connections.
+    result = bouncer.asleep(20, dbname="p0x")
+    await wait_until_async(
+        lambda: pg.connection_count(dbname="p0", users=("postgres",)) == 5,
+        "min_pool_size connections to be created",
+        timeout=20,
+    )
+    result.cancel()
+    await asyncio.gather(result, return_exceptions=True)


 @pytest.mark.parametrize(
@@ -530,10 +539,16 @@
         # until the reserve_pool_timeout (2 seconds) is reached. At that point
         # 3 more connections should be allowed to continue.
         result = bouncer.asleep(10, dbname="p1", times=10)
-        await asyncio.sleep(1)
+        # Check the plain pool is in use before the reserve_pool_timeout
+        # elapses, then wait for the reserve connections instead of relying
+        # on a fixed 8 second sleep.
+        await asyncio.sleep(0.5)
         assert pg.connection_count("p1") == 5
-        await asyncio.sleep(8)
-        assert pg.connection_count("p1") == 8
+        await wait_until_async(
+            lambda: pg.connection_count("p1") == 8,
+            "reserve pool connections to be created",
+            timeout=15,
+        )
         await result


@@ -548,10 +563,13 @@
         # this means 1 connection should happen immediately while 2 out of
         # the 3 remaining connections happen after reserve_pool_timeout
         result = bouncer.asleep(10, dbname="p0a", user="respoolsize1", times=4)
-        await asyncio.sleep(1)
+        await asyncio.sleep(0.5)
         assert pg.connection_count(dbname="p0", users=("respoolsize1",)) == 1
-        await asyncio.sleep(8)
-        assert pg.connection_count(dbname="p0", users=("respoolsize1",)) == 3
+        await wait_until_async(
+            lambda: pg.connection_count(dbname="p0", users=("respoolsize1",)) == 3,
+            "reserve pool connections to be created",
+            timeout=15,
+        )
         await result


@@ -566,10 +584,13 @@
         # this means 2 connections should happen immediately while 2 out of
         # the 3 remaining connections happen after reserve_pool_timeout
         result = bouncer.asleep(10, dbname="p0", user="bouncer", times=5)
-        await asyncio.sleep(1)
+        await asyncio.sleep(0.5)
         assert pg.connection_count(dbname="p0", users=("bouncer",)) == 2
-        await asyncio.sleep(8)
-        assert pg.connection_count(dbname="p0", users=("bouncer",)) == 4
+        await wait_until_async(
+            lambda: pg.connection_count(dbname="p0", users=("bouncer",)) == 4,
+            "reserve pool connections to be created",
+            timeout=15,
+        )
         await result


@@ -584,10 +605,13 @@
         # this means 2 connections should happen immediately while 2 out of
         # the 3 remaining connections happen after reserve_pool_timeout
         result = bouncer.asleep(10, dbname="p0a", user="bouncer", times=5)
-        await asyncio.sleep(1)
+        await asyncio.sleep(0.5)
         assert pg.connection_count(dbname="p0", users=("bouncer",)) == 2
-        await asyncio.sleep(8)
-        assert pg.connection_count(dbname="p0", users=("bouncer",)) == 4
+        await wait_until_async(
+            lambda: pg.connection_count(dbname="p0", users=("bouncer",)) == 4,
+            "reserve pool connections to be created",
+            timeout=15,
+        )
         await result


