fix(ci): use shared admin_conn fixture for test transaction visibility
Both admin_session and test_app now bind to the same connection (admin_conn), ensuring test-created data is visible to API endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -130,20 +130,30 @@ async def app_engine():
|
|||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def admin_session(admin_engine) -> AsyncGenerator[AsyncSession, None]:
|
async def admin_conn(admin_engine):
|
||||||
"""Per-test admin session with transaction rollback.
|
"""Shared admin connection with transaction rollback.
|
||||||
|
|
||||||
Each test gets a clean transaction that is rolled back after the test,
|
Both admin_session and test_app bind to the same connection so that
|
||||||
ensuring no state leakage between tests.
|
data created in the test (via admin_session) is visible to API
|
||||||
|
endpoints (via get_db / get_admin_db overrides).
|
||||||
"""
|
"""
|
||||||
async with admin_engine.connect() as conn:
|
conn = await admin_engine.connect()
|
||||||
trans = await conn.begin()
|
trans = await conn.begin()
|
||||||
session = AsyncSession(bind=conn, expire_on_commit=False)
|
try:
|
||||||
try:
|
yield conn
|
||||||
yield session
|
finally:
|
||||||
finally:
|
await trans.rollback()
|
||||||
await trans.rollback()
|
await conn.close()
|
||||||
await session.close()
|
|
||||||
|
|
||||||
|
@pytest_asyncio.fixture
|
||||||
|
async def admin_session(admin_conn) -> AsyncGenerator[AsyncSession, None]:
|
||||||
|
"""Per-test admin session sharing the admin_conn transaction."""
|
||||||
|
session = AsyncSession(bind=admin_conn, expire_on_commit=False)
|
||||||
|
try:
|
||||||
|
yield session
|
||||||
|
finally:
|
||||||
|
await session.close()
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
@@ -201,19 +211,12 @@ def app_session_factory(app_engine):
|
|||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def test_app(admin_engine, app_engine):
|
async def test_app(admin_conn, app_engine):
|
||||||
"""Create a FastAPI app instance with test database dependency overrides.
|
"""Create a FastAPI app instance with test database dependency overrides.
|
||||||
|
|
||||||
Both get_db and get_admin_db share the same *connection* (and therefore
|
Both get_db and get_admin_db bind to admin_conn (the shared connection
|
||||||
the same transaction) as the test's admin_session fixture. This means
|
from admin_conn fixture). This means data created via admin_session
|
||||||
data created via admin_session (inside a savepoint) is visible to the
|
is visible to API endpoints, and everything rolls back after the test.
|
||||||
API endpoints under test, and everything rolls back after the test.
|
|
||||||
|
|
||||||
We use the admin_engine for both overrides because:
|
|
||||||
- RLS isolation is tested separately in test_rls_isolation.py using
|
|
||||||
the app_session_factory (which gets its own RLS-enforced connections).
|
|
||||||
- API-level tests need to see test-created data (tenants, users),
|
|
||||||
which requires sharing the same connection/transaction.
|
|
||||||
"""
|
"""
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
|
|
||||||
@@ -256,20 +259,17 @@ async def test_app(admin_engine, app_engine):
|
|||||||
|
|
||||||
setup_rate_limiting(app)
|
setup_rate_limiting(app)
|
||||||
|
|
||||||
# Share a single connection for both get_db and get_admin_db so that
|
# API endpoints bind to the same shared connection as admin_session
|
||||||
# test data created in admin_session (savepoint) is visible to the API.
|
# so test-created data is visible across the transaction.
|
||||||
conn = await admin_engine.connect()
|
|
||||||
trans = await conn.begin()
|
|
||||||
|
|
||||||
async def override_get_db() -> AsyncGenerator[AsyncSession, None]:
|
async def override_get_db() -> AsyncGenerator[AsyncSession, None]:
|
||||||
session = AsyncSession(bind=conn, expire_on_commit=False)
|
session = AsyncSession(bind=admin_conn, expire_on_commit=False)
|
||||||
try:
|
try:
|
||||||
yield session
|
yield session
|
||||||
finally:
|
finally:
|
||||||
await session.close()
|
await session.close()
|
||||||
|
|
||||||
async def override_get_admin_db() -> AsyncGenerator[AsyncSession, None]:
|
async def override_get_admin_db() -> AsyncGenerator[AsyncSession, None]:
|
||||||
session = AsyncSession(bind=conn, expire_on_commit=False)
|
session = AsyncSession(bind=admin_conn, expire_on_commit=False)
|
||||||
try:
|
try:
|
||||||
yield session
|
yield session
|
||||||
finally:
|
finally:
|
||||||
@@ -281,8 +281,6 @@ async def test_app(admin_engine, app_engine):
|
|||||||
yield app
|
yield app
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
app.dependency_overrides.clear()
|
||||||
await trans.rollback()
|
|
||||||
await conn.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
|
|||||||
Reference in New Issue
Block a user