DispelEvilBot/tests/test_admin_schedules.py

197 lines
6.2 KiB
Python

"""Тесты раздела «Расписания» (без БД — репозитории подменяются)."""
from __future__ import annotations
from datetime import UTC, datetime
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 schedules_repo, templates_repo
TEMPLATE: dict[str, Any] = {
"key": "notify.blago",
"text": "Благо через 5 минут {usernames}",
"description": "Благо: начало",
"enabled": True,
"updated_at": datetime(2026, 1, 1, 12, 0, tzinfo=UTC),
"updated_by": None,
}
SCHEDULE: dict[str, Any] = {
"id": 1,
"template_key": "notify.blago",
"template_description": "Благо: начало",
"hour": 17,
"minute": 0,
"weekdays": [1, 5],
"timezone": "Europe/Moscow",
"enabled": True,
"description": "Благо",
"updated_at": datetime(2026, 1, 1, 12, 0, tzinfo=UTC),
"updated_by": None,
}
def _client() -> TestClient:
settings = AdminSettings(_env_file=None, bot_token="", db_password="test", app_env="dev")
return TestClient(create_app(settings, init_db=False))
@pytest.fixture
def fake_repos(monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]:
state: dict[str, Any] = {}
async def list_schedules() -> list[dict[str, Any]]:
return [SCHEDULE]
async def get_schedule(schedule_id: int) -> dict[str, Any] | None:
return SCHEDULE if schedule_id == 1 else None
async def schedule_history(schedule_id: int, *, limit: int = 10) -> list[dict[str, Any]]:
return []
async def create_schedule(data: dict[str, Any], *, editor_id: int | None = None) -> int:
state["created"] = data
return 2
async def update_schedule(schedule_id: int, data: dict[str, Any], *, editor_id: int | None = None) -> bool:
state["updated"] = (schedule_id, data)
return True
async def delete_schedule(schedule_id: int, *, editor_id: int | None = None) -> bool:
state["deleted"] = schedule_id
return schedule_id == 1
async def set_enabled(schedule_id: int, enabled: bool, *, editor_id: int | None = None) -> bool:
state["enabled"] = (schedule_id, enabled)
return True
async def list_templates() -> list[dict[str, Any]]:
return [TEMPLATE]
async def get_template(key: str) -> dict[str, Any] | None:
return TEMPLATE if key == TEMPLATE["key"] else None
monkeypatch.setattr(schedules_repo, "list_schedules", list_schedules)
monkeypatch.setattr(schedules_repo, "get_schedule", get_schedule)
monkeypatch.setattr(schedules_repo, "schedule_history", schedule_history)
monkeypatch.setattr(schedules_repo, "create_schedule", create_schedule)
monkeypatch.setattr(schedules_repo, "update_schedule", update_schedule)
monkeypatch.setattr(schedules_repo, "delete_schedule", delete_schedule)
monkeypatch.setattr(schedules_repo, "set_enabled", set_enabled)
monkeypatch.setattr(templates_repo, "list_templates", list_templates)
monkeypatch.setattr(templates_repo, "get_template", get_template)
return state
def test_schedules_list(fake_repos: dict[str, Any]) -> None:
response = _client().get("/schedules")
assert response.status_code == 200
assert "17:00" in response.text
assert "Пн, Пт" in response.text
assert "включено" in response.text
def test_new_schedule_form(fake_repos: dict[str, Any]) -> None:
response = _client().get("/schedules/new")
assert response.status_code == 200
assert "Новое расписание" in response.text
assert "notify.blago" in response.text
def test_create_schedule(fake_repos: dict[str, Any]) -> None:
response = _client().post(
"/schedules",
data={
"template_key": "notify.blago",
"hour": "17",
"minute": "0",
"weekdays": ["1", "5"],
"timezone": "Europe/Moscow",
"description": "Благо",
"enabled": "on",
},
)
assert response.status_code == 200
assert fake_repos["created"] == {
"template_key": "notify.blago",
"hour": 17,
"minute": 0,
"weekdays": [1, 5],
"timezone": "Europe/Moscow",
"enabled": True,
"description": "Благо",
}
def test_create_schedule_rejects_bad_hour(fake_repos: dict[str, Any]) -> None:
response = _client().post(
"/schedules",
data={"template_key": "notify.blago", "hour": "25", "minute": "0", "timezone": "Europe/Moscow"},
)
assert response.status_code == 400
assert "Час должен быть от 0 до 23" in response.text
assert "created" not in fake_repos
def test_create_schedule_rejects_unknown_template(fake_repos: dict[str, Any]) -> None:
response = _client().post(
"/schedules",
data={"template_key": "unknown.key", "hour": "17", "minute": "0", "timezone": "Europe/Moscow"},
)
assert response.status_code == 400
assert "Шаблон не найден" in response.text
def test_edit_schedule(fake_repos: dict[str, Any]) -> None:
response = _client().get("/schedules/1")
assert response.status_code == 200
assert "Расписание #1" in response.text
def test_edit_missing_schedule_404(fake_repos: dict[str, Any]) -> None:
assert _client().get("/schedules/99").status_code == 404
def test_update_schedule(fake_repos: dict[str, Any]) -> None:
response = _client().post(
"/schedules/1",
data={
"template_key": "notify.blago",
"hour": "18",
"minute": "30",
"weekdays": [],
"timezone": "Europe/Moscow",
"description": "",
"enabled": "on",
},
)
assert response.status_code == 200
schedule_id, data = fake_repos["updated"]
assert schedule_id == 1
assert data["hour"] == 18
assert data["minute"] == 30
def test_toggle_schedule(fake_repos: dict[str, Any]) -> None:
_client().post("/schedules/1/toggle")
assert fake_repos["enabled"] == (1, False)
def test_delete_schedule(fake_repos: dict[str, Any]) -> None:
_client().post("/schedules/1/delete")
assert fake_repos["deleted"] == 1