fix(tests): красный main — тесты #3466 читали настройки, которые #3556 перевёл в константы
All checks were successful
CI Trade-In / backend-tests (pull_request) Successful in 8m23s
CI Trade-In / changes (pull_request) Successful in 14s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 20s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped

Семантический конфликт двух PR, смерженных 17.09 подряд. Текстового конфликта не
было, CI каждого PR был зелёным на своей голове, а объединённое дерево падало:

  test_3466_corridor_tier_a.py (2), test_estimator_radius_floor.py (2)
  AttributeError: 'Settings' object has no attribute 'estimate_corridor_clamp_slack'
  TypeError: _run_estimate() got an unexpected keyword argument 'radius_floor_factor'

#3554 (Tier A и advisory_only) писал тесты против settings.estimate_corridor_clamp_*,
а #3556 (#2380) убрал эти поля из Settings в константы CORRIDOR_CLAMP_SLACK
(estimator) и CORRIDOR_CLAMP_MIN_N (app.core.config) и снял параметр
radius_floor_factor у хелпера. Продуктовый код не затронут: в app/ старые имена
остались только в комментарии, на проде AttributeError не было. Но красный test
блокировал деплой МЕРЫ: на проде до сих пор образ до второй пачки мержей.

Тесты переведены на константы, значения прежние (0.40, 10, 0.8).
pytest tests/ — 6374 passed, 44 skipped, rc=0; ruff check/format — rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
bot-backend 2026-09-17 14:45:54 +05:00
parent a130303cc9
commit 635381f7b8
2 changed files with 11 additions and 12 deletions

View file

@ -26,7 +26,6 @@ from unittest.mock import MagicMock, patch
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test") os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
from app.core.config import settings
from app.services import estimator from app.services import estimator
from app.services.geocoder import GeocodeResult from app.services.geocoder import GeocodeResult
@ -45,7 +44,7 @@ _ANCHOR_PPM2 = (190_000, 195_000, 200_000, 205_000, 210_000)
def _cap() -> float: def _cap() -> float:
return _CORRIDOR["high_ppm2"] * (1.0 + settings.estimate_corridor_clamp_slack) return _CORRIDOR["high_ppm2"] * (1.0 + estimator.CORRIDOR_CLAMP_SLACK)
def _call( def _call(

View file

@ -8,8 +8,8 @@
- radius median выше dkp_low × factor no-op (медиана не изменена) - radius median выше dkp_low × factor no-op (медиана не изменена)
- dkp_raw is None no-op (нет базы для floor) - dkp_raw is None no-op (нет базы для floor)
- anchor-путь (anchor_tier != None) не затронут floor'ом - anchor-путь (anchor_tier != None) не затронут floor'ом
- коридор ниже estimate_corridor_clamp_min_n floor не применяется (#3466) - коридор ниже CORRIDOR_CLAMP_MIN_N floor не применяется (#3466)
- коридор ровно estimate_corridor_clamp_min_n floor применяется (#3466) - коридор ровно CORRIDOR_CLAMP_MIN_N floor применяется (#3466)
""" """
from __future__ import annotations from __future__ import annotations
@ -194,23 +194,23 @@ def test_no_dkp_raw_no_floor() -> None:
def test_floor_not_applied_below_clamp_min_n() -> None: def test_floor_not_applied_below_clamp_min_n() -> None:
"""n = estimate_corridor_clamp_min_n 1: тот же floor, что в тесте 1, но выключен. """n = CORRIDOR_CLAMP_MIN_N 1: тот же floor, что в тесте 1, но выключен.
`DkpCorridor.advisory_only` (#3452) обосновывает себя общим порогом у клампа `DkpCorridor.advisory_only` (#3452) обосновывает себя общим порогом у клампа
headline И у radius-floor. Половина про кламп стережётся test_3452_*, эта headline И у radius-floor. Половина про кламп стережётся test_3452_*, эта
здесь: без гейта по count медиана 80k поднялась бы до 120k. здесь: без гейта по count медиана 80k поднялась бы до 120k.
""" """
from app.core.config import settings from app.core.config import CORRIDOR_CLAMP_MIN_N
analogs = _six(80_000.0) analogs = _six(80_000.0)
dkp_raw = { dkp_raw = {
"count": settings.estimate_corridor_clamp_min_n - 1, "count": CORRIDOR_CLAMP_MIN_N - 1,
"low_ppm2": 150_000, "low_ppm2": 150_000,
"median_ppm2": 180_000, "median_ppm2": 180_000,
"high_ppm2": 220_000, "high_ppm2": 220_000,
"period_months": 12, "period_months": 12,
} }
est = _run_estimate(analogs, dkp_raw, radius_floor_factor=0.8) est = _run_estimate(analogs, dkp_raw)
assert est.median_price_per_m2 < 100_000, ( assert est.median_price_per_m2 < 100_000, (
f"median_ppm2={est.median_price_per_m2}: коридор из " f"median_ppm2={est.median_price_per_m2}: коридор из "
@ -222,18 +222,18 @@ def test_floor_not_applied_below_clamp_min_n() -> None:
def test_floor_applied_at_exactly_clamp_min_n() -> None: def test_floor_applied_at_exactly_clamp_min_n() -> None:
"""n = estimate_corridor_clamp_min_n: advisory_only=False → floor обязан поднять. """n = CORRIDOR_CLAMP_MIN_N: advisory_only=False → floor обязан поднять.
Тест 4 стережёт «ниже порога нет», тест 1 «15 сделок да». Граница между Тест 4 стережёт «ниже порога нет», тест 1 «15 сделок да». Граница между
ними (`>=` против `>`) не стереглась: при n == min_n витрина не пишет подпись ними (`>=` против `>`) не стереглась: при n == min_n витрина не пишет подпись
«справочно», значит коридор обязан войти в цену. «справочно», значит коридор обязан войти в цену.
""" """
from app.core.config import settings from app.core.config import CORRIDOR_CLAMP_MIN_N
from app.schemas.trade_in import DkpCorridor from app.schemas.trade_in import DkpCorridor
analogs = _six(80_000.0) analogs = _six(80_000.0)
dkp_raw = { dkp_raw = {
"count": settings.estimate_corridor_clamp_min_n, "count": CORRIDOR_CLAMP_MIN_N,
"low_ppm2": 150_000, "low_ppm2": 150_000,
"median_ppm2": 180_000, "median_ppm2": 180_000,
"high_ppm2": 220_000, "high_ppm2": 220_000,
@ -242,7 +242,7 @@ def test_floor_applied_at_exactly_clamp_min_n() -> None:
# Та же граница с другой стороны: на ней коридор уже не справочный. # Та же граница с другой стороны: на ней коридор уже не справочный.
assert DkpCorridor(**dkp_raw).advisory_only is False assert DkpCorridor(**dkp_raw).advisory_only is False
est = _run_estimate(analogs, dkp_raw, radius_floor_factor=0.8) est = _run_estimate(analogs, dkp_raw)
assert est.median_price_per_m2 == 150_000 * 0.8, ( assert est.median_price_per_m2 == 150_000 * 0.8, (
f"median_ppm2={est.median_price_per_m2}: коридор из {dkp_raw['count']} сделок " f"median_ppm2={est.median_price_per_m2}: коридор из {dkp_raw['count']} сделок "