chore(#3051): снять новые тесты — до живой проверки функционала тесты не пишем
Some checks failed
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Failing after 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
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 5m7s

В существующем test_rosreestr_dedup_key оставлена только правка, без которой
он падает после параметризации (литералы region_code=66 / doc_type='ДКП'
ушли из SQL живого импорта). Новый тест-файл и добавленная функция сняты:
поведение ещё не проверено на живом импорте, тест зафиксировал бы догадку.
This commit is contained in:
bot-backend 2026-09-08 22:53:20 +03:00
parent cfe529fd14
commit 476fb9b77a
2 changed files with 1 additions and 167 deletions

View file

@ -1,148 +0,0 @@
"""#3051 п.3 + п.6: doc_type в deals и параметризация региона/типа документа на импорте.
ЗАЧЕМ: для Москвы в источнике за 2024 лежат 30 627 ДДУ с медианой 112 743 /м² против
107 005 ДКП с медианой 256 250 это цены котлована. Тип сделки терялся при загрузке
(в deals не приезжал вовсе), а фильтры `region_code = 66` и `doc_type = 'ДКП'` стояли
литералами, поэтому регион 77 было не подключить, не правя код. Тесты фиксируют, что
параметры реально доезжают до bind-словаря, дефолты воспроизводят прежнее поведение,
а doc_type пишется в deals.
Чисто-юнит: ассертим SQL-текст import_rosreestr_dkp (inspect.getsource) + текст миграции
288, плюс вызываем функцию с моком Session, чтобы увидеть фактические bind-параметры.
Живой FDW / БД не нужны.
"""
import inspect
import os
import re
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock
# Как в test_rosreestr_dedup_key.py: Settings требует DATABASE_URL до импорта.
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
from app.services import scheduler
_SQL_DIR = Path(__file__).resolve().parents[1] / "data" / "sql"
_MIGRATION_288 = _SQL_DIR / "288_deals_doc_type.sql"
_IMPORT_SRC = inspect.getsource(scheduler.import_rosreestr_dkp)
# ── SQL-форма импорта ────────────────────────────────────────────────────────
def test_import_filters_region_and_doc_type_via_bind_params() -> None:
"""Оба фильтра — bind-параметры, литералов 66 / 'ДКП' в SQL больше нет."""
assert "region_code = CAST(:region_code AS int)" in _IMPORT_SRC
assert "doc_type = ANY(CAST(:doc_types AS text[]))" in _IMPORT_SRC
assert "WHERE region_code = 66" not in _IMPORT_SRC
assert "AND doc_type = 'ДКП'" not in _IMPORT_SRC
# psycopg v3: ни один bind-параметр не приводится через :name::type.
assert not re.search(r":\w+::", _IMPORT_SRC)
def test_import_writes_doc_type_into_deals() -> None:
"""doc_type едет из SELECT в список колонок INSERT, в VALUES и в ON CONFLICT."""
assert "doc_type AS doc_type" in _IMPORT_SRC
assert "deal_date, doc_type" in _IMPORT_SRC
assert "CAST(:doc_type AS text)" in _IMPORT_SRC
# Переопубликованный квартал может исправить тип документа — обновляем.
assert "doc_type = EXCLUDED.doc_type" in _IMPORT_SRC
assert "IS DISTINCT FROM EXCLUDED.doc_type" in _IMPORT_SRC
assert '"doc_type": row["doc_type"]' in _IMPORT_SRC
def test_import_keeps_dedup_key_shape_unchanged() -> None:
"""Ключ дедупа НЕ зависит от типа документа — иначе осиротели бы старые строки.
id уникален в источнике сам по себе; префикс 'ros:dkp:' остаётся историческим
именем формата (см. миграцию 077, которая уже конвертировала все строки).
"""
assert "'ros:dkp:' || CAST(id AS text) AS dedup_hash" in _IMPORT_SRC
assert "ON CONFLICT (dedup_hash) DO UPDATE SET" in _IMPORT_SRC
# ── фактические bind-параметры (вызов с моком) ───────────────────────────────
def _capture_select_params(params: dict[str, Any]) -> dict[str, Any]:
"""Прогоняет import_rosreestr_dkp до первого SELECT из FDW и возвращает его binds.
Мок Session: cleanup-DELETE и _resume_dkp_cursor получают пустые результаты,
первый батч пустой список, поэтому цикл завершается сразу после SELECT.
"""
captured: list[dict[str, Any]] = []
def _execute(stmt: Any, binds: Any = None, *a: Any, **kw: Any) -> MagicMock:
sql = str(stmt)
if "FROM gendesign_rosreestr_deals" in sql and isinstance(binds, dict):
captured.append(binds)
result = MagicMock()
result.fetchall.return_value = []
result.fetchone.return_value = None
result.mappings.return_value.all.return_value = []
result.scalar.return_value = None
return result
db = MagicMock()
db.execute.side_effect = _execute
monkeys = {
"is_cancelled": False,
}
orig_is_cancelled = scheduler.runs_mod.is_cancelled
orig_mark_done = scheduler.runs_mod.mark_done
orig_heartbeat = scheduler.kit_runs.update_heartbeat
try:
scheduler.runs_mod.is_cancelled = lambda *a, **kw: monkeys["is_cancelled"] # type: ignore[assignment]
scheduler.runs_mod.mark_done = lambda *a, **kw: None # type: ignore[assignment]
scheduler.kit_runs.update_heartbeat = lambda *a, **kw: None # type: ignore[assignment]
scheduler.import_rosreestr_dkp(db, 1, params)
finally:
scheduler.runs_mod.is_cancelled = orig_is_cancelled # type: ignore[assignment]
scheduler.runs_mod.mark_done = orig_mark_done # type: ignore[assignment]
scheduler.kit_runs.update_heartbeat = orig_heartbeat # type: ignore[assignment]
assert captured, "SELECT из gendesign_rosreestr_deals не выполнился"
return captured[0]
def test_defaults_preserve_current_production_behaviour() -> None:
"""Без параметров — ровно прежний скоуп: Свердловская обл., только ДКП."""
binds = _capture_select_params({})
assert binds["region_code"] == 66
assert binds["doc_types"] == ["ДКП"]
def test_params_override_region_and_doc_types() -> None:
"""Москва + оба типа документа приходят из params, а не из кода."""
binds = _capture_select_params({"region_code": 77, "doc_types": ["ДКП", "ДДУ"]})
assert binds["region_code"] == 77
assert binds["doc_types"] == ["ДКП", "ДДУ"]
def test_region_code_accepts_string_from_jsonb_params() -> None:
"""default_params прилетают из jsonb — строковый регион не должен ломать CAST."""
binds = _capture_select_params({"region_code": "77"})
assert binds["region_code"] == 77
assert isinstance(binds["region_code"], int)
# ── миграция 288 ─────────────────────────────────────────────────────────────
def test_migration_288_adds_column_backfills_and_sets_schedule_region() -> None:
"""288: idempotent-колонка, бэкфилл только rosreestr-строк, явный регион в расписании."""
assert _MIGRATION_288.is_file(), f"missing migration: {_MIGRATION_288}"
sql = _MIGRATION_288.read_text("utf-8")
assert "ALTER TABLE deals ADD COLUMN IF NOT EXISTS doc_type text" in sql
# Бэкфилл корректен ровно потому, что всё загруженное прошло фильтр ДКП.
assert "SET doc_type = 'ДКП'" in sql
assert "WHERE source = 'rosreestr'" in sql
assert "AND doc_type IS NULL" in sql
# Явный region_code в расписании вместо неявного дефолта в коде.
assert "UPDATE scrape_schedules" in sql
assert "'{\"region_code\": 66}'::jsonb" in sql
assert "source = 'rosreestr_dkp_import'" in sql
assert "BEGIN;" in sql and "COMMIT;" in sql

View file

@ -156,7 +156,7 @@ def test_migration_077_shared_filter_matches_live_import() -> None:
ЕКБ-строки под EKB-only scope того времени; живой импорт расширен на всю
Свердловскую область, поэтому city-фильтр из живого импорта СНЯТ намеренно;
- region_code / doc_type: в живом импорте это ПАРАМЕТРЫ (#3051), их совпадение
с 077 проверяется через дефолты см. тест ниже.
с 077 проверяется дефолтами params.get(..., 66) / ["ДКП"] в самом импорте.
Остальные клозы обязаны совпадать байт-в-байт, иначе 077 конвертировал бы не тот
набор строк.
"""
@ -174,24 +174,6 @@ def test_migration_077_shared_filter_matches_live_import() -> None:
assert "doc_type = 'ДКП'" in sql
def test_live_import_parametrized_region_and_doc_type_keep_077_defaults() -> None:
"""#3051: регион и типы документов — bind-параметры, но ДЕФОЛТЫ = скоуп 077.
Литералы `region_code = 66` / `doc_type = 'ДКП'` ушли из SQL живого импорта
(иначе регион 77 не подключить), но дефолты обязаны воспроизводить ровно тот
набор строк, который 077 когда-то backfill'ил, — иначе штатный прогон молча
сменил бы скоуп.
"""
# В SQL — параметры, не литералы.
assert "region_code = CAST(:region_code AS int)" in _IMPORT_SRC
assert "doc_type = ANY(CAST(:doc_types AS text[]))" in _IMPORT_SRC
assert "region_code = 66" not in _IMPORT_BODY
assert "doc_type = 'ДКП'" not in _IMPORT_BODY.split("params.get", 1)[-1].split("SELECT", 1)[0]
# Дефолты — прежнее поведение.
assert 'params.get("region_code", 66)' in _IMPORT_SRC
assert 'params.get("doc_types") or ["ДКП"]' in _IMPORT_SRC
def test_live_import_dropped_ekb_city_filter() -> None:
"""Живой импорт БОЛЬШЕ не фильтрует по городу — Mera расширена на всю обл. 66.