diff --git a/backend/app/services/scrapers/domrf_catalog.py b/backend/app/services/scrapers/domrf_catalog.py index e1306a61..9fd2d536 100644 --- a/backend/app/services/scrapers/domrf_catalog.py +++ b/backend/app/services/scrapers/domrf_catalog.py @@ -80,7 +80,12 @@ _STATUS_KW_RE = re.compile( re.IGNORECASE | re.UNICODE, ) # CSS-классы, характерные для статус-бейджей в Next.js/Tailwind. -_STATUS_BADGE_CLS_RE = re.compile(r"status|badge|tag|chip|label", re.IGNORECASE) +# Требуем слово «status» как отдельный токен внутри hyphen-separated CSS-класса +# (напр. «status-badge», «flat-status-tag», «object-status»). +# Предыдущий паттерн «status|badge|tag|chip|label» был слишком широк — +# срабатывал на любой generic элемент с классом «tag»/«chip»/«label»/«badge», +# не имеющим отношения к статусу продажи (#1609 follow-up, #1686). +_STATUS_BADGE_CLS_RE = re.compile(r"(? str | None: diff --git a/backend/tests/services/scrapers/test_domrf_catalog_parse.py b/backend/tests/services/scrapers/test_domrf_catalog_parse.py index c75f3fdc..a9214814 100644 --- a/backend/tests/services/scrapers/test_domrf_catalog_parse.py +++ b/backend/tests/services/scrapers/test_domrf_catalog_parse.py @@ -211,6 +211,31 @@ def test_prodana_feminine_classified_as_sold() -> None: assert result.get("status") == STATUS_SOLD +def test_generic_label_tag_not_treated_as_badge() -> None: + """Элемент с классом 'label' или 'tag' (без слова 'status') НЕ должен + активировать Уровень 1 (#1686 follow-up: regex был слишком широк). + + Страница: generic с текстом 'В продаже' в шапке, + реальный статус-бейдж с классом 'status-badge' содержит 'Продана'. + Уровень 1 должен выбрать только блок status-badge, а не label-шапку. + """ + html = ( + "" + # generic label в шапке — НЕ должен активировать Level-1 badge match + 'В продаже' + 'Новостройка' + 'ЕКБ' + 'Акция' + # настоящий статус-бейдж — должен быть подхвачен Level-1 + '
Продана
' + "" + ) + result = parse_catalog_flat(html) + # Level-1 должен взять status-badge (Продана → sold), + # generic label/tag/chip/badge не должны перебить правильный результат + assert result.get("status") == STATUS_SOLD + + def test_full_page_sold_flat_no_false_free() -> None: """Реалистичный HTML проданной квартиры: навигация 'в продаже', бейдж 'продано'.""" html = (