fix(integration): asyncio auto-mode, per-test truncation, artist images IN-subquery
This commit is contained in:
@@ -157,12 +157,16 @@ class ArtistService:
|
||||
if artist is None:
|
||||
return None
|
||||
|
||||
stmt = (
|
||||
select(ImageRecord)
|
||||
.join(ImageProvenance, ImageProvenance.image_record_id == ImageRecord.id)
|
||||
# Dedupe via IN-subquery rather than JOIN + DISTINCT: an image can
|
||||
# have several provenance rows for one artist, and SELECT DISTINCT
|
||||
# over ImageRecord fails in Postgres because its json columns have
|
||||
# no equality operator.
|
||||
stmt = select(ImageRecord).where(
|
||||
ImageRecord.id.in_(
|
||||
select(ImageProvenance.image_record_id)
|
||||
.join(Source, Source.id == ImageProvenance.source_id)
|
||||
.where(Source.artist_id == artist.id)
|
||||
.distinct()
|
||||
)
|
||||
)
|
||||
if cursor:
|
||||
cur_ts, cur_id = decode_cursor(cursor)
|
||||
|
||||
@@ -9,6 +9,11 @@ where = ["."]
|
||||
include = ["backend*"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
# auto mode: async test fns and async fixtures don't need explicit
|
||||
# @pytest.mark.asyncio / @pytest_asyncio.fixture decoration. Required
|
||||
# because the test_api_* fixtures use plain @pytest.fixture on async
|
||||
# app/client; strict mode errors on those under pytest-asyncio 1.x.
|
||||
asyncio_mode = "auto"
|
||||
# The fast backend job runs `-m "not integration"`; the dedicated CI
|
||||
# `integration` job (pgvector Postgres + Redis service containers) runs
|
||||
# `-m integration`. Both also runnable locally via docker-compose.
|
||||
|
||||
@@ -18,6 +18,8 @@ from sqlalchemy.ext.asyncio import (
|
||||
)
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from backend.app.models import Base
|
||||
|
||||
|
||||
def _async_database_url() -> str:
|
||||
user = os.environ.get("DB_USER", "fabledcurator")
|
||||
@@ -73,3 +75,27 @@ def db_sync(sync_engine):
|
||||
yield session
|
||||
finally:
|
||||
session.rollback()
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _truncate_after_integration(request):
|
||||
"""Integration tests exercise app/celery code that commits on its own
|
||||
connection, which the rollback fixtures above can't undo — so data
|
||||
would leak between tests. Truncate every model table after each
|
||||
integration-marked test. Scoped to the integration marker so the
|
||||
DB-less fast unit job never connects here.
|
||||
"""
|
||||
yield
|
||||
if request.node.get_closest_marker("integration") is None:
|
||||
return
|
||||
tables = ", ".join(t.name for t in Base.metadata.sorted_tables)
|
||||
if not tables:
|
||||
return
|
||||
eng = create_engine(_sync_database_url(), future=True)
|
||||
try:
|
||||
with eng.begin() as conn:
|
||||
conn.exec_driver_sql(
|
||||
f"TRUNCATE {tables} RESTART IDENTITY CASCADE"
|
||||
)
|
||||
finally:
|
||||
eng.dispose()
|
||||
|
||||
Reference in New Issue
Block a user