From c087bd74b1106bc79822c1cc256b738ac7114748 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 7 Jun 2026 14:22:26 +0300 Subject: [PATCH] =?UTF-8?q?fix(sf):=20verify=3DFalse=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=B5=D0=BA=D0=B0=D1=82=D0=B5=D1=80=D0=B8=D0=BD=D0=B1=D1=83?= =?UTF-8?q?=D1=80=D0=B3.=D1=80=D1=84=20(CA=20=D0=9C=D0=B8=D0=BD=D1=86?= =?UTF-8?q?=D0=B8=D1=84=D1=80=D1=8B)=20+=20=D1=81=D0=B8=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D1=82=D0=B8=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20CI-=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=20=D0=9A=D0=A0=D0=A2-=D0=BF=D0=B0=D1=80?= =?UTF-8?q?=D1=81=D0=B5=D1=80=D0=B0=20(=D1=80=D0=B5=D0=B2=D1=8C=D1=8E=20#1?= =?UTF-8?q?126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/services/scrapers/ekb_krt_registry.py | 7 ++- .../scrapers/test_ekb_krt_registry.py | 58 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/backend/app/services/scrapers/ekb_krt_registry.py b/backend/app/services/scrapers/ekb_krt_registry.py index 79613385..8f02e67d 100644 --- a/backend/app/services/scrapers/ekb_krt_registry.py +++ b/backend/app/services/scrapers/ekb_krt_registry.py @@ -70,7 +70,12 @@ def fetch_krt_page() -> str: Сеть отделена от парсинга для testability. Raises: httpx.HTTPError при недоступности сервера. """ - with httpx.Client(headers=_HEADERS, timeout=30, follow_redirects=True) as client: + with httpx.Client( + headers=_HEADERS, + timeout=30, + follow_redirects=True, + verify=False, # CA Минцифры РФ — нет в certifi. Аналог ekburg_permits.py #242. + ) as client: resp = client.get(KRT_URL) resp.raise_for_status() logger.info( diff --git a/backend/tests/services/scrapers/test_ekb_krt_registry.py b/backend/tests/services/scrapers/test_ekb_krt_registry.py index 6991d208..2463fc8b 100644 --- a/backend/tests/services/scrapers/test_ekb_krt_registry.py +++ b/backend/tests/services/scrapers/test_ekb_krt_registry.py @@ -139,6 +139,64 @@ class TestEmptyAndGarbage: assert parse_krt_page(html) == [] +# ── Синтетический блок: площадка без застройщика / договора / цен ───────────── +# Имитирует площадки «в работе» (только geography + технические параметры). +# Не требует локального дампа — всегда исполняется в CI. +_BABUSHKINA_BLOCK = textwrap.dedent( + """\ +

2024 год

+

 1. КРТ жилой застройки в районе ул. Бабушкина:

+

Площадь территории: 5,10 га;
\ +Градостроительный потенциал: 120 000 кв. м;
\ +Всего многоквартирных домов: 18;
\ +3. Издано Постановление Администрации города Екатеринбурга от 15.01.2024 № 42 \ +«О комплексном развитии территории».

+

Стадия реализации: проектирование

+ """ +) + + +class TestParseBabushkinaBlock: + """Синтетический тест: площадка без застройщика, договора и цен аукциона. + + Не зависит от локального дампа — всегда запускается в CI. + """ + + @pytest.fixture(scope="class") + def site(self): + sites = parse_krt_page(_BABUSHKINA_BLOCK) + assert sites, "parse_krt_page вернул пустой список для синтетического блока" + return sites[0] + + def test_parses_exactly_one_site(self): + """Синтетический блок содержит ровно одну площадку.""" + assert len(parse_krt_page(_BABUSHKINA_BLOCK)) == 1 + + def test_developer_is_none(self, site): + """Застройщик не указан → None.""" + assert site.developer_name is None + + def test_contract_number_is_none(self, site): + """Договор не заключён → None.""" + assert site.contract_number is None + + def test_area_ha_filled(self, site): + """Площадь территории заполнена.""" + assert site.area_ha == pytest.approx(5.10, rel=1e-3) + + def test_gradpotential_filled(self, site): + """Градостроительный потенциал заполнен.""" + assert site.gradpotential_sqm == pytest.approx(120000.0, rel=1e-3) + + def test_district_filled(self, site): + """Район извлечён из названия блока.""" + assert site.district is not None + assert "Бабушкина" in site.district + + def test_krt_kind(self, site): + assert site.krt_kind == "жилой застройки" + + class TestMultiSiteCountFromDump: """Проверка количества площадок из реального дампа страницы."""