Stabilize staff concurrency and premium emoji; enable verified Drone deployment
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
120
tests/test_access_runtime.py
Normal file
120
tests/test_access_runtime.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import asyncio
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from aiogram import Bot, Dispatcher, Router, F
|
||||
from aiogram.fsm.storage.memory import MemoryStorage, SimpleEventIsolation
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from aiogram.types import Message, Chat, User as TelegramUser, CallbackQuery, Update
|
||||
from sqlalchemy import select
|
||||
|
||||
from src.core.access import is_admin, is_staff
|
||||
from src.core.database import async_session_maker
|
||||
from src.core.models import User, Winner
|
||||
from src.core.services import UserService
|
||||
from src.middlewares.access import AccessMiddleware
|
||||
from src.controllers.bot_controller import BotController
|
||||
|
||||
|
||||
async def test_assigned_admin_and_cashier_permissions_update_immediately():
|
||||
async with async_session_maker() as session:
|
||||
admin = await UserService.get_or_create_user(session, 301)
|
||||
admin.is_admin = True
|
||||
cashier = await UserService.get_or_create_user(session, 302)
|
||||
cashier.is_cashier = True
|
||||
await session.commit()
|
||||
assert await is_admin(301)
|
||||
assert await is_staff(302) and not await is_admin(302)
|
||||
admin.is_admin = False
|
||||
await session.commit()
|
||||
assert not await is_admin(301)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role,allow_cashier,permitted", [("user", False, False), ("user", True, False),
|
||||
("cashier", False, False), ("cashier", True, True), ("admin", False, True)])
|
||||
async def test_every_staff_fsm_step_checks_permissions(role, allow_cashier, permitted):
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_or_create_user(session, 303)
|
||||
user.is_admin = role == "admin"
|
||||
user.is_cashier = role == "cashier"
|
||||
await session.commit()
|
||||
handler = AsyncMock()
|
||||
event = SimpleNamespace(from_user=SimpleNamespace(id=303), answer=AsyncMock())
|
||||
await AccessMiddleware(allow_cashier=allow_cashier)(handler, event, {})
|
||||
assert handler.await_count == int(permitted)
|
||||
|
||||
|
||||
async def test_callback_main_menu_uses_clicker_identity():
|
||||
from main import back_to_main_handler
|
||||
import main
|
||||
from contextlib import asynccontextmanager
|
||||
controller = SimpleNamespace(handle_start=AsyncMock())
|
||||
@asynccontextmanager
|
||||
async def get_controller():
|
||||
yield controller
|
||||
old = main.get_controller
|
||||
main.get_controller = get_controller
|
||||
try:
|
||||
actor = TelegramUser(id=304, is_bot=False, first_name="Human")
|
||||
event = SimpleNamespace(from_user=actor, message=object(), answer=AsyncMock())
|
||||
state = SimpleNamespace(clear=AsyncMock())
|
||||
await back_to_main_handler(event, state)
|
||||
controller.handle_start.assert_awaited_once_with(event.message, actor=actor)
|
||||
state.clear.assert_awaited_once()
|
||||
finally:
|
||||
main.get_controller = old
|
||||
|
||||
|
||||
async def test_slow_user_does_not_block_other_user_and_same_user_is_serialized():
|
||||
storage = MemoryStorage()
|
||||
dispatcher = Dispatcher(storage=storage, events_isolation=SimpleEventIsolation())
|
||||
router = Router()
|
||||
bot = Bot("123456:TEST_TOKEN_FOR_ISOLATED_TESTS")
|
||||
first_started, release_first, other_finished = asyncio.Event(), asyncio.Event(), asyncio.Event()
|
||||
seen = []
|
||||
@router.message(F.text)
|
||||
async def receive(message: Message, state: FSMContext):
|
||||
if message.text == "slow":
|
||||
first_started.set()
|
||||
await release_first.wait()
|
||||
data = await state.get_data()
|
||||
seen.append((message.from_user.id, data.get("count", 0)))
|
||||
await state.update_data(count=data.get("count", 0) + 1)
|
||||
if message.from_user.id == 402:
|
||||
other_finished.set()
|
||||
dispatcher.include_router(router)
|
||||
def update(number, user_id, text):
|
||||
return Update(update_id=number, message=Message(message_id=number, date=datetime.now(timezone.utc),
|
||||
chat=Chat(id=user_id, type="private"), from_user=TelegramUser(id=user_id, is_bot=False, first_name="Actor"), text=text))
|
||||
slow = asyncio.create_task(dispatcher.feed_update(bot, update(1, 401, "slow")))
|
||||
await asyncio.wait_for(first_started.wait(), 1)
|
||||
same = asyncio.create_task(dispatcher.feed_update(bot, update(2, 401, "next")))
|
||||
other = asyncio.create_task(dispatcher.feed_update(bot, update(3, 402, "fast")))
|
||||
try:
|
||||
await asyncio.wait_for(other_finished.wait(), 1)
|
||||
assert not same.done()
|
||||
finally:
|
||||
release_first.set()
|
||||
await asyncio.gather(slow, same, other)
|
||||
await bot.session.close()
|
||||
await dispatcher.fsm.close()
|
||||
assert seen == [(402, 0), (401, 0), (401, 1)]
|
||||
|
||||
|
||||
async def test_non_owner_cannot_confirm_user_only_prize():
|
||||
from src.handlers.redraw_handlers import confirm_winner_callback
|
||||
from src.core.services import LotteryService
|
||||
async with async_session_maker() as session:
|
||||
owner = await UserService.get_or_create_user(session, 601)
|
||||
draw = await LotteryService.create_lottery(session, "Draw", "", ["Prize"], owner.id)
|
||||
winner = Winner(lottery_id=draw.id, user_id=owner.id, place=1)
|
||||
session.add(winner)
|
||||
await session.commit()
|
||||
winner_id = winner.id
|
||||
callback = SimpleNamespace(data=f"confirm_win_{winner_id}", from_user=SimpleNamespace(id=602), answer=AsyncMock())
|
||||
await confirm_winner_callback(callback)
|
||||
async with async_session_maker() as session:
|
||||
assert not (await session.get(Winner, winner_id)).is_claimed
|
||||
callback.answer.assert_awaited_once()
|
||||
Reference in New Issue
Block a user