Merge remote-tracking branch 'origin/dev-3.x' into dev-3.x-require-sugar

# Conflicts:
#	poetry.lock
This commit is contained in:
mpa 2020-07-21 04:49:37 +04:00
commit ca44f9c01a
No known key found for this signature in database
GPG key ID: BCCFBFCCC9B754A8
122 changed files with 1347 additions and 605 deletions

View file

@ -5,6 +5,7 @@ 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
class MockedSession(BaseSession):
@ -23,8 +24,10 @@ class MockedSession(BaseSession):
async def close(self):
pass
async def make_request(self, token: str, method: TelegramMethod[T]) -> T:
self.requests.append(method.build_request())
async def make_request(
self, bot: Bot, method: TelegramMethod[T], timeout: Optional[int] = UNSET
) -> T:
self.requests.append(method.build_request(bot))
response: Response[T] = self.responses.pop()
self.raise_for_status(response)
return response.result # type: ignore

View file

@ -42,12 +42,13 @@ class TestBot:
new_callable=CoroutineMock,
) as mocked_make_request:
await bot(method)
mocked_make_request.assert_awaited_with("42:TEST", method)
mocked_make_request.assert_awaited_with(bot, method, timeout=None)
@pytest.mark.asyncio
async def test_close(self):
bot = Bot("42:TEST", session=AiohttpSession())
await bot.session.create_session()
session = AiohttpSession()
bot = Bot("42:TEST", session=session)
await session.create_session()
with patch(
"aiogram.api.client.session.aiohttp.AiohttpSession.close", new_callable=CoroutineMock

View file

@ -5,9 +5,11 @@ 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
from tests.mocked_bot import MockedBot
try:
from asynctest import CoroutineMock, patch
@ -147,7 +149,7 @@ class TestAiohttpSession:
assert isinstance(fields[1][2], BareInputFile)
@pytest.mark.asyncio
async def test_make_request(self, aresponses: ResponsesMockServer):
async def test_make_request(self, bot: MockedBot, aresponses: ResponsesMockServer):
aresponses.add(
aresponses.ANY,
"/bot42:TEST/method",
@ -164,14 +166,14 @@ class TestAiohttpSession:
class TestMethod(TelegramMethod[int]):
__returning__ = int
def build_request(self) -> Request:
def build_request(self, bot: Bot) -> Request:
return Request(method="method", data={})
call = TestMethod()
with patch(
"aiogram.api.client.session.base.BaseSession.raise_for_status"
) as patched_raise_for_status:
result = await session.make_request("42:TEST", call)
result = await session.make_request(bot, call)
assert isinstance(result, int)
assert result == 42

View file

@ -1,12 +1,13 @@
import datetime
import json
from typing import AsyncContextManager, AsyncGenerator
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
try:
from asynctest import CoroutineMock, patch
@ -18,7 +19,7 @@ class CustomSession(BaseSession):
async def close(self):
pass
async def make_request(self, token: str, method: TelegramMethod[T]) -> None: # type: ignore
async def make_request(self, token: str, method: TelegramMethod[T], timeout: Optional[int] = UNSET) -> None: # type: ignore
assert isinstance(token, str)
assert isinstance(method, TelegramMethod)
@ -49,14 +50,9 @@ class TestBaseSession:
return json.dumps
session.json_dumps = custom_dumps
assert session.json_dumps == custom_dumps == session._json_dumps
assert session.json_dumps == custom_dumps
session.json_loads = custom_loads
assert session.json_loads == custom_loads == session._json_loads
different_session = CustomSession()
assert all(
not hasattr(different_session, attr) for attr in ("_json_loads", "_json_dumps", "_api")
)
assert session.json_loads == custom_loads
def test_timeout(self):
session = CustomSession()

View file

@ -29,17 +29,14 @@ class TestAnswerInlineQuery:
assert request.method == "answerInlineQuery"
assert response == prepare_result.result
def test_parse_mode(self):
def test_parse_mode(self, bot: MockedBot):
query = AnswerInlineQuery(
inline_query_id="query id",
results=[InlineQueryResultPhoto(id="result id", photo_url="photo", thumb_url="thumb")],
)
request = query.build_request()
request = query.build_request(bot)
assert request.data["results"][0]["parse_mode"] is None
token = Bot.set_current(Bot(token="42:TEST", parse_mode="HTML"))
try:
request = query.build_request()
assert request.data["results"][0]["parse_mode"] == "HTML"
finally:
Bot.reset_current(token)
new_bot = Bot(token="42:TEST", parse_mode="HTML")
request = query.build_request(new_bot)
assert request.data["results"][0]["parse_mode"] == "HTML"

View file

@ -4,6 +4,7 @@ import pytest
from aiogram import Bot
from aiogram.api.methods.base import prepare_parse_mode
from tests.mocked_bot import MockedBot
class TestPrepareFile:
@ -35,19 +36,19 @@ class TestPrepareParseMode:
)
@pytest.mark.asyncio
async def test_default_parse_mode(
self, parse_mode: str, data: Dict[str, str], result: Optional[str]
self, bot: MockedBot, parse_mode: str, data: Dict[str, str], result: Optional[str]
):
async with Bot(token="42:TEST", parse_mode=parse_mode).context() as bot:
assert bot.parse_mode == parse_mode
prepare_parse_mode(data)
prepare_parse_mode(bot, data)
assert data.get("parse_mode") == result
@pytest.mark.asyncio
async def test_list(self):
data = [{}] * 2
data.append({"parse_mode": "HTML"})
async with Bot(token="42:TEST", parse_mode="Markdown").context():
prepare_parse_mode(data)
bot = Bot(token="42:TEST", parse_mode="Markdown")
prepare_parse_mode(bot, data)
assert isinstance(data, list)
assert len(data) == 3
@ -56,7 +57,7 @@ class TestPrepareParseMode:
assert data[1]["parse_mode"] == "Markdown"
assert data[2]["parse_mode"] == "HTML"
def test_bot_not_in_context(self):
def test_bot_not_in_context(self, bot: MockedBot):
data = {}
prepare_parse_mode(data)
assert "parse_mode" not in data
prepare_parse_mode(bot, data)
assert data["parse_mode"] is None

View file

@ -0,0 +1,29 @@
import pytest
from aiogram.api.types import ChatMember, User
user = User(id=42, is_bot=False, first_name="User", last_name=None)
class TestChatMember:
@pytest.mark.parametrize(
"status,result", [["administrator", True], ["creator", True], ["member", False]]
)
def test_is_chat_admin(self, status: str, result: bool):
chat_member = ChatMember(user=user, status=status)
assert chat_member.is_chat_admin == result
@pytest.mark.parametrize(
"status,result",
[
["administrator", True],
["creator", True],
["member", True],
["restricted", True],
["kicked", False],
["left", False],
],
)
def test_is_chat_member(self, status: str, result: bool):
chat_member = ChatMember(user=user, status=status)
assert chat_member.is_chat_member == result

View file

@ -1,6 +1,6 @@
import pytest
from aiogram.utils.helper import Helper, HelperMode, Item, ListItem, OrderedHelper
from aiogram.utils.helper import Default, Helper, HelperMode, Item, ListItem, OrderedHelper
class TestHelper:
@ -132,3 +132,50 @@ class TestOrderedHelper:
B = ListItem()
assert MyOrderedHelper.all() == ["A", "D", "C", "B"]
class TestDefaultDescriptor:
def test_descriptor_fs(self):
obj = type("ClassA", (), {})()
default_x_val = "some_x"
x = Default(default_x_val)
# we can omit owner, usually it's just obj.__class__
assert x.__get__(instance=obj, owner=None) == default_x_val
assert x.__get__(instance=obj, owner=obj.__class__) == default_x_val
new_x_val = "new_x"
assert x.__set__(instance=obj, value=new_x_val) is None
with pytest.raises(AttributeError) as exc:
x.__set__(instance=obj.__class__, value="will never be set")
assert "Instance cannot be class or None" in str(exc.value)
assert x.__get__(instance=obj, owner=obj.__class__) == new_x_val
with pytest.raises(AttributeError) as exc:
x.__delete__(instance=obj.__class__)
assert "Instance cannot be class or None" in str(exc.value)
x.__delete__(instance=obj)
assert x.__get__(instance=obj, owner=obj.__class__) == default_x_val
def test_init(self):
class A:
x = Default(fget=lambda a_inst: "nothing")
assert isinstance(A.__dict__["x"], Default)
a = A()
assert a.x == "nothing"
x = Default("x")
assert x.__get__(None, None) == "x"
assert x.fget(None) == x.__get__(None, None)
def test_nullability(self):
class A:
x = Default(default=None, fget=None)
assert A.x is None
assert A().x is None