gendesign/backend/tests/services/test_utility_characteristics.py
Light1YT 9183829d08
All checks were successful
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Successful in 1m33s
CI / openapi-codegen-check (pull_request) Successful in 2m30s
CI / backend-tests (pull_request) Successful in 14m52s
feat(site-finder): max-info popups — конкурент/§3-таблица/польз.точка/соседи (#2111)
Audit #2111 (MEDIUM/LOW): попапы/карточки показывали данные, которые есть в
ответе, но недоливались до UI. Все поля едут через существующие loose
dict[str, Any] passthrough'ы (competitors, neighbors_summary) — без изменения
контракта/codegen.

- Конкурент (MarketLayers): «Застройщик» (dev_name) + «Ср. площадь кв.»
  (avg_area_pd — SQL уже считал, прокидываем явно в _competitor_with_coords).
- §3 «Инж. сооружения рядом» (NspdEngineeringNearbyBlock): вторичная строка
  Назначение/Характеристики/Адрес/№ ЕГРН (EngineeringStructure уже несёт эти поля).
- Польз. точка (CustomPoiLayer): scope-метка (глобальная/привязана к участку) +
  добавлена/изменена (formatDate с guard).
- Соседи (NeighborsBlock): Назначение + Статус (cad_buildings.purpose/status,
  добавлены в neighbors CTE SELECT + neighbor dict; прод-verified ~100% заполнено).
- Utility material → RU («сталь/металл/бетон», unknown → passthrough) — «человеческим
  языком» (ui-microcopy). Юнит-тесты material RU + avg_area_pd passthrough.

Примечание: neighbor purpose/status покрыты только DB-gated EXPLAIN-тестом
(integration), unit-покрытия нет by design.
2026-06-30 18:29:39 +05:00

145 lines
5.9 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.

"""Тесты для _utility_characteristics (#2111 §3 «Сети и точки подключения»).
Покрывает гуманизацию OSM tags инженерных сетей в одну RU-строку для попапов:
напряжение (multi/кВ/В), оператор, диаметр трубопровода, материал, цепи, фильтр
junk-значений. Хелпер module-private — импортируем напрямую.
"""
from __future__ import annotations
from app.services.site_finder.utility_infrastructure_loader import (
_format_diameter,
_format_voltage,
_utility_characteristics,
)
# ── _format_voltage ───────────────────────────────────────────────────────────
def test_format_voltage_kv_single() -> None:
assert _format_voltage("110000") == "110 кВ"
def test_format_voltage_volts_below_1000() -> None:
assert _format_voltage("400") == "400 В"
def test_format_voltage_multi_mixed() -> None:
# «110000;10000» → оба ≥1000 → кВ, join через «/».
assert _format_voltage("110000;10000") == "110 кВ/10 кВ"
def test_format_voltage_multi_kv_and_volts() -> None:
assert _format_voltage("10000;400") == "10 кВ/400 В"
def test_format_voltage_fractional_kv() -> None:
# 400 В → «400 В»; 6600 → «6.6 кВ» (без хвостового нуля).
assert _format_voltage("6600") == "6.6 кВ"
def test_format_voltage_skips_non_numeric() -> None:
assert _format_voltage("110000;junk;10000") == "110 кВ/10 кВ"
def test_format_voltage_all_junk_returns_none() -> None:
assert _format_voltage("abc;;def") is None
# ── _format_diameter ──────────────────────────────────────────────────────────
def test_format_diameter_plain_digits() -> None:
assert _format_diameter("500") == "⌀ 500 мм"
def test_format_diameter_strips_mm_suffix() -> None:
assert _format_diameter("500mm") == "⌀ 500 мм"
assert _format_diameter("300 мм") == "⌀ 300 мм"
def test_format_diameter_no_digits_returns_none() -> None:
assert _format_diameter("large") is None
def test_format_diameter_multidim_takes_first_run() -> None:
# «500x300» (прямоугольный короб) → берём первый числовой run, не склеиваем.
assert _format_diameter("500x300") == "⌀ 500 мм"
assert _format_diameter("DN500") == "⌀ 500 мм"
# ── _utility_characteristics ──────────────────────────────────────────────────
def test_characteristics_power_voltage_and_operator() -> None:
out = _utility_characteristics("power", {"voltage": "110000;10000", "operator": "МРСК Урала"})
assert out == "110 кВ/10 кВ · МРСК Урала"
def test_characteristics_pipeline_diameter_and_material() -> None:
# material переводится на RU («человеческим языком»): steel → сталь.
out = _utility_characteristics("gas", {"diameter": "500mm", "material": "steel"})
assert out == "⌀ 500 мм · сталь"
def test_characteristics_material_ru_and_unknown_passthrough() -> None:
# known → RU; неизвестный материал остаётся сырым (лучше сырое, чем потерять).
assert _utility_characteristics("power", {"material": "metal"}) == "металл"
assert _utility_characteristics("power", {"material": "Concrete"}) == "бетон"
assert _utility_characteristics("gas", {"material": "unobtanium"}) == "unobtanium"
def test_characteristics_single_voltage_volts() -> None:
assert _utility_characteristics("power", {"voltage": "400"}) == "400 В"
def test_characteristics_circuits_when_numeric() -> None:
out = _utility_characteristics("power", {"voltage": "10000", "circuits": "2"})
assert out == "10 кВ · 2 цеп."
def test_characteristics_cables_fallback_for_circuits() -> None:
out = _utility_characteristics("power", {"cables": "3"})
assert out == "3 цеп."
def test_characteristics_non_numeric_circuits_skipped() -> None:
# circuits=«single» не число → пропускаем; остаётся только напряжение.
assert _utility_characteristics("power", {"voltage": "400", "circuits": "single"}) == "400 В"
def test_characteristics_junk_voltage_filtered() -> None:
# voltage=yes (junk) → пропуск; ничего больше → None.
assert _utility_characteristics("power", {"voltage": "yes"}) is None
def test_characteristics_filters_empty_and_no() -> None:
assert _utility_characteristics("water", {"operator": " ", "material": "no"}) is None
def test_characteristics_none_tags_returns_none() -> None:
assert _utility_characteristics("power", None) is None
def test_characteristics_empty_dict_returns_none() -> None:
assert _utility_characteristics("power", {}) is None
def test_characteristics_not_a_dict_returns_none() -> None:
# Защитный guard: tags из БД мог прийти не dict (broken JSONB) → None, не падаем.
assert _utility_characteristics("power", "not-a-dict") is None # type: ignore[arg-type]
def test_characteristics_full_order() -> None:
# Порядок частей: напряжение · оператор · диаметр · материал · цепи.
out = _utility_characteristics(
"power",
{
"voltage": "10000",
"operator": "Облкоммунэнерго",
"diameter": "200",
"material": "медь",
"circuits": "2",
},
)
assert out == "10 кВ · Облкоммунэнерго · ⌀ 200 мм · медь · 2 цеп."