test(ptica): покрыть переиспользование сессии после close() в отдаче фотографий (#2464-C) (#2930)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m4s
Deploy / build-worker (push) Successful in 3m19s
Deploy / deploy (push) Successful in 1m40s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 8s

This commit is contained in:
bot-backend 2026-08-19 11:52:13 +00:00
parent bc489e1a6f
commit 3c9505c8b0

View file

@ -114,3 +114,55 @@ def test_missing_row_still_404(_session_with_photo_row) -> None:
with pytest.raises(HTTPException) as exc:
photos.get_photo(db=_session_with_photo_row, obj_id=999, file_id="nope", size="thumb")
assert exc.value.status_code == 404
def test_session_usable_after_close_for_thumb_update(
_session_with_photo_row, tmp_path: Path, monkeypatch
) -> None:
"""Сессия переиспользуется ПОСЛЕ close(): UPDATE thumb_path + commit доезжают.
Это самый рискованный участок #2928: `db.close()` стоит выше по коду, а ниже
сессия ещё раз работает с БД. Поведение штатное (`close()` возвращает
соединение в пул, следующий execute берёт новое и открывает свою транзакцию),
но на проде эта ветка сегодня не исполняется НИ РАЗУ: замер 19.08.2026
0 строк `domrf_kn_photos` с непустым local_path и пустым thumb_path (все 1 889
закешированных уже с миниатюрами). То есть первый же ленивый фетч новой
фотографии пойдёт по коду, который не проверял никто.
Дополнительно фиксируем, что генерация миниатюры (медленная работа) идёт уже
без открытой транзакции то же свойство, что и для внешнего фетча.
"""
from app.api.v1 import photos
original = tmp_path / "orig.jpg"
original.write_bytes(b"jpeg")
_session_with_photo_row.execute(
text("UPDATE domrf_kn_photos SET local_path = :p"), {"p": str(original)}
)
_session_with_photo_row.commit()
generated = tmp_path / "orig.webp"
seen: dict[str, bool] = {}
def _fake_make_thumbnail(src: Path):
seen["in_transaction"] = _session_with_photo_row.in_transaction()
generated.write_bytes(b"webp")
return generated
monkeypatch.setattr(photos, "make_thumbnail", _fake_make_thumbnail)
resp = photos.get_photo(db=_session_with_photo_row, obj_id=1, file_id="f1", size="thumb")
assert (
seen.get("in_transaction") is False
), "миниатюра генерируется при открытой транзакции — соединение пула занято"
assert getattr(resp, "path", None) == str(generated)
# Главное: запись ПОСЛЕ close() действительно доехала до БД — читаем ОТДЕЛЬНЫМ
# соединением. Через ту же сессию проверять нельзя: она видит собственную
# незакоммиченную транзакцию, и пропажа commit() прошла бы незамеченной.
with _session_with_photo_row.get_bind().connect() as fresh:
stored = fresh.execute(
text("SELECT thumb_path FROM domrf_kn_photos WHERE obj_id = 1")
).scalar_one()
assert stored == str(generated), "UPDATE после close() не закоммичен"