mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-14 02:52:12 +00:00
Beta 3 (#884)
* Rework middlewares, separate management to `MiddlewareManager` class * Rework middlewares * Added changes description for redis * Added changes description for redis * Fixed tests with Redis // aioredis replacement * Changed msg.<html/md>_text attributes behaviour * Added changelog for spoilers * Added possibility to get command magic result as handler arguments
This commit is contained in:
parent
930bca0876
commit
286cf39c8a
51 changed files with 1380 additions and 804 deletions
|
|
@ -2,7 +2,7 @@ from pathlib import Path
|
|||
|
||||
import pytest
|
||||
from _pytest.config import UsageError
|
||||
from aioredis.connection import parse_url as parse_redis_url
|
||||
from redis.asyncio.connection import parse_url as parse_redis_url
|
||||
|
||||
from aiogram import Bot, Dispatcher
|
||||
from aiogram.dispatcher.fsm.storage.memory import (
|
||||
|
|
|
|||
|
|
@ -215,11 +215,11 @@ class TestBaseSession:
|
|||
return await make_request(bot, method)
|
||||
|
||||
session = CustomSession()
|
||||
assert not session.middlewares
|
||||
assert not session.middleware._middlewares
|
||||
|
||||
session.middleware(my_middleware)
|
||||
assert my_middleware in session.middlewares
|
||||
assert len(session.middlewares) == 1
|
||||
assert my_middleware in session.middleware
|
||||
assert len(session.middleware) == 1
|
||||
|
||||
async def test_use_middleware(self, bot: MockedBot):
|
||||
flag_before = False
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
from aiogram import Bot
|
||||
from aiogram.client.session.middlewares.base import (
|
||||
BaseRequestMiddleware,
|
||||
NextRequestMiddlewareType,
|
||||
)
|
||||
from aiogram.client.session.middlewares.manager import RequestMiddlewareManager
|
||||
from aiogram.methods import Response, TelegramMethod
|
||||
from aiogram.types import TelegramObject
|
||||
|
||||
|
||||
class TestMiddlewareManager:
|
||||
async def test_register(self):
|
||||
manager = RequestMiddlewareManager()
|
||||
|
||||
@manager
|
||||
async def middleware(handler, event, data):
|
||||
await handler(event, data)
|
||||
|
||||
assert middleware in manager._middlewares
|
||||
manager.unregister(middleware)
|
||||
assert middleware not in manager._middlewares
|
||||
|
||||
async def test_wrap_middlewares(self):
|
||||
manager = RequestMiddlewareManager()
|
||||
|
||||
class MyMiddleware(BaseRequestMiddleware):
|
||||
async def __call__(
|
||||
self,
|
||||
make_request: NextRequestMiddlewareType,
|
||||
bot: Bot,
|
||||
method: TelegramMethod[TelegramObject],
|
||||
) -> Response[TelegramObject]:
|
||||
return await make_request(bot, method)
|
||||
|
||||
manager.register(MyMiddleware())
|
||||
|
||||
@manager()
|
||||
@manager
|
||||
async def middleware(make_request, bot, method):
|
||||
return await make_request(bot, method)
|
||||
|
||||
async def target_call(bot, method, timeout: int = None):
|
||||
return timeout
|
||||
|
||||
assert await manager.wrap_middlewares(target_call, timeout=42)(None, None) == 42
|
||||
|
|
@ -641,13 +641,15 @@ class TestMessage:
|
|||
assert method.message_id == message.message_id
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"text,entities,correct",
|
||||
"text,entities,mode,expected_value",
|
||||
[
|
||||
["test", [MessageEntity(type="bold", offset=0, length=4)], True],
|
||||
["", [], False],
|
||||
["test", [MessageEntity(type="bold", offset=0, length=4)], "html", "<b>test</b>"],
|
||||
["test", [MessageEntity(type="bold", offset=0, length=4)], "md", "*test*"],
|
||||
["", [], "html", ""],
|
||||
["", [], "md", ""],
|
||||
],
|
||||
)
|
||||
def test_html_text(self, text, entities, correct):
|
||||
def test_html_text(self, text, entities, mode, expected_value):
|
||||
message = Message(
|
||||
message_id=42,
|
||||
chat=Chat(id=42, type="private"),
|
||||
|
|
@ -655,11 +657,4 @@ class TestMessage:
|
|||
text=text,
|
||||
entities=entities,
|
||||
)
|
||||
if correct:
|
||||
assert message.html_text
|
||||
assert message.md_text
|
||||
else:
|
||||
with pytest.raises(TypeError):
|
||||
assert message.html_text
|
||||
with pytest.raises(TypeError):
|
||||
assert message.md_text
|
||||
assert getattr(message, f"{mode}_text") == expected_value
|
||||
|
|
|
|||
|
|
@ -5,27 +5,38 @@ from aiogram.dispatcher.router import Router
|
|||
from tests.deprecated import check_deprecated
|
||||
|
||||
OBSERVERS = {
|
||||
"callback_query",
|
||||
"channel_post",
|
||||
"chosen_inline_result",
|
||||
"edited_channel_post",
|
||||
"edited_message",
|
||||
"errors",
|
||||
"inline_query",
|
||||
"message",
|
||||
"edited_message",
|
||||
"channel_post",
|
||||
"edited_channel_post",
|
||||
"inline_query",
|
||||
"chosen_inline_result",
|
||||
"callback_query",
|
||||
"shipping_query",
|
||||
"pre_checkout_query",
|
||||
"poll",
|
||||
"poll_answer",
|
||||
"pre_checkout_query",
|
||||
"shipping_query",
|
||||
"my_chat_member",
|
||||
"chat_member",
|
||||
"chat_join_request",
|
||||
"errors",
|
||||
}
|
||||
|
||||
DEPRECATED_OBSERVERS = {observer + "_handler" for observer in OBSERVERS}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("observer_name", DEPRECATED_OBSERVERS)
|
||||
@pytest.mark.parametrize("observer_name", OBSERVERS)
|
||||
def test_deprecated_handlers_name(observer_name: str):
|
||||
router = Router()
|
||||
|
||||
with check_deprecated("3.2", exception=AttributeError):
|
||||
observer = getattr(router, observer_name)
|
||||
observer = getattr(router, f"{observer_name}_handler")
|
||||
assert isinstance(observer, TelegramEventObserver)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("observer_name", OBSERVERS)
|
||||
def test_deprecated_register_handlers(observer_name: str):
|
||||
router = Router()
|
||||
|
||||
with check_deprecated("3.2", exception=AttributeError):
|
||||
register = getattr(router, f"register_{observer_name}")
|
||||
register(lambda event: True)
|
||||
assert callable(register)
|
||||
|
|
|
|||
|
|
@ -74,7 +74,22 @@ class TestDispatcher:
|
|||
|
||||
assert dp.update.handlers
|
||||
assert dp.update.handlers[0].callback == dp._listen_update
|
||||
assert dp.update.outer_middlewares
|
||||
assert dp.update.outer_middleware
|
||||
|
||||
def test_data_bind(self):
|
||||
dp = Dispatcher()
|
||||
assert dp.get("foo") is None
|
||||
assert dp.get("foo", 42) == 42
|
||||
|
||||
dp["foo"] = 1
|
||||
assert dp._data["foo"] == 1
|
||||
assert dp["foo"] == 1
|
||||
|
||||
del dp["foo"]
|
||||
assert "foo" not in dp._data
|
||||
|
||||
def test_storage_property(self, dispatcher: Dispatcher):
|
||||
assert dispatcher.storage is dispatcher.fsm.storage
|
||||
|
||||
def test_parent_router(self, dispatcher: Dispatcher):
|
||||
with pytest.raises(RuntimeError):
|
||||
|
|
|
|||
42
tests/test_dispatcher/test_event/test_middleware.py
Normal file
42
tests/test_dispatcher/test_event/test_middleware.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
from functools import partial
|
||||
|
||||
from aiogram.dispatcher.middlewares.manager import MiddlewareManager
|
||||
|
||||
|
||||
class TestMiddlewareManager:
|
||||
async def test_register(self):
|
||||
manager = MiddlewareManager()
|
||||
|
||||
@manager
|
||||
async def middleware(handler, event, data):
|
||||
await handler(event, data)
|
||||
|
||||
assert middleware in manager._middlewares
|
||||
manager.unregister(middleware)
|
||||
assert middleware not in manager._middlewares
|
||||
|
||||
async def test_wrap_middlewares(self):
|
||||
manager = MiddlewareManager()
|
||||
|
||||
async def target(*args, **kwargs):
|
||||
kwargs["target"] = True
|
||||
kwargs["stack"].append(-1)
|
||||
return kwargs
|
||||
|
||||
async def middleware1(handler, event, data):
|
||||
data["mw1"] = True
|
||||
data["stack"].append(1)
|
||||
return await handler(event, data)
|
||||
|
||||
async def middleware2(handler, event, data):
|
||||
data["mw2"] = True
|
||||
data["stack"].append(2)
|
||||
return await handler(event, data)
|
||||
|
||||
wrapped = manager.wrap_middlewares([middleware1, middleware2], target)
|
||||
|
||||
assert isinstance(wrapped, partial)
|
||||
assert wrapped.func is middleware1
|
||||
|
||||
result = await wrapped(None, {"stack": []})
|
||||
assert result == {"mw1": True, "mw2": True, "target": True, "stack": [1, 2, -1]}
|
||||
|
|
@ -297,10 +297,9 @@ class TestTelegramEventObserver:
|
|||
def test_register_middleware(self, middleware_type):
|
||||
event_observer = TelegramEventObserver(Router(), "test")
|
||||
|
||||
middlewares = getattr(event_observer, f"{middleware_type}s")
|
||||
decorator = getattr(event_observer, middleware_type)
|
||||
middlewares = getattr(event_observer, middleware_type)
|
||||
|
||||
@decorator
|
||||
@middlewares
|
||||
async def my_middleware1(handler, event, data):
|
||||
pass
|
||||
|
||||
|
|
@ -308,7 +307,7 @@ class TestTelegramEventObserver:
|
|||
assert my_middleware1.__name__ == "my_middleware1"
|
||||
assert my_middleware1 in middlewares
|
||||
|
||||
@decorator()
|
||||
@middlewares()
|
||||
async def my_middleware2(handler, event, data):
|
||||
pass
|
||||
|
||||
|
|
@ -319,13 +318,13 @@ class TestTelegramEventObserver:
|
|||
async def my_middleware3(handler, event, data):
|
||||
pass
|
||||
|
||||
decorator(my_middleware3)
|
||||
middlewares(my_middleware3)
|
||||
|
||||
assert my_middleware3 is not None
|
||||
assert my_middleware3.__name__ == "my_middleware3"
|
||||
assert my_middleware3 in middlewares
|
||||
|
||||
assert middlewares == [my_middleware1, my_middleware2, my_middleware3]
|
||||
assert list(middlewares) == [my_middleware1, my_middleware2, my_middleware3]
|
||||
|
||||
def test_register_global_filters(self):
|
||||
router = Router(use_builtin_filters=False)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class MyCallback(CallbackData, prefix="test"):
|
|||
|
||||
class TestCallbackData:
|
||||
def test_init_subclass_prefix_required(self):
|
||||
assert MyCallback.prefix == "test"
|
||||
assert MyCallback.__prefix__ == "test"
|
||||
|
||||
with pytest.raises(ValueError, match="prefix required.+"):
|
||||
|
||||
|
|
@ -38,12 +38,12 @@ class TestCallbackData:
|
|||
pass
|
||||
|
||||
def test_init_subclass_sep_validation(self):
|
||||
assert MyCallback.sep == ":"
|
||||
assert MyCallback.__separator__ == ":"
|
||||
|
||||
class MyCallback2(CallbackData, prefix="test2", sep="@"):
|
||||
pass
|
||||
|
||||
assert MyCallback2.sep == "@"
|
||||
assert MyCallback2.__separator__ == "@"
|
||||
|
||||
with pytest.raises(ValueError, match="Separator symbol '@' .+ 'sp@m'"):
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,18 @@ class TestCommandFilter:
|
|||
command = Command(commands=["test"])
|
||||
assert bool(await command(message=message, bot=bot)) is result
|
||||
|
||||
async def test_command_magic_result(self, bot: MockedBot):
|
||||
message = Message(
|
||||
message_id=0,
|
||||
text="/test 42",
|
||||
chat=Chat(id=42, type="private"),
|
||||
date=datetime.datetime.now(),
|
||||
)
|
||||
command = Command(commands=["test"], command_magic=(F.args.as_("args")))
|
||||
result = await command(message=message, bot=bot)
|
||||
assert "args" in result
|
||||
assert result["args"] == "42"
|
||||
|
||||
|
||||
class TestCommandObject:
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import re
|
|||
|
||||
import pytest
|
||||
|
||||
from aiogram import Dispatcher, F
|
||||
from aiogram import Dispatcher
|
||||
from aiogram.dispatcher.filters import ExceptionMessageFilter, ExceptionTypeFilter
|
||||
from aiogram.types import Update
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ class TestMagicDataFilter:
|
|||
assert value.spam is True
|
||||
return value
|
||||
|
||||
f = MagicData(magic_data=F.func(check))
|
||||
f = MagicData(magic_data=F.func(check).as_("test"))
|
||||
result = await f(Update(update_id=123), "foo", "bar", spam=True)
|
||||
|
||||
assert called
|
||||
assert isinstance(result, bool)
|
||||
assert result
|
||||
assert isinstance(result, dict)
|
||||
assert result["test"]
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ class TestSimpleI18nMiddleware:
|
|||
middleware = SimpleI18nMiddleware(i18n=i18n)
|
||||
middleware.setup(router=dp)
|
||||
|
||||
assert middleware not in dp.update.outer_middlewares
|
||||
assert middleware in dp.message.outer_middlewares
|
||||
assert middleware not in dp.update.outer_middleware
|
||||
assert middleware in dp.message.outer_middleware
|
||||
|
||||
async def test_get_unknown_locale(self, i18n: I18n):
|
||||
dp = Dispatcher()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from typing import Any, Dict
|
|||
|
||||
import pytest
|
||||
|
||||
from aiogram.utils.link import create_telegram_link, create_tg_link
|
||||
from aiogram.utils.link import BRANCH, create_telegram_link, create_tg_link, docs_url
|
||||
|
||||
|
||||
class TestLink:
|
||||
|
|
@ -22,3 +22,12 @@ class TestLink:
|
|||
)
|
||||
def test_create_telegram_link(self, base: str, params: Dict[str, Any], result: str):
|
||||
assert create_telegram_link(base, **params) == result
|
||||
|
||||
def test_fragment(self):
|
||||
assert (
|
||||
docs_url("test.html", fragment_="test")
|
||||
== f"https://docs.aiogram.dev/en/{BRANCH}/test.html#test"
|
||||
)
|
||||
|
||||
def test_docs(self):
|
||||
assert docs_url("test.html") == f"https://docs.aiogram.dev/en/{BRANCH}/test.html"
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ class TestTextDecoration:
|
|||
'<a href="tg://user?id=42">test</a>',
|
||||
],
|
||||
[html_decoration, MessageEntity(type="url", offset=0, length=5), "test"],
|
||||
[
|
||||
html_decoration,
|
||||
MessageEntity(type="spoiler", offset=0, length=5),
|
||||
'<span class="tg-spoiler">test</span>',
|
||||
],
|
||||
[
|
||||
html_decoration,
|
||||
MessageEntity(type="text_link", offset=0, length=5, url="https://aiogram.dev"),
|
||||
|
|
@ -76,6 +81,7 @@ class TestTextDecoration:
|
|||
[markdown_decoration, MessageEntity(type="bot_command", offset=0, length=5), "test"],
|
||||
[markdown_decoration, MessageEntity(type="email", offset=0, length=5), "test"],
|
||||
[markdown_decoration, MessageEntity(type="phone_number", offset=0, length=5), "test"],
|
||||
[markdown_decoration, MessageEntity(type="spoiler", offset=0, length=5), "|test|"],
|
||||
[
|
||||
markdown_decoration,
|
||||
MessageEntity(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue