197 lines
6.2 KiB
Python
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
|