250 lines
8.4 KiB
Python
250 lines
8.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 templates_repo
|
|
|
|
TEMPLATE: dict[str, Any] = {
|
|
"key": "info.help",
|
|
"text": "Справка по командам",
|
|
"description": "Справка (/помощь)",
|
|
"enabled": True,
|
|
"updated_at": datetime(2026, 1, 1, 12, 0, tzinfo=UTC),
|
|
"updated_by": None,
|
|
}
|
|
|
|
|
|
def _patch_template(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
async def fake_get(key: str) -> dict[str, Any] | None:
|
|
return TEMPLATE if key == TEMPLATE["key"] else None
|
|
|
|
async def fake_history(key: str, *, limit: int = 10) -> list[dict[str, Any]]:
|
|
return []
|
|
|
|
monkeypatch.setattr(templates_repo, "get_template", fake_get)
|
|
monkeypatch.setattr(templates_repo, "template_history", fake_history)
|
|
|
|
|
|
def test_healthz(client_factory) -> None:
|
|
response = client_factory(authenticated=False).get("/healthz")
|
|
|
|
assert response.status_code == 200
|
|
payload = response.json()
|
|
assert payload["status"] == "ok"
|
|
assert payload["app_env"] == "dev"
|
|
|
|
|
|
def test_index_renders_sections(client_factory) -> None:
|
|
response = client_factory().get("/")
|
|
|
|
assert response.status_code == 200
|
|
assert "Панель администрирования" in response.text
|
|
assert "Расписания" in response.text
|
|
|
|
|
|
def test_messages_list(client_factory, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
async def fake_list() -> list[dict[str, Any]]:
|
|
return [{**TEMPLATE, "text_length": len(TEMPLATE["text"])}]
|
|
|
|
monkeypatch.setattr(templates_repo, "list_templates", fake_list)
|
|
|
|
response = client_factory().get("/messages")
|
|
|
|
assert response.status_code == 200
|
|
assert "info.help" in response.text
|
|
assert "Справка (/помощь)" in response.text
|
|
|
|
|
|
def test_message_editor_and_history(client_factory, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_patch_template(monkeypatch)
|
|
|
|
response = client_factory().get("/messages/info.help")
|
|
|
|
assert response.status_code == 200
|
|
assert "Справка по командам" in response.text
|
|
assert "Доступные теги" in response.text
|
|
|
|
|
|
def test_message_editor_404(client_factory, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
async def fake_get(key: str) -> dict[str, Any] | None:
|
|
return None
|
|
|
|
monkeypatch.setattr(templates_repo, "get_template", fake_get)
|
|
|
|
assert client_factory().get("/messages/unknown").status_code == 404
|
|
|
|
|
|
def test_preview_renders_tags(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
_patch_template(monkeypatch)
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/info.help").text)
|
|
|
|
response = client.post("/messages/preview", data={"text": "Привет, {nick}!", "csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "ТестовыйНик" in response.text
|
|
assert "<iframe" in response.text
|
|
|
|
|
|
def test_preview_reports_unknown_tag(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
_patch_template(monkeypatch)
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/info.help").text)
|
|
|
|
response = client.post("/messages/preview", data={"text": "Привет, {unknown_tag}!", "csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "Неизвестные теги" in response.text
|
|
|
|
|
|
def test_preview_requires_csrf(client_factory, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
_patch_template(monkeypatch)
|
|
|
|
response = client_factory().post("/messages/preview", data={"text": "Привет"})
|
|
|
|
assert response.status_code == 400
|
|
|
|
|
|
def test_save_rejects_unknown_tag(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
_patch_template(monkeypatch)
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/info.help").text)
|
|
|
|
response = client.post(
|
|
"/messages/info.help",
|
|
data={"text": "текст {unknown_tag}", "csrf_token": token},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
assert "Неизвестные теги" in response.text
|
|
|
|
|
|
def test_save_updates_template(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
saved: dict[str, Any] = {}
|
|
|
|
async def fake_update(key: str, text: str, *, editor_id: int | None = None) -> dict[str, Any]:
|
|
saved.update(key=key, text=text, editor_id=editor_id)
|
|
return {"key": key, "text": text, "old_text": TEMPLATE["text"], "changed": True}
|
|
|
|
_patch_template(monkeypatch)
|
|
monkeypatch.setattr(templates_repo, "update_template", fake_update)
|
|
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/info.help").text)
|
|
response = client.post("/messages/info.help", data={"text": "Новая справка", "csrf_token": token})
|
|
|
|
assert response.status_code == 200
|
|
assert "Текст сохранён" in response.text
|
|
assert saved == {"key": "info.help", "text": "Новая справка", "editor_id": 1}
|
|
|
|
|
|
def test_new_template_form(client_factory) -> None:
|
|
response = client_factory().get("/messages/new")
|
|
|
|
assert response.status_code == 200
|
|
assert "Новый шаблон" in response.text
|
|
assert "shop.rules" in response.text
|
|
|
|
|
|
def test_create_template(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
created: dict[str, Any] = {}
|
|
|
|
async def fake_create(
|
|
key: str,
|
|
text: str,
|
|
description: str,
|
|
*,
|
|
enabled: bool = True,
|
|
editor_id: int | None = None,
|
|
) -> bool:
|
|
created.update(key=key, text=text, description=description, enabled=enabled, editor_id=editor_id)
|
|
return True
|
|
|
|
async def fake_get(key: str) -> dict[str, Any] | None:
|
|
return {
|
|
"key": key,
|
|
"text": "Правила магазина",
|
|
"description": "Правила",
|
|
"enabled": True,
|
|
"updated_at": datetime(2026, 1, 1, tzinfo=UTC),
|
|
"updated_by": 1,
|
|
}
|
|
|
|
async def fake_history(key: str, *, limit: int = 10) -> list[dict[str, Any]]:
|
|
return []
|
|
|
|
monkeypatch.setattr(templates_repo, "create_template", fake_create)
|
|
monkeypatch.setattr(templates_repo, "get_template", fake_get)
|
|
monkeypatch.setattr(templates_repo, "template_history", fake_history)
|
|
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/new").text)
|
|
response = client.post(
|
|
"/messages",
|
|
data={
|
|
"key": "shop.rules",
|
|
"description": "Правила",
|
|
"text": "Правила магазина",
|
|
"enabled": "on",
|
|
"csrf_token": token,
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert created == {
|
|
"key": "shop.rules",
|
|
"text": "Правила магазина",
|
|
"description": "Правила",
|
|
"enabled": True,
|
|
"editor_id": 1,
|
|
}
|
|
|
|
|
|
def test_create_template_duplicate(client_factory, monkeypatch: pytest.MonkeyPatch, csrf_token) -> None:
|
|
async def fake_create(*args: Any, **kwargs: Any) -> bool:
|
|
return False
|
|
|
|
monkeypatch.setattr(templates_repo, "create_template", fake_create)
|
|
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/new").text)
|
|
response = client.post(
|
|
"/messages",
|
|
data={"key": "shop.rules", "text": "Текст", "csrf_token": token},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
assert "уже существует" in response.text
|
|
|
|
|
|
def test_create_template_rejects_bad_key(client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/new").text)
|
|
|
|
response = client.post(
|
|
"/messages",
|
|
data={"key": "Плохой ключ", "text": "Текст", "csrf_token": token},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
assert "Ключ:" in response.text
|
|
|
|
|
|
def test_create_template_rejects_unknown_tag(client_factory, csrf_token) -> None:
|
|
client: TestClient = client_factory()
|
|
token = csrf_token(client.get("/messages/new").text)
|
|
|
|
response = client.post(
|
|
"/messages",
|
|
data={"key": "shop.rules", "text": "Текст {nope}", "csrf_token": token},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
assert "Неизвестные теги" in response.text
|