Upgrade architecture + 5.0 Bot API (#469)

Upgrade architecture + 5.0 Bot API (#469)
* Moved `methods`, `types` and `client` to root package
* Removed update handler from routers to dispatcher
* Reworked events propagation mechanism to handlers
* Reworked inner middlewares logic (very small change)
* Updated to Bot API 5.0
* Initial migration from MkDocs to Sphinx + config for readthedocs
This commit is contained in:
Alex Root Junior 2021-01-26 21:20:52 +02:00 committed by GitHub
parent 566b7ff282
commit 4008a3114d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
608 changed files with 12537 additions and 6427 deletions

View file

@ -9,7 +9,9 @@ import aiogram
@contextmanager
def check_deprecated(
max_version: str, exception: Type[Exception], warning: Type[Warning] = DeprecationWarning,
max_version: str,
exception: Type[Exception],
warning: Type[Warning] = DeprecationWarning,
) -> None:
"""
Should be used for modules that are being deprecated or already removed from aiogram

View file

@ -2,10 +2,10 @@ from collections import deque
from typing import TYPE_CHECKING, AsyncGenerator, Deque, Optional, Type
from aiogram import Bot
from aiogram.api.client.session.base import BaseSession
from aiogram.api.methods import TelegramMethod
from aiogram.api.methods.base import Request, Response, T
from aiogram.api.types import UNSET
from aiogram.client.session.base import BaseSession
from aiogram.methods import TelegramMethod
from aiogram.methods.base import Request, Response, T
from aiogram.types import UNSET
class MockedSession(BaseSession):

View file

@ -1,4 +1,4 @@
from aiogram.api.client.telegram import PRODUCTION
from aiogram.client.telegram import PRODUCTION, TelegramAPIServer
class TestAPIServer:
@ -9,3 +9,13 @@ class TestAPIServer:
def test_file_url(self):
file_url = PRODUCTION.file_url(token="42:TEST", path="path")
assert file_url == "https://api.telegram.org/file/bot42:TEST/path"
def test_from_base(self):
local_server = TelegramAPIServer.from_base("http://localhost:8081", is_local=True)
method_url = local_server.api_url("42:TEST", method="apiMethod")
file_url = local_server.file_url(token="42:TEST", path="path")
assert method_url == "http://localhost:8081/bot42:TEST/apiMethod"
assert file_url == "http://localhost:8081/file/bot42:TEST/path"
assert local_server.is_local

View file

@ -5,15 +5,16 @@ import pytest
from aresponses import ResponsesMockServer
from aiogram import Bot
from aiogram.api.client.session.aiohttp import AiohttpSession
from aiogram.api.methods import GetFile, GetMe
from aiogram.api.types import File, PhotoSize
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.methods import GetFile, GetMe
from aiogram.types import File, PhotoSize
from tests.mocked_bot import MockedBot
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
class TestBot:
@ -38,7 +39,7 @@ class TestBot:
method = GetMe()
with patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.make_request",
"aiogram.client.session.aiohttp.AiohttpSession.make_request",
new_callable=CoroutineMock,
) as mocked_make_request:
await bot(method)
@ -51,16 +52,16 @@ class TestBot:
await session.create_session()
with patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
"aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
) as mocked_close:
await bot.close()
await bot.session.close()
mocked_close.assert_awaited()
@pytest.mark.asyncio
@pytest.mark.parametrize("close", [True, False])
async def test_context_manager(self, close: bool):
with patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
"aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
) as mocked_close:
async with Bot("42:TEST", session=AiohttpSession()).context(auto_close=close) as bot:
assert isinstance(bot, Bot)

View file

@ -1,20 +1,21 @@
from typing import AsyncContextManager, AsyncGenerator
import aiohttp
import aiohttp_socks
import pytest
from aresponses import ResponsesMockServer
from aiogram import Bot
from aiogram.api.client.session.aiohttp import AiohttpSession
from aiogram.api.methods import Request, TelegramMethod
from aiogram.api.types import InputFile, UNSET
from aiogram.client.session import aiohttp
from aiogram.client.session.aiohttp import AiohttpSession
from aiogram.methods import Request, TelegramMethod
from aiogram.types import InputFile, UNSET
from tests.mocked_bot import MockedBot
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
class BareInputFile(InputFile):
@ -172,7 +173,7 @@ class TestAiohttpSession:
call = TestMethod()
with patch(
"aiogram.api.client.session.base.BaseSession.raise_for_status"
"aiogram.client.session.base.BaseSession.raise_for_status"
) as patched_raise_for_status:
result = await session.make_request(bot, call)
assert isinstance(result, int)
@ -206,12 +207,12 @@ class TestAiohttpSession:
assert isinstance(session, AsyncContextManager)
with patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.create_session",
"aiogram.client.session.aiohttp.AiohttpSession.create_session",
new_callable=CoroutineMock,
) as mocked_create_session, patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
"aiogram.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock
) as mocked_close:
async with session as ctx:
assert session == ctx
mocked_close.awaited_once()
mocked_create_session.awaited_once()
mocked_close.assert_awaited_once()
mocked_create_session.assert_awaited_once()

View file

@ -4,15 +4,16 @@ from typing import AsyncContextManager, AsyncGenerator, Optional
import pytest
from aiogram.api.client.session.base import BaseSession, T
from aiogram.api.client.telegram import PRODUCTION, TelegramAPIServer
from aiogram.api.methods import GetMe, Response, TelegramMethod
from aiogram.api.types import UNSET
from aiogram.client.session.base import BaseSession, T
from aiogram.client.telegram import PRODUCTION, TelegramAPIServer
from aiogram.methods import GetMe, Response, TelegramMethod
from aiogram.types import UNSET
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
class CustomSession(BaseSession):
@ -169,4 +170,4 @@ class TestBaseSession:
) as mocked_close:
async with session as ctx:
assert session == ctx
mocked_close.awaited_once()
mocked_close.assert_awaited_once()

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import AddStickerToSet, Request
from aiogram.methods import AddStickerToSet, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import AnswerCallbackQuery, Request
from aiogram.methods import AnswerCallbackQuery, Request
from tests.mocked_bot import MockedBot

View file

@ -1,8 +1,8 @@
import pytest
from aiogram import Bot
from aiogram.api.methods import AnswerInlineQuery, Request
from aiogram.api.types import InlineQueryResult, InlineQueryResultPhoto
from aiogram.methods import AnswerInlineQuery, Request
from aiogram.types import InlineQueryResult, InlineQueryResultPhoto
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import AnswerPreCheckoutQuery, Request
from aiogram.methods import AnswerPreCheckoutQuery, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import AnswerShippingQuery, Request
from aiogram.methods import AnswerShippingQuery, Request
from tests.mocked_bot import MockedBot

View file

@ -3,7 +3,7 @@ from typing import Dict, Optional
import pytest
from aiogram import Bot
from aiogram.api.methods.base import prepare_parse_mode
from aiogram.methods.base import prepare_parse_mode
from tests.mocked_bot import MockedBot

View file

@ -0,0 +1,26 @@
import pytest
from aiogram.methods import Close, Request
from tests.mocked_bot import MockedBot
class TestClose:
@pytest.mark.asyncio
async def test_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(Close, ok=True, result=True)
response: bool = await Close()
request: Request = bot.get_request()
assert request.method == "close"
# assert request.data == {}
assert response == prepare_result.result
@pytest.mark.asyncio
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(Close, ok=True, result=True)
response: bool = await bot.close()
request: Request = bot.get_request()
assert request.method == "close"
# assert request.data == {}
assert response == prepare_result.result

View file

@ -0,0 +1,35 @@
import pytest
from aiogram.methods import CopyMessage, Request
from aiogram.types import MessageId
from tests.mocked_bot import MockedBot
class TestCopyMessage:
@pytest.mark.asyncio
async def test_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(CopyMessage, ok=True, result=MessageId(message_id=42))
response: MessageId = await CopyMessage(
chat_id=42,
from_chat_id=42,
message_id=42,
)
request: Request = bot.get_request()
assert request.method == "copyMessage"
# assert request.data == {}
assert response == prepare_result.result
@pytest.mark.asyncio
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(CopyMessage, ok=True, result=MessageId(message_id=42))
response: MessageId = await bot.copy_message(
chat_id=42,
from_chat_id=42,
message_id=42,
)
request: Request = bot.get_request()
assert request.method == "copyMessage"
# assert request.data == {}
assert response == prepare_result.result

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import CreateNewStickerSet, Request
from aiogram.methods import CreateNewStickerSet, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import DeleteChatPhoto, Request
from aiogram.methods import DeleteChatPhoto, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import DeleteChatStickerSet, Request
from aiogram.methods import DeleteChatStickerSet, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import DeleteMessage, Request
from aiogram.methods import DeleteMessage, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import DeleteStickerFromSet, Request
from aiogram.methods import DeleteStickerFromSet, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import DeleteWebhook, Request
from aiogram.methods import DeleteWebhook, Request
from tests.mocked_bot import MockedBot

View file

@ -3,8 +3,8 @@ from typing import Union
import pytest
from aiogram.api.methods import EditMessageCaption, Request
from aiogram.api.types import Chat, Message
from aiogram.methods import EditMessageCaption, Request
from aiogram.types import Chat, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import EditMessageLiveLocation, Request
from aiogram.api.types import Message
from aiogram.methods import EditMessageLiveLocation, Request
from aiogram.types import Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import EditMessageMedia, Request
from aiogram.api.types import BufferedInputFile, InputMedia, InputMediaPhoto, Message
from aiogram.methods import EditMessageMedia, Request
from aiogram.types import BufferedInputFile, InputMedia, InputMediaPhoto, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import EditMessageReplyMarkup, Request
from aiogram.api.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from aiogram.methods import EditMessageReplyMarkup, Request
from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import EditMessageText, Request
from aiogram.api.types import Message
from aiogram.methods import EditMessageText, Request
from aiogram.types import Message
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import ExportChatInviteLink, Request
from aiogram.methods import ExportChatInviteLink, Request
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import ForwardMessage, Request
from aiogram.api.types import Chat, Message
from aiogram.methods import ForwardMessage, Request
from aiogram.types import Chat, Message
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetChat, Request
from aiogram.api.types import Chat
from aiogram.methods import GetChat, Request
from aiogram.types import Chat
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import List
import pytest
from aiogram.api.methods import GetChatAdministrators, Request
from aiogram.api.types import ChatMember, User
from aiogram.methods import GetChatAdministrators, Request
from aiogram.types import ChatMember, User
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetChatMember, Request
from aiogram.api.types import ChatMember, User
from aiogram.methods import GetChatMember, Request
from aiogram.types import ChatMember, User
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import GetChatMembersCount, Request
from aiogram.methods import GetChatMembersCount, Request
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetFile, Request
from aiogram.api.types import File
from aiogram.methods import GetFile, Request
from aiogram.types import File
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import List
import pytest
from aiogram.api.methods import GetGameHighScores, Request
from aiogram.api.types import GameHighScore, User
from aiogram.methods import GetGameHighScores, Request
from aiogram.types import GameHighScore, User
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetMe, Request
from aiogram.api.types import User
from aiogram.methods import GetMe, Request
from aiogram.types import User
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import List
import pytest
from aiogram.api.methods import GetMyCommands, Request
from aiogram.api.types import BotCommand
from aiogram.methods import GetMyCommands, Request
from aiogram.types import BotCommand
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetStickerSet, Request
from aiogram.api.types import Sticker, StickerSet
from aiogram.methods import GetStickerSet, Request
from aiogram.types import Sticker, StickerSet
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import List
import pytest
from aiogram.api.methods import GetUpdates, Request
from aiogram.api.types import Update
from aiogram.methods import GetUpdates, Request
from aiogram.types import Update
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetUserProfilePhotos, Request
from aiogram.api.types import PhotoSize, UserProfilePhotos
from aiogram.methods import GetUserProfilePhotos, Request
from aiogram.types import PhotoSize, UserProfilePhotos
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import GetWebhookInfo, Request
from aiogram.api.types import WebhookInfo
from aiogram.methods import GetWebhookInfo, Request
from aiogram.types import WebhookInfo
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import KickChatMember, Request
from aiogram.methods import KickChatMember, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import LeaveChat, Request
from aiogram.methods import LeaveChat, Request
from tests.mocked_bot import MockedBot

View file

@ -0,0 +1,26 @@
import pytest
from aiogram.methods import LogOut, Request
from tests.mocked_bot import MockedBot
class TestLogOut:
@pytest.mark.asyncio
async def test_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(LogOut, ok=True, result=True)
response: bool = await LogOut()
request: Request = bot.get_request()
assert request.method == "logOut"
# assert request.data == {}
assert response == prepare_result.result
@pytest.mark.asyncio
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(LogOut, ok=True, result=True)
response: bool = await bot.log_out()
request: Request = bot.get_request()
assert request.method == "logOut"
# assert request.data == {}
assert response == prepare_result.result

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import PinChatMessage, Request
from aiogram.methods import PinChatMessage, Request
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import PromoteChatMember, Request
from aiogram.methods import PromoteChatMember, Request
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, RestrictChatMember
from aiogram.api.types import ChatPermissions
from aiogram.methods import Request, RestrictChatMember
from aiogram.types import ChatPermissions
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendAnimation
from aiogram.api.types import Animation, Chat, Message
from aiogram.methods import Request, SendAnimation
from aiogram.types import Animation, Chat, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendAudio
from aiogram.api.types import Audio, Chat, File, Message
from aiogram.methods import Request, SendAudio
from aiogram.types import Audio, Chat, File, Message
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SendChatAction
from aiogram.methods import Request, SendChatAction
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendContact
from aiogram.api.types import Chat, Contact, Message
from aiogram.methods import Request, SendContact
from aiogram.types import Chat, Contact, Message
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, SendDice
from aiogram.api.types import Message
from aiogram.methods import Request, SendDice
from aiogram.types import Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendDocument
from aiogram.api.types import Chat, Document, Message
from aiogram.methods import Request, SendDocument
from aiogram.types import Chat, Document, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendGame
from aiogram.api.types import Chat, Game, Message, PhotoSize
from aiogram.methods import Request, SendGame
from aiogram.types import Chat, Game, Message, PhotoSize
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendInvoice
from aiogram.api.types import Chat, Invoice, LabeledPrice, Message
from aiogram.methods import Request, SendInvoice
from aiogram.types import Chat, Invoice, LabeledPrice, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendLocation
from aiogram.api.types import Chat, Location, Message
from aiogram.methods import Request, SendLocation
from aiogram.types import Chat, Location, Message
from tests.mocked_bot import MockedBot

View file

@ -3,8 +3,8 @@ from typing import List
import pytest
from aiogram.api.methods import Request, SendMediaGroup
from aiogram.api.types import (
from aiogram.methods import Request, SendMediaGroup
from aiogram.types import (
BufferedInputFile,
Chat,
InputMediaPhoto,

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendMessage
from aiogram.api.types import Chat, Message
from aiogram.methods import Request, SendMessage
from aiogram.types import Chat, Message
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendPhoto
from aiogram.api.types import Chat, Message, PhotoSize
from aiogram.methods import Request, SendPhoto
from aiogram.types import Chat, Message, PhotoSize
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendPoll
from aiogram.api.types import Chat, Message, Poll, PollOption
from aiogram.methods import Request, SendPoll
from aiogram.types import Chat, Message, Poll, PollOption
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendSticker
from aiogram.api.types import Chat, Message, Sticker
from aiogram.methods import Request, SendSticker
from aiogram.types import Chat, Message, Sticker
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendVenue
from aiogram.api.types import Chat, Location, Message, Venue
from aiogram.methods import Request, SendVenue
from aiogram.types import Chat, Location, Message, Venue
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendVideo
from aiogram.api.types import Chat, Message, Video
from aiogram.methods import Request, SendVideo
from aiogram.types import Chat, Message, Video
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendVideoNote
from aiogram.api.types import BufferedInputFile, Chat, Message, VideoNote
from aiogram.methods import Request, SendVideoNote
from aiogram.types import BufferedInputFile, Chat, Message, VideoNote
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ import datetime
import pytest
from aiogram.api.methods import Request, SendVoice
from aiogram.api.types import Chat, Message, Voice
from aiogram.methods import Request, SendVoice
from aiogram.types import Chat, Message, Voice
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetChatAdministratorCustomTitle, SetChatTitle
from aiogram.methods import Request, SetChatAdministratorCustomTitle, SetChatTitle
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetChatDescription
from aiogram.methods import Request, SetChatDescription
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, SetChatPermissions
from aiogram.api.types import ChatPermissions
from aiogram.methods import Request, SetChatPermissions
from aiogram.types import ChatPermissions
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, SetChatPhoto
from aiogram.api.types import BufferedInputFile, InputFile
from aiogram.methods import Request, SetChatPhoto
from aiogram.types import BufferedInputFile, InputFile
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetChatStickerSet
from aiogram.methods import Request, SetChatStickerSet
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetChatTitle
from aiogram.methods import Request, SetChatTitle
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import Request, SetGameScore
from aiogram.api.types import Message
from aiogram.methods import Request, SetGameScore
from aiogram.types import Message
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, SetMyCommands
from aiogram.api.types import BotCommand
from aiogram.methods import Request, SetMyCommands
from aiogram.types import BotCommand
from tests.mocked_bot import MockedBot
@ -21,7 +21,9 @@ class TestSetMyCommands:
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(SetMyCommands, ok=True, result=None)
response: bool = await bot.set_my_commands(commands=[],)
response: bool = await bot.set_my_commands(
commands=[],
)
request: Request = bot.get_request()
assert request.method == "setMyCommands"
# assert request.data == {}

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, SetPassportDataErrors
from aiogram.api.types import PassportElementError
from aiogram.methods import Request, SetPassportDataErrors
from aiogram.types import PassportElementError
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetStickerPositionInSet
from aiogram.methods import Request, SetStickerPositionInSet
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetStickerSetThumb
from aiogram.methods import Request, SetStickerSetThumb
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, SetWebhook
from aiogram.methods import Request, SetWebhook
from tests.mocked_bot import MockedBot

View file

@ -2,8 +2,8 @@ from typing import Union
import pytest
from aiogram.api.methods import Request, StopMessageLiveLocation
from aiogram.api.types import Message
from aiogram.methods import Request, StopMessageLiveLocation
from aiogram.types import Message
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, StopPoll
from aiogram.api.types import Poll, PollOption
from aiogram.methods import Request, StopPoll
from aiogram.types import Poll, PollOption
from tests.mocked_bot import MockedBot

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, UnbanChatMember
from aiogram.methods import Request, UnbanChatMember
from tests.mocked_bot import MockedBot

View file

@ -0,0 +1,30 @@
import pytest
from aiogram.methods import Request, UnpinAllChatMessages
from tests.mocked_bot import MockedBot
class TestUnpinAllChatMessages:
@pytest.mark.asyncio
async def test_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(UnpinAllChatMessages, ok=True, result=True)
response: bool = await UnpinAllChatMessages(
chat_id=42,
)
request: Request = bot.get_request()
assert request.method == "unpinAllChatMessages"
# assert request.data == {}
assert response == prepare_result.result
@pytest.mark.asyncio
async def test_bot_method(self, bot: MockedBot):
prepare_result = bot.add_result_for(UnpinAllChatMessages, ok=True, result=True)
response: bool = await bot.unpin_all_chat_messages(
chat_id=42,
)
request: Request = bot.get_request()
assert request.method == "unpinAllChatMessages"
# assert request.data == {}
assert response == prepare_result.result

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.methods import Request, UnpinChatMessage
from aiogram.methods import Request, UnpinChatMessage
from tests.mocked_bot import MockedBot

View file

@ -1,7 +1,7 @@
import pytest
from aiogram.api.methods import Request, UploadStickerFile
from aiogram.api.types import BufferedInputFile, File
from aiogram.methods import Request, UploadStickerFile
from aiogram.types import BufferedInputFile, File
from tests.mocked_bot import MockedBot

View file

@ -1,5 +1,5 @@
from aiogram.api.methods import AnswerCallbackQuery
from aiogram.api.types import CallbackQuery, User
from aiogram.methods import AnswerCallbackQuery
from aiogram.types import CallbackQuery, User
class TestCallbackQuery:

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.types import ChatMember, User
from aiogram.types import ChatMember, User
user = User(id=42, is_bot=False, first_name="User", last_name=None)

View file

@ -1,5 +1,5 @@
from aiogram.api.methods import AnswerInlineQuery
from aiogram.api.types import InlineQuery, User
from aiogram.methods import AnswerInlineQuery
from aiogram.types import InlineQuery, User
class TestInlineQuery:

View file

@ -4,7 +4,7 @@ import pytest
from aresponses import ResponsesMockServer
from aiogram import Bot
from aiogram.api.types import BufferedInputFile, FSInputFile, InputFile, URLInputFile
from aiogram.types import BufferedInputFile, FSInputFile, InputFile, URLInputFile
class TestInputFile:

View file

@ -3,7 +3,7 @@ from typing import Any, Dict, Type, Union
import pytest
from aiogram.api.methods import (
from aiogram.methods import (
SendAnimation,
SendAudio,
SendContact,
@ -22,7 +22,7 @@ from aiogram.api.methods import (
SendVideoNote,
SendVoice,
)
from aiogram.api.types import (
from aiogram.types import (
Animation,
Audio,
Chat,
@ -45,7 +45,7 @@ from aiogram.api.types import (
VideoNote,
Voice,
)
from aiogram.api.types.message import ContentType, Message
from aiogram.types.message import ContentType, Message
class TestMessage:
@ -448,7 +448,12 @@ class TestMessage:
["sticker", dict(sticker="sticker"), SendSticker],
[
"venue",
dict(latitude=0.42, longitude=0.42, title="title", address="address",),
dict(
latitude=0.42,
longitude=0.42,
title="title",
address="address",
),
SendVenue,
],
["video", dict(video="video"), SendVideo],

View file

@ -1,5 +1,5 @@
from aiogram.api.methods import AnswerPreCheckoutQuery
from aiogram.api.types import PreCheckoutQuery, User
from aiogram.methods import AnswerPreCheckoutQuery
from aiogram.types import PreCheckoutQuery, User
class TestPreCheckoutQuery:

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.types import ReplyKeyboardRemove
from aiogram.types import ReplyKeyboardRemove
class TestReplyKeyboardRemove:
@ -14,8 +14,7 @@ class TestReplyKeyboardRemove:
), "Remove keyboard has incorrect default value!"
@pytest.mark.parametrize(
"kwargs,expected",
[[{}, True], [{"remove_keyboard": True}, True], [{"remove_keyboard": False}, False]],
"kwargs,expected", [[{}, True], [{"remove_keyboard": True}, True]],
)
def test_remove_keyboard_values(self, kwargs, expected):
assert ReplyKeyboardRemove(**kwargs).remove_keyboard is expected

View file

@ -1,5 +1,5 @@
from aiogram.api.methods import AnswerShippingQuery
from aiogram.api.types import LabeledPrice, ShippingAddress, ShippingOption, ShippingQuery, User
from aiogram.methods import AnswerShippingQuery
from aiogram.types import LabeledPrice, ShippingAddress, ShippingOption, ShippingQuery, User
class TestInlineQuery:

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.api.types import User
from aiogram.types import User
class TestUser:

View file

@ -17,7 +17,6 @@ OBSERVERS = {
"poll_answer",
"pre_checkout_query",
"shipping_query",
"update",
}
DEPRECATED_OBSERVERS = {observer + "_handler" for observer in OBSERVERS}

View file

@ -2,30 +2,47 @@ import asyncio
import datetime
import time
import warnings
from typing import Any
import pytest
from aiogram import Bot
from aiogram.api.methods import GetMe, GetUpdates, SendMessage
from aiogram.api.types import Chat, Message, Update, User
from aiogram.dispatcher.dispatcher import Dispatcher
from aiogram.dispatcher.event.bases import NOT_HANDLED
from aiogram.dispatcher.event.bases import UNHANDLED, SkipHandler
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 (
CallbackQuery,
Chat,
ChosenInlineResult,
InlineQuery,
Message,
Poll,
PollAnswer,
PollOption,
PreCheckoutQuery,
ShippingAddress,
ShippingQuery,
Update,
User,
)
from tests.mocked_bot import MockedBot
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
async def simple_message_handler(message: Message):
await asyncio.sleep(1.5)
await asyncio.sleep(0.2)
return message.answer("ok")
async def invalid_message_handler(message: Message):
await asyncio.sleep(1.5)
await asyncio.sleep(0.2)
raise Exception(42)
@ -43,6 +60,13 @@ UPDATE = Update(**RAW_UPDATE)
class TestDispatcher:
def test_init(self):
dp = Dispatcher()
assert dp.update.handlers
assert dp.update.handlers[0].callback == dp._listen_update
assert dp.update.outer_middlewares
def test_parent_router(self):
dp = Dispatcher()
with pytest.raises(RuntimeError):
@ -145,6 +169,290 @@ class TestDispatcher:
assert await dispatcher._process_update(bot=bot, update=Update(update_id=42))
@pytest.mark.asyncio
@pytest.mark.parametrize(
"event_type,update,has_chat,has_user",
[
pytest.param(
"message",
Update(
update_id=42,
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(
"edited_message",
Update(
update_id=42,
edited_message=Message(
message_id=42,
date=datetime.datetime.now(),
text="edited test",
chat=Chat(id=42, type="private"),
from_user=User(id=42, is_bot=False, first_name="Test"),
),
),
True,
True,
),
pytest.param(
"channel_post",
Update(
update_id=42,
channel_post=Message(
message_id=42,
date=datetime.datetime.now(),
text="test",
chat=Chat(id=-42, type="private"),
),
),
True,
False,
),
pytest.param(
"edited_channel_post",
Update(
update_id=42,
edited_channel_post=Message(
message_id=42,
date=datetime.datetime.now(),
text="test",
chat=Chat(id=-42, type="private"),
),
),
True,
False,
),
pytest.param(
"inline_query",
Update(
update_id=42,
inline_query=InlineQuery(
id="query id",
from_user=User(id=42, is_bot=False, first_name="Test"),
query="query",
offset="offser",
),
),
False,
True,
),
pytest.param(
"chosen_inline_result",
Update(
update_id=42,
chosen_inline_result=ChosenInlineResult(
result_id="result id",
from_user=User(id=42, is_bot=False, first_name="Test"),
query="query",
),
),
False,
True,
),
pytest.param(
"callback_query",
Update(
update_id=42,
callback_query=CallbackQuery(
id="query id",
from_user=User(id=42, is_bot=False, first_name="Test"),
chat_instance="instance",
data="placeholder",
),
),
False,
True,
),
pytest.param(
"callback_query",
Update(
update_id=42,
callback_query=CallbackQuery(
id="query id",
from_user=User(id=42, is_bot=False, first_name="Test"),
chat_instance="instance",
data="placeholder",
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(
"shipping_query",
Update(
update_id=42,
shipping_query=ShippingQuery(
id="id",
from_user=User(id=42, is_bot=False, first_name="Test"),
invoice_payload="payload",
shipping_address=ShippingAddress(
country_code="placeholder",
state="placeholder",
city="placeholder",
street_line1="placeholder",
street_line2="placeholder",
post_code="placeholder",
),
),
),
False,
True,
),
pytest.param(
"pre_checkout_query",
Update(
update_id=42,
pre_checkout_query=PreCheckoutQuery(
id="query id",
from_user=User(id=42, is_bot=False, first_name="Test"),
currency="BTC",
total_amount=1,
invoice_payload="payload",
),
),
False,
True,
),
pytest.param(
"poll",
Update(
update_id=42,
poll=Poll(
id="poll id",
question="Q?",
options=[
PollOption(text="A1", voter_count=2),
PollOption(text="A2", voter_count=3),
],
is_closed=False,
is_anonymous=False,
type="quiz",
allows_multiple_answers=False,
total_voter_count=0,
correct_option_id=1,
),
),
False,
False,
),
pytest.param(
"poll_answer",
Update(
update_id=42,
poll_answer=PollAnswer(
poll_id="poll id",
user=User(id=42, is_bot=False, first_name="Test"),
option_ids=[42],
),
),
False,
True,
),
],
)
async def test_listen_update(
self, event_type: str, update: Update, has_chat: bool, has_user: bool
):
router = Dispatcher()
observer = router.observers[event_type]
@observer()
async def my_handler(event: Any, **kwargs: Any):
assert event == getattr(update, event_type)
if has_chat:
assert Chat.get_current(False)
if has_user:
assert User.get_current(False)
return kwargs
result = await router.update.trigger(update, test="PASS")
assert isinstance(result, dict)
assert result["event_update"] == update
assert result["event_router"] == router
assert result["test"] == "PASS"
@pytest.mark.asyncio
async def test_listen_unknown_update(self):
dp = Dispatcher()
with pytest.raises(SkipHandler):
await dp._listen_update(Update(update_id=42))
@pytest.mark.asyncio
async def test_listen_unhandled_update(self):
dp = Dispatcher()
observer = dp.observers["message"]
@observer(lambda event: False)
async def handler(event: Any):
pass
response = await dp._listen_update(
Update(
update_id=42,
poll=Poll(
id="poll id",
question="Q?",
options=[
PollOption(text="A1", voter_count=2),
PollOption(text="A2", voter_count=3),
],
is_closed=False,
is_anonymous=False,
type="quiz",
allows_multiple_answers=False,
total_voter_count=0,
correct_option_id=0,
),
)
)
assert response is UNHANDLED
@pytest.mark.asyncio
async def test_nested_router_listen_update(self):
dp = Dispatcher()
router0 = Router()
router1 = Router()
dp.include_router(router0)
router0.include_router(router1)
observer = router1.message
@observer()
async def my_handler(event: Message, **kwargs: Any):
return kwargs
update = Update(
update_id=42,
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"),
),
)
result = await dp._listen_update(update, test="PASS")
assert isinstance(result, dict)
assert result["event_update"] == update
assert result["event_router"] == router1
assert result["test"] == "PASS"
@pytest.mark.asyncio
async def test_process_update_call_request(self, bot: MockedBot):
dispatcher = Dispatcher()
@ -192,6 +500,45 @@ class TestDispatcher:
await dispatcher._polling(bot=bot)
mocked_process_update.assert_awaited()
@pytest.mark.asyncio
async def test_exception_handler_catch_exceptions(self):
dp = Dispatcher()
router = Router()
dp.include_router(router)
@router.message()
async def message_handler(message: Message):
raise Exception("KABOOM")
update = Update(
update_id=42,
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"),
),
)
with pytest.raises(Exception, match="KABOOM"):
await dp.update.trigger(update)
@router.errors()
async def error_handler(event: Update, exception: Exception):
return "KABOOM"
response = await dp.update.trigger(update)
assert response == "KABOOM"
@dp.errors()
async def root_error_handler(event: Update, exception: Exception):
return exception
response = await dp.update.trigger(update)
assert isinstance(response, Exception)
assert str(response) == "KABOOM"
@pytest.mark.asyncio
async def test_start_polling(self, bot: MockedBot):
dispatcher = Dispatcher()
@ -231,21 +578,11 @@ class TestDispatcher:
dispatcher = Dispatcher()
dispatcher.message.register(simple_message_handler)
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=2)
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.3)
assert isinstance(response, dict)
assert response["method"] == "sendMessage"
assert response["text"] == "ok"
# @pytest.mark.asyncio
# async def test_feed_webhook_update_fast_process_error(self, bot: MockedBot):
# dispatcher = Dispatcher()
# dispatcher.message_handler.register(invalid_message_handler)
#
# response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=2)
# assert isinstance(response, dict)
# assert response["method"] == "sendMessage"
# assert response["text"] == "ok"
@pytest.mark.asyncio
async def test_feed_webhook_update_slow_process(self, bot: MockedBot, recwarn):
warnings.simplefilter("always")
@ -257,9 +594,9 @@ class TestDispatcher:
"aiogram.dispatcher.dispatcher.Dispatcher._silent_call_request",
new_callable=CoroutineMock,
) as mocked_silent_call_request:
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=1)
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1)
assert response is None
await asyncio.sleep(1)
await asyncio.sleep(0.2)
mocked_silent_call_request.assert_awaited()
@pytest.mark.asyncio
@ -269,9 +606,9 @@ class TestDispatcher:
dispatcher = Dispatcher()
dispatcher.message.register(invalid_message_handler)
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=1)
response = await dispatcher.feed_webhook_update(bot, RAW_UPDATE, _timeout=0.1)
assert response is None
await asyncio.sleep(1)
await asyncio.sleep(0.2)
log_records = [rec.message for rec in caplog.records]
assert "Cause exception while process update" in log_records[0]

View file

@ -9,7 +9,8 @@ from aiogram.dispatcher.event.handler import HandlerObject
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
async def my_handler(value: str, index: int = 0) -> Any:
@ -51,7 +52,8 @@ class TestEventObserver:
assert observer.handlers[2].awaitable
with patch(
"aiogram.dispatcher.event.handler.CallableMixin.call", new_callable=CoroutineMock,
"aiogram.dispatcher.event.handler.CallableMixin.call",
new_callable=CoroutineMock,
) as mocked_my_handler:
results = await observer.trigger("test")
assert results is None

View file

@ -3,11 +3,11 @@ from typing import Any, Dict, Union
import pytest
from aiogram.api.types import Update
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
def callback1(foo: int, bar: int, baz: int):

View file

@ -4,12 +4,12 @@ from typing import Any, Awaitable, Callable, Dict, NoReturn, Union
import pytest
from aiogram.api.types import Chat, Message, User
from aiogram.dispatcher.event.bases import SkipHandler
from aiogram.dispatcher.event.handler import HandlerObject
from aiogram.dispatcher.event.telegram import TelegramEventObserver
from aiogram.dispatcher.filters.base import BaseFilter
from aiogram.dispatcher.router import Router
from aiogram.types import Chat, Message, User
# TODO: Test middlewares in routers tree

View file

@ -7,7 +7,8 @@ from aiogram.dispatcher.filters.base import BaseFilter
try:
from asynctest import CoroutineMock, patch
except ImportError:
from unittest.mock import AsyncMock as CoroutineMock, patch # type: ignore
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
from unittest.mock import patch
class MyFilter(BaseFilter):

View file

@ -4,9 +4,9 @@ from typing import Match
import pytest
from aiogram.api.methods import GetMe
from aiogram.api.types import Chat, Message, User
from aiogram.dispatcher.filters import Command, CommandObject
from aiogram.methods import GetMe
from aiogram.types import Chat, Message, User
from tests.mocked_bot import MockedBot

View file

@ -4,8 +4,8 @@ from typing import cast
import pytest
from pydantic import ValidationError
from aiogram.api.types import ContentType, Message
from aiogram.dispatcher.filters import ContentTypesFilter
from aiogram.types import ContentType, Message
@dataclass

View file

@ -5,9 +5,9 @@ from typing import Sequence, Type
import pytest
from pydantic import ValidationError
from aiogram.api.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User
from aiogram.dispatcher.filters import BUILTIN_FILTERS
from aiogram.dispatcher.filters.text import Text
from aiogram.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User
class TestText:

View file

@ -6,9 +6,9 @@ from typing import Any
import pytest
from aiogram import Bot
from aiogram.api.types import Chat, Message, Update
from aiogram.dispatcher.event.handler import HandlerObject
from aiogram.dispatcher.handler.base import BaseHandler
from aiogram.types import Chat, Message, Update
class MyHandler(BaseHandler):

Some files were not shown because too many files have changed in this diff Show more