mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Merge branch 'dev-3.x' into mongo_storage
Conflicts resolved
This commit is contained in:
commit
04ae6dcd7a
164 changed files with 3923 additions and 736 deletions
|
|
@ -14,6 +14,7 @@ from aiogram.methods import GetFile, GetMe
|
|||
from aiogram.types import File, PhotoSize
|
||||
from tests.deprecated import check_deprecated
|
||||
from tests.mocked_bot import MockedBot
|
||||
from tests.test_api.test_client.test_session.test_base_session import CustomSession
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
|
|
@ -42,16 +43,28 @@ class TestBot:
|
|||
assert isinstance(bot.session, AiohttpSession)
|
||||
assert bot.id == 42
|
||||
|
||||
async def test_bot_context_manager_over_session(self):
|
||||
session = CustomSession()
|
||||
with patch(
|
||||
"tests.test_api.test_client.test_session.test_base_session.CustomSession.close",
|
||||
new_callable=AsyncMock,
|
||||
) as mocked_close:
|
||||
async with Bot(token="42:TEST", session=session) as bot:
|
||||
assert bot.id == 42
|
||||
assert bot.session is session
|
||||
|
||||
mocked_close.assert_awaited_once()
|
||||
|
||||
def test_init_default(self):
|
||||
with check_deprecated(
|
||||
max_version="3.5.0",
|
||||
max_version="3.7.0",
|
||||
exception=TypeError,
|
||||
):
|
||||
bot = Bot(token="42:Test", parse_mode="HTML")
|
||||
|
||||
def test_deprecated_parse_mode(self):
|
||||
with check_deprecated(
|
||||
max_version="3.5.0",
|
||||
max_version="3.7.0",
|
||||
exception=AttributeError,
|
||||
):
|
||||
bot = Bot(token="42:Test", parse_mode="HTML")
|
||||
|
|
@ -59,7 +72,7 @@ class TestBot:
|
|||
|
||||
def test_disable_web_page_preview(self):
|
||||
with check_deprecated(
|
||||
max_version="3.5.0",
|
||||
max_version="3.7.0",
|
||||
exception=TypeError,
|
||||
):
|
||||
bot = Bot(token="42:Test", disable_web_page_preview=True)
|
||||
|
|
@ -67,7 +80,7 @@ class TestBot:
|
|||
|
||||
def test_deprecated_protect_content(self):
|
||||
with check_deprecated(
|
||||
max_version="3.5.0",
|
||||
max_version="3.7.0",
|
||||
exception=AttributeError,
|
||||
):
|
||||
bot = Bot(token="42:Test", protect_content=True)
|
||||
|
|
|
|||
68
tests/test_api/test_client/test_default.py
Normal file
68
tests/test_api/test_client/test_default.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.client.default import Default, DefaultBotProperties
|
||||
from aiogram.enums import ParseMode
|
||||
from aiogram.types import LinkPreviewOptions
|
||||
|
||||
|
||||
class TestDefault:
|
||||
def test_init(self):
|
||||
default = Default("test")
|
||||
assert default._name == "test"
|
||||
|
||||
def test_name_property(self):
|
||||
default = Default("test")
|
||||
assert default.name == "test"
|
||||
|
||||
def test_str(self):
|
||||
default = Default("test")
|
||||
assert str(default) == "Default('test')"
|
||||
|
||||
def test_repr(self):
|
||||
default = Default("test")
|
||||
assert repr(default) == "<Default('test')>"
|
||||
|
||||
|
||||
class TestDefaultBotProperties:
|
||||
def test_post_init_empty(self):
|
||||
default_bot_properties = DefaultBotProperties()
|
||||
|
||||
assert default_bot_properties.link_preview is None
|
||||
|
||||
def test_post_init_auto_fill_link_preview(self):
|
||||
default_bot_properties = DefaultBotProperties(
|
||||
link_preview_is_disabled=True,
|
||||
link_preview_prefer_small_media=True,
|
||||
link_preview_prefer_large_media=True,
|
||||
link_preview_show_above_text=True,
|
||||
)
|
||||
|
||||
assert default_bot_properties.link_preview == LinkPreviewOptions(
|
||||
is_disabled=True,
|
||||
prefer_small_media=True,
|
||||
prefer_large_media=True,
|
||||
show_above_text=True,
|
||||
)
|
||||
|
||||
def test_getitem(self):
|
||||
default_bot_properties = DefaultBotProperties(
|
||||
parse_mode=ParseMode.HTML,
|
||||
link_preview_is_disabled=True,
|
||||
link_preview_prefer_small_media=True,
|
||||
link_preview_prefer_large_media=True,
|
||||
link_preview_show_above_text=True,
|
||||
)
|
||||
|
||||
assert default_bot_properties["parse_mode"] == ParseMode.HTML
|
||||
assert default_bot_properties["link_preview_is_disabled"] is True
|
||||
assert default_bot_properties["link_preview_prefer_small_media"] is True
|
||||
assert default_bot_properties["link_preview_prefer_large_media"] is True
|
||||
assert default_bot_properties["link_preview_show_above_text"] is True
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 12), reason="requires python3.11 or higher")
|
||||
def test_dataclass_creation_3_10_plus(self):
|
||||
params = DefaultBotProperties.__dataclass_params__
|
||||
assert params.slots is True
|
||||
assert params.kw_only is True
|
||||
|
|
@ -26,7 +26,7 @@ from aiogram.exceptions import (
|
|||
TelegramUnauthorizedError,
|
||||
)
|
||||
from aiogram.methods import DeleteMessage, GetMe, TelegramMethod
|
||||
from aiogram.types import UNSET_PARSE_MODE, User, LinkPreviewOptions
|
||||
from aiogram.types import UNSET_PARSE_MODE, LinkPreviewOptions, User
|
||||
from aiogram.types.base import UNSET_DISABLE_WEB_PAGE_PREVIEW, UNSET_PROTECT_CONTENT
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from aiogram.enums import StickerFormat
|
||||
from aiogram.methods import AddStickerToSet
|
||||
from aiogram.types import InputSticker
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
|
@ -10,7 +11,9 @@ class TestAddStickerToSet:
|
|||
response: bool = await bot.add_sticker_to_set(
|
||||
user_id=42,
|
||||
name="test stickers pack",
|
||||
sticker=InputSticker(sticker="file id", emoji_list=[":)"]),
|
||||
sticker=InputSticker(
|
||||
sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]
|
||||
),
|
||||
)
|
||||
request = bot.get_request()
|
||||
assert response == prepare_result.result
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ class TestCreateNewStickerSet:
|
|||
name="name",
|
||||
title="title",
|
||||
stickers=[
|
||||
InputSticker(sticker="file id", emoji_list=[":)"]),
|
||||
InputSticker(sticker=FSInputFile("file.png"), emoji_list=["=("]),
|
||||
InputSticker(sticker="file id", format=StickerFormat.STATIC, emoji_list=[":)"]),
|
||||
InputSticker(
|
||||
sticker=FSInputFile("file.png"), format=StickerFormat.STATIC, emoji_list=["=("]
|
||||
),
|
||||
],
|
||||
sticker_format=StickerFormat.STATIC,
|
||||
)
|
||||
|
|
|
|||
24
tests/test_api/test_methods/test_get_business_connection.py
Normal file
24
tests/test_api/test_methods/test_get_business_connection.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
from aiogram.methods import GetBusinessConnection
|
||||
from aiogram.types import BusinessConnection, User
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestGetBusinessConnection:
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(
|
||||
GetBusinessConnection,
|
||||
ok=True,
|
||||
result=BusinessConnection(
|
||||
id="test",
|
||||
user=User(id=42, is_bot=False, first_name="User"),
|
||||
user_chat_id=42,
|
||||
date=42,
|
||||
can_reply=True,
|
||||
is_enabled=True,
|
||||
),
|
||||
)
|
||||
response: BusinessConnection = await bot.get_business_connection(
|
||||
business_connection_id="test"
|
||||
)
|
||||
request = bot.get_request()
|
||||
assert response == prepare_result.result
|
||||
21
tests/test_api/test_methods/test_replace_sticker_in_set.py
Normal file
21
tests/test_api/test_methods/test_replace_sticker_in_set.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
from aiogram.methods import ReplaceStickerInSet
|
||||
from aiogram.types import InputSticker
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
||||
class TestReplaceStickerInSet:
|
||||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(ReplaceStickerInSet, ok=True, result=True)
|
||||
|
||||
response: bool = await bot.replace_sticker_in_set(
|
||||
user_id=42,
|
||||
name="test",
|
||||
old_sticker="test",
|
||||
sticker=InputSticker(
|
||||
sticker="test",
|
||||
format="static",
|
||||
emoji_list=["test"],
|
||||
),
|
||||
)
|
||||
request = bot.get_request()
|
||||
assert response == prepare_result.result
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from aiogram.enums import StickerFormat
|
||||
from aiogram.methods import Request, SetStickerSetThumbnail
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
|
|
@ -6,6 +7,8 @@ class TestSetStickerSetThumbnail:
|
|||
async def test_bot_method(self, bot: MockedBot):
|
||||
prepare_result = bot.add_result_for(SetStickerSetThumbnail, ok=True, result=None)
|
||||
|
||||
response: bool = await bot.set_sticker_set_thumbnail(name="test", user_id=42)
|
||||
response: bool = await bot.set_sticker_set_thumbnail(
|
||||
name="test", format=StickerFormat.STATIC, user_id=42
|
||||
)
|
||||
request = bot.get_request()
|
||||
assert response == prepare_result.result
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ from aiogram.types import (
|
|||
PollOption,
|
||||
ProximityAlertTriggered,
|
||||
ReactionTypeCustomEmoji,
|
||||
SharedUser,
|
||||
Sticker,
|
||||
Story,
|
||||
SuccessfulPayment,
|
||||
|
|
@ -469,7 +470,7 @@ TEST_MESSAGE_USERS_SHARED = Message(
|
|||
from_user=None,
|
||||
users_shared=UsersShared(
|
||||
request_id=0,
|
||||
user_ids=[1, 2],
|
||||
users=[SharedUser(user_id=1), SharedUser(user_id=2)],
|
||||
),
|
||||
)
|
||||
TEST_CHAT_SHARED = Message(
|
||||
|
|
@ -485,8 +486,6 @@ TEST_MESSAGE_STORY = Message(
|
|||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
story=Story(chat=Chat(id=42, type="private"), id=42),
|
||||
forward_signature="Test",
|
||||
forward_date=datetime.datetime.now(),
|
||||
)
|
||||
|
||||
TEST_MESSAGE_GIVEAWAY = Message(
|
||||
|
|
@ -527,13 +526,6 @@ TEST_MESSAGE_GIVEAWAY_COMPLETED = Message(
|
|||
from_user=None,
|
||||
giveaway_completed=GiveawayCompleted(winner_count=10),
|
||||
)
|
||||
TEST_MESSAGE_HAS_MEDIA_SPOILER = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=None,
|
||||
has_media_spoiler=True,
|
||||
)
|
||||
TEST_MESSAGE_GENERAL_FORUM_TOPIC_HIDDEN = Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
|
|
@ -552,7 +544,6 @@ TEST_MESSAGE_WRITE_ACCESS_ALLOWED = Message(
|
|||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=None,
|
||||
write_access_allowed=WriteAccessAllowed(),
|
||||
)
|
||||
TEST_MESSAGE_BOOST_ADDED = Message(
|
||||
|
|
@ -625,7 +616,6 @@ MESSAGES_AND_CONTENT_TYPES = [
|
|||
[TEST_MESSAGE_GIVEAWAY_CREATED, ContentType.GIVEAWAY_CREATED],
|
||||
[TEST_MESSAGE_GIVEAWAY_WINNERS, ContentType.GIVEAWAY_WINNERS],
|
||||
[TEST_MESSAGE_GIVEAWAY_COMPLETED, ContentType.GIVEAWAY_COMPLETED],
|
||||
[TEST_MESSAGE_HAS_MEDIA_SPOILER, ContentType.HAS_MEDIA_SPOILER],
|
||||
[TEST_MESSAGE_GENERAL_FORUM_TOPIC_HIDDEN, ContentType.GENERAL_FORUM_TOPIC_HIDDEN],
|
||||
[TEST_MESSAGE_GENERAL_FORUM_TOPIC_UNHIDDEN, ContentType.GENERAL_FORUM_TOPIC_UNHIDDEN],
|
||||
[TEST_MESSAGE_WRITE_ACCESS_ALLOWED, ContentType.WRITE_ACCESS_ALLOWED],
|
||||
|
|
@ -674,7 +664,6 @@ MESSAGES_AND_COPY_METHODS = [
|
|||
[TEST_MESSAGE_USER_SHARED, None],
|
||||
[TEST_CHAT_SHARED, None],
|
||||
[TEST_MESSAGE_GIVEAWAY_COMPLETED, None],
|
||||
[TEST_MESSAGE_HAS_MEDIA_SPOILER, None],
|
||||
[TEST_MESSAGE_WEB_APP_DATA, None],
|
||||
[TEST_FORUM_TOPIC_CREATED, None],
|
||||
[TEST_FORUM_TOPIC_EDITED, None],
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler
|
|||
from aiogram.dispatcher.router import Router
|
||||
from aiogram.methods import GetMe, GetUpdates, SendMessage, TelegramMethod
|
||||
from aiogram.types import (
|
||||
BusinessConnection,
|
||||
BusinessMessagesDeleted,
|
||||
CallbackQuery,
|
||||
Chat,
|
||||
ChatBoost,
|
||||
|
|
@ -525,6 +527,65 @@ class TestDispatcher:
|
|||
True,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"deleted_business_messages",
|
||||
Update(
|
||||
update_id=42,
|
||||
deleted_business_messages=BusinessMessagesDeleted(
|
||||
chat=Chat(id=-42, type="private"),
|
||||
business_connection_id="qwerty",
|
||||
message_ids=[1, 2, 3],
|
||||
),
|
||||
),
|
||||
True,
|
||||
False,
|
||||
),
|
||||
pytest.param(
|
||||
"business_connection",
|
||||
Update(
|
||||
update_id=42,
|
||||
business_connection=BusinessConnection(
|
||||
id="qwerty",
|
||||
user=User(id=42, is_bot=False, first_name="Test"),
|
||||
user_chat_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
can_reply=True,
|
||||
is_enabled=True,
|
||||
),
|
||||
),
|
||||
False,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
"edited_business_message",
|
||||
Update(
|
||||
update_id=42,
|
||||
edited_business_message=Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
),
|
||||
True,
|
||||
True,
|
||||
),
|
||||
pytest.param(
|
||||
"business_message",
|
||||
Update(
|
||||
update_id=42,
|
||||
business_message=Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
),
|
||||
True,
|
||||
True,
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_listen_update(
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@ from unittest.mock import patch
|
|||
|
||||
import pytest
|
||||
|
||||
from aiogram.dispatcher.middlewares.user_context import UserContextMiddleware
|
||||
from aiogram.types import Update
|
||||
from aiogram.dispatcher.middlewares.user_context import (
|
||||
EventContext,
|
||||
UserContextMiddleware,
|
||||
)
|
||||
from aiogram.types import Chat, Update, User
|
||||
|
||||
|
||||
async def next_handler(*args, **kwargs):
|
||||
|
|
@ -18,9 +21,23 @@ class TestUserContextMiddleware:
|
|||
async def test_call(self):
|
||||
middleware = UserContextMiddleware()
|
||||
data = {}
|
||||
with patch.object(UserContextMiddleware, "resolve_event_context", return_value=[1, 2, 3]):
|
||||
|
||||
chat = Chat(id=1, type="private", title="Test")
|
||||
user = User(id=2, first_name="Test", is_bot=False)
|
||||
thread_id = 3
|
||||
|
||||
with patch.object(
|
||||
UserContextMiddleware,
|
||||
"resolve_event_context",
|
||||
return_value=EventContext(user=user, chat=chat, thread_id=3),
|
||||
):
|
||||
await middleware(next_handler, Update(update_id=42), data)
|
||||
|
||||
assert data["event_chat"] == 1
|
||||
assert data["event_from_user"] == 2
|
||||
assert data["event_thread_id"] == 3
|
||||
event_context = data["event_context"]
|
||||
assert isinstance(event_context, EventContext)
|
||||
assert event_context.chat is chat
|
||||
assert event_context.user is user
|
||||
assert event_context.thread_id == thread_id
|
||||
assert data["event_chat"] is chat
|
||||
assert data["event_from_user"] is user
|
||||
assert data["event_thread_id"] == thread_id
|
||||
|
|
|
|||
|
|
@ -2,41 +2,81 @@ from typing import Literal, Optional
|
|||
|
||||
import pytest
|
||||
|
||||
from aiogram.fsm.storage.base import DEFAULT_DESTINY, StorageKey
|
||||
from aiogram.fsm.storage.redis import DefaultKeyBuilder
|
||||
from aiogram.fsm.storage.base import DEFAULT_DESTINY, DefaultKeyBuilder, StorageKey
|
||||
|
||||
PREFIX = "test"
|
||||
BOT_ID = 42
|
||||
CHAT_ID = -1
|
||||
USER_ID = 2
|
||||
THREAD_ID = 3
|
||||
BUSINESS_CONNECTION_ID = "4"
|
||||
FIELD = "data"
|
||||
|
||||
|
||||
class TestDefaultKeyBuilder:
|
||||
@pytest.mark.parametrize(
|
||||
"with_bot_id,with_destiny,field,result",
|
||||
"key_builder,field,result",
|
||||
[
|
||||
[False, False, FIELD, f"{PREFIX}:{CHAT_ID}:{USER_ID}:{FIELD}"],
|
||||
[True, False, FIELD, f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{FIELD}"],
|
||||
[True, True, FIELD, f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}"],
|
||||
[False, True, FIELD, f"{PREFIX}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}"],
|
||||
[False, False, None, f"{PREFIX}:{CHAT_ID}:{USER_ID}"],
|
||||
[
|
||||
DefaultKeyBuilder(
|
||||
prefix=PREFIX,
|
||||
with_bot_id=True,
|
||||
with_destiny=True,
|
||||
with_business_connection_id=True,
|
||||
),
|
||||
FIELD,
|
||||
f"{PREFIX}:{BOT_ID}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(prefix=PREFIX, with_bot_id=True, with_destiny=True),
|
||||
None,
|
||||
f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(
|
||||
prefix=PREFIX, with_bot_id=True, with_business_connection_id=True
|
||||
),
|
||||
FIELD,
|
||||
f"{PREFIX}:{BOT_ID}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{FIELD}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(prefix=PREFIX, with_bot_id=True),
|
||||
None,
|
||||
f"{PREFIX}:{BOT_ID}:{CHAT_ID}:{USER_ID}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(
|
||||
prefix=PREFIX, with_destiny=True, with_business_connection_id=True
|
||||
),
|
||||
FIELD,
|
||||
f"{PREFIX}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}:{FIELD}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(prefix=PREFIX, with_destiny=True),
|
||||
None,
|
||||
f"{PREFIX}:{CHAT_ID}:{USER_ID}:{DEFAULT_DESTINY}",
|
||||
],
|
||||
[
|
||||
DefaultKeyBuilder(prefix=PREFIX, with_business_connection_id=True),
|
||||
FIELD,
|
||||
f"{PREFIX}:{BUSINESS_CONNECTION_ID}:{CHAT_ID}:{USER_ID}:{FIELD}",
|
||||
],
|
||||
[DefaultKeyBuilder(prefix=PREFIX), None, f"{PREFIX}:{CHAT_ID}:{USER_ID}"],
|
||||
],
|
||||
)
|
||||
async def test_generate_key(
|
||||
self,
|
||||
with_bot_id: bool,
|
||||
with_destiny: bool,
|
||||
key_builder: DefaultKeyBuilder,
|
||||
field: Optional[Literal["data", "state", "lock"]],
|
||||
result: str,
|
||||
):
|
||||
key_builder = DefaultKeyBuilder(
|
||||
prefix=PREFIX,
|
||||
with_bot_id=with_bot_id,
|
||||
with_destiny=with_destiny,
|
||||
key = StorageKey(
|
||||
chat_id=CHAT_ID,
|
||||
user_id=USER_ID,
|
||||
bot_id=BOT_ID,
|
||||
business_connection_id=BUSINESS_CONNECTION_ID,
|
||||
destiny=DEFAULT_DESTINY,
|
||||
)
|
||||
key = StorageKey(chat_id=CHAT_ID, user_id=USER_ID, bot_id=BOT_ID, destiny=DEFAULT_DESTINY)
|
||||
assert key_builder.build(key, field) == result
|
||||
|
||||
async def test_destiny_check(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue