gendesign/backend/tests/services/test_2464_docstring_matches_code.py
bot-backend 14818f0303
All checks were successful
CI Trade-In / changes (pull_request) Successful in 15s
CI / changes (pull_request) Successful in 15s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m56s
CI Trade-In / backend-tests (pull_request) Successful in 5m25s
CI / backend-tests (pull_request) Successful in 18m18s
chore(format): нормализация под ruff 0.15.20 — 161 файл, только формат (#2864)
Почему: файлы в main были отформатированы старым ruff (хук 0.7.4), а после
#3021 хук, CI и `uv run ruff format` — на 0.15.20. Без нормализации каждый
коммит, касающийся одного из этих 161 файлов, тащил бы посторонние хунки
на нетронутых строках (длинные assert-сообщения и т.п.).

Что: `ruff format` 0.15.20 по backend/ и tradein-mvp/backend/; ничего,
кроме форматирования (958+/1034−). `ruff check` 0.15.20 зелёный на обоих
проектах, повторный `format --check` — 1001 файл без изменений
(идемпотентно), compileall чистый.

Refs #2864
2026-08-21 16:39:28 +05:00

110 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Две докстроки обещали то, чего в коде нет (#2464).
1. `QuarterDump` (nspd_client): «Default = только core, чтобы не сжигать rate-limit
на 17 запросов». Фактический дефолт `search_by_quarter` — `include_zouit=True`,
то есть 5 ЗОУИТ-слоёв входят в дефолтный вызов. Числа 17 тоже нет: grid-walk при
grid_n=7 даёт по 49 запросов на слой. Причём докстрока самого метода 640 строками
ниже пишет верно — «Default True»: правильный образец лежал рядом с дефектом.
2. `find_active_on_demand_job` (cadastre_fetch): «Если в БД есть FAILED on-demand за
последние 60 секунд — тоже None (чтобы повторно пробовать)». В SQL нет ни слова
'failed', ни какого-либо временного фильтра. Обещание вдвойне вредно: оно
подразумевало, что failed СТАРШЕ 60 секунд вернётся как активный (не вернётся),
и отправляло отлаживающего искать окно, которого нет.
Гейты сверяют утверждение докстроки с кодом, а не читаемость текста.
"""
from __future__ import annotations
import inspect
def _body(fn) -> str:
"""Тело функции без её докстроки.
НЕ `split('\"\"\"')[-1]`: SQL внутри сам обёрнут в тройные кавычки, поэтому
последний кусок — это хвост ПОСЛЕ запроса, и проверка шла бы по огрызку.
Отбрасываем ровно первую докстроку и склеиваем остальное обратно.
"""
части = inspect.getsource(fn).split('"""')
return '"""'.join(части[2:]) if len(части) > 2 else части[-1]
def test_quarter_dump_docstring_matches_the_real_default() -> None:
"""Головной 1: если докстрока обещает «только core» — дефолт обязан быть False.
На origin/main она это обещает, а `include_zouit` по умолчанию True.
"""
from app.services.scrapers.nspd_client import NSPDClient, QuarterDump
doc = inspect.getdoc(QuarterDump) or ""
default = inspect.signature(NSPDClient.search_by_quarter).parameters["include_zouit"].default
обещает_только_core = "Default = только core" in doc
assert not (обещает_только_core and default is True), (
f"докстрока QuarterDump обещает «Default = только core», "
f"а include_zouit по умолчанию {default}"
)
def test_quarter_dump_docstring_does_not_claim_17_requests() -> None:
"""Контроль числа: «17 запросов» противоречит grid-walk (49 запросов на слой).
Просто убрать слово «core» было бы недостаточно — довод про rate-limit
держался на выдуманном числе.
"""
from app.services.scrapers.nspd_client import QuarterDump
doc = inspect.getdoc(QuarterDump) or ""
assert "не сжигать rate-limit на 17 запросов" not in doc, (
"в докстроке осталось число 17, противоречащее grid-walk"
)
def test_on_demand_docstring_does_not_promise_a_60s_window() -> None:
"""Головной 2: обещание окна «60 секунд» должно подтверждаться SQL.
На origin/main докстрока его обещает, а в запросе нет ни 'failed',
ни временного фильтра.
"""
from app.services.site_finder.cadastre_fetch import find_active_on_demand_job
doc = inspect.getdoc(find_active_on_demand_job) or ""
тело = _body(find_active_on_demand_job)
обещает_окно = "за последние 60" in doc
есть_фильтр = any(kw in тело.lower() for kw in ("interval", "now()", "failed"))
assert not (обещает_окно and not есть_фильтр), (
f"докстрока обещает окно «за последние 60 секунд», а в SQL нет ни "
f"временного фильтра, ни статуса failed:\n{тело.strip()[:400]}"
)
def test_on_demand_sql_really_ignores_failed() -> None:
"""Характеризующий: запрос отбирает ровно три статуса, failed среди них нет.
Зелёный с обеих сторон — фиксирует фактическое поведение, о котором теперь
говорит докстрока. Если кто-то добавит окно, тест покраснеет и заставит
обновить и текст.
"""
from app.services.site_finder.cadastre_fetch import find_active_on_demand_job
тело = _body(find_active_on_demand_job)
assert "'queued', 'running', 'paused'" in тело, тело[:300]
assert "failed" not in тело.lower(), "в запросе появился failed — обнови докстроку"
def test_docstrings_state_the_actual_behaviour() -> None:
"""Контроль от вычёркивания: обе докстроки обязаны НАЗЫВАТЬ фактическое поведение.
Молча убрать неверную фразу — не починка: молчание читается как «всё хорошо».
"""
from app.services.scrapers.nspd_client import QuarterDump
from app.services.site_finder.cadastre_fetch import find_active_on_demand_job
qd = inspect.getdoc(QuarterDump) or ""
assert "include_zouit" in qd, "не назван фактический дефолт дампа"
od = inspect.getdoc(find_active_on_demand_job) or ""
assert "НИКОГДА" in od or "никогда" in od, (
"не сказано, что failed не возвращается независимо от давности"
)