Files
PrimoExampleBot/tests/database/test_user_stats.py
2025-08-30 07:39:44 +07:00

194 lines
8.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime, timedelta, timezone
import pytest
from sqlalchemy import select, Sequence
from sqlalchemy.ext.asyncio import AsyncSession
from database import User, UserMessage, BotDatabase
@pytest.mark.asyncio
class TestUserStatistics:
"""Тесты для статистики пользователей с полной строгой типизацией"""
async def test_add_user(self, test_db: BotDatabase, test_session: AsyncSession) -> None:
"""
Тест добавления пользователя.
Проверяет, что пользователь создаётся с правильными данными и статусом 'active'.
"""
user_id: int = 111222333
await test_db.add_user(
user_id=user_id,
username="new_user",
full_name="New User"
)
user: User | None = await test_session.get(User, user_id)
assert user is not None
assert user.username == "new_user"
assert user.status.value == "active"
async def test_add_message_creates_user(
self, test_db: BotDatabase, test_session: AsyncSession
) -> None:
"""
Тест, что добавление сообщения создаёт пользователя, если его нет.
Проверяет, что пользователь и сообщение корректно создаются.
"""
user_id: int = 111222333
await test_db.add_message(
user_id=user_id,
message_text="Тестовое сообщение"
)
user: User | None = await test_session.get(User, user_id)
assert user is not None
assert user.status.value == "active"
stmt = select(UserMessage).where(UserMessage.user_id == user_id)
result = await test_session.execute(stmt)
messages: Sequence[UserMessage] = result.scalars().all()
assert len(messages) == 1
assert messages[0].message_text == "Тестовое сообщение"
async def test_message_stats_calculation(
self, test_db: BotDatabase, test_user_with_messages: int
) -> None:
"""
Тест расчёта статистики сообщений пользователя.
Проверяет корректность статистики по дням, неделям, месяцам и общему количеству сообщений.
"""
user_id: int = test_user_with_messages
# Получаем статистику
day: int
week: int
month: int
total: int
day, week, month, total = await test_db.get_message_stats(user_id)
assert total >= 10, f"Ожидается минимум 10 сообщений, получено {total}"
assert day >= 0
assert week >= 0
assert month >= 0
assert total >= 0
assert day <= week <= month <= total
async def test_message_stats_with_dates(
self, test_db: BotDatabase, test_user: int
) -> None:
"""
Тест статистики с конкретными известными датами сообщений.
Проверяет подсчёт сообщений за день, неделю, месяц и общее количество.
"""
user_id: int = test_user
now: datetime = datetime.now(timezone.utc)
# Очищаем старые сообщения
async with test_db.session_factory() as session:
stmt = select(UserMessage).where(UserMessage.user_id == user_id)
result = await session.execute(stmt)
old_messages: Sequence[UserMessage] = result.scalars().all()
for msg in old_messages:
await session.delete(msg)
await session.commit()
# Создаём сообщения с фиксированными датами
test_messages: list[tuple[datetime, str]] = [
(now - timedelta(days=45), "45 дней назад"),
(now - timedelta(days=30), "30 дней назад"),
(now - timedelta(days=15), "15 дней назад"),
(now - timedelta(days=7), "7 дней назад"),
(now - timedelta(days=3), "3 дня назад"),
(now - timedelta(hours=6), "6 часов назад"),
(now, "сейчас")
]
for date, text in test_messages:
await test_db.add_message(user_id, text, date)
day: int
week: int
month: int
total: int
day, week, month, total = await test_db.get_message_stats(user_id)
assert total == 7, f"Ожидалось 7 сообщений, получено {total}"
day_start: datetime = now.replace(hour=0, minute=0, second=0, microsecond=0)
expected_day: int = sum(1 for date, _ in test_messages if date >= day_start)
assert day == expected_day, f"За день: ожидалось {expected_day}, получено {day}"
monday: datetime = (now - timedelta(days=now.weekday())).replace(hour=0, minute=0, second=0, microsecond=0)
expected_week: int = sum(1 for date, _ in test_messages if date >= monday)
assert week == expected_week, f"За неделю: ожидалось {expected_week}, получено {week}"
month_start: datetime = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
expected_month: int = sum(1 for date, _ in test_messages if date >= month_start)
assert month == expected_month, f"За месяц: ожидалось {expected_month}, получено {month}"
async def test_empty_user_stats(self, test_db: BotDatabase) -> None:
"""
Тест статистики для пользователя без сообщений.
Все значения должны быть равны нулю.
"""
user_id: int = 0o00111222
await test_db.add_user(user_id, "empty_user", "Empty User")
day: int
week: int
month: int
total: int
day, week, month, total = await test_db.get_message_stats(user_id)
assert day == 0
assert week == 0
assert month == 0
assert total == 0
async def test_user_management(self, test_db: BotDatabase) -> None:
"""
Тест управления пользователями.
Проверяет добавление, назначение админа, бан/разбан и возврат статуса пользователя.
"""
user_id: int = 555666777
# Добавление пользователя
await test_db.add_user(user_id, "managed_user", "Managed User")
async with test_db.session_factory() as session:
user: User | None = await session.get(User, user_id)
assert user is not None
assert user.status.value == "active"
# Назначение админом
await test_db.set_admin(user_id, True)
async with test_db.session_factory() as session:
user = await session.get(User, user_id)
assert user is not None
assert user.status.value == "admin"
# Бан пользователя
await test_db.ban_user(user_id)
async with test_db.session_factory() as session:
user = await session.get(User, user_id)
assert user is not None
assert user.status.value == "banned"
# Разбан
await test_db.unban_user(user_id)
async with test_db.session_factory() as session:
user = await session.get(User, user_id)
assert user is not None
assert user.status.value == "active"
# Снятие админки
await test_db.set_admin(user_id, False)
async with test_db.session_factory() as session:
user = await session.get(User, user_id)
assert user is not None
assert user.status.value == "active"