Ужесточает валидацию ДКП-сделок в _is_plausible_deal: - floor < 1 или floor=0 → drop (floor=-5/0/999 из битого парсера) - area_m2 задана и <= 0 → drop (нулевая/отрицательная площадь из битого парсера) - price_rub задана и <= 0 → drop (нерыночная/техническая сделка) - floor=None → допустимо (graceful, нечем судить) Все новые параметры area_m2/price_rub keyword-only с дефолтом None → backward-compatible для существующих вызовов. Caller _fetch_deals обновлён для передачи area_m2/price_rub из данных сделки. Тесты: 16 новых тест-кейсов в test_deals_sanitize.py (floor=-5, floor=999, floor=None ok, area_m2=0 drop, price_rub=-1 drop и пр.).
122 lines
4 KiB
Python
122 lines
4 KiB
Python
"""#699 — ДКП-выброс sanitize: _is_plausible_deal guard-bands (floor + ppm²)."""
|
||
|
||
import os
|
||
|
||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost/test_db")
|
||
|
||
from app.services.estimator import (
|
||
DEAL_MAX_FLOOR,
|
||
DEAL_MAX_PPM2,
|
||
DEAL_MIN_PPM2,
|
||
_is_plausible_deal,
|
||
)
|
||
|
||
|
||
def test_drops_implausible_floor_100() -> None:
|
||
# репорт-кейс: floor:100 — заведомо битый этаж.
|
||
assert _is_plausible_deal(150_000, 100, None) is False
|
||
|
||
|
||
def test_drops_submarket_ppm2_3970() -> None:
|
||
# репорт-кейс: 4.98М за 125 м² = ~39.7К/м² — нерыночный.
|
||
assert _is_plausible_deal(39_700, 5, 9) is False
|
||
|
||
|
||
def test_drops_floor_above_total_floors() -> None:
|
||
# 30-й этаж в 5-этажке физически невозможен.
|
||
assert _is_plausible_deal(150_000, 30, 5) is False
|
||
|
||
|
||
def test_drops_ppm2_above_band() -> None:
|
||
assert _is_plausible_deal(DEAL_MAX_PPM2 + 1, 5, 9) is False
|
||
|
||
|
||
def test_keeps_normal_deal() -> None:
|
||
assert _is_plausible_deal(150_000, 5, 9) is True
|
||
|
||
|
||
def test_keeps_high_floor_in_tall_building() -> None:
|
||
# 30-й этаж в 30-этажке — реально для ЕКБ, не выброс.
|
||
assert _is_plausible_deal(160_000, 30, 30) is True
|
||
|
||
|
||
def test_keeps_at_band_edges() -> None:
|
||
assert _is_plausible_deal(DEAL_MIN_PPM2, 1, None) is True
|
||
assert _is_plausible_deal(DEAL_MAX_PPM2, DEAL_MAX_FLOOR, None) is True
|
||
|
||
|
||
def test_null_fields_not_judged() -> None:
|
||
# ppm²/floor=None — нечем судить, оставляем (None-safe).
|
||
assert _is_plausible_deal(None, None, None) is True
|
||
assert _is_plausible_deal(None, 100, None) is False # floor всё равно судим
|
||
assert _is_plausible_deal(39_700, None, None) is False # ppm² всё равно судим
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Mera-audit fix-2: floor edge cases + area_m2/price_rub guards
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def test_drops_floor_negative() -> None:
|
||
# floor=-5 из битого парсера → drop.
|
||
assert _is_plausible_deal(150_000, -5, 9) is False
|
||
|
||
|
||
def test_drops_floor_zero() -> None:
|
||
# floor=0 — не существует (1-indexed) → drop.
|
||
assert _is_plausible_deal(150_000, 0, 9) is False
|
||
|
||
|
||
def test_drops_floor_999() -> None:
|
||
# floor=999 из битого парсера → drop (> DEAL_MAX_FLOOR=60).
|
||
assert _is_plausible_deal(150_000, 999, None) is False
|
||
|
||
|
||
def test_keeps_floor_none_graceful() -> None:
|
||
# floor=None → допустимо, не судим.
|
||
assert _is_plausible_deal(150_000, None, None) is True
|
||
|
||
|
||
def test_keeps_floor_1_valid() -> None:
|
||
assert _is_plausible_deal(150_000, 1, 9) is True
|
||
|
||
|
||
def test_keeps_floor_at_max() -> None:
|
||
assert _is_plausible_deal(150_000, DEAL_MAX_FLOOR, None) is True
|
||
|
||
|
||
def test_drops_area_m2_zero() -> None:
|
||
# area_m2=0 из битого парсера → drop.
|
||
assert _is_plausible_deal(150_000, 5, 9, area_m2=0.0) is False
|
||
|
||
|
||
def test_drops_area_m2_negative() -> None:
|
||
# area_m2=-10 → drop.
|
||
assert _is_plausible_deal(150_000, 5, 9, area_m2=-10.0) is False
|
||
|
||
|
||
def test_keeps_area_m2_none() -> None:
|
||
# area_m2=None → не судим (graceful).
|
||
assert _is_plausible_deal(150_000, 5, 9, area_m2=None) is True
|
||
|
||
|
||
def test_keeps_area_m2_valid() -> None:
|
||
assert _is_plausible_deal(150_000, 5, 9, area_m2=55.0) is True
|
||
|
||
|
||
def test_drops_price_rub_zero() -> None:
|
||
# price_rub=0 → нерыночная/техническая сделка → drop.
|
||
assert _is_plausible_deal(150_000, 5, 9, price_rub=0.0) is False
|
||
|
||
|
||
def test_drops_price_rub_negative() -> None:
|
||
assert _is_plausible_deal(150_000, 5, 9, price_rub=-1.0) is False
|
||
|
||
|
||
def test_keeps_price_rub_none() -> None:
|
||
# price_rub=None → не судим (graceful).
|
||
assert _is_plausible_deal(150_000, 5, 9, price_rub=None) is True
|
||
|
||
|
||
def test_keeps_price_rub_valid() -> None:
|
||
assert _is_plausible_deal(150_000, 5, 9, price_rub=8_000_000.0) is True
|