mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-12 02:03:04 +00:00
Backport and improvements (#601)
* Backport RedisStorage, deep-linking * Allow prereleases for aioredis * Bump dependencies * Correctly skip Redis tests on Windows * Reformat tests code and bump Makefile
This commit is contained in:
parent
32bc05130f
commit
83d6ab48c5
43 changed files with 1004 additions and 327 deletions
|
|
@ -1,9 +1,64 @@
|
|||
import pytest
|
||||
from _pytest.config import UsageError
|
||||
from aioredis.connection import parse_url as parse_redis_url
|
||||
|
||||
from aiogram import Bot
|
||||
from aiogram.dispatcher.fsm.storage.memory import MemoryStorage
|
||||
from aiogram.dispatcher.fsm.storage.redis import RedisStorage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--redis", default=None, help="run tests which require redis connection")
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line("markers", "redis: marked tests require redis connection to run")
|
||||
|
||||
|
||||
def pytest_collection_modifyitems(config, items):
|
||||
redis_uri = config.getoption("--redis")
|
||||
if redis_uri is None:
|
||||
skip_redis = pytest.mark.skip(reason="need --redis option with redis URI to run")
|
||||
for item in items:
|
||||
if "redis" in item.keywords:
|
||||
item.add_marker(skip_redis)
|
||||
return
|
||||
try:
|
||||
parse_redis_url(redis_uri)
|
||||
except ValueError as e:
|
||||
raise UsageError(f"Invalid redis URI {redis_uri!r}: {e}")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def redis_server(request):
|
||||
redis_uri = request.config.getoption("--redis")
|
||||
return redis_uri
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@pytest.mark.redis
|
||||
async def redis_storage(redis_server):
|
||||
if not redis_server:
|
||||
pytest.skip("Redis is not available here")
|
||||
storage = RedisStorage.from_url(redis_server)
|
||||
try:
|
||||
yield storage
|
||||
finally:
|
||||
conn = await storage.redis
|
||||
await conn.flushdb()
|
||||
await storage.close()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
async def memory_storage():
|
||||
storage = MemoryStorage()
|
||||
try:
|
||||
yield storage
|
||||
finally:
|
||||
await storage.close()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def bot():
|
||||
bot = MockedBot()
|
||||
|
|
|
|||
7
tests/docker-compose.yml
Normal file
7
tests/docker-compose.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
version: "3.9"
|
||||
|
||||
services:
|
||||
redis:
|
||||
image: redis:6-alpine
|
||||
ports:
|
||||
- "${REDIS_PORT-6379}:6379"
|
||||
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
|
||||
from aiogram.client.session.base import BaseSession, TelegramType
|
||||
from aiogram.client.telegram import PRODUCTION, TelegramAPIServer
|
||||
from aiogram.methods import DeleteMessage, GetMe, Response, TelegramMethod
|
||||
from aiogram.methods import DeleteMessage, GetMe, TelegramMethod
|
||||
from aiogram.types import UNSET
|
||||
|
||||
try:
|
||||
|
|
@ -20,7 +20,9 @@ class CustomSession(BaseSession):
|
|||
async def close(self):
|
||||
pass
|
||||
|
||||
async def make_request(self, token: str, method: TelegramMethod[TelegramType], timeout: Optional[int] = UNSET) -> None: # type: ignore
|
||||
async def make_request(
|
||||
self, token: str, method: TelegramMethod[TelegramType], timeout: Optional[int] = UNSET
|
||||
) -> None: # type: ignore
|
||||
assert isinstance(token, str)
|
||||
assert isinstance(method, TelegramMethod)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Union
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import EditMessageMedia, Request
|
||||
from aiogram.types import BufferedInputFile, InputMedia, InputMediaPhoto, Message
|
||||
from aiogram.types import BufferedInputFile, InputMediaPhoto, Message
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import datetime
|
|||
from typing import Optional
|
||||
|
||||
import pytest
|
||||
from aiogram.types import Chat, Message
|
||||
|
||||
from aiogram.types import Chat, Message
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import datetime
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import Request, SendAudio
|
||||
from aiogram.types import Audio, Chat, File, Message
|
||||
from aiogram.types import Audio, Chat, Message
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import Request, SetChatAdministratorCustomTitle, SetChatTitle
|
||||
from aiogram.methods import Request, SetChatAdministratorCustomTitle
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.methods import Request, SetChatPhoto
|
||||
from aiogram.types import BufferedInputFile, InputFile
|
||||
from aiogram.types import BufferedInputFile
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ import pytest
|
|||
from aiogram import Bot
|
||||
from aiogram.dispatcher.dispatcher import Dispatcher
|
||||
from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler
|
||||
from aiogram.dispatcher.fsm.strategy import FSMStrategy
|
||||
from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware
|
||||
from aiogram.dispatcher.router import Router
|
||||
from aiogram.methods import GetMe, GetUpdates, SendMessage
|
||||
from aiogram.types import (
|
||||
|
|
@ -423,7 +421,7 @@ class TestDispatcher:
|
|||
assert User.get_current(False)
|
||||
return kwargs
|
||||
|
||||
result = await router.update.trigger(update, test="PASS")
|
||||
result = await router.update.trigger(update, test="PASS", bot=None)
|
||||
assert isinstance(result, dict)
|
||||
assert result["event_update"] == update
|
||||
assert result["event_router"] == router
|
||||
|
|
@ -526,8 +524,9 @@ class TestDispatcher:
|
|||
assert len(log_records) == 1
|
||||
assert "Cause exception while process update" in log_records[0]
|
||||
|
||||
@pytest.mark.parametrize("as_task", [True, False])
|
||||
@pytest.mark.asyncio
|
||||
async def test_polling(self, bot: MockedBot):
|
||||
async def test_polling(self, bot: MockedBot, as_task: bool):
|
||||
dispatcher = Dispatcher()
|
||||
|
||||
async def _mock_updates(*_):
|
||||
|
|
@ -539,8 +538,11 @@ class TestDispatcher:
|
|||
"aiogram.dispatcher.dispatcher.Dispatcher._listen_updates"
|
||||
) as patched_listen_updates:
|
||||
patched_listen_updates.return_value = _mock_updates()
|
||||
await dispatcher._polling(bot=bot)
|
||||
mocked_process_update.assert_awaited()
|
||||
await dispatcher._polling(bot=bot, handle_as_tasks=as_task)
|
||||
if as_task:
|
||||
pass
|
||||
else:
|
||||
mocked_process_update.assert_awaited()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_exception_handler_catch_exceptions(self):
|
||||
|
|
@ -548,9 +550,12 @@ class TestDispatcher:
|
|||
router = Router()
|
||||
dp.include_router(router)
|
||||
|
||||
class CustomException(Exception):
|
||||
pass
|
||||
|
||||
@router.message()
|
||||
async def message_handler(message: Message):
|
||||
raise Exception("KABOOM")
|
||||
raise CustomException("KABOOM")
|
||||
|
||||
update = Update(
|
||||
update_id=42,
|
||||
|
|
@ -562,23 +567,23 @@ class TestDispatcher:
|
|||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
)
|
||||
with pytest.raises(Exception, match="KABOOM"):
|
||||
await dp.update.trigger(update)
|
||||
with pytest.raises(CustomException, match="KABOOM"):
|
||||
await dp.update.trigger(update, bot=None)
|
||||
|
||||
@router.errors()
|
||||
async def error_handler(event: Update, exception: Exception):
|
||||
return "KABOOM"
|
||||
|
||||
response = await dp.update.trigger(update)
|
||||
response = await dp.update.trigger(update, bot=None)
|
||||
assert response == "KABOOM"
|
||||
|
||||
@dp.errors()
|
||||
async def root_error_handler(event: Update, exception: Exception):
|
||||
return exception
|
||||
|
||||
response = await dp.update.trigger(update)
|
||||
response = await dp.update.trigger(update, bot=None)
|
||||
|
||||
assert isinstance(response, Exception)
|
||||
assert isinstance(response, CustomException)
|
||||
assert str(response) == "KABOOM"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
@ -654,20 +659,3 @@ class TestDispatcher:
|
|||
|
||||
log_records = [rec.message for rec in caplog.records]
|
||||
assert "Cause exception while process update" in log_records[0]
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"strategy,case,expected",
|
||||
[
|
||||
[FSMStrategy.USER_IN_CHAT, (-42, 42), (-42, 42)],
|
||||
[FSMStrategy.CHAT, (-42, 42), (-42, -42)],
|
||||
[FSMStrategy.GLOBAL_USER, (-42, 42), (42, 42)],
|
||||
[FSMStrategy.USER_IN_CHAT, (42, 42), (42, 42)],
|
||||
[FSMStrategy.CHAT, (42, 42), (42, 42)],
|
||||
[FSMStrategy.GLOBAL_USER, (42, 42), (42, 42)],
|
||||
],
|
||||
)
|
||||
def test_get_current_state_context(self, strategy, case, expected):
|
||||
dp = Dispatcher(fsm_strategy=strategy)
|
||||
chat_id, user_id = case
|
||||
state = dp.current_state(chat_id=chat_id, user_id=user_id)
|
||||
assert (state.chat_id, state.user_id) == expected
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import pytest
|
|||
|
||||
from aiogram import F
|
||||
from aiogram.dispatcher.event.handler import CallableMixin, FilterObject, HandlerObject
|
||||
from aiogram.dispatcher.filters import Text
|
||||
from aiogram.dispatcher.filters.base import BaseFilter
|
||||
from aiogram.dispatcher.handler.base import BaseHandler
|
||||
from aiogram.types import Update
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import datetime
|
||||
import re
|
||||
from typing import Match
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram import F
|
||||
from aiogram.dispatcher.filters import Command, CommandObject
|
||||
from aiogram.dispatcher.filters.command import CommandStart
|
||||
from aiogram.methods import GetMe
|
||||
from aiogram.types import Chat, Message, User
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
|
@ -18,45 +19,54 @@ class TestCommandFilter:
|
|||
assert cmd.commands[0] == "start"
|
||||
assert cmd == Command(commands=["start"])
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text,command,result",
|
||||
[
|
||||
["/test@tbot", Command(commands=["test"], commands_prefix="/"), True],
|
||||
["!test", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["/test@mention", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["/tests", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["/", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["/ test", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["", Command(commands=["test"], commands_prefix="/"), False],
|
||||
[" ", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["test", Command(commands=["test"], commands_prefix="/"), False],
|
||||
[" test", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["a", Command(commands=["test"], commands_prefix="/"), False],
|
||||
["/test@tbot some args", Command(commands=["test"]), True],
|
||||
["/test42@tbot some args", Command(commands=[re.compile(r"test(\d+)")]), True],
|
||||
[
|
||||
"/test42@tbot some args",
|
||||
Command(commands=[re.compile(r"test(\d+)")], command_magic=F.args == "some args"),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"/test42@tbot some args",
|
||||
Command(commands=[re.compile(r"test(\d+)")], command_magic=F.args == "test"),
|
||||
False,
|
||||
],
|
||||
["/start test", CommandStart(), True],
|
||||
["/start", CommandStart(deep_link=True), False],
|
||||
["/start test", CommandStart(deep_link=True), True],
|
||||
["/start test", CommandStart(deep_link=True, deep_link_encoded=True), False],
|
||||
["/start dGVzdA", CommandStart(deep_link=True, deep_link_encoded=True), True],
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_parse_command(self, bot: MockedBot):
|
||||
# TODO: parametrize
|
||||
async def test_parse_command(self, bot: MockedBot, text: str, result: bool, command: Command):
|
||||
# TODO: test ignore case
|
||||
# TODO: test ignore mention
|
||||
|
||||
bot.add_result_for(
|
||||
GetMe, ok=True, result=User(id=42, is_bot=True, first_name="The bot", username="tbot")
|
||||
)
|
||||
command = Command(commands=["test", re.compile(r"test(\d+)")], commands_prefix="/")
|
||||
|
||||
assert await command.parse_command("/test@tbot", bot)
|
||||
assert not await command.parse_command("!test", bot)
|
||||
assert not await command.parse_command("/test@mention", bot)
|
||||
assert not await command.parse_command("/tests", bot)
|
||||
assert not await command.parse_command("/", bot)
|
||||
assert not await command.parse_command("/ test", bot)
|
||||
assert not await command.parse_command("", bot)
|
||||
assert not await command.parse_command(" ", bot)
|
||||
assert not await command.parse_command("test", bot)
|
||||
assert not await command.parse_command(" test", bot)
|
||||
assert not await command.parse_command("a", bot)
|
||||
message = Message(
|
||||
message_id=0, text=text, chat=Chat(id=42, type="private"), date=datetime.datetime.now()
|
||||
)
|
||||
|
||||
result = await command.parse_command("/test@tbot some args", bot)
|
||||
assert isinstance(result, dict)
|
||||
assert "command" in result
|
||||
assert isinstance(result["command"], CommandObject)
|
||||
assert result["command"].command == "test"
|
||||
assert result["command"].mention == "tbot"
|
||||
assert result["command"].args == "some args"
|
||||
|
||||
result = await command.parse_command("/test42@tbot some args", bot)
|
||||
assert isinstance(result, dict)
|
||||
assert "command" in result
|
||||
assert isinstance(result["command"], CommandObject)
|
||||
assert result["command"].command == "test42"
|
||||
assert result["command"].mention == "tbot"
|
||||
assert result["command"].args == "some args"
|
||||
assert isinstance(result["command"].match, Match)
|
||||
response = await command(message, bot)
|
||||
assert bool(response) is result
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.fsm.storage.memory import MemoryStorage, MemoryStorageRecord
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def storage():
|
||||
return MemoryStorage()
|
||||
|
||||
|
||||
class TestMemoryStorage:
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_state(self, storage: MemoryStorage):
|
||||
assert await storage.get_state(chat_id=-42, user_id=42) is None
|
||||
|
||||
await storage.set_state(chat_id=-42, user_id=42, state="state")
|
||||
assert await storage.get_state(chat_id=-42, user_id=42) == "state"
|
||||
|
||||
assert -42 in storage.storage
|
||||
assert 42 in storage.storage[-42]
|
||||
assert isinstance(storage.storage[-42][42], MemoryStorageRecord)
|
||||
assert storage.storage[-42][42].state == "state"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_data(self, storage: MemoryStorage):
|
||||
assert await storage.get_data(chat_id=-42, user_id=42) == {}
|
||||
|
||||
await storage.set_data(chat_id=-42, user_id=42, data={"foo": "bar"})
|
||||
assert await storage.get_data(chat_id=-42, user_id=42) == {"foo": "bar"}
|
||||
|
||||
assert -42 in storage.storage
|
||||
assert 42 in storage.storage[-42]
|
||||
assert isinstance(storage.storage[-42][42], MemoryStorageRecord)
|
||||
assert storage.storage[-42][42].data == {"foo": "bar"}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data(self, storage: MemoryStorage):
|
||||
assert await storage.get_data(chat_id=-42, user_id=42) == {}
|
||||
assert await storage.update_data(chat_id=-42, user_id=42, data={"foo": "bar"}) == {
|
||||
"foo": "bar"
|
||||
}
|
||||
assert await storage.update_data(chat_id=-42, user_id=42, data={"baz": "spam"}) == {
|
||||
"foo": "bar",
|
||||
"baz": "spam",
|
||||
}
|
||||
21
tests/test_dispatcher/test_fsm/storage/test_redis.py
Normal file
21
tests/test_dispatcher/test_fsm/storage/test_redis.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.fsm.storage.redis import RedisStorage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
@pytest.mark.redis
|
||||
class TestRedisStorage:
|
||||
@pytest.mark.parametrize(
|
||||
"prefix_bot,result",
|
||||
[
|
||||
[False, "fsm:-1:2"],
|
||||
[True, "fsm:42:-1:2"],
|
||||
[{42: "kaboom"}, "fsm:kaboom:-1:2"],
|
||||
[lambda bot: "kaboom", "fsm:kaboom:-1:2"],
|
||||
],
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_key(self, bot: MockedBot, redis_server, prefix_bot, result):
|
||||
storage = RedisStorage.from_url(redis_server, prefix_bot=prefix_bot)
|
||||
assert storage.generate_key(bot, -1, 2) == result
|
||||
44
tests/test_dispatcher/test_fsm/storage/test_storages.py
Normal file
44
tests/test_dispatcher/test_fsm/storage/test_storages.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.fsm.storage.base import BaseStorage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"storage",
|
||||
[pytest.lazy_fixture("redis_storage"), pytest.lazy_fixture("memory_storage")],
|
||||
)
|
||||
class TestStorages:
|
||||
@pytest.mark.asyncio
|
||||
async def test_lock(self, bot: MockedBot, storage: BaseStorage):
|
||||
# TODO: ?!?
|
||||
async with storage.lock(bot=bot, chat_id=-42, user_id=42):
|
||||
assert True, "You are kidding me?"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_state(self, bot: MockedBot, storage: BaseStorage):
|
||||
assert await storage.get_state(bot=bot, chat_id=-42, user_id=42) is None
|
||||
|
||||
await storage.set_state(bot=bot, chat_id=-42, user_id=42, state="state")
|
||||
assert await storage.get_state(bot=bot, chat_id=-42, user_id=42) == "state"
|
||||
await storage.set_state(bot=bot, chat_id=-42, user_id=42, state=None)
|
||||
assert await storage.get_state(bot=bot, chat_id=-42, user_id=42) is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_data(self, bot: MockedBot, storage: BaseStorage):
|
||||
assert await storage.get_data(bot=bot, chat_id=-42, user_id=42) == {}
|
||||
|
||||
await storage.set_data(bot=bot, chat_id=-42, user_id=42, data={"foo": "bar"})
|
||||
assert await storage.get_data(bot=bot, chat_id=-42, user_id=42) == {"foo": "bar"}
|
||||
await storage.set_data(bot=bot, chat_id=-42, user_id=42, data={})
|
||||
assert await storage.get_data(bot=bot, chat_id=-42, user_id=42) == {}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_update_data(self, bot: MockedBot, storage: BaseStorage):
|
||||
assert await storage.get_data(bot=bot, chat_id=-42, user_id=42) == {}
|
||||
assert await storage.update_data(
|
||||
bot=bot, chat_id=-42, user_id=42, data={"foo": "bar"}
|
||||
) == {"foo": "bar"}
|
||||
assert await storage.update_data(
|
||||
bot=bot, chat_id=-42, user_id=42, data={"baz": "spam"}
|
||||
) == {"foo": "bar", "baz": "spam"}
|
||||
|
|
@ -2,27 +2,28 @@ import pytest
|
|||
|
||||
from aiogram.dispatcher.fsm.context import FSMContext
|
||||
from aiogram.dispatcher.fsm.storage.memory import MemoryStorage
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def state():
|
||||
def state(bot: MockedBot):
|
||||
storage = MemoryStorage()
|
||||
ctx = storage.storage[-42][42]
|
||||
ctx = storage.storage[bot][-42][42]
|
||||
ctx.state = "test"
|
||||
ctx.data = {"foo": "bar"}
|
||||
return FSMContext(storage=storage, user_id=-42, chat_id=42)
|
||||
return FSMContext(bot=bot, storage=storage, user_id=-42, chat_id=42)
|
||||
|
||||
|
||||
class TestFSMContext:
|
||||
@pytest.mark.asyncio
|
||||
async def test_address_mapping(self):
|
||||
async def test_address_mapping(self, bot: MockedBot):
|
||||
storage = MemoryStorage()
|
||||
ctx = storage.storage[-42][42]
|
||||
ctx = storage.storage[bot][-42][42]
|
||||
ctx.state = "test"
|
||||
ctx.data = {"foo": "bar"}
|
||||
state = FSMContext(storage=storage, chat_id=-42, user_id=42)
|
||||
state2 = FSMContext(storage=storage, chat_id=42, user_id=42)
|
||||
state3 = FSMContext(storage=storage, chat_id=69, user_id=69)
|
||||
state = FSMContext(bot=bot, storage=storage, chat_id=-42, user_id=42)
|
||||
state2 = FSMContext(bot=bot, storage=storage, chat_id=42, user_id=42)
|
||||
state3 = FSMContext(bot=bot, storage=storage, chat_id=69, user_id=69)
|
||||
|
||||
assert await state.get_state() == "test"
|
||||
assert await state2.get_state() is None
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.handler import ChosenInlineResultHandler
|
||||
from aiogram.types import CallbackQuery, ChosenInlineResult, User
|
||||
from aiogram.types import ChosenInlineResult, User
|
||||
|
||||
|
||||
class TestChosenInlineResultHandler:
|
||||
|
|
|
|||
|
|
@ -2,16 +2,7 @@ from typing import Any
|
|||
|
||||
import pytest
|
||||
|
||||
from aiogram.dispatcher.handler import ErrorHandler, PollHandler
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
InlineQuery,
|
||||
Poll,
|
||||
PollOption,
|
||||
ShippingAddress,
|
||||
ShippingQuery,
|
||||
User,
|
||||
)
|
||||
from aiogram.dispatcher.handler import ErrorHandler
|
||||
|
||||
|
||||
class TestErrorHandler:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.handler import InlineQueryHandler
|
||||
from aiogram.types import CallbackQuery, InlineQuery, User
|
||||
from aiogram.types import InlineQuery, User
|
||||
|
||||
|
||||
class TestCallbackQueryHandler:
|
||||
|
|
|
|||
|
|
@ -3,15 +3,7 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.handler import PollHandler
|
||||
from aiogram.types import (
|
||||
CallbackQuery,
|
||||
InlineQuery,
|
||||
Poll,
|
||||
PollOption,
|
||||
ShippingAddress,
|
||||
ShippingQuery,
|
||||
User,
|
||||
)
|
||||
from aiogram.types import Poll, PollOption
|
||||
|
||||
|
||||
class TestShippingQueryHandler:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any
|
|||
import pytest
|
||||
|
||||
from aiogram.dispatcher.handler import ShippingQueryHandler
|
||||
from aiogram.types import CallbackQuery, InlineQuery, ShippingAddress, ShippingQuery, User
|
||||
from aiogram.types import ShippingAddress, ShippingQuery, User
|
||||
|
||||
|
||||
class TestShippingQueryHandler:
|
||||
|
|
|
|||
27
tests/test_utils/test_auth_widget.py
Normal file
27
tests/test_utils/test_auth_widget.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.utils.auth_widget import check_integrity
|
||||
|
||||
TOKEN = "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def data():
|
||||
return {
|
||||
"id": "42",
|
||||
"first_name": "John",
|
||||
"last_name": "Smith",
|
||||
"username": "username",
|
||||
"photo_url": "https://t.me/i/userpic/320/picname.jpg",
|
||||
"auth_date": "1565810688",
|
||||
"hash": "c303db2b5a06fe41d23a9b14f7c545cfc11dcc7473c07c9c5034ae60062461ce",
|
||||
}
|
||||
|
||||
|
||||
class TestCheckIntegrity:
|
||||
def test_ok(self, data):
|
||||
assert check_integrity(TOKEN, data) is True
|
||||
|
||||
def test_fail(self, data):
|
||||
data.pop("username")
|
||||
assert check_integrity(TOKEN, data) is False
|
||||
94
tests/test_utils/test_deep_linking.py
Normal file
94
tests/test_utils/test_deep_linking.py
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import pytest
|
||||
from async_lru import alru_cache
|
||||
|
||||
from aiogram.utils.deep_linking import (
|
||||
create_start_link,
|
||||
create_startgroup_link,
|
||||
decode_payload,
|
||||
encode_payload,
|
||||
)
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
PAYLOADS = [
|
||||
"foo",
|
||||
"AAbbCCddEEff1122334455",
|
||||
"aaBBccDDeeFF5544332211",
|
||||
-12345678901234567890,
|
||||
12345678901234567890,
|
||||
]
|
||||
WRONG_PAYLOADS = [
|
||||
"@BotFather",
|
||||
"Some:special$characters#=",
|
||||
"spaces spaces spaces",
|
||||
1234567890123456789.0,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(params=PAYLOADS, name="payload")
|
||||
def payload_fixture(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(params=WRONG_PAYLOADS, name="wrong_payload")
|
||||
def wrong_payload_fixture(request):
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def get_bot_user_fixture(monkeypatch):
|
||||
"""Monkey patching of bot.me calling."""
|
||||
|
||||
@alru_cache()
|
||||
async def get_bot_user_mock(self):
|
||||
from aiogram.types import User
|
||||
|
||||
return User(
|
||||
id=12345678,
|
||||
is_bot=True,
|
||||
first_name="FirstName",
|
||||
last_name="LastName",
|
||||
username="username",
|
||||
language_code="uk-UA",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(MockedBot, "me", get_bot_user_mock)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestDeepLinking:
|
||||
async def test_get_start_link(self, bot, payload):
|
||||
link = await create_start_link(bot=bot, payload=payload)
|
||||
assert link == f"https://t.me/username?start={payload}"
|
||||
|
||||
async def test_wrong_symbols(self, bot, wrong_payload):
|
||||
with pytest.raises(ValueError):
|
||||
await create_start_link(bot, wrong_payload)
|
||||
|
||||
async def test_get_startgroup_link(self, bot, payload):
|
||||
link = await create_startgroup_link(bot, payload)
|
||||
assert link == f"https://t.me/username?startgroup={payload}"
|
||||
|
||||
async def test_filter_encode_and_decode(self, payload):
|
||||
encoded = encode_payload(payload)
|
||||
decoded = decode_payload(encoded)
|
||||
assert decoded == str(payload)
|
||||
|
||||
async def test_get_start_link_with_encoding(self, bot, wrong_payload):
|
||||
# define link
|
||||
link = await create_start_link(bot, wrong_payload, encode=True)
|
||||
|
||||
# define reference link
|
||||
encoded_payload = encode_payload(wrong_payload)
|
||||
|
||||
assert link == f"https://t.me/username?start={encoded_payload}"
|
||||
|
||||
async def test_64_len_payload(self, bot):
|
||||
payload = "p" * 64
|
||||
link = await create_start_link(bot, payload)
|
||||
assert link
|
||||
|
||||
async def test_too_long_payload(self, bot):
|
||||
payload = "p" * 65
|
||||
print(payload, len(payload))
|
||||
with pytest.raises(ValueError):
|
||||
await create_start_link(bot, payload)
|
||||
24
tests/test_utils/test_link.py
Normal file
24
tests/test_utils/test_link.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from typing import Any, Dict
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.utils.link import create_telegram_link, create_tg_link
|
||||
|
||||
|
||||
class TestLink:
|
||||
@pytest.mark.parametrize(
|
||||
"base,params,result",
|
||||
[["user", dict(id=42), "tg://user?id=42"]],
|
||||
)
|
||||
def test_create_tg_link(self, base: str, params: Dict[str, Any], result: str):
|
||||
assert create_tg_link(base, **params) == result
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"base,params,result",
|
||||
[
|
||||
["username", dict(), "https://t.me/username"],
|
||||
["username", dict(start="test"), "https://t.me/username?start=test"],
|
||||
],
|
||||
)
|
||||
def test_create_telegram_link(self, base: str, params: Dict[str, Any], result: str):
|
||||
assert create_telegram_link(base, **params) == result
|
||||
|
|
@ -35,7 +35,7 @@ class TestMarkdown:
|
|||
[hitalic, ("test", "test"), " ", "<i>test test</i>"],
|
||||
[code, ("test", "test"), " ", "`test test`"],
|
||||
[hcode, ("test", "test"), " ", "<code>test test</code>"],
|
||||
[pre, ("test", "test"), " ", "```test test```"],
|
||||
[pre, ("test", "test"), " ", "```\ntest test\n```"],
|
||||
[hpre, ("test", "test"), " ", "<pre>test test</pre>"],
|
||||
[underline, ("test", "test"), " ", "__\rtest test__\r"],
|
||||
[hunderline, ("test", "test"), " ", "<u>test test</u>"],
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class TestTextDecoration:
|
|||
[markdown_decoration, MessageEntity(type="bold", offset=0, length=5), "*test*"],
|
||||
[markdown_decoration, MessageEntity(type="italic", offset=0, length=5), "_\rtest_\r"],
|
||||
[markdown_decoration, MessageEntity(type="code", offset=0, length=5), "`test`"],
|
||||
[markdown_decoration, MessageEntity(type="pre", offset=0, length=5), "```test```"],
|
||||
[markdown_decoration, MessageEntity(type="pre", offset=0, length=5), "```\ntest\n```"],
|
||||
[
|
||||
markdown_decoration,
|
||||
MessageEntity(type="pre", offset=0, length=5, language="python"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue