All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 16s
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
CI Trade-In / backend-tests (pull_request) Successful in 5m50s
Прод отдавал «г Екатеринбург, ул Фролова , д. 29, корп.»: реестровый readable_address (ЕГРН) приходит УЖЕ склеенным вместе с пустыми частями — маркер без значения печатается, лишний пробел перед запятой остаётся, и все тиры /suggest (cadastral / geoportal / houses / DaData / Nominatim) пропускали строку насквозь. Чиним склейку, а не конкретный случай: `tidy_address()` — один проход по частям (схлопнуть пробелы, выбросить пустые и маркер-без-номера: корп./стр./лит./кв./оф./пом. и пр.), склейка обратно. Применяется в `GeocodeSuggestion.__post_init__` — единственной точке, через которую проходят все тиры, включая локальные, которые питают и `geocode()`. Тест на функцию склейки, фальсифицирован снятием фильтра пустых: краснеет по значению строки (10 из 14), а не исключением. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
70 lines
3.7 KiB
Python
70 lines
3.7 KiB
Python
"""Склейка адреса в подсказках: пустые части ВЫПАДАЮТ, а не печатаются.
|
||
|
||
Прод-дефект: «г Екатеринбург, ул Фролова , д. 29, корп.» — двойной пробел
|
||
перед запятой и висячее «корп.» без номера (ЕГРН `readable_address` отдаёт
|
||
маркер даже когда значения нет, и все тиры /suggest пропускали строку насквозь).
|
||
"""
|
||
|
||
import pytest
|
||
|
||
from app.services.geocoder import GeocodeSuggestion, tidy_address
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("raw", "expected"),
|
||
[
|
||
# Прод-случай целиком.
|
||
(
|
||
"г Екатеринбург, ул Фролова , д. 29, корп.",
|
||
"г Екатеринбург, ул Фролова, д. 29",
|
||
),
|
||
# Непустой корпус ОБЯЗАН остаться.
|
||
(
|
||
"г Екатеринбург, ул Фролова, д. 29, корп. 2",
|
||
"г Екатеринбург, ул Фролова, д. 29, корп. 2",
|
||
),
|
||
# Соседние типы частей — та же болезнь, тот же механизм.
|
||
("г Екатеринбург, ул Мира, д. 1, стр.", "г Екатеринбург, ул Мира, д. 1"),
|
||
("г Екатеринбург, ул Мира, д. 1, лит.", "г Екатеринбург, ул Мира, д. 1"),
|
||
("г Екатеринбург, ул Мира, д. 1, кв.", "г Екатеринбург, ул Мира, д. 1"),
|
||
("г Екатеринбург, ул Мира, д. 1, стр. 5", "г Екатеринбург, ул Мира, д. 1, стр. 5"),
|
||
("г Екатеринбург, ул Мира, д. 1, лит. А", "г Екатеринбург, ул Мира, д. 1, лит. А"),
|
||
# Пустая часть посередине и подряд идущие запятые.
|
||
("г Екатеринбург, , ул Мира, корп. , д. 1", "г Екатеринбург, ул Мира, д. 1"),
|
||
# Маркер без точки и без номера.
|
||
("г Екатеринбург, ул Мира, д 1, корпус", "г Екатеринбург, ул Мира, д 1"),
|
||
# Чистая строка не меняется.
|
||
("г Екатеринбург, ул Малышева, д 30", "г Екатеринбург, ул Малышева, д 30"),
|
||
],
|
||
)
|
||
def test_tidy_address(raw: str, expected: str) -> None:
|
||
assert tidy_address(raw) == expected
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
"raw",
|
||
[
|
||
"г Екатеринбург, ул Фролова , д. 29, корп.",
|
||
"г Екатеринбург, , ул Мира, корп. , д. 1",
|
||
"г Екатеринбург, ул Мира, д. 1, стр. , лит.",
|
||
],
|
||
)
|
||
def test_no_double_spaces_or_dangling_parts(raw: str) -> None:
|
||
out = tidy_address(raw)
|
||
assert " " not in out
|
||
assert " ," not in out
|
||
assert not out.endswith(",")
|
||
assert out.split(", ")[-1] not in {"корп.", "стр.", "лит.", "кв.", "д."}
|
||
|
||
|
||
def test_suggestion_cleans_both_label_and_full_address() -> None:
|
||
"""Чистка стоит в единой точке — её проходят ВСЕ тиры /suggest."""
|
||
s = GeocodeSuggestion(
|
||
label="ул Фролова , д. 29, корп.",
|
||
full_address="г Екатеринбург, ул Фролова , д. 29, корп.",
|
||
lat=56.8,
|
||
lon=60.6,
|
||
kind="house",
|
||
)
|
||
assert s.label == "ул Фролова, д. 29"
|
||
assert s.full_address == "г Екатеринбург, ул Фролова, д. 29"
|