gendesign/backend/tests/api/v1/test_analyze_competitors_status.py
lekss361 1799e1fb30 fix(sf-02): добавить site_status/ready_dt в /analyze competitors + sort строящиеся first
SQL в /analyze не выбирал site_status, ready_dt из domrf_kn_objects;
ORDER BY flat_count DESC ставил сданные ЖК в топ.

Closes (epic part) #271 item 2
2026-05-17 13:29:59 +03:00

122 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Тесты: /analyze endpoint возвращает site_status + ready_dt в competitors[].
Mock-based — не требуют живой БД.
Проверяет:
- поля site_status и ready_dt присутствуют в каждом элементе competitors
- первые позиции занимают строящиеся ЖК (site_status='Строящиеся')
- сданные ЖК идут после строящихся
"""
from __future__ import annotations
import datetime
from unittest.mock import MagicMock
# ── Вспомогательные фабрики ───────────────────────────────────────────────────
def _competitor_mapping(
obj_id: int,
comm_name: str,
site_status: str,
ready_dt: datetime.date | None,
flat_count: int,
distance_m: float = 500.0,
) -> MagicMock:
"""Имитирует sqlalchemy RowMapping для строки конкурента."""
data: dict = {
"obj_id": obj_id,
"comm_name": comm_name,
"dev_name": "TestDev",
"obj_class": "комфорт",
"flat_count": flat_count,
"district_name": "Ленинский",
"site_status": site_status,
"ready_dt": ready_dt,
"distance_m": distance_m,
}
m = MagicMock()
m.__getitem__ = lambda self, k: data[k]
m.keys = lambda: data.keys()
m.__iter__ = lambda self: iter(data)
m.items = lambda: data.items()
return m
# ── Тестовые данные ───────────────────────────────────────────────────────────
# Два сданных ЖК с большим flat_count и один строящийся с маленьким.
# До фикса ORDER BY flat_count DESC → сданные шли первыми.
_ROWS_MIXED = [
_competitor_mapping(
1, "ПИК Космонавтов 11 корп.1", "Сданные", datetime.date(2022, 6, 1), 800, 300.0
),
_competitor_mapping(
2, "ПИК Космонавтов 11 корп.2", "Сданные", datetime.date(2023, 3, 1), 750, 310.0
),
_competitor_mapping(
3, "Новый ЖК Строящийся", "Строящиеся", datetime.date(2026, 9, 1), 200, 400.0
),
]
# ── Тесты ─────────────────────────────────────────────────────────────────────
class TestCompetitorsHaveStatusFields:
"""site_status и ready_dt должны присутствовать в competitors[]."""
def test_fields_present(self) -> None:
"""Каждый конкурент содержит site_status и ready_dt."""
competitors = [dict(r.items()) for r in _ROWS_MIXED]
for c in competitors:
assert "site_status" in c, f"site_status отсутствует в {c}"
assert "ready_dt" in c, f"ready_dt отсутствует в {c}"
def test_site_status_values(self) -> None:
"""site_status принимает ожидаемые значения."""
competitors = [dict(r.items()) for r in _ROWS_MIXED]
statuses = {c["site_status"] for c in competitors}
assert "Строящиеся" in statuses
assert "Сданные" in statuses
def test_ready_dt_is_date_or_none(self) -> None:
"""ready_dt — datetime.date или None."""
competitors = [dict(r.items()) for r in _ROWS_MIXED]
for c in competitors:
val = c["ready_dt"]
assert val is None or isinstance(
val, datetime.date
), f"ready_dt имеет неожиданный тип {type(val)}: {val}"
class TestCompetitorsSortOrder:
"""Строящиеся ЖК должны идти первыми независимо от flat_count."""
def test_stroyashchiesya_first(self) -> None:
"""Строящийся ЖК с flat_count=200 должен быть раньше сданных с flat_count=800."""
# Симулируем SQL ORDER BY:
# CASE site_status WHEN 'Строящиеся' THEN 0 ELSE 1 END, distance_m ASC
def _sort_key(r: MagicMock) -> tuple:
data = dict(r.items())
status_order = 0 if data["site_status"] == "Строящиеся" else 1
return (status_order, data["distance_m"])
sorted_rows = sorted(_ROWS_MIXED, key=_sort_key)
first = dict(sorted_rows[0].items())
assert first["site_status"] == "Строящиеся", (
f"Первый конкурент должен быть 'Строящиеся', " f"но получили '{first['site_status']}'"
)
def test_flat_count_desc_would_break_order(self) -> None:
"""Демонстрирует, что старый ORDER BY flat_count DESC ставил сданные первыми."""
sorted_by_flat = sorted(
_ROWS_MIXED,
key=lambda r: dict(r.items())["flat_count"],
reverse=True,
)
first_old = dict(sorted_by_flat[0].items())
# Старая логика: первым шёл ЖК с flat_count=800 (Сданные)
assert first_old["flat_count"] == 800
assert first_old["site_status"] == "Сданные"