"""Smoke тесты для avito_detail parser. Загружает minimal HTML fixture и проверяет selector + conversion logic без сетевых запросов. """ import pytest from app.services.scrapers.avito_detail import DetailEnrichment, parse_detail_html MINIMAL_HTML = """
№ 7986882804 · 18 мая в 13:40 · 1899 просмотров (+ 12 сегодня)
11 990 000 ₽
Дом

1 собственник

Последняя смена собственника 5 мая 2020

Не найдены ограничения и обременения

Совпадают площадь, адрес и этаж

Отличная квартира в новом ЖК. Чкаловская 11-15 мин пешком.
""" SOURCE_URL = "https://www.avito.ru/ekaterinburg/kvartiry/test_7986882804" def test_minimal_parse() -> None: result = parse_detail_html(MINIMAL_HTML, SOURCE_URL) assert isinstance(result, DetailEnrichment) assert result.item_id == "7986882804" assert result.price_rub == 11990000 assert result.rooms == 3 assert result.area_m2 == 75.0 assert result.kitchen_area_m2 == 15.0 assert result.floor == 20 assert result.total_floors == 26 assert result.balcony_loggia == "loggia" assert result.repair_state == "good" # «евро» → good (normalized enum, #621) assert result.sale_type == "free" assert result.mortgage_available is True assert result.house_type == "monolith" assert result.total_floors_house == 26 assert result.lat == 56.797713 assert result.lon == 60.609135 assert result.avito_location_id == 654070 assert result.house_catalog_url is not None and "3171365" in result.house_catalog_url assert result.owners_count == 1 assert result.owners_at_least is False assert result.last_owner_change_date is not None and result.last_owner_change_date.year == 2020 assert result.encumbrances_clean is True assert result.registry_match is True assert result.views_total == 1899 assert result.views_today == 12 def test_metro_extraction() -> None: result = parse_detail_html(MINIMAL_HTML, SOURCE_URL) # "Чкаловская 11-15 мин пешком" → metro_stations assert len(result.metro_stations) >= 1 station = result.metro_stations[0] assert station["name"] == "Чкаловская" assert station["min_from"] == 11 assert station["min_to"] == 15 assert station["mode"] == "walk" def test_owners_at_least_false() -> None: """Один собственник (без 'или больше') → owners_at_least=False.""" result = parse_detail_html(MINIMAL_HTML, SOURCE_URL) assert result.owners_count == 1 assert result.owners_at_least is False def test_owners_at_least_true() -> None: """'3 собственника или больше' → owners_at_least=True.""" html = """
№ 1234567890
5 000 000 ₽

3 собственника или больше

""" result = parse_detail_html(html, "https://www.avito.ru/test_1234567890") assert result.owners_count == 3 assert result.owners_at_least is True def test_missing_domoteka_all_none() -> None: """Если нет domoteka-блока — все 5 полей None.""" html = """
№ 9999999999
3 000 000 ₽ """ result = parse_detail_html(html, "https://www.avito.ru/test_9999999999") assert result.owners_count is None assert result.owners_at_least is None assert result.last_owner_change_date is None assert result.encumbrances_clean is None assert result.registry_match is None def test_invalid_html_raises_value_error() -> None: """HTML без item_id → ValueError.""" html = "

Нет айди

" with pytest.raises(ValueError, match="Cannot extract item_id"): parse_detail_html(html, "https://www.avito.ru/test") def test_house_catalog_url_full() -> None: """house_catalog_url должен быть absolute URL.""" result = parse_detail_html(MINIMAL_HTML, SOURCE_URL) assert result.house_catalog_url is not None assert result.house_catalog_url.startswith("https://www.avito.ru") def test_bathroom_type_combined() -> None: html = """
№ 1111111111
4 000 000 ₽
""" result = parse_detail_html(html, "https://www.avito.ru/test_1111111111") assert result.bathroom_type == "combined" assert result.windows_view == "both" assert result.room_layout == "isolated" def test_photo_urls_extracted() -> None: html = """
№ 2222222222
7 000 000 ₽
""" result = parse_detail_html(html, "https://www.avito.ru/test_2222222222") assert len(result.photo_urls) == 2 assert result.photo_urls[0] == "https://img.avito.ru/img1.jpg" def test_source_url_preserved() -> None: result = parse_detail_html(MINIMAL_HTML, SOURCE_URL) assert result.source_url == SOURCE_URL # ── repair_state fallback из описания (#622) ────────────────────────────────── def test_repair_state_structured_wins_over_description() -> None: """Структурное «Ремонт: евро» (good) в приоритете даже при сигнале в тексте.""" html = """
№ 3333333333
6 000 000 ₽
Квартира без отделки, под чистовую
""" result = parse_detail_html(html, "https://www.avito.ru/test_3333333333") assert result.repair_state == "good" # структурное поле, не needs_repair из текста def test_repair_state_inferred_from_description_when_structured_absent() -> None: """Нет блока «Ремонт» → инференс из описания (#622).""" html = """
№ 4444444444
8 000 000 ₽
Сделан свежий евроремонт, заезжай и живи
""" result = parse_detail_html(html, "https://www.avito.ru/test_4444444444") assert result.repair_state == "good" def test_repair_state_none_when_no_signal() -> None: """Нет структурного поля и нет сигнала в тексте → None (не фабрикуем).""" html = """
№ 5555555555
9 000 000 ₽
Светлая квартира рядом с метро
""" result = parse_detail_html(html, "https://www.avito.ru/test_5555555555") assert result.repair_state is None