All checks were successful
Deploy / changes (push) Successful in 9s
Deploy Trade-In / changes (push) Successful in 13s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy / build-backend (push) Successful in 2m23s
Deploy Trade-In / test (push) Successful in 3m56s
Deploy / build-worker (push) Successful in 4m16s
Deploy Trade-In / build-backend (push) Successful in 1m19s
Deploy / deploy (push) Successful in 1m49s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 12s
Deploy Trade-In / deploy (push) Successful in 2m25s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 11s
103 lines
6.9 KiB
Python
103 lines
6.9 KiB
Python
"""Докстрока `_page_contains_table` не обещает защиты, которой нет (#2464).
|
||
|
||
Прежняя редакция утверждала:
|
||
|
||
«Требуем явный caption «Таблица N» — hint-только режим (оглавление,
|
||
перекрёстные ссылки) даёт false positive и подавляется.»
|
||
|
||
Кода подавления в функции никогда не было — это `bool(cap.search(text))`, то есть
|
||
обычный поиск подстроки. Строка оглавления «Таблица 11 Баланс территории ..... 34» от
|
||
настоящей подписи не отличается: падеж тот же, именительный.
|
||
|
||
Замер 20.08.2026 уточнил и сам пункт эпика: приведённый в нём пример перекрёстной
|
||
ссылки «показатели приведены в таблице 12» регекс НЕ ловит — он требует именительное
|
||
«Таблица N». Опасно ровно оглавление, а не любое упоминание. Это зафиксировано
|
||
характеризующим тестом, чтобы следующая попытка подавления целилась в признаки
|
||
оглавления, а не в падежи.
|
||
|
||
Обещание защиты, которой нет, опаснее её отсутствия: читающий не станет её
|
||
добавлять. Тот же класс, что «объявлено ≠ исполняется».
|
||
|
||
Почему подавление не реализовано, а докстрока приведена к коду: таблица
|
||
`ekb_ppt_tep` на проде пуста (0 строк) — URL в `_SEED_DOCS` заглушка, живых PDF нет,
|
||
эвристику отсева оглавления не на чем откалибровать. Правило, придуманное без
|
||
образцов, ловит ровно те случаи, которые придумали вместе с ним.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import inspect
|
||
|
||
from app.services.scrapers import ekb_ppt_tep_parser as mod
|
||
|
||
|
||
def test_docstring_does_not_promise_suppression_that_is_absent() -> None:
|
||
"""Головной: докстрока не должна обещать подавление false positive.
|
||
|
||
На origin/main она содержит «даёт false positive и подавляется», а в теле
|
||
функции — один `cap.search(text)`. Красное значение конкретно: обещание есть,
|
||
реализации нет.
|
||
"""
|
||
doc = inspect.getdoc(mod._page_contains_table) or ""
|
||
src = inspect.getsource(mod._page_contains_table)
|
||
тело = src.split('"""')[-1]
|
||
# Ищем именно ОБЕЩАНИЕ («подавляется»), а не разговор о подавлении: новая
|
||
# редакция объясняет, почему подавление НЕ реализовано, и не должна краснеть.
|
||
обещает = "подавляется" in doc
|
||
реализует = any(kw in тело for kw in ("оглавлен", "leader", "count(", "finditer", "rsplit"))
|
||
assert not (обещает and not реализует), (
|
||
"докстрока обещает подавление false positive, а в теле только поиск "
|
||
f"подстроки:\n{тело.strip()}"
|
||
)
|
||
|
||
|
||
def test_docstring_names_the_actual_behaviour() -> None:
|
||
"""Докстрока обязана честно называть false positive, а не умалчивать о нём.
|
||
|
||
Просто убрать обещание было бы недостаточно: молчание читается как «всё
|
||
хорошо». Ловит «починку» через вычёркивание неудобной фразы.
|
||
"""
|
||
doc = (inspect.getdoc(mod._page_contains_table) or "").lower()
|
||
assert "false positive" in doc or "оглавлен" in doc, (
|
||
"докстрока не предупреждает о ложных срабатываниях на оглавлении"
|
||
)
|
||
|
||
|
||
def test_toc_line_really_is_a_false_positive() -> None:
|
||
"""Характеризующий: строка оглавления действительно проходит как подпись.
|
||
|
||
Зелёный с обеих сторон — он фиксирует ФАКТИЧЕСКОЕ поведение, о котором теперь
|
||
говорит докстрока. Если кто-то реализует подавление, тест покраснеет и
|
||
заставит обновить и докстроку, и его.
|
||
"""
|
||
оглавление = "СОДЕРЖАНИЕ\nТаблица 11 Баланс территории .......... 34\nТаблица 12 ТЭП .... 36"
|
||
assert mod._page_contains_table(оглавление, 11) is True, "оглавление обязано срабатывать"
|
||
|
||
# А вот перекрёстные ссылки в косвенных падежах регекс НЕ ловит: он требует
|
||
# именительное «Таблица N». Пункт эпика приводил «приведены в таблице 12» как
|
||
# пример false positive — замер это опроверг, и докстрока теперь говорит точнее.
|
||
for мимо in (
|
||
"Основные показатели приведены в таблице 12 настоящей записки.",
|
||
"см. Таблицу 12 ниже",
|
||
"Данные в табл. 12",
|
||
):
|
||
assert mod._page_contains_table(мимо, 12) is False, f"неожиданно сработало: {мимо!r}"
|
||
|
||
assert mod._page_contains_table("Никаких таблиц тут нет", 11) is False
|
||
|
||
|
||
def test_seed_comment_does_not_send_to_a_dead_host() -> None:
|
||
"""Комментарий сида не должен отправлять искать документ на несуществующем хосте.
|
||
|
||
`gisogd.ekburg.ru` не резолвится ни с рабочей машины, ни с прод-хоста
|
||
(проверено 20.08.2026), а прежний комментарий называл его местом, где «лежит
|
||
реальный URL». Живой портал — gisogd66.midural.ru.
|
||
"""
|
||
from pathlib import Path
|
||
|
||
src = Path(inspect.getsourcefile(mod)).resolve().parents[3]
|
||
текст = (src / "app" / "workers" / "tasks" / "ekb_ppt_tep_sync.py").read_text(encoding="utf-8")
|
||
assert "НЕ СУЩЕСТВУЕТ" in текст or "не резолв" in текст.lower(), (
|
||
"комментарий сида не предупреждает, что gisogd.ekburg.ru мёртв"
|
||
)
|
||
assert "gisogd66.midural.ru" in текст, "не назван живой портал ГИСОГД-СО"
|