"""Тесты page_reservation_parser (#1091, #1062) — парсер постановлений об изъятии/резервировании.""" from __future__ import annotations from datetime import date import pytest from app.services.scrapers.page_reservation_parser import ( ReservationRecord, extract_reservations, ) # ── Фикстура-текст с типичным содержимым постановления ──────────────────────── _FULL_TEXT = """ ПОСТАНОВЛЕНИЕ Правительства Свердловской области от 12.03.2024 № 509-ПП Об изъятии земельных участков для государственных нужд в целях реконструкции улицы Татищева в городе Екатеринбурге. В соответствии со статьёй 49 Земельного кодекса Российской Федерации Правительство Свердловской области ПОСТАНОВЛЯЕТ: 1. Изъять для государственных нужд следующие земельные участки: 66:41:0101001:123 — площадь 450 кв.м; 66:41:0101001:456 — площадь 300 кв.м; 66:41:0101002:789 — площадь 680 кв.м. 2. Министерству по управлению государственным имуществом Свердловской области обеспечить государственную регистрацию права собственности. Губернатор Свердловской области """ _REZERV_TEXT = """ ПОСТАНОВЛЕНИЕ Правительства Свердловской области от 05 июня 2024 г. № 210-ПП О резервировании земельных участков для строительства дороги. Зарезервировать земельные участки: 66:41:0203010:11 66:41:0203010:22 в целях строительства объекта дорожной инфраструктуры. """ _GARBAGE_TEXT = "Это текст без кад-номеров и без постановлений." _BAD_DATE_TEXT = """ № 99-ПП от 99.99.9999 Изъять земельный участок 66:41:0101001:500. """ # ── Тесты extract_reservations ──────────────────────────────────────────────── def test_extracts_cad_nums() -> None: """Все три КН извлекаются из постановления об изъятии.""" records = extract_reservations(_FULL_TEXT) cad_nums = {r.cad_num for r in records} assert cad_nums == {"66:41:0101001:123", "66:41:0101001:456", "66:41:0101002:789"} def test_deduplicates_cad_nums() -> None: """Дублирующиеся КН в документе схлопываются в одну запись.""" text_with_dup = _FULL_TEXT + "\n66:41:0101001:123 повторно упомянут." records = extract_reservations(text_with_dup) cad_nums = [r.cad_num for r in records] assert cad_nums.count("66:41:0101001:123") == 1 def test_kind_izyatie() -> None: """Определяет 'изъятие' из текста с «Изъять».""" records = extract_reservations(_FULL_TEXT) assert all(r.reservation_kind == "изъятие" for r in records) def test_kind_rezervirovanie() -> None: """Определяет 'резервирование' из текста с «резервирование».""" records = extract_reservations(_REZERV_TEXT) assert all(r.reservation_kind == "резервирование" for r in records) def test_act_number_parsed() -> None: """Номер постановления 509-ПП корректно извлекается.""" records = extract_reservations(_FULL_TEXT) assert records[0].act_number == "509-ПП" def test_act_date_numeric() -> None: """Дата «от 12.03.2024» парсится как date(2024, 3, 12).""" records = extract_reservations(_FULL_TEXT) assert records[0].act_date == date(2024, 3, 12) def test_act_date_word_month() -> None: """Дата «05 июня 2024 г.» парсится как date(2024, 6, 5).""" records = extract_reservations(_REZERV_TEXT) assert records[0].act_date == date(2024, 6, 5) def test_purpose_extracted() -> None: """Поле purpose содержит осмысленный текст (best-effort).""" records = extract_reservations(_FULL_TEXT) # Хотя бы у одной записи purpose не None. assert any(r.purpose is not None for r in records) def test_raw_excerpt_first_record() -> None: """raw_excerpt есть у первой записи (±120 символов).""" records = extract_reservations(_FULL_TEXT) first = records[0] assert first.raw_excerpt is not None assert "66:41:0101001:123" in (first.raw_excerpt or "") def test_garbage_text_returns_empty() -> None: """Мусорный текст без КН → пустой список.""" records = extract_reservations(_GARBAGE_TEXT) assert records == [] def test_empty_string_returns_empty() -> None: """Пустая строка → пустой список.""" assert extract_reservations("") == [] def test_bad_date_does_not_raise() -> None: """Нечитаемая дата (99.99.9999) → act_date=None, не падает.""" records = extract_reservations(_BAD_DATE_TEXT) assert len(records) == 1 assert records[0].act_date is None assert records[0].cad_num == "66:41:0101001:500" def test_default_kind_applied() -> None: """Если нет маркеров изъятия/резервирования — берётся default_kind.""" text = "Земельный участок 66:41:0101001:777 подлежит ограничению." records = extract_reservations(text, default_kind="резервирование") assert records[0].reservation_kind == "резервирование" def test_record_is_frozen() -> None: """ReservationRecord — frozen dataclass.""" r = ReservationRecord( cad_num="66:41:0101001:1", reservation_kind="изъятие", act_number="509-ПП", act_date=None, purpose=None, raw_excerpt=None, ) with pytest.raises((AttributeError, TypeError)): r.cad_num = "other" # type: ignore[misc]