"""Тесты раздела «Админы»: добавление, роли, включение и удаление.""" 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