fix(tests): починить и вычистить проверки, которые не исполнялись ни разу (#2744)
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy Trade-In / changes (push) Successful in 11s
Deploy / build-frontend (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 32s
Deploy / build-worker (push) Successful in 32s
Deploy / deploy (push) Successful in 1m8s
Deploy Trade-In / test (push) Successful in 3m5s
Deploy Trade-In / build-backend (push) Successful in 29s
Deploy Trade-In / deploy (push) Successful in 1m5s

This commit is contained in:
bot-backend 2026-08-06 16:57:28 +00:00
parent 9f51c98ff4
commit a091da1aa8
5 changed files with 24 additions and 104 deletions

View file

@ -5,32 +5,16 @@
from __future__ import annotations
# scrape_cadastre зависит от app.scrapers.nspd_bulk_client (PR 2/5).
# Используем importlib.util.find_spec вместо прямого import — иначе
# `import app.workers...` пересоздаёт `app` как Python package и
# перебивает FastAPI instance, привязанный в строке выше → AttributeError
# на app.dependency_overrides.
import importlib.util
# NB: `import app.workers...` здесь запрещён — он пересоздаёт `app` как Python
# package и перебивает FastAPI instance, привязанный ниже → AttributeError на
# app.dependency_overrides. Патчим воркер строкой, через patch("app.workers...").
from typing import Any
from unittest.mock import MagicMock, patch
import pytest
from fastapi.testclient import TestClient
from app.main import app
_SCRAPE_CADASTRE_AVAILABLE = (
importlib.util.find_spec("app.workers.tasks.scrape_cadastre") is not None
)
requires_scrape_cadastre = pytest.mark.skipif(
not _SCRAPE_CADASTRE_AVAILABLE,
reason=(
"app.scrapers.nspd_bulk_client отсутствует — нужно смержить main (PR #170). "
"Команда: git rebase origin/main"
),
)
# Токен для тестов (не реальный)
ADMIN_TOKEN = "test-admin-token"
ADMIN_HEADERS = {"X-Admin-Token": ADMIN_TOKEN}
@ -113,7 +97,6 @@ def _make_sample_job_row() -> dict[str, Any]:
# ── Tests ────────────────────────────────────────────────────────────────────
@requires_scrape_cadastre
@patch("app.core.config.settings.scrape_admin_token", ADMIN_TOKEN)
def test_create_job_pilot_returns_job_id() -> None:
"""POST /cadastre/jobs scope=pilot → job_id + targets_total."""
@ -144,7 +127,6 @@ def test_create_job_pilot_returns_job_id() -> None:
app.dependency_overrides.clear()
@requires_scrape_cadastre
@patch("app.core.config.settings.scrape_admin_token", ADMIN_TOKEN)
def test_create_job_manual_list() -> None:
"""POST /cadastre/jobs scope=manual_list с явным списком."""
@ -174,7 +156,6 @@ def test_create_job_manual_list() -> None:
app.dependency_overrides.clear()
@requires_scrape_cadastre
@patch("app.core.config.settings.scrape_admin_token", ADMIN_TOKEN)
def test_create_job_manual_list_empty_quarters_returns_400() -> None:
"""scope=manual_list без quarters → 400."""
@ -294,22 +275,3 @@ def test_cancel_job_not_found_returns_404() -> None:
assert response.status_code == 404
finally:
app.dependency_overrides.clear()
@pytest.mark.skip(
reason=(
"X-Admin-Token gate удалён в #437 (refactor(security): убрать X-Admin-Token — "
"Caddy basic_auth + RBAC middleware достаточны). Admin endpoint'ы больше не "
"несут verify_admin_token dependency; защита — на уровне Caddy/RBAC. В test-mode "
"RBAC bypass'ится (CI-rehab 1/3), поэтому 401/503 здесь больше недостижим без "
"реверта security-решения #437. Тест проверял удалённое поведение."
)
)
def test_create_job_no_token_returns_401() -> None:
"""Без X-Admin-Token → 401/503 (устарело: токен-гейт удалён в #437)."""
client = TestClient(app)
response = client.post(
"/api/v1/admin/cadastre/jobs",
json={"scope": "pilot"},
)
assert response.status_code in (401, 503)

View file

@ -73,19 +73,3 @@ def test_trigger_invalid_year_returns_422(bad_year: int) -> None:
client = TestClient(app)
response = client.post(ENDPOINT, json={"year": bad_year}, headers=ADMIN_HEADERS)
assert response.status_code == 422, f"year={bad_year} должен возвращать 422"
@pytest.mark.skip(
reason=(
"X-Admin-Token gate удалён в #437 (refactor(security): убрать X-Admin-Token — "
"Caddy basic_auth + RBAC middleware достаточны). trigger_ekburg_permits больше не "
"несёт verify_admin_token dependency; защита — на уровне Caddy/RBAC. В test-mode "
"RBAC bypass'ится (CI-rehab 1/3), поэтому 401/503 здесь больше недостижим без "
"реверта security-решения #437. Тест проверял удалённое поведение."
)
)
def test_trigger_no_token_returns_401_or_503() -> None:
"""Без X-Admin-Token → 401 или 503 (устарело: токен-гейт удалён в #437)."""
client = TestClient(app)
response = client.post(ENDPOINT, json={})
assert response.status_code in (401, 503), response.text

View file

@ -202,9 +202,7 @@ class TestParseTable11:
# Реальная зона должна быть в результате
assert "Зона жилой застройки" in zone_names
# Артефакты (zone_name пуст, не is_total) отброшены
non_total_no_zone = [
r for r in result if not r["zone_name"] and not r.get("is_total")
]
non_total_no_zone = [r for r in result if not r["zone_name"] and not r.get("is_total")]
assert non_total_no_zone == []
# Итого-строка осталась
assert any(r.get("is_total") for r in result)
@ -483,15 +481,23 @@ class TestParsePptTepMocked:
# ── Тест на реальном PDF-образце (skip в CI) ─────────────────────────────────
_REAL_PDF_PATH = Path(
"C:/Users/user/source/repos/gendesign/.playwright-mcp/ekb-docs/ppt2018_22823_poyasnit_PPT.pdf"
#
# Путь repo-relative, а не абсолютный `C:/Users/user/source/repos/...`: с
# Windows-путём условие пропуска не выполнялось НИ НА ОДНОЙ машине кроме одного
# старого дев-бокса, т.е. эти 6 тестов были не «пропущены по среде», а мертвы.
# Образец не в git (untracked `.playwright-mcp/`), поэтому в CI пропуск остаётся —
# но теперь он снимается тем, что файл кладут в репо-каталог на любой ОС.
_REAL_PDF_PATH = (
Path(__file__).resolve().parents[4]
/ ".playwright-mcp"
/ "ekb-docs"
/ "ppt2018_22823_poyasnit_PPT.pdf"
)
@pytest.mark.skipif(
not _REAL_PDF_PATH.exists(),
reason="Реальный PDF-образец отсутствует (только для локального запуска)",
reason=f"Реальный PDF-образец отсутствует ({_REAL_PDF_PATH}) — только локальный запуск",
)
class TestParsePptTepRealPdf:
"""Интеграционные тесты на реальном образце ppt2018_22823."""

View file

@ -399,41 +399,3 @@ def test_list_profiles_with_system_service(monkeypatch: pytest.MonkeyPatch) -> N
assert len(captured_params) == 1
assert captured_params[0]["system_user_id"] == SYSTEM_USER_ID
assert captured_params[0]["user_id"] == "user-test"
# ── Auth ───────────────────────────────────────────────────────────────────────
_TOKEN_REMOVED_REASON = (
"App-level X-Admin-Token header удалён 2026-05-23 (см. docstring "
"app/api/v1/admin_weight_profiles.py: Caddy basic_auth PR #426 + RBAC достаточны, "
"двойная auth избыточна). Endpoint больше не несёт verify_admin_token dependency. "
"В test-mode RBAC bypass'ится (CI-rehab 1/3) → 401 здесь недостижим без реверта "
"security-решения. Тест проверял удалённое поведение."
)
@pytest.mark.skip(reason=_TOKEN_REMOVED_REASON)
def test_unauthorized_no_token(monkeypatch: pytest.MonkeyPatch) -> None:
"""Запрос без X-Admin-Token → 401 (устарело: токен-гейт удалён 2026-05-23)."""
monkeypatch.setattr("app.core.config.settings.scrape_admin_token", _ADMIN_TOKEN)
client = TestClient(app)
r = client.get(
"/api/v1/admin/site-finder/weight-profiles",
params={"user_id": "user-1"},
# без headers — нет X-Admin-Token
)
assert r.status_code == 401
@pytest.mark.skip(reason=_TOKEN_REMOVED_REASON)
def test_unauthorized_wrong_token(monkeypatch: pytest.MonkeyPatch) -> None:
"""Неверный X-Admin-Token → 401 (устарело: токен-гейт удалён 2026-05-23)."""
monkeypatch.setattr("app.core.config.settings.scrape_admin_token", _ADMIN_TOKEN)
client = TestClient(app)
r = client.get(
"/api/v1/admin/site-finder/weight-profiles",
params={"user_id": "user-1"},
headers={"X-Admin-Token": "wrong-token"},
)
assert r.status_code == 401

View file

@ -449,8 +449,14 @@ def test_upsert_and_canon_match_populates_gar_flat_count(gar_dir: str) -> None:
db = _live_session()
assert db is not None
try:
db.execute(text(_NORMALIZER_FN))
db.execute(text(_CANON_FN))
# exec_driver_sql, НЕ text(): в обеих функциях есть regexp-группы `(?:...)`,
# а `text()` читает `:Россия` / `:кв` / `:асть` как bind-параметры и падает
# с InvalidRequestError ещё до первого утверждения. Драйверный путь ничего
# не парсит. (Тест жил с 2026-06-28 и до #2741 не исполнялся ни разу — под
# skipif «нет БД» падение было не видно.)
raw = db.connection()
raw.exec_driver_sql(_NORMALIZER_FN)
raw.exec_driver_sql(_CANON_FN)
db.execute(
text(
"CREATE TEMP TABLE gar_house_flats ("