test(ptica): покрыть переиспользование сессии после close() в отдаче фотографий (#2464-C)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 16m5s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m56s
CI / backend-tests (pull_request) Successful in 16m5s
#2928 поставил db.close() выше по коду, а ниже сессия ещё раз работает с БД: UPDATE thumb_path + commit в ветке генерации миниатюры из локального оригинала. Поведение штатное — close() возвращает соединение в пул, следующий execute берёт новое, — но проверял это только комментарий. На проде ветка сегодня не исполняется ни разу: замер 19.08 по domrf_kn_photos — 0 строк с непустым local_path и пустым thumb_path (все 1889 закешированных уже с миниатюрами). Значит первый же ленивый фетч новой фотографии пойдёт по коду, который не проверял никто, и обычная эксплуатация дефект бы не показала. Проверка читает результат ОТДЕЛЬНЫМ соединением, а не той же сессией: сессия видит собственную незакоммиченную транзакцию, и пропажа commit() прошла бы мимо. Фальсификация: с убранным db.commit() тест краснеет (assert None == ...webp). Форматирование assert-сообщения — от pre-commit ruff 0.7.4, не моё (#2864).
This commit is contained in:
parent
c3ea0364e7
commit
63170ff2f5
1 changed files with 52 additions and 0 deletions
|
|
@ -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() не закоммичен"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue