"""Общие фикстуры для api/v1 analyze-тестов. PR-A (#1891) сделал geoportal зон-резолвер (``get_or_fetch_zone_regulation``) БЕЗУСЛОВНЫМ в ``analyze_parcel`` — раньше он был gated за наличием dump-зоны (``_nspd_zoning is not None``), теперь вызывается для любого ЕКБ-участка с centroid. Analyze-тесты с ПОЗИЦИОННЫМ DB-моком (``_make_db_for_analyze`` + ``call_idx``/ ``responses[...]``) НЕ мокали резолвер (до PR-A он не вызывался). Новый безусловный вызов добавляет ``db.execute`` (get_cached) → сдвигает последовательность ответов мока → последующие запросы (напр. ``parcel_meta``) получают чужой/пустой ответ и становятся ``None``. Локально проходило (резолвер мог не разрезолвить точку), в CI падало (``test_parcel_meta_found_in_cad_parcels``). Авто-фикстура по умолчанию глушит резолвер в no-op (``return None``) для ВСЕХ тестов в этом каталоге → блок 9e-bis ничего не синтезирует и не трогает БД (поведение идентично до-PR-A). Тесты, которые проверяют САМ резолвер (``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 MagicMock, patch import pytest @pytest.fixture(autouse=True) def _stub_zone_regulation_resolver() -> Iterator[None]: """No-op geoportal зон-резолвер по умолчанию (без сети, без desync DB-мока). ``return None`` → ``analyze`` не синтезирует ``nspd_zoning`` и не делает лишних ``db.execute``. Тесты резолвера переопределяют ``get_or_fetch_zone_regulation`` своим ``patch`` per-test. """ 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)