Skip to content

Commit a4fccba

Browse files
committed
test(api): make default test run DB-free via integration marker
- auto-mark tests using db fixtures (client/db_session/rate_limit_client) as integration; mark test_rls_migration (direct engine use) explicitly - gate setup_database/cleanup autouse fixtures on the integration marker so a default run never opens a DB connection - deselect integration + e2e by default; run with -m integration + a real Postgres
1 parent 1d37519 commit a4fccba

3 files changed

Lines changed: 38 additions & 7 deletions

File tree

apps/api/pyproject.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ testpaths = ["tests"]
5959
asyncio_mode = "auto"
6060
asyncio_default_fixture_loop_scope = "session"
6161
asyncio_default_test_loop_scope = "session"
62-
# Skip e2e tests by default — they require a live server (E2E_API_URL).
63-
# Run them explicitly with: uv run pytest -m e2e
64-
addopts = "-m 'not e2e'"
62+
# Default run is DB-free: e2e (live server) and integration (real Postgres) are
63+
# deselected. Run them explicitly: uv run pytest -m e2e / uv run pytest -m integration
64+
# (integration needs TEST_DATABASE_URL pointing at a real Postgres).
65+
addopts = "-m 'not e2e and not integration'"
6566
markers = [
6667
"e2e: end-to-end smoke tests against a running API instance (requires E2E_API_URL)",
68+
"integration: tests that hit a real database (auto-applied to tests using db fixtures)",
6769
]

apps/api/tests/conftest.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,32 @@ async def create_test_experience(
195195
# ---------------------------------------------------------------------------
196196

197197

198+
_DB_FIXTURES = {"client", "rate_limit_client", "db_session"}
199+
200+
201+
@pytest.hookimpl(tryfirst=True)
202+
def pytest_collection_modifyitems(config, items):
203+
"""Auto-mark any test that requests a DB-backed fixture as ``integration``.
204+
205+
Runs tryfirst so the marker is in place before pytest evaluates the ``-m``
206+
deselection expression. Keeps the default run DB-free without per-file edits.
207+
"""
208+
for item in items:
209+
if _DB_FIXTURES & set(getattr(item, "fixturenames", ())):
210+
item.add_marker("integration")
211+
212+
198213
@pytest.fixture(scope="session", autouse=True)
199-
async def setup_database():
200-
"""Create all tables before tests, drop after."""
214+
async def setup_database(request):
215+
"""Create all tables before tests, drop after — integration runs only.
216+
217+
No-op when no integration test is selected, so a DB-free default run never
218+
opens a connection.
219+
"""
220+
if not any(item.get_closest_marker("integration") for item in request.session.items):
221+
yield
222+
return
223+
201224
# Import all models so SQLAlchemy metadata is complete.
202225
import src.auth.models # noqa: F401
203226
import src.contact_relationships.models # noqa: F401
@@ -218,8 +241,10 @@ async def setup_database():
218241

219242

220243
@pytest.fixture(autouse=True)
221-
async def cleanup():
222-
"""Truncate all tables before each test for isolation."""
244+
async def cleanup(request):
245+
"""Truncate all tables before each test for isolation — integration only."""
246+
if request.node.get_closest_marker("integration") is None:
247+
return
223248
async with test_engine.begin() as conn:
224249
for table in reversed(Base.metadata.sorted_tables):
225250
await conn.execute(text(f'TRUNCATE TABLE "{table.name}" CASCADE'))

apps/api/tests/test_rls_migration.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515

1616
from tests.conftest import TestSessionFactory
1717

18+
# Talks to a real Postgres directly via the test engine (not through a fixture),
19+
# so mark the whole module integration to keep the default run DB-free.
20+
pytestmark = pytest.mark.integration
21+
1822

1923
@pytest.mark.asyncio
2024
async def test_rls_policy_sql_is_valid():

0 commit comments

Comments
 (0)