gendesign/backend/tests/services/site_finder/test_2464_capacity_savepoints.py
bot-backend cdf493f345
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-backend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m56s
Deploy / build-worker (push) Successful in 4m16s
Deploy Trade-In / build-backend (push) Successful in 1m19s
Deploy / deploy (push) Successful in 1m49s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / deploy (push) Successful in 2m25s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
chore(format): нормализация под ruff 0.15.20 — 161 файл, только формат (#2864) (#3022)
2026-08-21 12:01:52 +00:00

85 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Сбой в connection-capacity не должен травить общую сессию (#2464).
`_query_power_points` и `_query_water_latest` делали `db.execute` без SAVEPOINT, тогда как
четыре соседних `_query_*` в том же файле его имеют. Сессия здесь ОБЩАЯ с вызывающим, и
цепочка выходила такая:
сбой в _query_power_points
→ исключение всплывает (в get_connection_capacity try/except нет)
его ГЛОТАЕТ _get_connection_capacity в full_report_pdf.py ради деградации §3
→ транзакция остаётся aborted
→ падает уже СЛЕДУЮЩИЙ запрос — рыночная цена в _generate_concept_result
→ концепция молча уходит в class_norm-фолбэк
То есть отказ проявляется не там, где случился, и выглядит как «нет рыночных данных».
Проверяется на `PostgresLikeSession` (tests/support/pg_session.py) — двойнике с настоящей
семантикой aborted-транзакции. На `MagicMock` такой тест был бы зелёным по построению:
у него нет aborted-состояния, и любой следующий execute «успешен».
"""
from __future__ import annotations
import os
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
import pytest
from tests.support.pg_session import AbortedTransactionError, PostgresLikeSession
_QUERIES = ("_query_power_points", "_query_water_latest")
def _call(name: str, db: PostgresLikeSession):
from app.services.site_finder import connection_capacity_lookup as mod
fn = getattr(mod, name)
if name == "_query_power_points":
return fn(db, "POLYGON((0 0,0 1,1 1,1 0,0 0))", 5000)
return fn(db)
@pytest.mark.parametrize("name", _QUERIES)
def test_failure_leaves_session_usable(name: str) -> None:
"""После проглоченного вызывающим сбоя следующий запрос обязан пройти.
На origin/main второй execute падает с AbortedTransactionError — ровно та
отложенная поломка, что уводит концепцию в class_norm.
"""
db = PostgresLikeSession(fail_on=(1,))
with pytest.raises(RuntimeError): # сам сбой пробрасывается, это ожидаемо
_call(name, db)
# Вызывающий (full_report_pdf._get_connection_capacity) исключение глотает,
# и дальше по коду идёт СЛЕДУЮЩИЙ запрос на той же сессии.
db.execute("SELECT 1") # на origin/main здесь AbortedTransactionError
@pytest.mark.parametrize("name", _QUERIES)
def test_savepoint_is_actually_entered(name: str) -> None:
"""Запрос действительно идёт внутри SAVEPOINT, а не просто «не падает».
Отдельно от предыдущего: тест выше прошёл бы и если бы кто-то «починил»
функцию, убрав из неё запрос целиком.
"""
db = PostgresLikeSession(fail_on=(), rows=[]) # здоровый путь
_call(name, db)
assert db.savepoints_entered >= 1, (
f"{name}: запрос выполнен вне SAVEPOINT — при сбое сессия останется aborted"
)
assert db.calls >= 1, f"{name}: запрос вообще не выполнялся"
def test_double_reproduces_poisoning_without_savepoint() -> None:
"""Контроль на сам двойник: без SAVEPOINT он ДОЛЖЕН травить сессию.
Без этой проверки зелёные тесты выше нельзя отличить от «двойник ничего не
моделирует» — та же ловушка, что с MagicMock.
"""
db = PostgresLikeSession(fail_on=(1,))
with pytest.raises(RuntimeError):
db.execute("BOOM")
with pytest.raises(AbortedTransactionError):
db.execute("SELECT 1")