tests: function-scoped engine + frisk session per request
Hver test får sin egen engine (function-scoped) med friske tabeller (drop_all + create_all). Appen får sin egen session per request via dependency override — ingen deling af sessions. admin_user/normal_user opretter data via egen session og lukker den. client bruger async_sessionmaker i override_get_db. Ingen _clean_db fixture, ingen outer transaction, ingen SAVEPOINT — hver test starter blankt og engine.dispose() rydder op.
This commit is contained in:
+39
-45
@@ -1,4 +1,9 @@
|
|||||||
"""Test fixtures — async database and HTTP client."""
|
"""Test fixtures — async database and HTTP client.
|
||||||
|
|
||||||
|
Hver test får sin egen engine (function-scoped) med friske tabeller.
|
||||||
|
Appen får sin egen session per request via dependency override.
|
||||||
|
Ingen delte sessions — ingen "another operation is in progress".
|
||||||
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pytest_asyncio
|
import pytest_asyncio
|
||||||
@@ -21,72 +26,61 @@ DATABASE_URL = os.getenv(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture(scope="session")
|
@pytest_asyncio.fixture(autouse=True)
|
||||||
async def engine():
|
async def engine():
|
||||||
"""Create a fresh database for testing."""
|
"""Frisk engine per test — skaber tabeller, smider væk bagefter."""
|
||||||
e = create_async_engine(DATABASE_URL, echo=False)
|
e = create_async_engine(DATABASE_URL, echo=False)
|
||||||
async with e.begin() as conn:
|
async with e.begin() as conn:
|
||||||
await conn.run_sync(Base.metadata.drop_all)
|
await conn.run_sync(Base.metadata.drop_all)
|
||||||
await conn.run_sync(Base.metadata.create_all)
|
await conn.run_sync(Base.metadata.create_all)
|
||||||
yield e
|
yield e
|
||||||
async with e.begin() as conn:
|
|
||||||
await conn.run_sync(Base.metadata.drop_all)
|
|
||||||
await e.dispose()
|
await e.dispose()
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def session(engine):
|
async def client(engine):
|
||||||
"""Per-test database session.
|
"""HTTP client — appen får en frisk session på testens engine per request."""
|
||||||
|
|
||||||
Data commited her er synlig for appens egen session via HTTP client.
|
|
||||||
Renses automatisk af _clean_db efter hver test.
|
|
||||||
"""
|
|
||||||
maker = async_sessionmaker(bind=engine, expire_on_commit=False)
|
maker = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||||||
async with maker() as s:
|
|
||||||
yield s
|
|
||||||
|
|
||||||
|
async def override_get_db():
|
||||||
|
async with maker() as s:
|
||||||
|
yield s
|
||||||
|
|
||||||
@pytest_asyncio.fixture(autouse=True)
|
app.dependency_overrides[get_db] = override_get_db
|
||||||
async def _clean_db(engine):
|
|
||||||
"""Slet alt data fra alle tabeller efter hver test."""
|
|
||||||
yield
|
|
||||||
async with engine.begin() as conn:
|
|
||||||
for table in reversed(Base.metadata.sorted_tables):
|
|
||||||
await conn.execute(table.delete())
|
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
|
||||||
async def client():
|
|
||||||
"""HTTP client — appen bruger sin egen DB session via get_db()."""
|
|
||||||
transport = ASGITransport(app=app)
|
transport = ASGITransport(app=app)
|
||||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||||
yield ac
|
yield ac
|
||||||
|
app.dependency_overrides.clear()
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def admin_user(session):
|
async def admin_user(engine):
|
||||||
"""Insert admin user into test DB (ryddes af _clean_db)."""
|
"""Insert admin user — frisk session, committer, lukker."""
|
||||||
u = User(
|
maker = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||||||
username="admin",
|
async with maker() as s:
|
||||||
password_hash=hash_password("admin123"),
|
u = User(
|
||||||
role="admin",
|
username="admin",
|
||||||
)
|
password_hash=hash_password("admin123"),
|
||||||
session.add(u)
|
role="admin",
|
||||||
await session.commit()
|
)
|
||||||
return u
|
s.add(u)
|
||||||
|
await s.commit()
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
async def normal_user(session):
|
async def normal_user(engine):
|
||||||
"""Insert normal user into test DB (ryddes af _clean_db)."""
|
"""Insert normal user — frisk session, committer, lukker."""
|
||||||
u = User(
|
maker = async_sessionmaker(bind=engine, expire_on_commit=False)
|
||||||
username="testuser",
|
async with maker() as s:
|
||||||
password_hash=hash_password("test123"),
|
u = User(
|
||||||
role="user",
|
username="testuser",
|
||||||
)
|
password_hash=hash_password("test123"),
|
||||||
session.add(u)
|
role="user",
|
||||||
await session.commit()
|
)
|
||||||
return u
|
s.add(u)
|
||||||
|
await s.commit()
|
||||||
|
return u
|
||||||
|
|
||||||
|
|
||||||
@pytest_asyncio.fixture
|
@pytest_asyncio.fixture
|
||||||
|
|||||||
Reference in New Issue
Block a user