Skip to content

Commit 1c7673c

Browse files
committed
Test cleanup
1 parent e42410c commit 1c7673c

3 files changed

Lines changed: 49 additions & 19 deletions

File tree

api/src/tasking/audit/repository.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,15 +145,16 @@ async def list_project_events(
145145
page, page_size = _clamp_page(page, page_size, max_page_size=200)
146146
order_dir = _normalise_dir(order_dir)
147147

148-
# `task_number` is stored inside `details` JSONB rather than as a
149-
# column, so filter via the typed accessor.
148+
# The task number is stored inside `details` JSONB rather than as a
149+
# column, under the camelCase key `taskNumber` that the task
150+
# repository emits, so filter via the typed accessor on that key.
150151
where = ["project_id = :pid"]
151152
params: dict = {"pid": project_id}
152153
if event_type is not None:
153154
where.append("event_type = :et")
154155
params["et"] = event_type.value
155156
if task_number is not None:
156-
where.append("(details->>'task_number')::int = :tn")
157+
where.append("(details->>'taskNumber')::int = :tn")
157158
params["tn"] = task_number
158159
if actor_user_id is not None:
159160
where.append("actor_user_auth_uid = :au")
@@ -215,14 +216,14 @@ async def list_task_events(
215216
page, page_size = _clamp_page(page, page_size, max_page_size=200)
216217
order_dir = _normalise_dir(order_dir)
217218

218-
# Match either `task_id = :tid` or the `task_number` in `details`
219+
# Match either `task_id = :tid` or the `taskNumber` in `details`
219220
# — early audit rows for task creation set task_id but later
220221
# rows that reference a task (e.g. project-level lock-extensions)
221-
# may only persist the task_number. Both forms point at the
222+
# may only persist the taskNumber. Both forms point at the
222223
# same task, so OR them.
223224
where = [
224225
"project_id = :pid",
225-
"(task_id = :tid OR (details->>'task_number')::int = :tn)",
226+
"(task_id = :tid OR (details->>'taskNumber')::int = :tn)",
226227
]
227228
params: dict = {"pid": project_id, "tid": task_id, "tn": task_number}
228229
if event_type is not None:

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ asyncio_default_fixture_loop_scope = "function"
5353
markers = [
5454
"integration: tests that require a live PostGIS database (Docker + testcontainers)",
5555
]
56+
filterwarnings = [
57+
# SQLModel deprecates `session.execute()` in favour of `session.exec()`,
58+
# but many repository queries are raw `text()` SQL where `execute()` is the
59+
# correct call. Silence this one nudge rather than rewrite ~85 call sites;
60+
# every other warning still surfaces. (?s) lets `.*` span the multi-line
61+
# warning body.
62+
"ignore:(?s).*You probably want to use .session.exec:DeprecationWarning",
63+
]
5664

5765
[tool.black]
5866
line-length = 88

tests/integration/conftest.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -389,32 +389,53 @@ def _clear_token() -> None:
389389

390390

391391
@pytest.fixture
392-
def app():
393-
"""Real app for the integration suite.
394-
395-
Overrides the unit-suite ``app`` fixture (tests/conftest.py), which swaps
396-
in ``FakeSession`` objects for the DB dependencies. Here we want the real
397-
engines wired up by the autouse ``_per_test_db_sessions`` fixture, so this
398-
just yields the actual FastAPI app untouched. The shared ``client``
399-
fixture depends on ``app`` by name and picks this up for integration
400-
tests. Per-test overrides (sessions, ``validate_token``) are installed and
401-
torn down by their own fixtures.
392+
def app(request, task_session, osm_session):
393+
"""App fixture for everything under ``tests/integration/``.
394+
395+
This directory hosts two kinds of test that share one conftest:
396+
397+
* **Container-backed** tests (marked ``integration``) run against a real
398+
PostGIS database; their DB sessions are the real engines wired by the
399+
autouse ``_per_test_db_sessions`` fixture, so we hand back the app
400+
untouched here.
401+
* **FakeSession-backed** tests (unmarked — the CLAUDE.md data-fetcher
402+
model) run the real routes/repositories but fake the ``AsyncSession``.
403+
For those we wire the fakes in exactly like the unit-suite ``app``
404+
fixture this shadows.
405+
406+
Keying on the ``integration`` marker keeps the container machinery from
407+
leaking onto the FakeSession tests (which need no Docker).
402408
"""
409+
from api.core.database import get_osm_session, get_task_session
403410
from api.main import app as fastapi_app
404411

405-
return fastapi_app
412+
if request.node.get_closest_marker("integration"):
413+
yield fastapi_app
414+
return
415+
416+
fastapi_app.dependency_overrides[get_task_session] = lambda: task_session
417+
fastapi_app.dependency_overrides[get_osm_session] = lambda: osm_session
418+
yield fastapi_app
419+
fastapi_app.dependency_overrides.clear()
406420

407421

408422
@pytest.fixture(autouse=True)
409-
async def _per_test_db_sessions(_pg_urls):
423+
async def _per_test_db_sessions(request):
424+
# Only container-backed (``integration``-marked) tests need real DB
425+
# sessions; for FakeSession tests this fixture is a no-op so they never
426+
# pull in ``_pg_urls`` (which would boot / require the testcontainer).
427+
if not request.node.get_closest_marker("integration"):
428+
yield
429+
return
430+
410431
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
411432
from sqlalchemy.pool import NullPool
412433
from sqlmodel.ext.asyncio.session import AsyncSession
413434

414435
from api.core.database import get_osm_session, get_task_session
415436
from api.main import app
416437

417-
task_url, osm_url = _pg_urls
438+
task_url, osm_url = request.getfixturevalue("_pg_urls")
418439

419440
# NullPool disables connection reuse: each session checkout opens a
420441
# fresh connection on the current event loop and disposes it on

0 commit comments

Comments
 (0)