162 lines
5.4 KiB
Python
162 lines
5.4 KiB
Python
"""Тесты раздела «Админы»: добавление, роли, включение и удаление."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from common.repositories import admins_repo
|
|
|
|
ADMIN_1: dict[str, Any] = {
|
|
"telegram_id": 1,
|
|
"username": "admin",
|
|
"role": "admin",
|
|
"enabled": True,
|
|
"created_at": datetime(2026, 1, 1, tzinfo=UTC),
|
|
"last_login_at": datetime(2026, 10, 25, 12, 0, tzinfo=UTC),
|
|
}
|
|
ADMIN_2: dict[str, Any] = {
|
|
"telegram_id": 2,
|
|
"username": "second",
|
|
"role": "moderator",
|
|
"enabled": True,
|
|
"created_at": datetime(2026, 1, 1, tzinfo=UTC),
|
|
"last_login_at": None,
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_admins(monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]:
|
|
state: dict[str, Any] = {"admins": [ADMIN_1, ADMIN_2]}
|
|
|
|
async def list_admins() -> list[dict[str, Any]]:
|
|
return state["admins"]
|
|
|
|
async def upsert_admin(telegram_id: int, username: str | None, role: str) -> None:
|
|
state["upsert"] = (telegram_id, username, role)
|
|
|
|
async def set_admin_role(telegram_id: int, role: str) -> bool:
|
|
state["role"] = (telegram_id, role)
|
|
return True
|
|
|
|
async def set_admin_enabled(telegram_id: int, enabled: bool) -> bool:
|
|
state["enabled"] = (telegram_id, enabled)
|
|
return True
|
|
|
|
async def delete_admin(telegram_id: int) -> bool:
|
|
state["deleted"] = telegram_id
|
|
return True
|
|
|
|
monkeypatch.setattr(admins_repo, "list_admins", list_admins)
|
|
monkeypatch.setattr(admins_repo, "upsert_admin", upsert_admin)
|
|
monkeypatch.setattr(admins_repo, "set_admin_role", set_admin_role)
|
|
monkeypatch.setattr(admins_repo, "set_admin_enabled", set_admin_enabled)
|
|
monkeypatch.setattr(admins_repo, "delete_admin", delete_admin)
|
|
return state
|
|
|
|
|
|
def _csrf(client: TestClient, csrf_token) -> str:
|
|
return csrf_token(client.get("/admins").text)
|
|
|
|
|
|
def test_admins_list(fake_admins: dict[str, Any], client_factory) -> None:
|
|
response = client_factory().get("/admins")
|
|
|
|
assert response.status_code == 200
|
|
assert "second" in response.text
|
|
assert "(вы)" in response.text
|
|
|
|
|
|
def test_add_admin(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post(
|
|
"/admins",
|
|
data={"telegram_id": "12345", "username": "@third", "role": "moderator", "csrf_token": token},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert "Администратор добавлен" in response.text
|
|
assert fake_admins["upsert"] == (12345, "third", "moderator")
|
|
|
|
|
|
def test_add_admin_rejects_bad_id(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins", data={"telegram_id": "abc", "role": "admin", "csrf_token": token})
|
|
|
|
assert response.status_code == 400
|
|
assert "Telegram-ID должен быть числом" in response.text
|
|
assert "upsert" not in fake_admins
|
|
|
|
|
|
def test_change_role(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins/2/role", data={"role": "admin", "csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "Роль обновлена" in response.text
|
|
assert fake_admins["role"] == (2, "admin")
|
|
|
|
|
|
def test_cannot_toggle_self(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins/1/toggle", data={"csrf_token": token})
|
|
|
|
assert response.status_code == 400
|
|
assert "самого себя" in response.text
|
|
assert "enabled" not in fake_admins
|
|
|
|
|
|
def test_toggle_admin(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins/2/toggle", data={"csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "Доступ обновлён" in response.text
|
|
assert fake_admins["enabled"] == (2, False)
|
|
|
|
|
|
def test_cannot_delete_self(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins/1/delete", data={"csrf_token": token})
|
|
|
|
assert response.status_code == 400
|
|
assert "удалить самого себя" in response.text
|
|
|
|
|
|
def test_cannot_delete_last_admin(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
fake_admins["admins"] = [ADMIN_2]
|
|
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
response = client.post("/admins/2/delete", data={"csrf_token": token})
|
|
|
|
assert response.status_code == 400
|
|
assert "последнего администратора" in response.text
|
|
assert "deleted" not in fake_admins
|
|
|
|
|
|
def test_delete_admin(fake_admins: dict[str, Any], client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = _csrf(client, csrf_token)
|
|
|
|
response = client.post("/admins/2/delete", data={"csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "Администратор удалён" in response.text
|
|
assert fake_admins["deleted"] == 2
|