From 98a582e24259f41f4678fc6ae8c34c3638460d9d Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 12 Sep 2026 14:09:23 +0000 Subject: [PATCH] =?UTF-8?q?perf(tests):=20=D1=83=D0=B1=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D1=8C=20=D1=84=D0=B8=D0=BA=D1=81=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=BE=D0=B6=D0=B8=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B8=D0=B7=20analyze-=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2=20Site=20Finder=20(#3502)=20Co-authored-by:=20bot-?= =?UTF-8?q?backend=20=20Co-committed-by:=20bot-?= =?UTF-8?q?backend=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/api/v1/conftest.py | 43 +++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/backend/tests/api/v1/conftest.py b/backend/tests/api/v1/conftest.py index 463d56ea..33df9426 100644 --- a/backend/tests/api/v1/conftest.py +++ b/backend/tests/api/v1/conftest.py @@ -17,12 +17,22 @@ Analyze-тесты с ПОЗИЦИОННЫМ DB-моком (``_make_db_for_analy (``test_analyze_zoning_regulation.py``), переопределяют этот же target своим per-test ``patch`` — он применяется ПОВЕРХ авто-фикстуры (вложенный mock-scope), так что их ожидаемые значения резолвера сохраняются. + +Perf-fix (2026-09-12): в конце ``analyze_parcel`` — безусловный best-effort +``forecast_site_finder_report.delay(...)`` (§22-форсайт enqueue, см. app/api/v1/parcels.py). +В песочнице тестов Celery-брокер (Redis) недоступен → ``.delay()`` синхронно ждёт +kombu-реконнект с растущим backoff (~69с) ДО того как try/except его проглотит — +эта пауза оказалась внутри КАЖДОГО теста, который дергает ``POST /analyze`` и не +мокал форсайт-таску. Авто-фикстура ниже глушит ``.delay`` в no-op-мок для ВСЕХ +тестов каталога (как и с резолвером выше) — тесты самого enqueue +(``test_parcels_forecast.py``, ``test_run_history_and_response_contract.py``) +переопределяют тот же target своим per-test ``patch`` поверх авто-фикстуры. """ from __future__ import annotations from collections.abc import Iterator -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pytest @@ -37,3 +47,34 @@ def _stub_zone_regulation_resolver() -> Iterator[None]: """ with patch("app.api.v1.parcels.get_or_fetch_zone_regulation", return_value=None): yield + + +@pytest.fixture(autouse=True) +def _stub_forecast_enqueue() -> Iterator[None]: + """No-op форсайт-enqueue по умолчанию (без реального Celery/Redis round-trip). + + ``.delay(...)`` в проде — fire-and-forget (best-effort, обёрнут в try/except в + ``analyze_parcel``), тестам сам форсайт не нужен, а живой брокер в CI/локальной + песочнице недоступен и держит запрос ~69с на реконнект-backoff. + """ + with patch("app.workers.tasks.forecast.forecast_site_finder_report.delay", MagicMock()): + yield + + +@pytest.fixture(autouse=True) +def _fast_inline_fetch_wait(monkeypatch: pytest.MonkeyPatch) -> None: + """Схлопнуть inline-ожидание NSPD-фетча (#93 graceful fallback) до миллисекунд. + + В ``analyze_parcel`` ветка «участка нет в БД» ждёт появления геометрии циклом + ``sleep(_INLINE_FETCH_POLL_INTERVAL_S)`` до ``_INLINE_FETCH_WAIT_S`` (15с прод- + значение). В тестах фетч замокан и геометрия не появится никогда → каждый такой + тест честно спал 16с (``test_market_price_invalid_cad_returns_404``, + ``test_recent_permits_invalid_cad_no_regression``). + + Оставляем цикл РАБОЧИМ (несколько итераций по 10мс), а не выключаем его нулём: + тесты, проверяющие сам fast-path «строка появилась на N-м опросе», продолжают + видеть опросы. Тесты с собственным ``patch`` того же имени (напр. + ``test_run_history_and_response_contract.py``) переопределяют это поверх. + """ + monkeypatch.setattr("app.api.v1.parcels._INLINE_FETCH_WAIT_S", 0.05) + monkeypatch.setattr("app.api.v1.parcels._INLINE_FETCH_POLL_INTERVAL_S", 0.01)