"""Regression guard: list_persons must batch — a constant number of queries, not one (or three) per person. A 2k-person tree took ~4s before this was fixed.""" import sqlalchemy as sa from tests.conftest import auth, register async def test_list_persons_does_not_n_plus_one(client, engine): owner = auth(await register(client, "perf-owner@ex.com")) tid = (await client.post("/api/v1/trees", json={"name": "Perf"}, headers=owner)).json()["id"] n = 25 for i in range(n): await client.post( f"/api/v1/trees/{tid}/persons", json={"given": f"P{i}", "surname": "X"}, headers=owner, ) selects = 0 def _count(conn, cursor, statement, params, context, executemany): nonlocal selects if statement.lstrip().upper().startswith("SELECT"): selects += 1 sa.event.listen(engine.sync_engine, "before_cursor_execute", _count) try: resp = await client.get(f"/api/v1/trees/{tid}/persons", headers=owner) finally: sa.event.remove(engine.sync_engine, "before_cursor_execute", _count) assert resp.status_code == 200 body = resp.json() assert len(body) == n assert all(p["primary_name"] for p in body) # names still resolve correctly # Batched: a small constant (auth, role, persons, one names query, …) — NOT # proportional to n. The old per-person path was ~3·n SELECTs. assert 0 < selects < n, f"expected a constant query count, got {selects} for {n} people"