"""#3234: порог и окно этажности Tier H берутся из settings, дефолты = прежние литералы. Tier H в _fetch_analogs отдаёт «тот же класс дома» только при ≥ N комплов, иначе уходит в Tier W без фильтра по классу. N и окно total_floors были литералами (5 и ×0.7…×1.3); вынесены, чтобы бэктест сравнил варианты через env. Проверяем значения, которые реально уходят в SQL, и тир, который реально возвращается. """ from __future__ import annotations import os from unittest.mock import MagicMock os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test") import pytest import app.services.estimator as est def _run(n_tier_h: int, total_floors: int) -> tuple[str, dict]: """_fetch_analogs на mock-БД: Tier H отдаёт n_tier_h строк, Tier W — одну.""" tier_h_params: dict = {} def side_effect(stmt, params): # type: ignore[no-untyped-def] result = MagicMock() if "tf_min" in params: tier_h_params.update(params) rows = [{"source": "avito", "relevance_score": float(i)} for i in range(n_tier_h)] else: rows = [{"source": "cian", "relevance_score": 0.0}] result.mappings.return_value.all.return_value = rows return result db = MagicMock() db.execute.side_effect = side_effect _, _, tier = est._fetch_analogs( db, lat=56.83, lon=60.6, rooms=2, area=50.0, radius_m=1000, year_built=1975, total_floors=total_floors, ) return tier, tier_h_params def test_defaults_keep_old_literals() -> None: tier, params = _run(n_tier_h=5, total_floors=9) assert (params["tf_min"], params["tf_max"]) == (6, 12) # floor(6.3), ceil(11.7) assert tier == "H" tier, params = _run(n_tier_h=4, total_floors=16) assert (params["tf_min"], params["tf_max"]) == (11, 21) # floor(11.2), ceil(20.8) assert tier == "W" def test_min_comps_from_settings(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(est.settings, "estimate_tier_h_min_comps", 4) assert _run(n_tier_h=4, total_floors=9)[0] == "H" assert _run(n_tier_h=3, total_floors=9)[0] == "W" def test_floors_tol_from_settings(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr(est.settings, "estimate_tier_h_floors_tol", 0.5) _, params = _run(n_tier_h=5, total_floors=9) assert (params["tf_min"], params["tf_max"]) == (4, 14) # floor(4.5), ceil(13.5) def test_env_reaches_settings(monkeypatch: pytest.MonkeyPatch) -> None: # Бэктест вариантов идёт через `docker exec -e ESTIMATE_TIER_H_…` — env обязан доехать. from app.core.config import Settings monkeypatch.setenv("ESTIMATE_TIER_H_MIN_COMPS", "4") monkeypatch.setenv("ESTIMATE_TIER_H_FLOORS_TOL", "0.5") s = Settings() assert (s.estimate_tier_h_min_comps, s.estimate_tier_h_floors_tol) == (4, 0.5)