-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
988 additions
and
598 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,78 @@ | ||
import re | ||
import unittest.mock | ||
|
||
import pydantic | ||
import pytest | ||
from pydantic import ValidationError | ||
|
||
from aiogram_broadcaster.contents.base import VALIDATOR_KEY, BaseContent | ||
|
||
|
||
class DummyContent(BaseContent, register=False): | ||
async def __call__(self, **kwargs): | ||
return {"method": "dummy_method", **kwargs} | ||
|
||
|
||
class TestBaseContent: | ||
def test_register_and_unregister(self): | ||
def test_registration(self): | ||
assert not DummyContent.is_registered() | ||
DummyContent.register() | ||
assert DummyContent.is_registered() | ||
DummyContent.unregister() | ||
assert not DummyContent.is_registered() | ||
with pytest.raises( | ||
TypeError, | ||
match="BaseContent cannot be registered.", | ||
): | ||
BaseContent.register() | ||
|
||
class RegisteredContent(BaseContent): | ||
pass | ||
|
||
class UnregisteredContent(BaseContent, register=False): | ||
pass | ||
|
||
assert RegisteredContent.is_registered() | ||
RegisteredContent.unregister() | ||
assert not RegisteredContent.is_registered() | ||
def test_validate_invalid_type(self): | ||
with pytest.raises(ValidationError): | ||
BaseContent.model_validate(object()) | ||
|
||
def test_double_registration(self): | ||
DummyContent.register() | ||
with pytest.raises( | ||
RuntimeError, | ||
match="The content 'RegisteredContent' is not registered.", | ||
match="The content 'DummyContent' is already registered.", | ||
): | ||
RegisteredContent.unregister() | ||
|
||
assert not UnregisteredContent.is_registered() | ||
UnregisteredContent.register() | ||
assert UnregisteredContent.is_registered() | ||
DummyContent.register() | ||
DummyContent.unregister() | ||
|
||
def test_unregister_non_registered(self): | ||
with pytest.raises( | ||
RuntimeError, | ||
match="The content 'UnregisteredContent' is already registered.", | ||
match="The content 'DummyContent' is not registered.", | ||
): | ||
UnregisteredContent.register() | ||
DummyContent.unregister() | ||
|
||
UnregisteredContent.unregister() | ||
async def test_callable(self): | ||
content = DummyContent() | ||
result = await content(param="value") | ||
assert result == {"method": "dummy_method", "param": "value"} | ||
|
||
async def test_as_method(self): | ||
MOCK_RESULT = unittest.mock.sentinel.RESULT | ||
|
||
class MyContent(BaseContent, register=False): | ||
async def __call__(self, test_param): | ||
MOCK_RESULT.test_param = test_param | ||
return MOCK_RESULT | ||
|
||
content = MyContent() | ||
result = await content.as_method(test_param=1) | ||
assert result is MOCK_RESULT | ||
assert result.test_param == 1 | ||
|
||
async def test_as_method_calls_callback(self): | ||
class MyContent(BaseContent, register=False, frozen=False): | ||
async def __call__(self, **kwargs): | ||
return | ||
|
||
content = MyContent() | ||
with unittest.mock.patch.object( | ||
target=MyContent, | ||
attribute="_callback", | ||
new_callable=unittest.mock.AsyncMock, | ||
) as mocked_callback: | ||
await content.as_method(test_param=1) | ||
mocked_callback.call.assert_called_once_with(content, test_param=1) | ||
|
||
def test_serialization(self): | ||
class MyContent(BaseContent, register=False): | ||
some_field: str | ||
|
||
async def __call__(self): | ||
return | ||
|
||
content = MyContent(some_field="test") | ||
serialized = content.model_dump() | ||
assert serialized[VALIDATOR_KEY] == "MyContent" | ||
assert serialized["some_field"] == "test" | ||
content = DummyContent() | ||
result = await content.as_method(param="value") | ||
assert result == {"method": "dummy_method", "param": "value"} | ||
|
||
def test_validation(self): | ||
class MyContent(BaseContent, register=True): | ||
some_field: str | ||
|
||
async def __call__(self): | ||
return | ||
|
||
with pytest.raises(pydantic.ValidationError): | ||
BaseContent.model_validate(object()) | ||
DummyContent.register() | ||
valid_data = {VALIDATOR_KEY: "DummyContent", "param": "value"} | ||
content = DummyContent.model_validate(valid_data) | ||
assert content.param == "value" | ||
|
||
invalid_data = {VALIDATOR_KEY: "NonExistentContent", "param": "value"} | ||
with pytest.raises( | ||
ValueError, | ||
ValidationError, | ||
match=re.escape( | ||
"Content 'NotExistsContent' has not been registered, " | ||
"you can register using the 'NotExistsContent.register()' method.", | ||
"Content 'NonExistentContent' has not been registered, " | ||
"you can register using the 'NonExistentContent.register()' method.", | ||
), | ||
): | ||
BaseContent.model_validate({VALIDATOR_KEY: "NotExistsContent", "some_field": "test"}) | ||
|
||
validated_content = BaseContent.model_validate({ | ||
VALIDATOR_KEY: "MyContent", | ||
"some_field": "test", | ||
}) | ||
assert isinstance(validated_content, MyContent) | ||
assert validated_content.some_field == "test" | ||
BaseContent.model_validate(invalid_data) | ||
DummyContent.unregister() | ||
|
||
MyContent.unregister() | ||
def test_serialization(self): | ||
content = DummyContent(param="value") | ||
serialized = content.model_dump() | ||
assert serialized[VALIDATOR_KEY] == "DummyContent" | ||
assert serialized["param"] == "value" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
import pytest | ||
from aiogram.methods.base import TelegramMethod | ||
from aiogram.methods.copy_message import CopyMessage | ||
from aiogram.methods.forward_message import ForwardMessage | ||
from aiogram.methods.send_animation import SendAnimation | ||
from aiogram.methods.send_audio import SendAudio | ||
from aiogram.methods.send_chat_action import SendChatAction | ||
from aiogram.methods.send_contact import SendContact | ||
from aiogram.methods.send_dice import SendDice | ||
from aiogram.methods.send_document import SendDocument | ||
from aiogram.methods.send_game import SendGame | ||
from aiogram.methods.send_invoice import SendInvoice | ||
from aiogram.methods.send_location import SendLocation | ||
from aiogram.methods.send_media_group import SendMediaGroup | ||
from aiogram.methods.send_message import SendMessage | ||
from aiogram.methods.send_photo import SendPhoto | ||
from aiogram.methods.send_poll import SendPoll | ||
from aiogram.methods.send_sticker import SendSticker | ||
from aiogram.methods.send_venue import SendVenue | ||
from aiogram.methods.send_video import SendVideo | ||
from aiogram.methods.send_video_note import SendVideoNote | ||
from aiogram.methods.send_voice import SendVoice | ||
|
||
from aiogram_broadcaster.contents.animation import AnimationContent | ||
from aiogram_broadcaster.contents.audio import AudioContent | ||
from aiogram_broadcaster.contents.chat_action import ChatActionContent | ||
from aiogram_broadcaster.contents.contact import ContactContent | ||
from aiogram_broadcaster.contents.dice import DiceContent | ||
from aiogram_broadcaster.contents.document import DocumentContent | ||
from aiogram_broadcaster.contents.from_chat import FromChatCopyContent, FromChatForwardContent | ||
from aiogram_broadcaster.contents.game import GameContent | ||
from aiogram_broadcaster.contents.invoice import InvoiceContent | ||
from aiogram_broadcaster.contents.location import LocationContent | ||
from aiogram_broadcaster.contents.media_group import MediaGroupContent | ||
from aiogram_broadcaster.contents.message import ( | ||
MessageCopyContent, | ||
MessageForwardContent, | ||
MessageSendContent, | ||
) | ||
from aiogram_broadcaster.contents.photo import PhotoContent | ||
from aiogram_broadcaster.contents.poll import PollContent | ||
from aiogram_broadcaster.contents.sticker import StickerContent | ||
from aiogram_broadcaster.contents.text import TextContent | ||
from aiogram_broadcaster.contents.venue import VenueContent | ||
from aiogram_broadcaster.contents.video import VideoContent | ||
from aiogram_broadcaster.contents.video_note import VideoNoteContent | ||
from aiogram_broadcaster.contents.voice import VoiceContent | ||
|
||
|
||
@pytest.mark.parametrize( | ||
("expected_method", "content_class", "content_data"), | ||
[ | ||
( | ||
SendAnimation, | ||
AnimationContent, | ||
{"animation": "test"}, | ||
), | ||
( | ||
SendAudio, | ||
AudioContent, | ||
{"audio": "test"}, | ||
), | ||
( | ||
SendChatAction, | ||
ChatActionContent, | ||
{"action": "test"}, | ||
), | ||
( | ||
SendContact, | ||
ContactContent, | ||
{"phone_number": "test", "first_name": "test"}, | ||
), | ||
( | ||
SendDice, | ||
DiceContent, | ||
{}, | ||
), | ||
( | ||
SendDocument, | ||
DocumentContent, | ||
{"document": "test"}, | ||
), | ||
( | ||
CopyMessage, | ||
FromChatCopyContent, | ||
{"from_chat_id": 0, "message_id": 0}, | ||
), | ||
( | ||
ForwardMessage, | ||
FromChatForwardContent, | ||
{"from_chat_id": 0, "message_id": 0}, | ||
), | ||
( | ||
SendGame, | ||
GameContent, | ||
{"game_short_name": "test"}, | ||
), | ||
( | ||
SendInvoice, | ||
InvoiceContent, | ||
{ | ||
"title": "test", | ||
"description": "test", | ||
"payload": "test", | ||
"provider_token": "test", | ||
"currency": "test", | ||
"prices": [{"label": "test", "amount": 0}], | ||
}, | ||
), | ||
( | ||
SendLocation, | ||
LocationContent, | ||
{"latitude": 0, "longitude": 0}, | ||
), | ||
( | ||
SendMediaGroup, | ||
MediaGroupContent, | ||
{"media": [{"media": "test"}]}, | ||
), | ||
( | ||
CopyMessage, | ||
MessageCopyContent, | ||
{"message": {"message_id": 0, "date": 0, "chat": {"id": 0, "type": "test"}}}, | ||
), | ||
( | ||
ForwardMessage, | ||
MessageForwardContent, | ||
{"message": {"message_id": 0, "date": 0, "chat": {"id": 0, "type": "test"}}}, | ||
), | ||
( | ||
TelegramMethod, | ||
MessageSendContent, | ||
{ | ||
"message": { | ||
"message_id": 0, | ||
"date": 0, | ||
"chat": {"id": 0, "type": "test"}, | ||
"text": "test", | ||
}, | ||
}, | ||
), | ||
( | ||
SendPhoto, | ||
PhotoContent, | ||
{"photo": "test"}, | ||
), | ||
( | ||
SendPoll, | ||
PollContent, | ||
{"question": "test", "options": ["test"]}, | ||
), | ||
( | ||
SendSticker, | ||
StickerContent, | ||
{"sticker": "test"}, | ||
), | ||
( | ||
SendMessage, | ||
TextContent, | ||
{"text": "test"}, | ||
), | ||
( | ||
SendVenue, | ||
VenueContent, | ||
{"latitude": 0, "longitude": 0, "title": "test", "address": "test"}, | ||
), | ||
( | ||
SendVideo, | ||
VideoContent, | ||
{"video": "test"}, | ||
), | ||
( | ||
SendVideoNote, | ||
VideoNoteContent, | ||
{"video_note": "test"}, | ||
), | ||
( | ||
SendVoice, | ||
VoiceContent, | ||
{"voice": "test"}, | ||
), | ||
], | ||
) | ||
class TestContents: | ||
async def test_as_method(self, expected_method, content_class, content_data): | ||
content = content_class(**content_data, test_extra=1) | ||
result = await content.as_method(chat_id=1) | ||
assert isinstance(result, expected_method) | ||
assert result.chat_id == 1 | ||
if content_class is MessageSendContent: | ||
pytest.skip( | ||
"MessageSendContent does not support passing extra, " | ||
"since the Message.send_copy method does not accept **kwargs.", | ||
) | ||
assert result.test_extra == 1 |
Oops, something went wrong.