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
110 lines
6.3 KiB
Python
110 lines
6.3 KiB
Python
"""Две докстроки обещали то, чего в коде нет (#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 не возвращается независимо от давности"
|
||
)
|