DispelEvilBot/tests/conftest.py

125 lines
3.6 KiB
Python
Raw Permalink 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.

"""Общие приспособления тестов админки: Telegram-вход и CSRF-токен."""
from __future__ import annotations
import hashlib
import hmac
import re
import time
from typing import Any
import pytest
from fastapi.testclient import TestClient
from admin.app import create_app
from common.config import AdminSettings
from common.repositories import admins_repo
BOT_TOKEN = "123:TEST"
SESSION_SECRET = "test-secret"
ADMIN: dict[str, Any] = {
"telegram_id": 1,
"username": "admin",
"role": "admin",
"enabled": True,
"created_at": None,
"last_login_at": None,
}
_CSRF_RE = re.compile(r'name="csrf_token" value="([^"]+)"')
def admin_settings(**overrides: Any) -> AdminSettings:
values: dict[str, Any] = {
"_env_file": None,
"bot_token": BOT_TOKEN,
"bot_username": "debot_test_bot",
"admin_public_url": "https://debot-test.mpakosrv.ru",
"admin_session_secret": SESSION_SECRET,
"db_password": "test",
"app_env": "dev",
}
values.update(overrides)
return AdminSettings(**values)
def telegram_params(
*,
token: str = BOT_TOKEN,
telegram_id: int = 1,
auth_date: int | None = None,
) -> dict[str, str]:
"""Собрать query как от Telegram Login Widget (с валидной подписью)."""
data = {
"id": str(telegram_id),
"first_name": "Admin",
"username": "admin",
"auth_date": str(auth_date if auth_date is not None else int(time.time())),
}
check_string = "\n".join(f"{key}={value}" for key, value in sorted(data.items()))
secret = hashlib.sha256(token.encode()).digest()
data["hash"] = hmac.new(secret, check_string.encode(), hashlib.sha256).hexdigest()
return data
def csrf_from(html: str) -> str:
"""Достать CSRF-токен из HTML формы."""
match = _CSRF_RE.search(html)
assert match, "в странице нет csrf_token"
return match.group(1)
@pytest.fixture
def bot_token() -> str:
"""Токен тестового бота (подпись Telegram-логина)."""
return BOT_TOKEN
@pytest.fixture
def csrf_token():
"""Функция: достать CSRF-токен из HTML."""
return csrf_from
@pytest.fixture
def telegram_query():
"""Функция: собрать подписанный query Telegram-логина."""
return telegram_params
@pytest.fixture
def settings_factory():
"""Фабрика настроек админки для тестов."""
return admin_settings
@pytest.fixture
def client_factory(monkeypatch: pytest.MonkeyPatch):
"""Фабрика TestClient: подменяет проверку администратора и умеет логиниться."""
async def fake_get_admin(telegram_id: int) -> dict[str, Any] | None:
return ADMIN if int(telegram_id) == ADMIN["telegram_id"] else None
async def fake_touch_login(telegram_id: int) -> None:
return None
monkeypatch.setattr(admins_repo, "get_admin", fake_get_admin)
monkeypatch.setattr(admins_repo, "touch_login", fake_touch_login)
def make(
*,
authenticated: bool = True,
follow_redirects: bool = True,
settings: AdminSettings | None = None,
) -> TestClient:
client = TestClient(
create_app(settings or admin_settings(), init_db=False),
follow_redirects=follow_redirects,
)
if authenticated:
response = client.get("/auth/telegram", params=telegram_params())
assert response.status_code in {200, 303}
return client
return make