mirror of
https://github.com/aiogram/aiogram.git
synced 2025-12-12 10:11:52 +00:00
Dev 3.x flat package (#961)
* Move packages * Added changelog * Update examples/echo_bot.py Co-authored-by: Oleg A. <t0rr@mail.ru> * Rename `handler` -> `handlers` * Update __init__.py Co-authored-by: Oleg A. <t0rr@mail.ru>
This commit is contained in:
parent
5e7932ca20
commit
4315ecf1a2
111 changed files with 376 additions and 390 deletions
0
tests/test_filters/__init__.py
Normal file
0
tests/test_filters/__init__.py
Normal file
35
tests/test_filters/test_base.py
Normal file
35
tests/test_filters/test_base.py
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
from typing import Awaitable
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.filters import BaseFilter
|
||||
|
||||
try:
|
||||
from asynctest import CoroutineMock, patch
|
||||
except ImportError:
|
||||
from unittest.mock import AsyncMock as CoroutineMock # type: ignore
|
||||
from unittest.mock import patch
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class MyFilter(BaseFilter):
|
||||
foo: str
|
||||
|
||||
async def __call__(self, event: str):
|
||||
return
|
||||
|
||||
|
||||
class TestBaseFilter:
|
||||
async def test_awaitable(self):
|
||||
my_filter = MyFilter(foo="bar")
|
||||
|
||||
assert isinstance(my_filter, Awaitable)
|
||||
|
||||
with patch(
|
||||
"tests.test_filters.test_base.MyFilter.__call__",
|
||||
new_callable=CoroutineMock,
|
||||
) as mocked_call:
|
||||
call = my_filter(event="test")
|
||||
await call
|
||||
mocked_call.assert_awaited_with(event="test")
|
||||
181
tests/test_filters/test_callback_data.py
Normal file
181
tests/test_filters/test_callback_data.py
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
from decimal import Decimal
|
||||
from enum import Enum, auto
|
||||
from fractions import Fraction
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
from magic_filter import MagicFilter
|
||||
from pydantic import ValidationError
|
||||
|
||||
from aiogram import F
|
||||
from aiogram.filters.callback_data import CallbackData
|
||||
from aiogram.types import CallbackQuery, User
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class MyIntEnum(Enum):
|
||||
FOO = auto()
|
||||
|
||||
|
||||
class MyStringEnum(str, Enum):
|
||||
FOO = "FOO"
|
||||
|
||||
|
||||
class MyCallback(CallbackData, prefix="test"):
|
||||
foo: str
|
||||
bar: int
|
||||
|
||||
|
||||
class TestCallbackData:
|
||||
def test_init_subclass_prefix_required(self):
|
||||
assert MyCallback.__prefix__ == "test"
|
||||
|
||||
with pytest.raises(ValueError, match="prefix required.+"):
|
||||
|
||||
class MyInvalidCallback(CallbackData):
|
||||
pass
|
||||
|
||||
def test_init_subclass_sep_validation(self):
|
||||
assert MyCallback.__separator__ == ":"
|
||||
|
||||
class MyCallback2(CallbackData, prefix="test2", sep="@"):
|
||||
pass
|
||||
|
||||
assert MyCallback2.__separator__ == "@"
|
||||
|
||||
with pytest.raises(ValueError, match="Separator symbol '@' .+ 'sp@m'"):
|
||||
|
||||
class MyInvalidCallback(CallbackData, prefix="sp@m", sep="@"):
|
||||
pass
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"value,success,expected",
|
||||
[
|
||||
[None, True, ""],
|
||||
[42, True, "42"],
|
||||
["test", True, "test"],
|
||||
[9.99, True, "9.99"],
|
||||
[Decimal("9.99"), True, "9.99"],
|
||||
[Fraction("3/2"), True, "3/2"],
|
||||
[
|
||||
UUID("123e4567-e89b-12d3-a456-426655440000"),
|
||||
True,
|
||||
"123e4567-e89b-12d3-a456-426655440000",
|
||||
],
|
||||
[MyIntEnum.FOO, True, "1"],
|
||||
[MyStringEnum.FOO, True, "FOO"],
|
||||
[..., False, "..."],
|
||||
[object, False, "..."],
|
||||
[object(), False, "..."],
|
||||
[User(id=42, is_bot=False, first_name="test"), False, "..."],
|
||||
],
|
||||
)
|
||||
def test_encode_value(self, value, success, expected):
|
||||
callback = MyCallback(foo="test", bar=42)
|
||||
if success:
|
||||
assert callback._encode_value("test", value) == expected
|
||||
else:
|
||||
with pytest.raises(ValueError):
|
||||
assert callback._encode_value("test", value) == expected
|
||||
|
||||
def test_pack(self):
|
||||
with pytest.raises(ValueError, match="Separator symbol .+"):
|
||||
assert MyCallback(foo="te:st", bar=42).pack()
|
||||
|
||||
with pytest.raises(ValueError, match=".+is too long.+"):
|
||||
assert MyCallback(foo="test" * 32, bar=42).pack()
|
||||
|
||||
assert MyCallback(foo="test", bar=42).pack() == "test:test:42"
|
||||
|
||||
def test_pack_optional(self):
|
||||
class MyCallback1(CallbackData, prefix="test1"):
|
||||
foo: str
|
||||
bar: Optional[int] = None
|
||||
|
||||
assert MyCallback1(foo="spam").pack() == "test1:spam:"
|
||||
assert MyCallback1(foo="spam", bar=42).pack() == "test1:spam:42"
|
||||
|
||||
class MyCallback2(CallbackData, prefix="test2"):
|
||||
foo: Optional[str] = None
|
||||
bar: int
|
||||
|
||||
assert MyCallback2(bar=42).pack() == "test2::42"
|
||||
assert MyCallback2(foo="spam", bar=42).pack() == "test2:spam:42"
|
||||
|
||||
class MyCallback3(CallbackData, prefix="test3"):
|
||||
foo: Optional[str] = "experiment"
|
||||
bar: int
|
||||
|
||||
assert MyCallback3(bar=42).pack() == "test3:experiment:42"
|
||||
assert MyCallback3(foo="spam", bar=42).pack() == "test3:spam:42"
|
||||
|
||||
def test_unpack(self):
|
||||
with pytest.raises(TypeError, match=".+ takes 2 arguments but 3 were given"):
|
||||
MyCallback.unpack("test:test:test:test")
|
||||
|
||||
with pytest.raises(ValueError, match="Bad prefix .+"):
|
||||
MyCallback.unpack("spam:test:test")
|
||||
|
||||
assert MyCallback.unpack("test:test:42") == MyCallback(foo="test", bar=42)
|
||||
|
||||
def test_unpack_optional(self):
|
||||
with pytest.raises(ValidationError):
|
||||
assert MyCallback.unpack("test:test:")
|
||||
|
||||
class MyCallback1(CallbackData, prefix="test1"):
|
||||
foo: str
|
||||
bar: Optional[int] = None
|
||||
|
||||
assert MyCallback1.unpack("test1:spam:") == MyCallback1(foo="spam")
|
||||
assert MyCallback1.unpack("test1:spam:42") == MyCallback1(foo="spam", bar=42)
|
||||
|
||||
class MyCallback2(CallbackData, prefix="test2"):
|
||||
foo: Optional[str] = None
|
||||
bar: int
|
||||
|
||||
assert MyCallback2.unpack("test2::42") == MyCallback2(bar=42)
|
||||
assert MyCallback2.unpack("test2:spam:42") == MyCallback2(foo="spam", bar=42)
|
||||
|
||||
class MyCallback3(CallbackData, prefix="test3"):
|
||||
foo: Optional[str] = "experiment"
|
||||
bar: int
|
||||
|
||||
assert MyCallback3.unpack("test3:experiment:42") == MyCallback3(bar=42)
|
||||
assert MyCallback3.unpack("test3:spam:42") == MyCallback3(foo="spam", bar=42)
|
||||
|
||||
def test_build_filter(self):
|
||||
filter_object = MyCallback.filter(F.foo == "test")
|
||||
assert isinstance(filter_object.rule, MagicFilter)
|
||||
assert filter_object.callback_data is MyCallback
|
||||
|
||||
|
||||
class TestCallbackDataFilter:
|
||||
@pytest.mark.parametrize(
|
||||
"query,rule,result",
|
||||
[
|
||||
["test", F.foo == "test", False],
|
||||
["test:spam:42", F.foo == "test", False],
|
||||
["test:test:42", F.foo == "test", {"callback_data": MyCallback(foo="test", bar=42)}],
|
||||
["test:test:42", None, {"callback_data": MyCallback(foo="test", bar=42)}],
|
||||
["test:test:777", None, {"callback_data": MyCallback(foo="test", bar=777)}],
|
||||
["spam:test:777", None, False],
|
||||
["test:test:", F.foo == "test", False],
|
||||
["test:test:", None, False],
|
||||
],
|
||||
)
|
||||
async def test_call(self, query, rule, result):
|
||||
callback_query = CallbackQuery(
|
||||
id="1",
|
||||
from_user=User(id=42, is_bot=False, first_name="test"),
|
||||
data=query,
|
||||
chat_instance="test",
|
||||
)
|
||||
|
||||
filter_object = MyCallback.filter(rule)
|
||||
assert await filter_object(callback_query) == result
|
||||
|
||||
async def test_invalid_call(self):
|
||||
filter_object = MyCallback.filter(F.test)
|
||||
assert not await filter_object(User(id=42, is_bot=False, first_name="test"))
|
||||
345
tests/test_filters/test_chat_member_updated.py
Normal file
345
tests/test_filters/test_chat_member_updated.py
Normal file
|
|
@ -0,0 +1,345 @@
|
|||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.filters.chat_member_updated import (
|
||||
ADMINISTRATOR,
|
||||
IS_MEMBER,
|
||||
JOIN_TRANSITION,
|
||||
LEAVE_TRANSITION,
|
||||
ChatMemberUpdatedFilter,
|
||||
_MemberStatusGroupMarker,
|
||||
_MemberStatusMarker,
|
||||
_MemberStatusTransition,
|
||||
)
|
||||
from aiogram.types import Chat, ChatMember, ChatMemberUpdated, User
|
||||
|
||||
|
||||
class TestMemberStatusMarker:
|
||||
def test_str(self):
|
||||
marker = _MemberStatusMarker("test")
|
||||
assert str(marker) == "TEST"
|
||||
assert str(+marker) == "+TEST"
|
||||
assert str(-marker) == "-TEST"
|
||||
|
||||
def test_pos(self):
|
||||
marker = _MemberStatusMarker("test")
|
||||
assert marker.is_member is None
|
||||
|
||||
positive_marker = +marker
|
||||
assert positive_marker is not marker
|
||||
assert marker.is_member is None
|
||||
assert positive_marker.is_member is True
|
||||
|
||||
def test_neg(self):
|
||||
marker = _MemberStatusMarker("test")
|
||||
assert marker.is_member is None
|
||||
|
||||
negative_marker = -marker
|
||||
assert negative_marker is not marker
|
||||
assert marker.is_member is None
|
||||
assert negative_marker.is_member is False
|
||||
|
||||
def test_or(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
|
||||
combination = marker1 | marker2
|
||||
assert isinstance(combination, _MemberStatusGroupMarker)
|
||||
assert marker1 in combination.statuses
|
||||
assert marker2 in combination.statuses
|
||||
|
||||
combination2 = marker1 | marker1
|
||||
assert isinstance(combination2, _MemberStatusGroupMarker)
|
||||
assert len(combination2.statuses) == 1
|
||||
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
combination3 = marker3 | combination
|
||||
assert isinstance(combination3, _MemberStatusGroupMarker)
|
||||
assert marker3 in combination3.statuses
|
||||
assert len(combination3.statuses) == 3
|
||||
assert combination3 is not combination
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
marker1 | 42
|
||||
|
||||
def test_rshift(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
transition = marker1 >> marker2
|
||||
assert isinstance(transition, _MemberStatusTransition)
|
||||
assert marker1 in transition.old.statuses
|
||||
assert marker2 in transition.new.statuses
|
||||
|
||||
transition2 = marker1 >> (marker2 | marker3)
|
||||
assert isinstance(transition2, _MemberStatusTransition)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
marker1 >> 42
|
||||
|
||||
def test_lshift(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
transition = marker1 << marker2
|
||||
assert isinstance(transition, _MemberStatusTransition)
|
||||
assert marker2 in transition.old.statuses
|
||||
assert marker1 in transition.new.statuses
|
||||
|
||||
transition2 = marker1 << (marker2 | marker3)
|
||||
assert isinstance(transition2, _MemberStatusTransition)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
marker1 << 42
|
||||
|
||||
def test_hash(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker1_1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
assert hash(marker1) != hash(marker2)
|
||||
assert hash(marker1) == hash(marker1_1)
|
||||
assert hash(marker1) != hash(-marker1)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"name,is_member,member,result",
|
||||
[
|
||||
["test", None, ChatMember(status="member"), False],
|
||||
["test", None, ChatMember(status="test"), True],
|
||||
["test", True, ChatMember(status="test"), False],
|
||||
["test", True, ChatMember(status="test", is_member=True), True],
|
||||
["test", True, ChatMember(status="test", is_member=False), False],
|
||||
],
|
||||
)
|
||||
def test_check(self, name, is_member, member, result):
|
||||
marker = _MemberStatusMarker(name, is_member=is_member)
|
||||
assert marker.check(member=member) == result
|
||||
|
||||
|
||||
class TestMemberStatusGroupMarker:
|
||||
def test_init_unique(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
|
||||
group = _MemberStatusGroupMarker(marker1, marker1, marker2, marker3)
|
||||
assert len(group.statuses) == 3
|
||||
|
||||
def test_init_empty(self):
|
||||
with pytest.raises(ValueError):
|
||||
_MemberStatusGroupMarker()
|
||||
|
||||
def test_or(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
marker4 = _MemberStatusMarker("test4")
|
||||
|
||||
group1 = _MemberStatusGroupMarker(marker1, marker2)
|
||||
group2 = _MemberStatusGroupMarker(marker3, marker4)
|
||||
|
||||
group3 = group1 | marker3
|
||||
assert isinstance(group3, _MemberStatusGroupMarker)
|
||||
assert len(group3.statuses) == 3
|
||||
|
||||
group4 = group1 | group2
|
||||
assert isinstance(group4, _MemberStatusGroupMarker)
|
||||
assert len(group4.statuses) == 4
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
group4 | 42
|
||||
|
||||
def test_rshift(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
|
||||
group1 = _MemberStatusGroupMarker(marker1, marker2)
|
||||
group2 = _MemberStatusGroupMarker(marker1, marker3)
|
||||
|
||||
transition1 = group1 >> marker1
|
||||
assert isinstance(transition1, _MemberStatusTransition)
|
||||
assert transition1.old is group1
|
||||
assert marker1 in transition1.new.statuses
|
||||
|
||||
transition2 = group1 >> group2
|
||||
assert isinstance(transition2, _MemberStatusTransition)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
group1 >> 42
|
||||
|
||||
def test_lshift(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
marker3 = _MemberStatusMarker("test3")
|
||||
|
||||
group1 = _MemberStatusGroupMarker(marker1, marker2)
|
||||
group2 = _MemberStatusGroupMarker(marker1, marker3)
|
||||
|
||||
transition1 = group1 << marker1
|
||||
assert isinstance(transition1, _MemberStatusTransition)
|
||||
assert transition1.new is group1
|
||||
assert marker1 in transition1.old.statuses
|
||||
|
||||
transition2 = group1 << group2
|
||||
assert isinstance(transition2, _MemberStatusTransition)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
group1 << 42
|
||||
|
||||
def test_str(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker1_1 = +marker1
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
|
||||
group1 = marker1 | marker1
|
||||
assert str(group1) == "TEST1"
|
||||
|
||||
group2 = marker1 | marker2
|
||||
assert str(group2) == "(TEST1 | TEST2)"
|
||||
|
||||
group3 = marker1 | marker1_1
|
||||
assert str(group3) == "(+TEST1 | TEST1)"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"status,result",
|
||||
[
|
||||
["test", False],
|
||||
["test1", True],
|
||||
["test2", True],
|
||||
],
|
||||
)
|
||||
def test_check(self, status, result):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
group = marker1 | marker2
|
||||
|
||||
assert group.check(member=ChatMember(status=status)) is result
|
||||
|
||||
|
||||
class TestMemberStatusTransition:
|
||||
def test_invert(self):
|
||||
marker1 = _MemberStatusMarker("test1")
|
||||
marker2 = _MemberStatusMarker("test2")
|
||||
|
||||
transition1 = marker1 >> marker2
|
||||
transition2 = ~transition1
|
||||
|
||||
assert transition1 is not transition2
|
||||
assert transition1.old == transition2.new
|
||||
assert transition1.new == transition2.old
|
||||
|
||||
assert str(transition1) == "TEST1 >> TEST2"
|
||||
assert str(transition2) == "TEST2 >> TEST1"
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"transition,old,new,result",
|
||||
[
|
||||
[JOIN_TRANSITION, ChatMember(status="left"), ChatMember(status="member"), True],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="restricted", is_member=True),
|
||||
ChatMember(status="member"),
|
||||
False,
|
||||
],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
ChatMember(status="member"),
|
||||
True,
|
||||
],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="member"),
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
False,
|
||||
],
|
||||
[
|
||||
LEAVE_TRANSITION,
|
||||
ChatMember(status="member"),
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
True,
|
||||
],
|
||||
],
|
||||
)
|
||||
def test_check(self, transition, old, new, result):
|
||||
assert transition.check(old=old, new=new) == result
|
||||
|
||||
|
||||
class TestChatMemberUpdatedStatusFilter:
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"transition,old,new,result",
|
||||
[
|
||||
[JOIN_TRANSITION, ChatMember(status="left"), ChatMember(status="member"), True],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="restricted", is_member=True),
|
||||
ChatMember(status="member"),
|
||||
False,
|
||||
],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
ChatMember(status="member"),
|
||||
True,
|
||||
],
|
||||
[
|
||||
JOIN_TRANSITION,
|
||||
ChatMember(status="member"),
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
False,
|
||||
],
|
||||
[
|
||||
LEAVE_TRANSITION,
|
||||
ChatMember(status="member"),
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
True,
|
||||
],
|
||||
[
|
||||
ADMINISTRATOR,
|
||||
ChatMember(status="member"),
|
||||
ChatMember(status="administrator"),
|
||||
True,
|
||||
],
|
||||
[
|
||||
IS_MEMBER,
|
||||
ChatMember(status="restricted", is_member=False),
|
||||
ChatMember(status="member"),
|
||||
True,
|
||||
],
|
||||
],
|
||||
)
|
||||
async def test_call(self, transition, old, new, result):
|
||||
updated_filter = ChatMemberUpdatedFilter(member_status_changed=transition)
|
||||
user = User(id=42, first_name="Test", is_bot=False)
|
||||
update = {
|
||||
"user": user,
|
||||
"until_date": datetime.now(),
|
||||
"is_anonymous": False,
|
||||
"can_be_edited": True,
|
||||
"can_manage_chat": True,
|
||||
"can_delete_messages": True,
|
||||
"can_manage_video_chats": True,
|
||||
"can_restrict_members": True,
|
||||
"can_promote_members": True,
|
||||
"can_change_info": True,
|
||||
"can_invite_users": True,
|
||||
"can_post_messages": True,
|
||||
"can_edit_messages": True,
|
||||
"can_pin_messages": True,
|
||||
"can_send_messages": True,
|
||||
"can_send_media_messages": True,
|
||||
"can_send_polls": True,
|
||||
"can_send_other_messages": True,
|
||||
"can_add_web_page_previews": True,
|
||||
}
|
||||
event = ChatMemberUpdated(
|
||||
chat=Chat(id=42, type="test"),
|
||||
from_user=user,
|
||||
old_chat_member=old.copy(update=update),
|
||||
new_chat_member=new.copy(update=update),
|
||||
date=datetime.now(),
|
||||
)
|
||||
|
||||
assert await updated_filter(event) is result
|
||||
153
tests/test_filters/test_command.py
Normal file
153
tests/test_filters/test_command.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import datetime
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram import F
|
||||
from aiogram.filters import Command, CommandObject
|
||||
from aiogram.filters.command import CommandStart
|
||||
from aiogram.types import Chat, Message, User
|
||||
from tests.mocked_bot import MockedBot
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestCommandFilter:
|
||||
def test_convert_to_list(self):
|
||||
cmd = Command(commands="start")
|
||||
assert cmd.commands
|
||||
assert isinstance(cmd.commands, list)
|
||||
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],
|
||||
],
|
||||
)
|
||||
async def test_parse_command(self, bot: MockedBot, text: str, result: bool, command: Command):
|
||||
# TODO: test ignore case
|
||||
# TODO: test ignore mention
|
||||
|
||||
message = Message(
|
||||
message_id=0, text=text, chat=Chat(id=42, type="private"), date=datetime.datetime.now()
|
||||
)
|
||||
|
||||
response = await command(message, bot)
|
||||
assert bool(response) is result
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"message,result",
|
||||
[
|
||||
[
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
False,
|
||||
],
|
||||
[
|
||||
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,
|
||||
],
|
||||
],
|
||||
)
|
||||
async def test_call(self, message: Message, result: bool, bot: MockedBot):
|
||||
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(
|
||||
"obj,result",
|
||||
[
|
||||
[CommandObject(prefix="/", command="command", mention="mention", args="args"), True],
|
||||
[CommandObject(prefix="/", command="command", args="args"), False],
|
||||
],
|
||||
)
|
||||
def test_mentioned(self, obj: CommandObject, result: bool):
|
||||
assert isinstance(obj.mentioned, bool)
|
||||
assert obj.mentioned is result
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"obj,result",
|
||||
[
|
||||
[
|
||||
CommandObject(prefix="/", command="command", mention="mention", args="args"),
|
||||
"/command@mention args",
|
||||
],
|
||||
[
|
||||
CommandObject(prefix="/", command="command", mention="mention", args=None),
|
||||
"/command@mention",
|
||||
],
|
||||
[
|
||||
CommandObject(prefix="/", command="command", mention=None, args="args"),
|
||||
"/command args",
|
||||
],
|
||||
[CommandObject(prefix="/", command="command", mention=None, args=None), "/command"],
|
||||
[CommandObject(prefix="!", command="command", mention=None, args=None), "!command"],
|
||||
],
|
||||
)
|
||||
def test_text(self, obj: CommandObject, result: str):
|
||||
assert obj.text == result
|
||||
|
||||
def test_update_handler_flags(self):
|
||||
cmd = Command(commands=["start"])
|
||||
flags = {}
|
||||
cmd.update_handler_flags(flags)
|
||||
|
||||
assert "commands" in flags
|
||||
assert isinstance(flags["commands"], list)
|
||||
assert len(flags["commands"]) == 1
|
||||
assert flags["commands"][0] is cmd
|
||||
|
||||
cmd.update_handler_flags(flags)
|
||||
assert len(flags["commands"]) == 2
|
||||
52
tests/test_filters/test_content_types.py
Normal file
52
tests/test_filters/test_content_types.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import cast
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from aiogram.filters import ContentTypesFilter
|
||||
from aiogram.types import ContentType, Message
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
@dataclass
|
||||
class MinimalMessage:
|
||||
content_type: str
|
||||
|
||||
|
||||
class TestContentTypesFilter:
|
||||
def test_validator_empty_list(self):
|
||||
filter_ = ContentTypesFilter(content_types=[])
|
||||
assert filter_.content_types == []
|
||||
|
||||
def test_convert_to_list(self):
|
||||
filter_ = ContentTypesFilter(content_types="text")
|
||||
assert filter_.content_types
|
||||
assert isinstance(filter_.content_types, list)
|
||||
assert filter_.content_types[0] == "text"
|
||||
assert filter_ == ContentTypesFilter(content_types=["text"])
|
||||
|
||||
@pytest.mark.parametrize("values", [["text", "photo"], ["sticker"]])
|
||||
def test_validator_with_values(self, values):
|
||||
filter_ = ContentTypesFilter(content_types=values)
|
||||
assert filter_.content_types == values
|
||||
|
||||
@pytest.mark.parametrize("values", [["test"], ["text", "test"], ["TEXT"]])
|
||||
def test_validator_with_bad_values(self, values):
|
||||
with pytest.raises(ValidationError):
|
||||
ContentTypesFilter(content_types=values)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"values,content_type,result",
|
||||
[
|
||||
[[ContentType.TEXT], ContentType.TEXT, True],
|
||||
[[ContentType.PHOTO], ContentType.TEXT, False],
|
||||
[[ContentType.ANY], ContentType.TEXT, True],
|
||||
[[ContentType.TEXT, ContentType.PHOTO, ContentType.DOCUMENT], ContentType.TEXT, True],
|
||||
[[ContentType.ANY, ContentType.PHOTO, ContentType.DOCUMENT], ContentType.TEXT, True],
|
||||
],
|
||||
)
|
||||
async def test_call(self, values, content_type, result):
|
||||
filter_ = ContentTypesFilter(content_types=values)
|
||||
assert await filter_(cast(Message, MinimalMessage(content_type=content_type))) == result
|
||||
68
tests/test_filters/test_exception.py
Normal file
68
tests/test_filters/test_exception.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram import Dispatcher
|
||||
from aiogram.filters import ExceptionMessageFilter, ExceptionTypeFilter
|
||||
from aiogram.types import Update
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestExceptionMessageFilter:
|
||||
@pytest.mark.parametrize("value", ["value", re.compile("value")])
|
||||
def test_converter(self, value):
|
||||
obj = ExceptionMessageFilter(pattern=value)
|
||||
assert isinstance(obj.pattern, re.Pattern)
|
||||
|
||||
async def test_match(self):
|
||||
obj = ExceptionMessageFilter(pattern="KABOOM")
|
||||
|
||||
result = await obj(Update(update_id=0), exception=Exception())
|
||||
assert not result
|
||||
|
||||
result = await obj(Update(update_id=0), exception=Exception("KABOOM"))
|
||||
assert isinstance(result, dict)
|
||||
assert "match_exception" in result
|
||||
|
||||
|
||||
class MyException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class MyAnotherException(MyException):
|
||||
pass
|
||||
|
||||
|
||||
class TestExceptionTypeFilter:
|
||||
@pytest.mark.parametrize(
|
||||
"exception,value",
|
||||
[
|
||||
[Exception(), False],
|
||||
[ValueError(), False],
|
||||
[TypeError(), False],
|
||||
[MyException(), True],
|
||||
[MyAnotherException(), True],
|
||||
],
|
||||
)
|
||||
async def test_check(self, exception: Exception, value: bool):
|
||||
obj = ExceptionTypeFilter(exception=MyException)
|
||||
|
||||
result = await obj(Update(update_id=0), exception=exception)
|
||||
|
||||
assert result == value
|
||||
|
||||
|
||||
class TestDispatchException:
|
||||
async def test_handle_exception(self, bot):
|
||||
dp = Dispatcher()
|
||||
|
||||
@dp.update()
|
||||
async def update_handler(update):
|
||||
raise ValueError("KABOOM")
|
||||
|
||||
@dp.errors(ExceptionMessageFilter(pattern="KABOOM"))
|
||||
async def handler0(update, exception):
|
||||
return "Handled"
|
||||
|
||||
assert await dp.feed_update(bot, Update(update_id=0)) == "Handled"
|
||||
37
tests/test_filters/test_logic.py
Normal file
37
tests/test_filters/test_logic.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import pytest
|
||||
|
||||
from aiogram.filters import Text, and_f, invert_f, or_f
|
||||
from aiogram.filters.logic import _AndFilter, _InvertFilter, _OrFilter
|
||||
|
||||
|
||||
class TestLogic:
|
||||
@pytest.mark.parametrize(
|
||||
"obj,case,result",
|
||||
[
|
||||
[True, and_f(lambda t: t is True, lambda t: t is True), True],
|
||||
[True, and_f(lambda t: t is True, lambda t: t is False), False],
|
||||
[True, and_f(lambda t: t is False, lambda t: t is False), False],
|
||||
[True, and_f(lambda t: {"t": t}, lambda t: t is False), False],
|
||||
[True, and_f(lambda t: {"t": t}, lambda t: t is True), {"t": True}],
|
||||
[True, or_f(lambda t: t is True, lambda t: t is True), True],
|
||||
[True, or_f(lambda t: t is True, lambda t: t is False), True],
|
||||
[True, or_f(lambda t: t is False, lambda t: t is False), False],
|
||||
[True, or_f(lambda t: t is False, lambda t: t is True), True],
|
||||
[True, or_f(lambda t: t is False, lambda t: {"t": t}), {"t": True}],
|
||||
[True, or_f(lambda t: {"t": t}, lambda t: {"a": 42}), {"t": True}],
|
||||
[True, invert_f(lambda t: t is False), True],
|
||||
],
|
||||
)
|
||||
async def test_logic(self, obj, case, result):
|
||||
assert await case(obj) == result
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"case,type_",
|
||||
[
|
||||
[Text(text="test") | Text(text="test"), _OrFilter],
|
||||
[Text(text="test") & Text(text="test"), _AndFilter],
|
||||
[~Text(text="test"), _InvertFilter],
|
||||
],
|
||||
)
|
||||
def test_dunder_methods(self, case, type_):
|
||||
assert isinstance(case, type_)
|
||||
30
tests/test_filters/test_magic_data.py
Normal file
30
tests/test_filters/test_magic_data.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import pytest
|
||||
from magic_filter import AttrDict
|
||||
|
||||
from aiogram import F
|
||||
from aiogram.filters import MagicData
|
||||
from aiogram.types import Update
|
||||
|
||||
|
||||
class TestMagicDataFilter:
|
||||
@pytest.mark.asyncio
|
||||
async def test_call(self):
|
||||
called = False
|
||||
|
||||
def check(value):
|
||||
nonlocal called
|
||||
called = True
|
||||
|
||||
assert isinstance(value, AttrDict)
|
||||
assert value[0] == "foo"
|
||||
assert value[1] == "bar"
|
||||
assert value["spam"] is True
|
||||
assert value.spam is True
|
||||
return value
|
||||
|
||||
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, dict)
|
||||
assert result["test"]
|
||||
74
tests/test_filters/test_state.py
Normal file
74
tests/test_filters/test_state.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
from copy import copy
|
||||
from inspect import isclass
|
||||
|
||||
import pytest
|
||||
|
||||
from aiogram.dispatcher.event.handler import FilterObject
|
||||
from aiogram.filters import StateFilter
|
||||
from aiogram.fsm.state import State, StatesGroup
|
||||
from aiogram.types import Update
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class MyGroup(StatesGroup):
|
||||
state = State()
|
||||
|
||||
|
||||
class TestStateFilter:
|
||||
@pytest.mark.parametrize(
|
||||
"state", [None, State("test"), MyGroup, MyGroup(), "state", ["state"]]
|
||||
)
|
||||
def test_validator(self, state):
|
||||
f = StateFilter(state=state)
|
||||
assert isinstance(f.state, list)
|
||||
value = f.state[0]
|
||||
assert (
|
||||
isinstance(value, (State, str, MyGroup))
|
||||
or (isclass(value) and issubclass(value, StatesGroup))
|
||||
or value is None
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"state,current_state,result",
|
||||
[
|
||||
[State("state"), "@:state", True],
|
||||
[[State("state")], "@:state", True],
|
||||
[MyGroup, "MyGroup:state", True],
|
||||
[[MyGroup], "MyGroup:state", True],
|
||||
[MyGroup(), "MyGroup:state", True],
|
||||
[[MyGroup()], "MyGroup:state", True],
|
||||
["*", "state", True],
|
||||
[None, None, True],
|
||||
[[None], None, True],
|
||||
[None, "state", False],
|
||||
[[], "state", False],
|
||||
[[State("state"), "state"], "state", True],
|
||||
[[MyGroup(), State("state")], "@:state", True],
|
||||
[[MyGroup, State("state")], "state", False],
|
||||
],
|
||||
)
|
||||
@pytestmark
|
||||
async def test_filter(self, state, current_state, result):
|
||||
f = StateFilter(state=state)
|
||||
assert bool(await f(obj=Update(update_id=42), raw_state=current_state)) is result
|
||||
|
||||
@pytestmark
|
||||
async def test_create_filter_from_state(self):
|
||||
FilterObject(callback=State(state="state"))
|
||||
|
||||
@pytestmark
|
||||
async def test_state_copy(self):
|
||||
class SG(StatesGroup):
|
||||
state = State()
|
||||
|
||||
assert SG.state == copy(SG.state)
|
||||
|
||||
assert SG.state == "SG:state"
|
||||
assert "SG:state" == SG.state
|
||||
|
||||
assert State() == State()
|
||||
assert SG.state != 1
|
||||
|
||||
states = {SG.state: "OK"}
|
||||
assert states.get(copy(SG.state)) == "OK"
|
||||
246
tests/test_filters/test_text.py
Normal file
246
tests/test_filters/test_text.py
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
import datetime
|
||||
from itertools import permutations
|
||||
from typing import Sequence, Type
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from aiogram.filters import BUILTIN_FILTERS, Text
|
||||
from aiogram.types import CallbackQuery, Chat, InlineQuery, Message, Poll, PollOption, User
|
||||
|
||||
pytestmark = pytest.mark.asyncio
|
||||
|
||||
|
||||
class TestText:
|
||||
def test_default_for_observer(self):
|
||||
registered_for = {
|
||||
update_type for update_type, filters in BUILTIN_FILTERS.items() if Text in filters
|
||||
}
|
||||
assert registered_for == {
|
||||
"message",
|
||||
"edited_message",
|
||||
"channel_post",
|
||||
"edited_channel_post",
|
||||
"inline_query",
|
||||
"callback_query",
|
||||
}
|
||||
|
||||
def test_validator_not_enough_arguments(self):
|
||||
with pytest.raises(ValidationError):
|
||||
Text()
|
||||
with pytest.raises(ValidationError):
|
||||
Text(text_ignore_case=True)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"first,last",
|
||||
permutations(["text", "text_contains", "text_startswith", "text_endswith"], 2),
|
||||
)
|
||||
@pytest.mark.parametrize("ignore_case", [True, False])
|
||||
def test_validator_too_few_arguments(self, first, last, ignore_case):
|
||||
kwargs = {first: "test", last: "test"}
|
||||
if ignore_case:
|
||||
kwargs["text_ignore_case"] = True
|
||||
|
||||
with pytest.raises(ValidationError):
|
||||
Text(**kwargs)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"argument", ["text", "text_contains", "text_startswith", "text_endswith"]
|
||||
)
|
||||
@pytest.mark.parametrize("input_type", [str, list, tuple])
|
||||
def test_validator_convert_to_list(self, argument: str, input_type: Type):
|
||||
text = Text(**{argument: input_type("test")})
|
||||
assert hasattr(text, argument)
|
||||
assert isinstance(getattr(text, argument), Sequence)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"argument,ignore_case,input_value,update_type,result",
|
||||
[
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
False,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
caption="test",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"test",
|
||||
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,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
"TEst",
|
||||
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,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
False,
|
||||
"TEst",
|
||||
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"),
|
||||
),
|
||||
False,
|
||||
],
|
||||
[
|
||||
"text_startswith",
|
||||
False,
|
||||
"test",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text_endswith",
|
||||
False,
|
||||
"case",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text_contains",
|
||||
False,
|
||||
" ",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
text="test case",
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text_startswith",
|
||||
True,
|
||||
"question",
|
||||
Message(
|
||||
message_id=42,
|
||||
date=datetime.datetime.now(),
|
||||
poll=Poll(
|
||||
id="poll id",
|
||||
question="Question?",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="regular",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
),
|
||||
chat=Chat(id=42, type="private"),
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text_startswith",
|
||||
True,
|
||||
"callback:",
|
||||
CallbackQuery(
|
||||
id="query id",
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
chat_instance="instance",
|
||||
data="callback:data",
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text_startswith",
|
||||
True,
|
||||
"query",
|
||||
InlineQuery(
|
||||
id="query id",
|
||||
from_user=User(id=42, is_bot=False, first_name="Test"),
|
||||
query="query line",
|
||||
offset="offset",
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
"question",
|
||||
Poll(
|
||||
id="poll id",
|
||||
question="Question",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="regular",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
),
|
||||
True,
|
||||
],
|
||||
[
|
||||
"text",
|
||||
True,
|
||||
["question", "another question"],
|
||||
Poll(
|
||||
id="poll id",
|
||||
question="Another question",
|
||||
options=[PollOption(text="A", voter_count=0)],
|
||||
is_closed=False,
|
||||
is_anonymous=False,
|
||||
type="quiz",
|
||||
allows_multiple_answers=False,
|
||||
total_voter_count=0,
|
||||
correct_option_id=0,
|
||||
),
|
||||
True,
|
||||
],
|
||||
["text", True, ["question", "another question"], object(), False],
|
||||
],
|
||||
)
|
||||
async def test_check_text(self, argument, ignore_case, input_value, result, update_type):
|
||||
text = Text(**{argument: input_value}, text_ignore_case=ignore_case)
|
||||
assert await text(obj=update_type) is result
|
||||
Loading…
Add table
Add a link
Reference in a new issue