gendesign/tradein-mvp/backend/tests/conftest.py
bot-backend 6cf9172d96
All checks were successful
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m0s
Deploy Trade-In / build-backend (push) Successful in 1m1s
Deploy Trade-In / deploy (push) Successful in 1m9s
fix(tradein/auth): доля слотов сверки пароля на адрес — потолок перестаёт бить по своим (#2714) (#2717)
2026-08-06 10:42:39 +00:00

59 lines
3.1 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.

"""Repo-wide test config for tradein-mvp/backend.
Регистрирует кастомные pytest-маркеры (иначе PytestUnknownMarkWarning:
`--strict-markers` в pyproject.toml не включён, так что незарегистрированный
маркер только предупреждал бы) и сторожит глобальное состояние, которое
переживает отдельный тест, — см. `_no_leaked_password_verify_slots`.
"""
from __future__ import annotations
import sys
import pytest
def pytest_configure(config) -> None:
config.addinivalue_line(
"markers",
"pdf_render: real (non-mocked) WeasyPrint render — needs native "
"Pango/cairo/GObject libs, self-skips where unavailable (see "
"tests/test_pdf_real_render.py docstring for how to run it for real).",
)
@pytest.fixture(autouse=True)
def _no_leaked_password_verify_slots():
"""Тест не оставляет за собой занятых слотов проверки пароля (#2665, #2714).
Счётчики в `app.core.password` — состояние ПРОЦЕССА, а `pytest-asyncio` даёт
каждому тесту свой событийный цикл. Слот освобождает колбэк, посланный в
цикл через `call_soon_threadsafe`; если цикл к тому моменту закрыт,
`_schedule_verify_slot_release` ловит RuntimeError и слот не возвращается
никогда. На проде цикл живёт столько же, сколько процесс, и ветка
недостижима — а в тестах она копится молча и роняет НЕ ТОТ тест, который
её устроил: при пуле в 1 поток пары утечек хватает, чтобы всё дальнейшее
получало 429 «на ровном месте».
Поэтому проверка тут и общая: считаем слоты после каждого теста.
`sys.modules.get`, а не import: тестам, которые password.py не трогают
(большинство), незачем тянуть `Settings()` с его требованием DATABASE_URL.
"""
yield
password_mod = sys.modules.get("app.core.password")
if password_mod is None:
return
inflight = password_mod._verify_inflight
by_key = dict(password_mod._verify_inflight_by_key)
# Сброс ДО assert: иначе одна утечка красит все последующие тесты и виновник
# теряется среди пострадавших.
password_mod._verify_inflight = 0
password_mod._verify_inflight_by_key.clear()
assert inflight == 0 and not by_key, (
f"тест оставил {inflight} занятых слотов проверки пароля (по ключам: {by_key}) — "
"утечка слота при пуле в 1 поток это вечный 429 всем на входе"
)