Refactor and Overhaul the Test Suite (#3426)

This commit is contained in:
Harshil 2023-02-11 15:15:17 +05:30 committed by GitHub
parent 43c3c8f568
commit 963edbf191
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
129 changed files with 6046 additions and 5962 deletions

View file

@ -84,35 +84,13 @@ Here's how to make a one-off code change.
- In addition, PTB uses some formatting/styling and linting tools in the pre-commit setup. Some of those tools also have command line tools that can help to run these tools outside of the pre-commit step. If you'd like to leverage that, please have a look at the `pre-commit config file`_ for an overview of which tools (and which versions of them) are used. For example, we use `Black`_ for code formatting. Plugins for Black exist for some `popular editors`_. You can use those instead of manually formatting everything.
- Please ensure that the code you write is well-tested.
- Please ensure that the code you write is well-tested and that all automated tests still pass. We
have dedicated an `testing page`_ to help you with that.
- In addition to that, we provide the `dev` marker for pytest. If you write one or multiple tests and want to run only those, you can decorate them via `@pytest.mark.dev` and then run it with minimal overhead with `pytest ./path/to/test_file.py -m dev`.
- Dont break backward compatibility.
- Don't break backward compatibility.
- Add yourself to the AUTHORS.rst_ file in an alphabetical fashion.
- Before making a commit ensure that all automated tests still pass:
.. code-block:: bash
$ pytest -v
Since the tests can take a while to run, you can speed things up by running them in parallel
using `pytest-xdist`_ (note that this may effect the result of the test in some rare cases):
.. code-block:: bash
$ pytest -v -n auto --dist=loadfile
To run ``test_official`` (particularly useful if you made API changes), run
.. code-block:: bash
$ export TEST_OFFICIAL=true
prior to running the tests.
- If you want run style & type checks before committing run
.. code-block:: bash
@ -287,4 +265,4 @@ break the API classes. For example:
.. _`RTD build`: https://docs.python-telegram-bot.org/en/doc-fixes
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
.. _`section`: #documenting
.. _`pytest-xdist`: https://github.com/pytest-dev/pytest-xdist
.. _`testing page`: https://github.com/python-telegram-bot/python-telegram-bot/blob/master/tests/README.rst

View file

@ -44,42 +44,23 @@ jobs:
# The first & second one are achieved by mocking the corresponding import
# See test_helpers.py & test_no_passport.py for details
run: |
# Test without passport
pytest -v --cov -k test_no_passport.py
# We test without optional dependencies first. This includes:
# - without pytz
# - without jobqueue
# - without ratelimiter
# - without webhooks
# - without arbitrary callback data
# - without socks suppport
TO_TEST="test_no_passport.py or test_datetime.py or test_defaults.py or test_jobqueue.py or test_applicationbuilder.py or test_ratelimiter.py or test_updater.py or test_callbackdatacache.py or test_request.py"
pytest -v --cov -k "${TO_TEST}"
status=$?
# test without pytz
pytest -v --cov --cov-append -k test_datetime.py
status=$(( $? > status ? $? : status))
pytest -v --cov --cov-append -k test_defaults.py
status=$(( $? > status ? $? : status))
# test without pytz & jobqueue
pytest -v --cov --cov-append -k test_jobqueue.py
pytest -v --cov --cov-append -k test_applicationbuilder.py
status=$(( $? > status ? $? : status))
# Test without ratelimiter
pytest -v --cov --cov-append -k test_ratelimiter.py
status=$(( $? > status ? $? : status))
# Test without webhooks
pytest -v --cov --cov-append -k test_updater.py
status=$(( $? > status ? $? : status))
# Test without callback-data
pytest -v --cov --cov-append -k test_callbackdatacache.py
status=$(( $? > status ? $? : status))
# Test without socks
pytest -v --cov --cov-append -k test_request.py
status=$(( $? > status ? $? : status))
# Test the rest
export TEST_WITH_OPT_DEPS='true'
pip install -r requirements-opts.txt
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
# worker
# worker. Increasing number of workers has little effect on test duration, but it seems
# to increase flakyness, specially on python 3.7 with --dist=loadgroup.
pytest -v --cov --cov-append -n auto --dist loadfile
status=$(( $? > status ? $? : status))
exit ${status}

View file

@ -37,7 +37,7 @@ jobs:
- name: Compare Completeness
uses: jannekem/run-python-script-action@v1
with:
script: |
script: |
import json
import os
from pathlib import Path

View file

@ -32,7 +32,6 @@
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
Telegram Channel <https://t.me/pythontelegrambotchannel/>
Telegram User Group <https://t.me/pythontelegrambotgroup/>
contributing
coc
contributing
testing

1
docs/source/testing.rst Normal file
View file

@ -0,0 +1 @@
.. include:: ../../tests/README.rst

View file

@ -1,10 +1,10 @@
pre-commit
pre-commit # needed for pre-commit hooks in the git commit command
# For the test suite
pytest==7.2.1
pytest-asyncio==0.20.3
pytest-timeout==2.1.0 # used to timeout tests
pytest-asyncio==0.20.3 # needed because pytest doesn't come with native support for coroutines as tests
pytest-xdist==3.1.0 # xdist runs tests in parallel
flaky # Used for flaky tests (flaky decorator)
beautifulsoup4 # used in test_official for parsing tg docs
wheel # required for building the wheels for releases

View file

@ -36,7 +36,10 @@ filterwarnings =
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
; and instead do a trick directly in tests/conftest.py
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
markers = dev: If you want to test a specific test, use this
markers =
dev: If you want to test a specific test, use this
no_req
req
asyncio_mode = auto
[coverage:run]

101
tests/README.rst Normal file
View file

@ -0,0 +1,101 @@
==============
Testing in PTB
==============
PTB uses `pytest`_ for testing. To run the tests, you need to
have pytest installed along with a few other dependencies. You can find the list of dependencies
in the ``requirements-dev.txt`` file in the root of the repository.
Running tests
=============
To run the entire test suite, you can use the following command:
.. code-block:: bash
$ pytest
This will run all the tests, including the ones which make a request to the Telegram servers, which
may take a long time (total > 13 mins). To run only the tests that don't require a connection, you
can run the following command:
.. code-block:: bash
$ pytest -m no_req
Or alternatively, you can run the following command to run only the tests that require a connection:
.. code-block:: bash
$ pytest -m req
To further speed up the tests, you can run them in parallel using the ``-n`` flag (requires `pytest-xdist`_). But beware that
this will use multiple CPU cores on your machine. The ``--dist`` flag is used to specify how the
tests will be distributed across the cores. The ``loadgroup`` option is used to distribute the tests
such that tests marked with ``@pytest.mark.xdist_group("name")`` are run on the same core — important if you want avoid race conditions in some tests:
.. code-block:: bash
$ pytest -n auto --dist=loadgroup
This will result in a significant speedup, but may cause some tests to fail. If you want to run
the failed tests in isolation, you can use the ``--lf`` flag:
.. code-block:: bash
$ pytest --lf
Writing tests
=============
PTB has a separate test file for every file in the ``telegram.*`` namespace. Further, the tests for
the ``telegram`` module are split into two classes, based on whether the test methods in them make a
request or not. When writing tests, make sure to split them into these two classes, and make sure
to name the test class as: ``TestXXXWithoutRequest`` for tests that don't make a request, and ``TestXXXWithRequest`` for tests that do.
Writing tests is a creative process; allowing you to design your test however you'd like, but there
are a few conventions that you should follow:
- Each new test class needs a ``test_slot_behaviour``, ``test_to_dict``, ``test_de_json`` and
``test_equality`` (in most cases).
- Make use of pytest's fixtures and parametrize wherever possible. Having knowledge of pytest's
tooling can help you as well. You can look at the existing tests for examples and inspiration.
If you have made some API changes, you may want to run ``test_official`` to validate that the changes are
complete and correct. To run it, export an environment variable first:
.. code-block:: bash
$ export TEST_OFFICIAL=true
and then run ``pytest tests/test_official.py``.
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
Use as follows:
.. code-block:: bash
$ pytest -m dev
Bots used in tests
==================
If you run the tests locally, the test setup will use one of the two public bots available. Which
bot of the two gets chosen for the test session is random. Whereas when the tests on the
Github Actions CI are run, the test setup allocates a different, but same bot for every combination of Python version and
OS.
Thus, number of bots used for testing locally is 2 (called as fallback bots), and on the CI,
its [3.7, 3.8, 3.9, 3.10, 3.11] x [ubuntu-latest, macos-latest, windows-latest] = 15. Bringing the
total number of bots used for testing to 17.
That's it! If you have any questions, feel free to ask them in the `PTB dev
group`_.
.. _pytest: https://docs.pytest.org/en/stable/
.. _pytest-xdist: https://pypi.org/project/pytest-xdist/
.. _PTB dev group: https://t.me/pythontelegrambotgroup

View file

@ -45,26 +45,27 @@ if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None:
BOTS = json.loads(base64.b64decode(BOTS).decode("utf-8"))
JOB_INDEX = int(JOB_INDEX)
FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode("utf-8"))
FALLBACKS = json.loads(base64.b64decode(FALLBACKS).decode("utf-8")) # type: list[dict[str, str]]
def get(name, fallback):
# If we have TOKEN, PAYMENT_PROVIDER_TOKEN, CHAT_ID, SUPER_GROUP_ID,
# CHANNEL_ID, BOT_NAME, or BOT_USERNAME in the environment, then use that
val = os.getenv(name.upper())
if val:
return val
class BotInfoProvider:
def __init__(self):
self._cached = {}
def get_info(self):
if self._cached:
return self._cached
self._cached = {k: get(k, v) for k, v in random.choice(FALLBACKS).items()}
return self._cached
def get(key, fallback):
# If we're running as a github action then fetch bots from the repo secrets
if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None:
try:
return BOTS[JOB_INDEX][name]
except (KeyError, IndexError):
return BOTS[JOB_INDEX][key]
except (IndexError, KeyError):
pass
# Otherwise go with the fallback
return fallback
def get_bot():
return {k: get(k, v) for k, v in random.choice(FALLBACKS).items()}

View file

@ -22,7 +22,7 @@ import os
import re
import sys
from pathlib import Path
from typing import Callable, Optional
from typing import Callable, Dict, List, Optional
import pytest
from httpx import AsyncClient, Response
@ -48,16 +48,49 @@ from telegram.ext.filters import MessageFilter, UpdateFilter
from telegram.request import RequestData
from telegram.request._httpxrequest import HTTPXRequest
from tests.auxil.object_conversions import env_var_2_bool
from tests.bots import get_bot
from tests.bots import BotInfoProvider
BOT_INFO = BotInfoProvider()
# This is here instead of in setup.cfg due to https://github.com/pytest-dev/pytest/issues/8343
def pytest_runtestloop(session):
def pytest_runtestloop(session: pytest.Session):
session.add_marker(
pytest.mark.filterwarnings("ignore::telegram.warnings.PTBDeprecationWarning")
)
def pytest_collection_modifyitems(items: List[pytest.Item]):
"""Here we add a flaky marker to all request making tests and a (no_)req marker to the rest."""
for item in items: # items are the test methods
parent = item.parent # Get the parent of the item (class, or module if defined outside)
if parent is None: # should never happen, but just in case
return
if ( # Check if the class name ends with 'WithRequest' and if it has no flaky marker
parent.name.endswith("WithRequest")
and not parent.get_closest_marker( # get_closest_marker gets pytest.marks with `name`
name="flaky"
) # don't add/override any previously set markers
and not parent.get_closest_marker(name="req")
): # Add the flaky marker with a rerun filter to the class
parent.add_marker(pytest.mark.flaky(3, 1, rerun_filter=no_rerun_after_xfail_or_flood))
parent.add_marker(pytest.mark.req)
# Add the no_req marker to all classes that end with 'WithoutRequest' and don't have it
elif parent.name.endswith("WithoutRequest") and not parent.get_closest_marker(
name="no_req"
):
parent.add_marker(pytest.mark.no_req)
def no_rerun_after_xfail_or_flood(error, name, test: pytest.Function, plugin):
"""Don't rerun tests that have xfailed when marked with xfail, or when we hit a flood limit."""
xfail_present = test.get_closest_marker(name="xfail")
did_we_flood = "flood" in getattr(error[1], "msg", "") # _pytest.outcomes.XFailed has 'msg'
if xfail_present or did_we_flood:
return False
return True
GITHUB_ACTION = os.getenv("GITHUB_ACTION", False)
if GITHUB_ACTION:
@ -86,19 +119,12 @@ def event_loop(request):
# loop.close() # instead of closing here, do that at the every end of the test session
# Related to the above, see https://stackoverflow.com/a/67307042/10606962
def pytest_sessionfinish(session, exitstatus):
asyncio.get_event_loop().close()
@pytest.fixture(scope="session")
def bot_info():
return get_bot()
def bot_info() -> Dict[str, str]:
return BOT_INFO.get_info()
# Below classes are used to monkeypatch attributes since parent classes don't have __dict__
class TestHttpxRequest(HTTPXRequest):
async def _request_wrapper(
self,
@ -132,6 +158,10 @@ class DictExtBot(ExtBot):
# Makes it easier to work with the bot in tests
self._unfreeze()
# Here we override get_me for caching because we don't want to call the API repeatedly in tests
async def get_me(self, *args, **kwargs):
return await mocked_get_me(self)
class DictBot(Bot):
def __init__(self, *args, **kwargs):
@ -139,11 +169,42 @@ class DictBot(Bot):
# Makes it easier to work with the bot in tests
self._unfreeze()
# Here we override get_me for caching because we don't want to call the API repeatedly in tests
async def get_me(self, *args, **kwargs):
return await mocked_get_me(self)
class DictApplication(Application):
pass
async def mocked_get_me(bot: Bot):
if bot._bot_user is None:
bot._bot_user = get_bot_user(bot.token)
return bot._bot_user
def get_bot_user(token: str) -> User:
"""Used to return a mock user in bot.get_me(). This saves API calls on every init."""
bot_info = BOT_INFO.get_info()
# We don't take token from bot_info, because we need to make a bot with a specific ID. So we
# generate the correct user_id from the token (token from bot_info is random each test run).
# This is important in e.g. bot equality tests. The other parameters like first_name don't
# matter as much. In the future we may provide a way to get all the correct info from the token
user_id = int(token.split(":")[0])
first_name = bot_info.get("name")
username = bot_info.get("username").strip("@")
return User(
user_id,
first_name,
is_bot=True,
username=username,
can_join_groups=True,
can_read_all_group_messages=False,
supports_inline_queries=True,
)
@pytest.fixture(scope="session")
async def bot(bot_info):
"""Makes an ExtBot instance with the given bot_info"""
@ -151,6 +212,12 @@ async def bot(bot_info):
yield _bot
@pytest.fixture(scope="function")
def one_time_bot(bot_info):
"""A function scoped bot since the session bot would shutdown when `async with app` finishes"""
return make_bot(bot_info)
@pytest.fixture(scope="session")
async def cdc_bot(bot_info):
"""Makes an ExtBot instance with the given bot_info that uses arbitrary callback_data"""
@ -170,20 +237,36 @@ async def raw_bot(bot_info):
yield _bot
@pytest.fixture(scope="function")
# Here we store the default bots so that we don't have to create them again and again.
# They are initialized but not shutdown on pytest_sessionfinish because it is causing
# problems with the event loop (Event loop is closed).
default_bots = {}
@pytest.fixture(scope="session")
async def default_bot(request, bot_info):
param = request.param if hasattr(request, "param") else {}
defaults = Defaults(**param)
default_bot = make_bot(bot_info, defaults=Defaults(**param))
async with default_bot:
yield default_bot
# If the bot is already created, return it. Else make a new one.
default_bot = default_bots.get(defaults, None)
if default_bot is None:
default_bot = make_bot(bot_info, defaults=defaults)
await default_bot.initialize()
default_bots[defaults] = default_bot # Defaults object is hashable
return default_bot
@pytest.fixture(scope="function")
@pytest.fixture(scope="session")
async def tz_bot(timezone, bot_info):
default_bot = make_bot(bot_info, defaults=Defaults(tzinfo=timezone))
async with default_bot:
yield default_bot
defaults = Defaults(tzinfo=timezone)
try: # If the bot is already created, return it. Saves time since get_me is not called again.
return default_bots[defaults]
except KeyError:
default_bot = make_bot(bot_info, defaults=defaults)
await default_bot.initialize()
default_bots[defaults] = default_bot
return default_bot
@pytest.fixture(scope="session")
@ -243,16 +326,14 @@ def data_file(filename: str) -> Path:
@pytest.fixture(scope="function")
def thumb_file():
f = data_file("thumb.jpg").open("rb")
yield f
f.close()
with data_file("thumb.jpg").open("rb") as f:
yield f
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def class_thumb_file():
f = data_file("thumb.jpg").open("rb")
yield f
f.close()
with data_file("thumb.jpg").open("rb") as f:
yield f
def make_bot(bot_info=None, **kwargs):
@ -285,7 +366,7 @@ def make_message(text, **kwargs):
"""
bot = kwargs.pop("bot", None)
if bot is None:
bot = make_bot(get_bot())
bot = make_bot(BOT_INFO.get_info())
message = Message(
message_id=1,
from_user=kwargs.pop("user", User(id=1, first_name="", is_bot=False)),
@ -401,7 +482,7 @@ class BasicTimezone(datetime.tzinfo):
return datetime.timedelta(0)
@pytest.fixture(params=["Europe/Berlin", "Asia/Singapore", "UTC"])
@pytest.fixture(scope="session", params=["Europe/Berlin", "Asia/Singapore", "UTC"])
def tzinfo(request):
if TEST_WITH_OPT_DEPS:
return pytz.timezone(request.param)
@ -410,7 +491,7 @@ def tzinfo(request):
return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param)
@pytest.fixture()
@pytest.fixture(scope="session")
def timezone(tzinfo):
return tzinfo

View file

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import os
from pathlib import Path
@ -35,21 +36,19 @@ from tests.conftest import data_file
@pytest.fixture(scope="function")
def animation_file():
f = data_file("game.gif").open("rb")
yield f
f.close()
@pytest.fixture(scope="class")
async def animation(bot, chat_id):
with data_file("game.gif").open("rb") as f:
thumb = data_file("thumb.jpg")
yield f
@pytest.fixture(scope="module")
async def animation(bot, chat_id):
with data_file("game.gif").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
return (
await bot.send_animation(chat_id, animation=f, read_timeout=50, thumb=thumb.open("rb"))
await bot.send_animation(chat_id, animation=f, read_timeout=50, thumb=thumb)
).animation
class TestAnimation:
class TestAnimationBase:
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
width = 320
@ -63,6 +62,8 @@ class TestAnimation:
file_size = 5859
caption = "Test *animation*"
class TestAnimationWithoutRequest(TestAnimationBase):
def test_slot_behaviour(self, animation, mro_slots):
for attr in animation.__slots__:
assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'"
@ -80,215 +81,6 @@ class TestAnimation:
assert animation.file_name.startswith("game.gif") == self.file_name.startswith("game.gif")
assert isinstance(animation.thumb, PhotoSize)
@pytest.mark.flaky(3, 1)
async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file):
message = await bot.send_animation(
chat_id,
animation_file,
duration=self.duration,
width=self.width,
height=self.height,
caption=self.caption,
parse_mode="Markdown",
disable_notification=False,
protect_content=True,
thumb=thumb_file,
has_spoiler=True,
)
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ""
assert message.animation.file_unique_id != ""
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
assert message.animation.thumb.width == self.width
assert message.animation.thumb.height == self.height
assert message.has_protected_content
try:
assert message.has_media_spoiler
except AssertionError:
pytest.xfail("This is a bug on Telegram's end")
@pytest.mark.flaky(3, 1)
async def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_animation(chat_id, animation_file, filename="custom_filename")
monkeypatch.delattr(bot.request, "post")
@pytest.mark.flaky(3, 1)
async def test_get_and_download(self, bot, animation):
path = Path("game.gif")
if path.is_file():
path.unlink()
new_file = await bot.get_file(animation.file_id)
assert new_file.file_id == animation.file_id
assert new_file.file_path.startswith("https://")
new_filepath = await new_file.download_to_drive("game.gif")
assert new_filepath.is_file()
@pytest.mark.flaky(3, 1)
async def test_send_animation_url_file(self, bot, chat_id, animation):
message = await bot.send_animation(
chat_id=chat_id, animation=self.animation_file_url, caption=self.caption
)
assert message.caption == self.caption
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ""
assert message.animation.file_unique_id != ""
assert message.animation.duration == animation.duration
assert message.animation.file_name.startswith(
"game.gif"
) == animation.file_name.startswith("game.gif")
assert message.animation.mime_type == animation.mime_type
@pytest.mark.flaky(3, 1)
async def test_send_animation_caption_entities(self, bot, chat_id, animation):
test_string = "Italic Bold Code"
entities = [
MessageEntity(MessageEntity.ITALIC, 0, 6),
MessageEntity(MessageEntity.ITALIC, 7, 4),
MessageEntity(MessageEntity.ITALIC, 12, 4),
]
message = await bot.send_animation(
chat_id, animation, caption=test_string, caption_entities=entities
)
assert message.caption == test_string
assert message.caption_entities == tuple(entities)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_1(self, default_bot, chat_id, animation_file):
test_string = "Italic Bold Code"
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string
)
assert message.caption_markdown == test_markdown_string
assert message.caption == test_string
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animation_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string, parse_mode=None
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animation_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string, parse_mode="HTML"
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("animation") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("animation"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_animation(chat_id, file, thumb=file)
assert test_flag
monkeypatch.delattr(bot, "_post")
finally:
bot._local_mode = False
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot,custom",
[
({"allow_sending_without_reply": True}, None),
({"allow_sending_without_reply": False}, None),
({"allow_sending_without_reply": False}, True),
],
indirect=["default_bot"],
)
async def test_send_animation_default_allow_sending_without_reply(
self, default_bot, chat_id, animation, custom
):
reply_to_message = await default_bot.send_message(chat_id, "test")
await reply_to_message.delete()
if custom is not None:
message = await default_bot.send_animation(
chat_id,
animation,
allow_sending_without_reply=custom,
reply_to_message_id=reply_to_message.message_id,
)
assert message.reply_to_message is None
elif default_bot.defaults.allow_sending_without_reply:
message = await default_bot.send_animation(
chat_id, animation, reply_to_message_id=reply_to_message.message_id
)
assert message.reply_to_message is None
else:
with pytest.raises(BadRequest, match="message not found"):
await default_bot.send_animation(
chat_id, animation, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_animation_default_protect_content(self, default_bot, chat_id, animation):
animation_protected = await default_bot.send_animation(chat_id, animation)
assert animation_protected.has_protected_content
ani_unprotected = await default_bot.send_animation(
chat_id, animation, protect_content=False
)
assert not ani_unprotected.has_protected_content
@pytest.mark.flaky(3, 1)
async def test_resend(self, bot, chat_id, animation):
message = await bot.send_animation(chat_id, animation.file_id)
assert message.animation == animation
async def test_send_with_animation(self, monkeypatch, bot, chat_id, animation):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["animation"] == animation.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_animation(animation=animation, chat_id=chat_id)
assert message
def test_de_json(self, bot, animation):
json_dict = {
"file_id": self.animation_file_id,
@ -323,33 +115,6 @@ class TestAnimation:
assert animation_dict["mime_type"] == animation.mime_type
assert animation_dict["file_size"] == animation.file_size
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, chat_id):
animation_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.send_animation(chat_id=chat_id, animation=animation_file)
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_animation(chat_id=chat_id, animation="")
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_animation(chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, animation):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == animation.file_id
assert check_shortcut_signature(Animation.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(animation.get_file, animation.get_bot(), "get_file")
assert await check_defaults_handling(animation.get_file, animation.get_bot())
monkeypatch.setattr(animation.get_bot(), "get_file", make_assertion)
assert await animation.get_file()
def test_equality(self):
a = Animation(
self.animation_file_id,
@ -371,3 +136,222 @@ class TestAnimation:
assert a != e
assert hash(a) != hash(e)
async def test_send_animation_custom_filename(self, bot, chat_id, animation_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_animation(chat_id, animation_file, filename="custom_filename")
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_animation_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("animation") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("animation"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_animation(chat_id, file, thumb=file)
assert test_flag
finally:
bot._local_mode = False
async def test_send_with_animation(self, monkeypatch, bot, chat_id, animation):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["animation"] == animation.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_animation(animation=animation, chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, animation):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == animation.file_id
assert check_shortcut_signature(Animation.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(animation.get_file, animation.get_bot(), "get_file")
assert await check_defaults_handling(animation.get_file, animation.get_bot())
monkeypatch.setattr(animation.get_bot(), "get_file", make_assertion)
assert await animation.get_file()
class TestAnimationWithRequest(TestAnimationBase):
async def test_send_all_args(self, bot, chat_id, animation_file, animation, thumb_file):
message = await bot.send_animation(
chat_id,
animation_file,
duration=self.duration,
width=self.width,
height=self.height,
caption=self.caption,
parse_mode="Markdown",
disable_notification=False,
protect_content=True,
thumb=thumb_file,
has_spoiler=True,
)
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ""
assert message.animation.file_unique_id != ""
assert message.animation.file_name == animation.file_name
assert message.animation.mime_type == animation.mime_type
assert message.animation.file_size == animation.file_size
assert message.animation.thumb.width == self.width
assert message.animation.thumb.height == self.height
assert message.has_protected_content
try:
assert message.has_media_spoiler
except AssertionError:
pytest.xfail("This is a bug on Telegram's end")
async def test_get_and_download(self, bot, animation):
path = Path("game.gif")
if path.is_file():
path.unlink()
new_file = await bot.get_file(animation.file_id)
assert new_file.file_path.startswith("https://")
new_filepath = await new_file.download_to_drive("game.gif")
assert new_filepath.is_file()
async def test_send_animation_url_file(self, bot, chat_id, animation):
message = await bot.send_animation(
chat_id=chat_id, animation=self.animation_file_url, caption=self.caption
)
assert message.caption == self.caption
assert isinstance(message.animation, Animation)
assert isinstance(message.animation.file_id, str)
assert isinstance(message.animation.file_unique_id, str)
assert message.animation.file_id != ""
assert message.animation.file_unique_id != ""
assert message.animation.duration == animation.duration
assert message.animation.file_name.startswith(
"game.gif"
) == animation.file_name.startswith("game.gif")
assert message.animation.mime_type == animation.mime_type
async def test_send_animation_caption_entities(self, bot, chat_id, animation):
test_string = "Italic Bold Code"
entities = [
MessageEntity(MessageEntity.ITALIC, 0, 6),
MessageEntity(MessageEntity.ITALIC, 7, 4),
MessageEntity(MessageEntity.ITALIC, 12, 4),
]
message = await bot.send_animation(
chat_id, animation, caption=test_string, caption_entities=entities
)
assert message.caption == test_string
assert message.caption_entities == tuple(entities)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_1(self, default_bot, chat_id, animation_file):
test_string = "Italic Bold Code"
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string
)
assert message.caption_markdown == test_markdown_string
assert message.caption == test_string
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_2(self, default_bot, chat_id, animation_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string, parse_mode=None
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_animation_default_parse_mode_3(self, default_bot, chat_id, animation_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_animation(
chat_id, animation_file, caption=test_markdown_string, parse_mode="HTML"
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.parametrize(
"default_bot,custom",
[
({"allow_sending_without_reply": True}, None),
({"allow_sending_without_reply": False}, None),
({"allow_sending_without_reply": False}, True),
],
indirect=["default_bot"],
)
async def test_send_animation_default_allow_sending_without_reply(
self, default_bot, chat_id, animation, custom
):
reply_to_message = await default_bot.send_message(chat_id, "test")
await reply_to_message.delete()
if custom is not None:
message = await default_bot.send_animation(
chat_id,
animation,
allow_sending_without_reply=custom,
reply_to_message_id=reply_to_message.message_id,
)
assert message.reply_to_message is None
elif default_bot.defaults.allow_sending_without_reply:
message = await default_bot.send_animation(
chat_id, animation, reply_to_message_id=reply_to_message.message_id
)
assert message.reply_to_message is None
else:
with pytest.raises(BadRequest, match="message not found"):
await default_bot.send_animation(
chat_id, animation, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_animation_default_protect_content(self, default_bot, chat_id, animation):
tasks = asyncio.gather(
default_bot.send_animation(chat_id, animation),
default_bot.send_animation(chat_id, animation, protect_content=False),
)
anim_protected, anim_unprotected = await tasks
assert anim_protected.has_protected_content
assert not anim_unprotected.has_protected_content
async def test_resend(self, bot, chat_id, animation):
message = await bot.send_animation(chat_id, animation.file_id)
assert message.animation == animation
async def test_error_send_empty_file(self, bot, chat_id):
animation_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.send_animation(chat_id=chat_id, animation=animation_file)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_animation(chat_id=chat_id, animation="")
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_animation(chat_id=chat_id)

View file

@ -53,7 +53,13 @@ from telegram.ext import (
filters,
)
from telegram.warnings import PTBUserWarning
from tests.conftest import PROJECT_ROOT_PATH, call_after, make_message_update, send_webhook_message
from tests.conftest import (
PROJECT_ROOT_PATH,
call_after,
make_bot,
make_message_update,
send_webhook_message,
)
class CustomContext(CallbackContext):
@ -113,8 +119,8 @@ class TestApplication:
):
self.received = context.error.message
async def test_slot_behaviour(self, bot, mro_slots):
async with ApplicationBuilder().token(bot.token).build() as app:
async def test_slot_behaviour(self, one_time_bot, mro_slots):
async with ApplicationBuilder().bot(one_time_bot).build() as app:
for at in app.__slots__:
at = f"_Application{at}" if at.startswith("__") and not at.endswith("__") else at
assert getattr(app, at, "err") != "err", f"got extra slot '{at}'"
@ -144,12 +150,12 @@ class TestApplication:
"concurrent_updates, expected", [(0, 0), (4, 4), (False, 0), (True, 256)]
)
@pytest.mark.filterwarnings("ignore: `Application` instances should")
def test_init(self, bot, concurrent_updates, expected):
def test_init(self, one_time_bot, concurrent_updates, expected):
update_queue = asyncio.Queue()
job_queue = JobQueue()
persistence = PicklePersistence("file_path")
context_types = ContextTypes()
updater = Updater(bot=bot, update_queue=update_queue)
updater = Updater(bot=one_time_bot, update_queue=update_queue)
async def post_init(application: Application) -> None:
pass
@ -161,7 +167,7 @@ class TestApplication:
pass
app = Application(
bot=bot,
bot=one_time_bot,
update_queue=update_queue,
job_queue=job_queue,
persistence=persistence,
@ -172,7 +178,7 @@ class TestApplication:
post_shutdown=post_shutdown,
post_stop=post_stop,
)
assert app.bot is bot
assert app.bot is one_time_bot
assert app.update_queue is update_queue
assert app.job_queue is job_queue
assert app.persistence is persistence
@ -196,7 +202,7 @@ class TestApplication:
with pytest.raises(ValueError, match="must be a non-negative"):
Application(
bot=bot,
bot=one_time_bot,
update_queue=update_queue,
job_queue=job_queue,
persistence=persistence,
@ -208,19 +214,19 @@ class TestApplication:
post_stop=None,
)
def test_job_queue(self, bot, app, recwarn):
def test_job_queue(self, one_time_bot, app, recwarn):
expected_warning = (
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
"`pip install python-telegram-bot[job-queue]`."
)
assert app.job_queue is app._job_queue
application = ApplicationBuilder().token(bot.token).job_queue(None).build()
application = ApplicationBuilder().bot(one_time_bot).job_queue(None).build()
assert application.job_queue is None
assert len(recwarn) == 1
assert str(recwarn[0].message) == expected_warning
assert recwarn[0].filename == __file__, "wrong stacklevel"
def test_custom_context_init(self, bot):
def test_custom_context_init(self, one_time_bot):
cc = ContextTypes(
context=CustomContext,
user_data=int,
@ -228,14 +234,14 @@ class TestApplication:
bot_data=complex,
)
application = ApplicationBuilder().token(bot.token).context_types(cc).build()
application = ApplicationBuilder().bot(one_time_bot).context_types(cc).build()
assert isinstance(application.user_data[1], int)
assert isinstance(application.chat_data[1], float)
assert isinstance(application.bot_data, complex)
@pytest.mark.parametrize("updater", (True, False))
async def test_initialize(self, bot, monkeypatch, updater):
async def test_initialize(self, one_time_bot, monkeypatch, updater):
"""Initialization of persistence is tested test_basepersistence"""
self.test_flag = set()
@ -251,18 +257,18 @@ class TestApplication:
)
if updater:
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
await app.initialize()
assert self.test_flag == {"bot", "updater"}
await app.shutdown()
else:
app = ApplicationBuilder().token(bot.token).updater(None).build()
app = ApplicationBuilder().bot(one_time_bot).updater(None).build()
await app.initialize()
assert self.test_flag == {"bot"}
await app.shutdown()
@pytest.mark.parametrize("updater", (True, False))
async def test_shutdown(self, bot, monkeypatch, updater):
async def test_shutdown(self, one_time_bot, monkeypatch, updater):
"""Shutdown of persistence is tested in test_basepersistence"""
self.test_flag = set()
@ -278,11 +284,11 @@ class TestApplication:
)
if updater:
async with ApplicationBuilder().token(bot.token).build():
async with ApplicationBuilder().bot(one_time_bot).build():
pass
assert self.test_flag == {"bot", "updater"}
else:
async with ApplicationBuilder().token(bot.token).updater(None).build():
async with ApplicationBuilder().bot(one_time_bot).updater(None).build():
pass
assert self.test_flag == {"bot"}
@ -329,12 +335,12 @@ class TestApplication:
await app.shutdown()
await app.stop()
async def test_start_not_running_after_failure(self, bot, monkeypatch):
async def test_start_not_running_after_failure(self, one_time_bot, monkeypatch):
def start(_):
raise Exception("Test Exception")
monkeypatch.setattr(JobQueue, "start", start)
app = ApplicationBuilder().token(bot.token).job_queue(JobQueue()).build()
app = ApplicationBuilder().bot(one_time_bot).job_queue(JobQueue()).build()
async with app:
with pytest.raises(Exception, match="Test Exception"):
@ -405,12 +411,12 @@ class TestApplication:
@pytest.mark.parametrize("job_queue", (True, False))
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
async def test_start_stop_processing_updates(self, bot, job_queue):
async def test_start_stop_processing_updates(self, one_time_bot, job_queue):
# TODO: repeat a similar test for create_task, persistence processing and job queue
if job_queue:
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
else:
app = ApplicationBuilder().token(bot.token).job_queue(None).build()
app = ApplicationBuilder().bot(one_time_bot).job_queue(None).build()
async def callback(u, c):
self.received = u
@ -440,9 +446,12 @@ class TestApplication:
await asyncio.sleep(0.05)
assert app.update_queue.empty()
assert self.received == 1
await app.updater.start_polling()
await app.stop()
try: # just in case start_polling times out
await app.updater.start_polling()
except TelegramError:
pytest.xfail("start_polling timed out")
else:
await app.stop()
assert not app.running
# app.stop() should not stop the updater!
assert app.updater.running
@ -476,7 +485,7 @@ class TestApplication:
async def one(update, context):
self.received = context
def two(update, context):
async def two(update, context):
if update.message.text == "test":
if context is not self.received:
pytest.fail("Expected same context object, got different")
@ -643,7 +652,7 @@ class TestApplication:
await asyncio.sleep(0.05)
await app.stop()
async def test_flow_stop(self, app, bot):
async def test_flow_stop(self, app, one_time_bot):
passed = []
async def start1(b, u):
@ -668,7 +677,8 @@ class TestApplication:
],
),
)
update.message.set_bot(bot)
await one_time_bot.initialize()
update.message.set_bot(one_time_bot)
async with app:
# If ApplicationHandlerStop raised handlers in other groups should not be called.
@ -679,18 +689,18 @@ class TestApplication:
await app.process_update(update)
assert passed == ["start1"]
async def test_flow_stop_by_error_handler(self, app, bot):
async def test_flow_stop_by_error_handler(self, app):
passed = []
exception = Exception("General excepition")
exception = Exception("General exception")
async def start1(b, u):
async def start1(u, c):
passed.append("start1")
raise exception
async def start2(b, u):
async def start2(u, c):
passed.append("start2")
async def start3(b, u):
async def start3(u, c):
passed.append("start3")
async def error(u, c):
@ -728,7 +738,7 @@ class TestApplication:
# Higher groups should still be called
assert self.count == 42
async def test_error_in_handler_part_2(self, app, bot):
async def test_error_in_handler_part_2(self, app, one_time_bot):
passed = []
err = Exception("General exception")
@ -758,7 +768,8 @@ class TestApplication:
],
),
)
update.message.set_bot(bot)
await one_time_bot.initialize()
update.message.set_bot(one_time_bot)
async with app:
# If an unhandled exception was caught, no further handlers from the same group should
@ -837,7 +848,7 @@ class TestApplication:
await app.stop()
async def test_custom_context_error_handler(self, bot):
async def test_custom_context_error_handler(self, one_time_bot):
async def error_handler(_, context):
self.received = (
type(context),
@ -848,7 +859,7 @@ class TestApplication:
application = (
ApplicationBuilder()
.token(bot.token)
.bot(one_time_bot)
.context_types(
ContextTypes(
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
@ -866,8 +877,8 @@ class TestApplication:
await asyncio.sleep(0.05)
assert self.received == (CustomContext, float, complex, int)
async def test_custom_context_handler_callback(self, bot):
def callback(_, context):
async def test_custom_context_handler_callback(self, one_time_bot):
async def callback(_, context):
self.received = (
type(context),
type(context.user_data),
@ -877,7 +888,7 @@ class TestApplication:
application = (
ApplicationBuilder()
.token(bot.token)
.bot(one_time_bot)
.context_types(
ContextTypes(
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
@ -971,7 +982,7 @@ class TestApplication:
), "incorrect stacklevel!"
async def test_non_blocking_no_error_handler(self, app, caplog):
app.add_handler(TypeHandler(object, self.callback_raise_error, block=False))
app.add_handler(TypeHandler(object, self.callback_raise_error("Test error"), block=False))
with caplog.at_level(logging.ERROR):
async with app:
@ -997,7 +1008,7 @@ class TestApplication:
app.add_error_handler(async_error_handler, block=False)
app.add_error_handler(normal_error_handler)
app.add_handler(TypeHandler(object, self.callback_raise_error, block=handler_block))
app.add_handler(TypeHandler(object, self.callback_raise_error("err"), block=handler_block))
async with app:
await app.start()
@ -1041,14 +1052,15 @@ class TestApplication:
), "incorrect stacklevel!"
@pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)])
async def test_default_block_error_handler(self, bot, block, expected_output):
async def test_default_block_error_handler(self, bot_info, block, expected_output):
async def error_handler(*args, **kwargs):
await asyncio.sleep(0.1)
self.count = 5
app = Application.builder().token(bot.token).defaults(Defaults(block=block)).build()
bot = make_bot(bot_info, defaults=Defaults(block=block))
app = Application.builder().bot(bot).build()
async with app:
app.add_handler(TypeHandler(object, self.callback_raise_error))
app.add_handler(TypeHandler(object, self.callback_raise_error("error")))
app.add_error_handler(error_handler)
await app.process_update(1)
await asyncio.sleep(0.05)
@ -1057,8 +1069,9 @@ class TestApplication:
assert self.count == 5
@pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)])
async def test_default_block_handler(self, bot, block, expected_output):
app = Application.builder().token(bot.token).defaults(Defaults(block=block)).build()
async def test_default_block_handler(self, bot_info, block, expected_output):
bot = make_bot(bot_info, defaults=Defaults(block=block))
app = Application.builder().bot(bot).build()
async with app:
app.add_handler(TypeHandler(object, self.callback_set_count(5, sleep=0.1)))
await app.process_update(1)
@ -1072,7 +1085,7 @@ class TestApplication:
async def test_nonblocking_handler_raises_and_non_blocking_error_handler_raises(
self, app, caplog, handler_block, error_handler_block
):
handler = TypeHandler(object, self.callback_raise_error, block=handler_block)
handler = TypeHandler(object, self.callback_raise_error("error"), block=handler_block)
app.add_handler(handler)
app.add_error_handler(self.error_handler_raise_error, block=error_handler_block)
@ -1303,10 +1316,12 @@ class TestApplication:
await app.stop()
@pytest.mark.parametrize("concurrent_updates", (15, 50, 100))
async def test_concurrent_updates(self, bot, concurrent_updates):
async def test_concurrent_updates(self, one_time_bot, concurrent_updates):
# We don't test with `True` since the large number of parallel coroutines quickly leads
# to test instabilities
app = Application.builder().token(bot.token).concurrent_updates(concurrent_updates).build()
app = (
Application.builder().bot(one_time_bot).concurrent_updates(concurrent_updates).build()
)
events = {i: asyncio.Event() for i in range(app.concurrent_updates + 10)}
queue = asyncio.Queue()
for event in events.values():
@ -1337,8 +1352,8 @@ class TestApplication:
await app.stop()
async def test_concurrent_updates_done_on_shutdown(self, bot):
app = Application.builder().token(bot.token).concurrent_updates(True).build()
async def test_concurrent_updates_done_on_shutdown(self, one_time_bot):
app = Application.builder().bot(one_time_bot).concurrent_updates(True).build()
event = asyncio.Event()
async def callback(update, context):
@ -1422,7 +1437,7 @@ class TestApplication:
platform.system() == "Windows",
reason="Can't send signals without stopping whole process on windows",
)
def test_run_polling_post_init(self, bot, monkeypatch):
def test_run_polling_post_init(self, one_time_bot, monkeypatch):
events = []
async def get_updates(*args, **kwargs):
@ -1443,7 +1458,7 @@ class TestApplication:
async def post_init(app: Application) -> None:
events.append("post_init")
app = Application.builder().token(bot.token).post_init(post_init).build()
app = Application.builder().bot(one_time_bot).post_init(post_init).build()
app.bot._unfreeze()
monkeypatch.setattr(app.bot, "get_updates", get_updates)
monkeypatch.setattr(
@ -1465,7 +1480,7 @@ class TestApplication:
platform.system() == "Windows",
reason="Can't send signals without stopping whole process on windows",
)
def test_run_polling_post_shutdown(self, bot, monkeypatch):
def test_run_polling_post_shutdown(self, one_time_bot, monkeypatch):
events = []
async def get_updates(*args, **kwargs):
@ -1486,7 +1501,7 @@ class TestApplication:
async def post_shutdown(app: Application) -> None:
events.append("post_shutdown")
app = Application.builder().token(bot.token).post_shutdown(post_shutdown).build()
app = Application.builder().bot(one_time_bot).post_shutdown(post_shutdown).build()
app.bot._unfreeze()
monkeypatch.setattr(app.bot, "get_updates", get_updates)
monkeypatch.setattr(
@ -1691,7 +1706,7 @@ class TestApplication:
platform.system() == "Windows",
reason="Can't send signals without stopping whole process on windows",
)
def test_run_webhook_post_init(self, bot, monkeypatch):
def test_run_webhook_post_init(self, one_time_bot, monkeypatch):
events = []
async def delete_webhook(*args, **kwargs):
@ -1718,7 +1733,7 @@ class TestApplication:
async def post_init(app: Application) -> None:
events.append("post_init")
app = Application.builder().token(bot.token).post_init(post_init).build()
app = Application.builder().bot(one_time_bot).post_init(post_init).build()
app.bot._unfreeze()
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
@ -1751,7 +1766,7 @@ class TestApplication:
platform.system() == "Windows",
reason="Can't send signals without stopping whole process on windows",
)
def test_run_webhook_post_shutdown(self, bot, monkeypatch):
def test_run_webhook_post_shutdown(self, one_time_bot, monkeypatch):
events = []
async def delete_webhook(*args, **kwargs):
@ -1778,7 +1793,7 @@ class TestApplication:
async def post_shutdown(app: Application) -> None:
events.append("post_shutdown")
app = Application.builder().token(bot.token).post_shutdown(post_shutdown).build()
app = Application.builder().bot(one_time_bot).post_shutdown(post_shutdown).build()
app.bot._unfreeze()
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
@ -1883,7 +1898,7 @@ class TestApplication:
platform.system() == "Windows",
reason="Can't send signals without stopping whole process on windows",
)
def test_run_webhook_parameters_passing(self, bot, monkeypatch):
def test_run_webhook_parameters_passing(self, one_time_bot, monkeypatch):
# Check that we pass them correctly
async def start_webhook(_, **kwargs):
@ -1898,7 +1913,7 @@ class TestApplication:
monkeypatch.setattr(Updater, "start_webhook", start_webhook)
monkeypatch.setattr(Updater, "stop", stop)
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
app_signature = inspect.signature(app.run_webhook)
for name, param in updater_signature.parameters.items():
@ -1939,8 +1954,8 @@ class TestApplication:
assert set(self.received.keys()) == set(expected.keys())
assert self.received == expected
def test_run_without_updater(self, bot):
app = ApplicationBuilder().token(bot.token).updater(None).build()
def test_run_without_updater(self, one_time_bot):
app = ApplicationBuilder().bot(one_time_bot).updater(None).build()
with pytest.raises(RuntimeError, match="only available if the application has an Updater"):
app.run_webhook()
@ -1950,7 +1965,7 @@ class TestApplication:
@pytest.mark.parametrize("method", ["start", "initialize"])
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
def test_run_error_in_application(self, bot, monkeypatch, method):
def test_run_error_in_application(self, one_time_bot, monkeypatch, method):
shutdowns = []
async def raise_method(*args, **kwargs):
@ -1971,7 +1986,7 @@ class TestApplication:
monkeypatch.setattr(
Updater, "shutdown", call_after(Updater.shutdown, after_shutdown("updater"))
)
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
with pytest.raises(RuntimeError, match="Test Exception"):
app.run_polling(close_loop=False)
@ -1986,7 +2001,7 @@ class TestApplication:
@pytest.mark.parametrize("method", ["start_polling", "start_webhook"])
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
def test_run_error_in_updater(self, bot, monkeypatch, method):
def test_run_error_in_updater(self, one_time_bot, monkeypatch, method):
shutdowns = []
async def raise_method(*args, **kwargs):
@ -2007,7 +2022,7 @@ class TestApplication:
monkeypatch.setattr(
Updater, "shutdown", call_after(Updater.shutdown, after_shutdown("updater"))
)
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
with pytest.raises(RuntimeError, match="Test Exception"):
if "polling" in method:
app.run_polling(close_loop=False)
@ -2023,12 +2038,12 @@ class TestApplication:
reason="Only really relevant on windows",
)
@pytest.mark.parametrize("method", ["start_polling", "start_webhook"])
def test_run_stop_signal_warning_windows(self, bot, method, recwarn, monkeypatch):
def test_run_stop_signal_warning_windows(self, one_time_bot, method, recwarn, monkeypatch):
async def raise_method(*args, **kwargs):
raise RuntimeError("Prevent Actually Running")
monkeypatch.setattr(Application, "initialize", raise_method)
app = ApplicationBuilder().token(bot.token).build()
app = ApplicationBuilder().bot(one_time_bot).build()
with pytest.raises(RuntimeError, match="Prevent Actually Running"):
if "polling" in method:
@ -2054,7 +2069,7 @@ class TestApplication:
assert len(recwarn) == 0
@pytest.mark.timeout(6)
@pytest.mark.flaky(3, 1) # loop.call_later will error the test when a flood error is received
def test_signal_handlers(self, app, monkeypatch):
# this test should make sure that signal handlers are set by default on Linux + Mac,
# and not on Windows.
@ -2068,11 +2083,10 @@ class TestApplication:
loop = asyncio.get_event_loop()
monkeypatch.setattr(loop, "add_signal_handler", signal_handler_test)
async def abort_app():
await asyncio.sleep(2)
def abort_app():
raise SystemExit
loop.create_task(abort_app())
loop.call_later(0.6, abort_app)
app.run_polling(close_loop=False)
@ -2082,7 +2096,7 @@ class TestApplication:
assert received_signals == [signal.SIGINT, signal.SIGTERM, signal.SIGABRT]
received_signals.clear()
loop.create_task(abort_app())
loop.call_later(0.6, abort_app)
app.run_webhook(port=49152, webhook_url="example.com", close_loop=False)
if platform.system() == "Windows":

View file

@ -17,7 +17,6 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import os
from dataclasses import dataclass
import httpx
@ -38,10 +37,7 @@ from telegram.ext import (
from telegram.ext._applicationbuilder import _BOT_CHECKS
from telegram.request import HTTPXRequest
from .auxil.object_conversions import env_var_2_bool
from .conftest import PRIVATE_KEY, data_file
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
from .conftest import PRIVATE_KEY, TEST_WITH_OPT_DEPS, data_file
@pytest.fixture(scope="function")

View file

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import os
from pathlib import Path
@ -35,20 +36,17 @@ from tests.conftest import data_file
@pytest.fixture(scope="function")
def audio_file():
with open(data_file("telegram.mp3"), "rb") as f:
with data_file("telegram.mp3").open("rb") as f:
yield f
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
async def audio(bot, chat_id):
with data_file("telegram.mp3").open("rb") as f:
thumb = data_file("thumb.jpg")
return (
await bot.send_audio(chat_id, audio=f, read_timeout=50, thumb=thumb.open("rb"))
).audio
with data_file("telegram.mp3").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumb=thumb)).audio
class TestAudio:
class TestAudioBase:
caption = "Test *audio*"
performer = "Leandro Toledo"
title = "Teste"
@ -65,6 +63,8 @@ class TestAudio:
audio_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestAudioWithoutRequest(TestAudioBase):
def test_slot_behaviour(self, audio, mro_slots):
for attr in audio.__slots__:
assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'"
@ -88,176 +88,6 @@ class TestAudio:
assert audio.thumb.width == self.thumb_width
assert audio.thumb.height == self.thumb_height
@pytest.mark.flaky(3, 1)
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
message = await bot.send_audio(
chat_id,
audio=audio_file,
caption=self.caption,
duration=self.duration,
performer=self.performer,
title=self.title,
disable_notification=False,
protect_content=True,
parse_mode="Markdown",
thumb=thumb_file,
)
assert message.caption == self.caption.replace("*", "")
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == self.duration
assert message.audio.performer == self.performer
assert message.audio.title == self.title
assert message.audio.file_name == self.file_name
assert message.audio.mime_type == self.mime_type
assert message.audio.file_size == self.file_size
assert message.audio.thumb.file_size == self.thumb_file_size
assert message.audio.thumb.width == self.thumb_width
assert message.audio.thumb.height == self.thumb_height
assert message.has_protected_content
@pytest.mark.flaky(3, 1)
async def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_audio(chat_id, audio_file, filename="custom_filename")
@pytest.mark.flaky(3, 1)
async def test_get_and_download(self, bot, audio):
path = Path("telegram.mp3")
if path.is_file():
path.unlink()
new_file = await bot.get_file(audio.file_id)
assert new_file.file_size == self.file_size
assert new_file.file_id == audio.file_id
assert new_file.file_unique_id == audio.file_unique_id
assert str(new_file.file_path).startswith("https://")
await new_file.download_to_drive("telegram.mp3")
assert path.is_file()
@pytest.mark.flaky(3, 1)
async def test_send_mp3_url_file(self, bot, chat_id, audio):
message = await bot.send_audio(
chat_id=chat_id, audio=self.audio_file_url, caption=self.caption
)
assert message.caption == self.caption
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == audio.duration
assert message.audio.mime_type == audio.mime_type
assert message.audio.file_size == audio.file_size
@pytest.mark.flaky(3, 1)
async def test_resend(self, bot, chat_id, audio):
message = await bot.send_audio(chat_id=chat_id, audio=audio.file_id)
assert message.audio == audio
async def test_send_with_audio(self, monkeypatch, bot, chat_id, audio):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["audio"] == audio.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_audio(audio=audio, chat_id=chat_id)
assert message
@pytest.mark.flaky(3, 1)
async def test_send_audio_caption_entities(self, bot, chat_id, audio):
test_string = "Italic Bold Code"
entities = [
MessageEntity(MessageEntity.ITALIC, 0, 6),
MessageEntity(MessageEntity.ITALIC, 7, 4),
MessageEntity(MessageEntity.ITALIC, 12, 4),
]
message = await bot.send_audio(
chat_id, audio, caption=test_string, caption_entities=entities
)
assert message.caption == test_string
assert message.caption_entities == tuple(entities)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file):
test_string = "Italic Bold Code"
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string)
assert message.caption_markdown == test_markdown_string
assert message.caption == test_string
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(
chat_id, audio_file, caption=test_markdown_string, parse_mode=None
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(
chat_id, audio_file, caption=test_markdown_string, parse_mode="HTML"
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_audio_default_protect_content(self, default_bot, chat_id, audio):
protected_audio = await default_bot.send_audio(chat_id, audio)
assert protected_audio.has_protected_content
unprotected = await default_bot.send_audio(chat_id, audio, protect_content=False)
assert not unprotected.has_protected_content
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("audio") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("audio"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_audio(chat_id, file, thumb=file)
assert test_flag
monkeypatch.delattr(bot, "_post")
finally:
bot._local_mode = False
def test_de_json(self, bot, audio):
json_dict = {
"file_id": self.audio_file_id,
@ -294,33 +124,6 @@ class TestAudio:
assert audio_dict["file_size"] == audio.file_size
assert audio_dict["file_name"] == audio.file_name
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, chat_id):
audio_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.send_audio(chat_id=chat_id, audio=audio_file)
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_audio(chat_id=chat_id, audio="")
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_audio(chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, audio):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == audio.file_id
assert check_shortcut_signature(Audio.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(audio.get_file, audio.get_bot(), "get_file")
assert await check_defaults_handling(audio.get_file, audio.get_bot())
monkeypatch.setattr(audio._bot, "get_file", make_assertion)
assert await audio.get_file()
def test_equality(self, audio):
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
b = Audio("", audio.file_unique_id, audio.duration)
@ -340,3 +143,187 @@ class TestAudio:
assert a != e
assert hash(a) != hash(e)
async def test_send_with_audio(self, monkeypatch, bot, chat_id, audio):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["audio"] == audio.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_audio(audio=audio, chat_id=chat_id)
async def test_send_audio_custom_filename(self, bot, chat_id, audio_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_audio(chat_id, audio_file, filename="custom_filename")
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_audio_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("audio") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("audio"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_audio(chat_id, file, thumb=file)
assert test_flag
finally:
bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, audio):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == audio.file_id
assert check_shortcut_signature(Audio.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(audio.get_file, audio.get_bot(), "get_file")
assert await check_defaults_handling(audio.get_file, audio.get_bot())
monkeypatch.setattr(audio._bot, "get_file", make_assertion)
assert await audio.get_file()
class TestAudioWithRequest(TestAudioBase):
async def test_send_all_args(self, bot, chat_id, audio_file, thumb_file):
message = await bot.send_audio(
chat_id,
audio=audio_file,
caption=self.caption,
duration=self.duration,
performer=self.performer,
title=self.title,
disable_notification=False,
protect_content=True,
parse_mode="Markdown",
thumb=thumb_file,
)
assert message.caption == self.caption.replace("*", "")
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == self.duration
assert message.audio.performer == self.performer
assert message.audio.title == self.title
assert message.audio.file_name == self.file_name
assert message.audio.mime_type == self.mime_type
assert message.audio.file_size == self.file_size
assert message.audio.thumb.file_size == self.thumb_file_size
assert message.audio.thumb.width == self.thumb_width
assert message.audio.thumb.height == self.thumb_height
assert message.has_protected_content
async def test_get_and_download(self, bot, chat_id, audio):
path = Path("telegram.mp3")
if path.is_file():
path.unlink()
new_file = await bot.get_file(audio.file_id)
assert new_file.file_size == self.file_size
assert new_file.file_unique_id == audio.file_unique_id
assert str(new_file.file_path).startswith("https://")
await new_file.download_to_drive("telegram.mp3")
assert path.is_file()
async def test_send_mp3_url_file(self, bot, chat_id, audio):
message = await bot.send_audio(
chat_id=chat_id, audio=self.audio_file_url, caption=self.caption
)
assert message.caption == self.caption
assert isinstance(message.audio, Audio)
assert isinstance(message.audio.file_id, str)
assert isinstance(message.audio.file_unique_id, str)
assert message.audio.file_unique_id is not None
assert message.audio.file_id is not None
assert message.audio.duration == audio.duration
assert message.audio.mime_type == audio.mime_type
assert message.audio.file_size == audio.file_size
async def test_send_audio_caption_entities(self, bot, chat_id, audio):
test_string = "Italic Bold Code"
entities = [
MessageEntity(MessageEntity.ITALIC, 0, 6),
MessageEntity(MessageEntity.ITALIC, 7, 4),
MessageEntity(MessageEntity.ITALIC, 12, 4),
]
message = await bot.send_audio(
chat_id, audio, caption=test_string, caption_entities=entities
)
assert message.caption == test_string
assert message.caption_entities == tuple(entities)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_1(self, default_bot, chat_id, audio_file):
test_string = "Italic Bold Code"
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(chat_id, audio_file, caption=test_markdown_string)
assert message.caption_markdown == test_markdown_string
assert message.caption == test_string
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_2(self, default_bot, chat_id, audio_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(
chat_id, audio_file, caption=test_markdown_string, parse_mode=None
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_audio_default_parse_mode_3(self, default_bot, chat_id, audio_file):
test_markdown_string = "_Italic_ *Bold* `Code`"
message = await default_bot.send_audio(
chat_id, audio_file, caption=test_markdown_string, parse_mode="HTML"
)
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_audio_default_protect_content(self, default_bot, chat_id, audio):
tasks = asyncio.gather(
default_bot.send_audio(chat_id, audio),
default_bot.send_audio(chat_id, audio, protect_content=False),
)
protected, unprotected = await tasks
assert protected.has_protected_content
assert not unprotected.has_protected_content
async def test_resend(self, bot, chat_id, audio):
message = await bot.send_audio(chat_id=chat_id, audio=audio.file_id)
assert message.audio == audio
async def test_error_send_empty_file(self, bot, chat_id):
audio_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.send_audio(chat_id=chat_id, audio=audio_file)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_audio(chat_id=chat_id, audio="")
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_audio(chat_id=chat_id)

View file

@ -43,7 +43,7 @@ from telegram.ext import (
filters,
)
from telegram.warnings import PTBUserWarning
from tests.conftest import DictApplication, make_message_update
from tests.conftest import DictApplication, make_bot, make_message_update
class HandlerStates(int, enum.Enum):
@ -227,7 +227,11 @@ class PappInput(NamedTuple):
def build_papp(
token: str, store_data: dict = None, update_interval: float = None, fill_data: bool = False
bot_info: dict = None,
token: str = None,
store_data: dict = None,
update_interval: float = None,
fill_data: bool = False,
) -> Application:
store_data = PersistenceInput(**(store_data or {}))
if update_interval is not None:
@ -237,12 +241,15 @@ def build_papp(
else:
persistence = TrackingPersistence(store_data=store_data, fill_data=fill_data)
if bot_info is not None:
bot = make_bot(bot_info, arbitrary_callback_data=True)
else:
bot = make_bot(token=token, arbitrary_callback_data=True)
return (
ApplicationBuilder()
.token(token)
.bot(bot)
.persistence(persistence)
.application_class(DictApplication)
.arbitrary_callback_data(True)
.build()
)
@ -252,7 +259,7 @@ def build_conversation_handler(name: str, persistent: bool = True) -> BaseHandle
@pytest.fixture(scope="function")
def papp(request, bot) -> Application:
def papp(request, bot_info) -> Application:
papp_input = request.param
store_data = {}
if papp_input.bot_data is not None:
@ -265,7 +272,7 @@ def papp(request, bot) -> Application:
store_data["callback_data"] = papp_input.callback_data
app = build_papp(
bot.token,
bot_info=bot_info,
store_data=store_data,
update_interval=papp_input.update_interval,
fill_data=papp_input.fill_data,
@ -998,7 +1005,7 @@ class TestBasePersistence:
assert papp.persistence.dropped_chat_ids == {1: 1}
assert papp.persistence.updated_chat_ids == {2: 1}
async def test_errors_while_persisting(self, bot, caplog):
async def test_errors_while_persisting(self, bot_info, caplog):
class ErrorPersistence(TrackingPersistence):
def raise_error(self):
raise Exception("PersistenceError")
@ -1032,8 +1039,7 @@ class TestBasePersistence:
app = (
ApplicationBuilder()
.token(bot.token)
.arbitrary_callback_data(True)
.bot(make_bot(bot_info, arbitrary_callback_data=True))
.persistence(ErrorPersistence())
.build()
)

File diff suppressed because it is too large Load diff

View file

@ -22,12 +22,12 @@ import pytest
from telegram import BotCommand, Dice
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def bot_command():
return BotCommand(command="start", description="A command")
class TestBotCommand:
class TestBotCommandWithoutRequest:
command = "start"
description = "A command"

View file

@ -33,7 +33,7 @@ from telegram import (
)
@pytest.fixture(scope="class", params=["str", "int"])
@pytest.fixture(scope="module", params=["str", "int"])
def chat_id(request):
if request.param == "str":
return "@supergroupusername"
@ -57,7 +57,7 @@ def scope_type(request):
@pytest.fixture(
scope="class",
scope="module",
params=[
BotCommandScopeDefault,
BotCommandScopeAllPrivateChats,
@ -82,7 +82,7 @@ def scope_class(request):
@pytest.fixture(
scope="class",
scope="module",
params=[
(BotCommandScopeDefault, BotCommandScope.DEFAULT),
(BotCommandScopeAllPrivateChats, BotCommandScope.ALL_PRIVATE_CHATS),
@ -106,7 +106,7 @@ def scope_class_and_type(request):
return request.param
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def bot_command_scope(scope_class_and_type, chat_id):
# we use de_json here so that we don't have to worry about which class needs which arguments
return scope_class_and_type[0].de_json(
@ -115,7 +115,7 @@ def bot_command_scope(scope_class_and_type, chat_id):
# All the scope types are very similar, so we test everything via parametrization
class TestBotCommandScope:
class TestBotCommandScopeWithoutRequest:
def test_slot_behaviour(self, bot_command_scope, mro_slots):
for attr in bot_command_scope.__slots__:
assert getattr(bot_command_scope, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -16,7 +16,6 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import os
import time
from copy import deepcopy
from datetime import datetime
@ -28,7 +27,7 @@ from telegram import CallbackQuery, Chat, InlineKeyboardButton, InlineKeyboardMa
from telegram._utils.datetime import UTC
from telegram.ext import ExtBot
from telegram.ext._callbackdatacache import CallbackDataCache, InvalidCallbackData, _KeyboardData
from tests.auxil.object_conversions import env_var_2_bool
from tests.conftest import TEST_WITH_OPT_DEPS
@pytest.fixture(scope="function")
@ -36,9 +35,6 @@ def callback_data_cache(bot):
return CallbackDataCache(bot)
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
@pytest.mark.skipif(
TEST_WITH_OPT_DEPS,
reason="Only relevant if the optional dependency is not installed",

View file

@ -32,23 +32,23 @@ from tests.auxil.bot_method_checks import (
@pytest.fixture(scope="function", params=["message", "inline"])
def callback_query(bot, request):
cbq = CallbackQuery(
TestCallbackQuery.id_,
TestCallbackQuery.from_user,
TestCallbackQuery.chat_instance,
data=TestCallbackQuery.data,
game_short_name=TestCallbackQuery.game_short_name,
TestCallbackQueryBase.id_,
TestCallbackQueryBase.from_user,
TestCallbackQueryBase.chat_instance,
data=TestCallbackQueryBase.data,
game_short_name=TestCallbackQueryBase.game_short_name,
)
cbq.set_bot(bot)
cbq._unfreeze()
if request.param == "message":
cbq.message = TestCallbackQuery.message
cbq.message = TestCallbackQueryBase.message
cbq.message.set_bot(bot)
else:
cbq.inline_message_id = TestCallbackQuery.inline_message_id
cbq.inline_message_id = TestCallbackQueryBase.inline_message_id
return cbq
class TestCallbackQuery:
class TestCallbackQueryBase:
id_ = "id"
from_user = User(1, "test_user", False)
chat_instance = "chat_instance"
@ -57,6 +57,8 @@ class TestCallbackQuery:
inline_message_id = "inline_message_id"
game_short_name = "the_game"
class TestCallbackQueryWithoutRequest(TestCallbackQueryBase):
@staticmethod
def skip_params(callback_query: CallbackQuery):
if callback_query.inline_message_id:
@ -121,6 +123,26 @@ class TestCallbackQuery:
assert callback_query_dict["data"] == callback_query.data
assert callback_query_dict["game_short_name"] == callback_query.game_short_name
def test_equality(self):
a = CallbackQuery(self.id_, self.from_user, "chat")
b = CallbackQuery(self.id_, self.from_user, "chat")
c = CallbackQuery(self.id_, None, "")
d = CallbackQuery("", None, "chat")
e = Audio(self.id_, "unique_id", 1)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
async def test_answer(self, monkeypatch, callback_query):
async def make_assertion(*_, **kwargs):
return kwargs["callback_query_id"] == callback_query.id
@ -447,23 +469,3 @@ class TestCallbackQuery:
monkeypatch.setattr(callback_query.get_bot(), "copy_message", make_assertion)
assert await callback_query.copy_message(1)
def test_equality(self):
a = CallbackQuery(self.id_, self.from_user, "chat")
b = CallbackQuery(self.id_, self.from_user, "chat")
c = CallbackQuery(self.id_, None, "")
d = CallbackQuery("", None, "chat")
e = Audio(self.id_, "unique_id", 1)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -29,37 +29,37 @@ from tests.auxil.bot_method_checks import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat(bot):
chat = Chat(
TestChat.id_,
title=TestChat.title,
type=TestChat.type_,
username=TestChat.username,
sticker_set_name=TestChat.sticker_set_name,
can_set_sticker_set=TestChat.can_set_sticker_set,
permissions=TestChat.permissions,
slow_mode_delay=TestChat.slow_mode_delay,
bio=TestChat.bio,
linked_chat_id=TestChat.linked_chat_id,
location=TestChat.location,
TestChatBase.id_,
title=TestChatBase.title,
type=TestChatBase.type_,
username=TestChatBase.username,
sticker_set_name=TestChatBase.sticker_set_name,
can_set_sticker_set=TestChatBase.can_set_sticker_set,
permissions=TestChatBase.permissions,
slow_mode_delay=TestChatBase.slow_mode_delay,
bio=TestChatBase.bio,
linked_chat_id=TestChatBase.linked_chat_id,
location=TestChatBase.location,
has_private_forwards=True,
has_protected_content=True,
join_to_send_messages=True,
join_by_request=True,
has_restricted_voice_and_video_messages=True,
is_forum=True,
active_usernames=TestChat.active_usernames,
emoji_status_custom_emoji_id=TestChat.emoji_status_custom_emoji_id,
has_aggressive_anti_spam_enabled=TestChat.has_aggressive_anti_spam_enabled,
has_hidden_members=TestChat.has_hidden_members,
active_usernames=TestChatBase.active_usernames,
emoji_status_custom_emoji_id=TestChatBase.emoji_status_custom_emoji_id,
has_aggressive_anti_spam_enabled=TestChatBase.has_aggressive_anti_spam_enabled,
has_hidden_members=TestChatBase.has_hidden_members,
)
chat.set_bot(bot)
chat._unfreeze()
return chat
class TestChat:
class TestChatBase:
id_ = -28767330
title = "ToledosPalaceBot - Group"
type_ = "group"
@ -87,6 +87,8 @@ class TestChat:
has_aggressive_anti_spam_enabled = True
has_hidden_members = True
class TestChatWithoutRequest(TestChatBase):
def test_slot_behaviour(self, chat, mro_slots):
for attr in chat.__slots__:
assert getattr(chat, attr, "err") != "err", f"got extra slot '{attr}'"
@ -194,6 +196,26 @@ class TestChat:
chat = Chat(id=1, type="private")
assert chat.type is ChatType.PRIVATE
def test_equality(self):
a = Chat(self.id_, self.title, self.type_)
b = Chat(self.id_, self.title, self.type_)
c = Chat(self.id_, "", "")
d = Chat(0, self.title, self.type_)
e = User(self.id_, "", False)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
def test_link(self, chat):
assert chat.link == f"https://t.me/{chat.username}"
chat.username = None
@ -234,7 +256,6 @@ class TestChat:
monkeypatch.setattr(chat.get_bot(), "send_chat_action", make_assertion)
assert await chat.send_action(action=ChatAction.TYPING)
assert await chat.send_action(action=ChatAction.TYPING)
async def test_leave(self, monkeypatch, chat):
async def make_assertion(*_, **kwargs):
@ -1251,23 +1272,3 @@ class TestChat:
):
chat = Chat(id=1, type="foo", username="user\u2022name")
chat.mention_markdown_v2()
def test_equality(self):
a = Chat(self.id_, self.title, self.type_)
b = Chat(self.id_, self.title, self.type_)
c = Chat(self.id_, "", "")
d = Chat(0, self.title, self.type_)
e = User(self.id_, "", False)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -21,7 +21,7 @@ import pytest
from telegram import ChatAdministratorRights
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat_admin_rights():
return ChatAdministratorRights(
can_change_info=True,
@ -39,7 +39,7 @@ def chat_admin_rights():
)
class TestChatAdministratorRights:
class TestChatAdministratorRightsWithoutRequest:
def test_slot_behaviour(self, chat_admin_rights, mro_slots):
inst = chat_admin_rights
for attr in inst.__slots__:

View file

@ -24,27 +24,27 @@ from telegram import ChatInviteLink, User
from telegram._utils.datetime import to_timestamp
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def creator():
return User(1, "First name", False)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def invite_link(creator):
return ChatInviteLink(
TestChatInviteLink.link,
TestChatInviteLinkBase.link,
creator,
TestChatInviteLink.creates_join_request,
TestChatInviteLink.primary,
TestChatInviteLink.revoked,
expire_date=TestChatInviteLink.expire_date,
member_limit=TestChatInviteLink.member_limit,
name=TestChatInviteLink.name,
pending_join_request_count=TestChatInviteLink.pending_join_request_count,
TestChatInviteLinkBase.creates_join_request,
TestChatInviteLinkBase.primary,
TestChatInviteLinkBase.revoked,
expire_date=TestChatInviteLinkBase.expire_date,
member_limit=TestChatInviteLinkBase.member_limit,
name=TestChatInviteLinkBase.name,
pending_join_request_count=TestChatInviteLinkBase.pending_join_request_count,
)
class TestChatInviteLink:
class TestChatInviteLinkBase:
link = "thisialink"
creates_join_request = False
primary = True
@ -54,6 +54,8 @@ class TestChatInviteLink:
name = "LinkName"
pending_join_request_count = 42
class TestChatInviteLinkWithoutRequest(TestChatInviteLinkBase):
def test_slot_behaviour(self, mro_slots, invite_link):
for attr in invite_link.__slots__:
assert getattr(invite_link, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -29,26 +29,26 @@ from tests.auxil.bot_method_checks import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def time():
return datetime.datetime.now(tz=UTC)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat_join_request(bot, time):
cjr = ChatJoinRequest(
chat=TestChatJoinRequest.chat,
from_user=TestChatJoinRequest.from_user,
chat=TestChatJoinRequestBase.chat,
from_user=TestChatJoinRequestBase.from_user,
date=time,
bio=TestChatJoinRequest.bio,
invite_link=TestChatJoinRequest.invite_link,
user_chat_id=TestChatJoinRequest.from_user.id,
bio=TestChatJoinRequestBase.bio,
invite_link=TestChatJoinRequestBase.invite_link,
user_chat_id=TestChatJoinRequestBase.from_user.id,
)
cjr.set_bot(bot)
return cjr
class TestChatJoinRequest:
class TestChatJoinRequestBase:
chat = Chat(1, Chat.SUPERGROUP)
from_user = User(2, "first_name", False)
bio = "bio"
@ -61,6 +61,8 @@ class TestChatJoinRequest:
is_primary=False,
)
class TestChatJoinRequestWithoutRequest(TestChatJoinRequestBase):
def test_slot_behaviour(self, chat_join_request, mro_slots):
inst = chat_join_request
for attr in inst.__slots__:

View file

@ -22,15 +22,17 @@ import pytest
from telegram import ChatLocation, Location, User
@pytest.fixture(scope="class")
def chat_location(bot):
return ChatLocation(TestChatLocation.location, TestChatLocation.address)
@pytest.fixture(scope="module")
def chat_location():
return ChatLocation(TestChatLocationBase.location, TestChatLocationBase.address)
class TestChatLocation:
class TestChatLocationBase:
location = Location(123, 456)
address = "The Shire"
class TestChatLocationWithoutRequest(TestChatLocationBase):
def test_slot_behaviour(self, chat_location, mro_slots):
inst = chat_location
for attr in inst.__slots__:

View file

@ -187,7 +187,7 @@ def chat_member_type(request):
],
indirect=True,
)
class TestChatMemberTypes:
class TestChatMemberTypesWithoutRequest:
def test_slot_behaviour(self, chat_member_type, mro_slots):
inst = chat_member_type
for attr in inst.__slots__:

View file

@ -34,26 +34,26 @@ from telegram import (
from telegram._utils.datetime import UTC, to_timestamp
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def user():
return User(1, "First name", False)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat():
return Chat(1, Chat.SUPERGROUP, "Chat")
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def old_chat_member(user):
return ChatMember(user, TestChatMemberUpdated.old_status)
return ChatMember(user, TestChatMemberUpdatedBase.old_status)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def new_chat_member(user):
return ChatMemberAdministrator(
user,
TestChatMemberUpdated.new_status,
TestChatMemberUpdatedBase.new_status,
True,
True,
True,
@ -66,25 +66,27 @@ def new_chat_member(user):
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def time():
return datetime.datetime.now(tz=UTC)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def invite_link(user):
return ChatInviteLink("link", user, False, True, True)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat_member_updated(user, chat, old_chat_member, new_chat_member, invite_link, time):
return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link)
class TestChatMemberUpdated:
class TestChatMemberUpdatedBase:
old_status = ChatMember.MEMBER
new_status = ChatMember.ADMINISTRATOR
class TestChatMemberUpdatedWithoutRequest(TestChatMemberUpdatedBase):
def test_slot_behaviour(self, mro_slots, chat_member_updated):
action = chat_member_updated
for attr in action.__slots__:

View file

@ -22,7 +22,7 @@ import pytest
from telegram import ChatPermissions, User
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chat_permissions():
return ChatPermissions(
can_send_messages=True,
@ -43,7 +43,7 @@ def chat_permissions():
)
class TestChatPermissions:
class TestChatPermissionsBase:
can_send_messages = True
can_send_media_messages = True
can_send_polls = True
@ -60,6 +60,8 @@ class TestChatPermissions:
can_send_video_notes = False
can_send_voice_notes = None
class TestChatPermissionsWithoutRequest(TestChatPermissionsBase):
def test_slot_behaviour(self, chat_permissions, mro_slots):
inst = chat_permissions
for attr in inst.__slots__:

View file

@ -17,6 +17,7 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import os
from pathlib import Path
@ -35,12 +36,11 @@ from tests.conftest import data_file, expect_bad_request
@pytest.fixture(scope="function")
def chatphoto_file():
f = data_file("telegram.jpg").open("rb")
yield f
f.close()
with data_file("telegram.jpg").open("rb") as f:
yield f
@pytest.fixture(scope="function")
@pytest.fixture(scope="module")
async def chat_photo(bot, super_group_id):
async def func():
return (await bot.get_chat(super_group_id, read_timeout=50)).photo
@ -50,61 +50,20 @@ async def chat_photo(bot, super_group_id):
)
class TestChatPhoto:
class TestChatPhotoBase:
chatphoto_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
chatphoto_big_file_unique_id = "bigadc3145fd2e84d95b64d68eaa22aa33e"
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
class TestChatPhotoWithoutRequest(TestChatPhotoBase):
def test_slot_behaviour(self, chat_photo, mro_slots):
for attr in chat_photo.__slots__:
assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(chat_photo)) == len(set(mro_slots(chat_photo))), "duplicate slot"
@pytest.mark.flaky(3, 1)
async def test_send_all_args(
self, bot, super_group_id, chatphoto_file, chat_photo, thumb_file
):
async def func():
assert await bot.set_chat_photo(super_group_id, chatphoto_file)
await expect_bad_request(
func, "Type of file mismatch", "Telegram did not accept the file."
)
@pytest.mark.flaky(3, 1)
async def test_get_and_download(self, bot, chat_photo):
jpg_file = Path("telegram.jpg")
if jpg_file.is_file():
jpg_file.unlink()
new_file = await bot.get_file(chat_photo.small_file_id)
assert new_file.file_unique_id == chat_photo.small_file_unique_id
assert new_file.file_path.startswith("https://")
await new_file.download_to_drive(jpg_file)
assert jpg_file.is_file()
new_file = await bot.get_file(chat_photo.big_file_id)
assert new_file.file_unique_id == chat_photo.big_file_unique_id
assert new_file.file_path.startswith("https://")
await new_file.download_to_drive(jpg_file)
assert jpg_file.is_file()
async def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.parameters["photo"] == chat_photo.to_dict()
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
assert message
def test_de_json(self, bot, chat_photo):
json_dict = {
"small_file_id": self.chatphoto_small_file_id,
@ -128,46 +87,6 @@ class TestChatPhoto:
assert chat_photo_dict["small_file_unique_id"] == chat_photo.small_file_unique_id
assert chat_photo_dict["big_file_unique_id"] == chat_photo.big_file_unique_id
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, super_group_id):
chatphoto_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, super_group_id):
with pytest.raises(TelegramError):
await bot.set_chat_photo(chat_id=super_group_id, photo="")
async def test_error_send_without_required_args(self, bot, super_group_id):
with pytest.raises(TypeError):
await bot.set_chat_photo(chat_id=super_group_id)
async def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == chat_photo.small_file_id
assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(
chat_photo.get_small_file, chat_photo.get_bot(), "get_file"
)
assert await check_defaults_handling(chat_photo.get_small_file, chat_photo.get_bot())
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
assert await chat_photo.get_small_file()
async def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == chat_photo.big_file_id
assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(chat_photo.get_big_file, chat_photo.get_bot(), "get_file")
assert await check_defaults_handling(chat_photo.get_big_file, chat_photo.get_bot())
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
assert await chat_photo.get_big_file()
def test_equality(self):
a = ChatPhoto(
self.chatphoto_small_file_id,
@ -199,3 +118,80 @@ class TestChatPhoto:
assert a != e
assert hash(a) != hash(e)
async def test_send_with_chat_photo(self, monkeypatch, bot, super_group_id, chat_photo):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.parameters["photo"] == chat_photo.to_dict()
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id)
assert message
async def test_get_small_file_instance_method(self, monkeypatch, chat_photo):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == chat_photo.small_file_id
assert check_shortcut_signature(ChatPhoto.get_small_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(
chat_photo.get_small_file, chat_photo.get_bot(), "get_file"
)
assert await check_defaults_handling(chat_photo.get_small_file, chat_photo.get_bot())
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
assert await chat_photo.get_small_file()
async def test_get_big_file_instance_method(self, monkeypatch, chat_photo):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == chat_photo.big_file_id
assert check_shortcut_signature(ChatPhoto.get_big_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(chat_photo.get_big_file, chat_photo.get_bot(), "get_file")
assert await check_defaults_handling(chat_photo.get_big_file, chat_photo.get_bot())
monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion)
assert await chat_photo.get_big_file()
class TestChatPhotoWithRequest:
async def test_get_and_download(self, bot, chat_photo):
jpg_file = Path("telegram.jpg")
if jpg_file.is_file():
jpg_file.unlink()
tasks = {bot.get_file(chat_photo.small_file_id), bot.get_file(chat_photo.big_file_id)}
asserts = []
for task in asyncio.as_completed(tasks):
file = await task
if file.file_unique_id == chat_photo.small_file_unique_id:
asserts.append("small")
elif file.file_unique_id == chat_photo.big_file_unique_id:
asserts.append("big")
assert file.file_path.startswith("https://")
await file.download_to_drive(jpg_file)
assert jpg_file.is_file()
assert "small" in asserts and "big" in asserts
async def test_send_all_args(self, bot, super_group_id, chatphoto_file):
async def func():
assert await bot.set_chat_photo(super_group_id, chatphoto_file)
await expect_bad_request(
func, "Type of file mismatch", "Telegram did not accept the file."
)
async def test_error_send_empty_file(self, bot, super_group_id):
chatphoto_file = open(os.devnull, "rb")
with pytest.raises(TelegramError):
await bot.set_chat_photo(chat_id=super_group_id, photo=chatphoto_file)
async def test_error_send_empty_file_id(self, bot, super_group_id):
with pytest.raises(TelegramError):
await bot.set_chat_photo(chat_id=super_group_id, photo="")
async def test_error_send_without_required_args(self, bot, super_group_id):
with pytest.raises(TypeError):
await bot.set_chat_photo(chat_id=super_group_id)

View file

@ -22,22 +22,26 @@ import pytest
from telegram import ChosenInlineResult, Location, User, Voice
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def user():
user = User(1, "First name", False)
user._unfreeze()
return user
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def chosen_inline_result(user):
return ChosenInlineResult(TestChosenInlineResult.result_id, user, TestChosenInlineResult.query)
return ChosenInlineResult(
TestChosenInlineResultBase.result_id, user, TestChosenInlineResultBase.query
)
class TestChosenInlineResult:
class TestChosenInlineResultBase:
result_id = "result id"
query = "query text"
class TestChosenInlineResultWithoutRequest(TestChosenInlineResultBase):
def test_slot_behaviour(self, chosen_inline_result, mro_slots):
inst = chosen_inline_result
for attr in inst.__slots__:

View file

@ -16,10 +16,9 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import json
import pytest
from telegram import constants
from telegram._utils.enum import IntEnum, StringEnum
from telegram.error import BadRequest
@ -36,7 +35,7 @@ class IntEnumTest(IntEnum):
BAR = 2
class TestConstants:
class TestConstantsWithoutRequest:
"""Also test _utils.enum.StringEnum on the fly because tg.constants is currently the only
place where that class is used."""
@ -110,30 +109,6 @@ class TestConstants:
assert hash(IntEnumTest.FOO) == hash(1)
@pytest.mark.flaky(3, 1)
async def test_max_message_length(self, bot, chat_id):
await bot.send_message(chat_id=chat_id, text="a" * constants.MessageLimit.MAX_TEXT_LENGTH)
with pytest.raises(
BadRequest,
match="Message is too long",
):
await bot.send_message(
chat_id=chat_id, text="a" * (constants.MessageLimit.MAX_TEXT_LENGTH + 1)
)
@pytest.mark.flaky(3, 1)
async def test_max_caption_length(self, bot, chat_id):
good_caption = "a" * constants.MessageLimit.CAPTION_LENGTH
with data_file("telegram.png").open("rb") as f:
good_msg = await bot.send_photo(photo=f, caption=good_caption, chat_id=chat_id)
assert good_msg.caption == good_caption
bad_caption = good_caption + "Z"
match = "Message caption is too long"
with pytest.raises(BadRequest, match=match), data_file("telegram.png").open("rb") as f:
await bot.send_photo(photo=f, caption=bad_caption, chat_id=chat_id)
def test_bot_api_version_and_info(self):
assert constants.BOT_API_VERSION == str(constants.BOT_API_VERSION_INFO)
assert constants.BOT_API_VERSION_INFO == tuple(
@ -151,3 +126,29 @@ class TestConstants:
assert vi < (vi[0] + 1, vi[1] + 1)
assert vi[0] == vi.major
assert vi[1] == vi.minor
class TestConstantsWithRequest:
async def test_max_message_length(self, bot, chat_id):
good_text = "a" * constants.MessageLimit.MAX_TEXT_LENGTH
bad_text = good_text + "Z"
tasks = asyncio.gather(
bot.send_message(chat_id, text=good_text),
bot.send_message(chat_id, text=bad_text),
return_exceptions=True,
)
good_msg, bad_msg = await tasks
assert good_msg.text == good_text
assert isinstance(bad_msg, BadRequest) and "Message is too long" in str(bad_msg)
async def test_max_caption_length(self, bot, chat_id):
good_caption = "a" * constants.MessageLimit.CAPTION_LENGTH
bad_caption = good_caption + "Z"
tasks = asyncio.gather(
bot.send_photo(chat_id, data_file("telegram.png").read_bytes(), good_caption),
bot.send_photo(chat_id, data_file("telegram.png").read_bytes(), bad_caption),
return_exceptions=True,
)
good_msg, bad_msg = await tasks
assert good_msg.caption == good_caption
assert isinstance(bad_msg, BadRequest) and "Message caption is too long" in str(bad_msg)

View file

@ -17,6 +17,8 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import pytest
from telegram import Contact, Voice
@ -24,22 +26,24 @@ from telegram.error import BadRequest
from telegram.request import RequestData
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def contact():
return Contact(
TestContact.phone_number,
TestContact.first_name,
TestContact.last_name,
TestContact.user_id,
TestContactBase.phone_number,
TestContactBase.first_name,
TestContactBase.last_name,
TestContactBase.user_id,
)
class TestContact:
class TestContactBase:
phone_number = "+11234567890"
first_name = "Leandro"
last_name = "Toledo"
user_id = 23
class TestContactWithoutRequest(TestContactBase):
def test_slot_behaviour(self, contact, mro_slots):
for attr in contact.__slots__:
assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
@ -68,6 +72,48 @@ class TestContact:
assert contact.last_name == self.last_name
assert contact.user_id == self.user_id
def test_to_dict(self, contact):
contact_dict = contact.to_dict()
assert isinstance(contact_dict, dict)
assert contact_dict["phone_number"] == contact.phone_number
assert contact_dict["first_name"] == contact.first_name
assert contact_dict["last_name"] == contact.last_name
assert contact_dict["user_id"] == contact.user_id
def test_equality(self):
a = Contact(self.phone_number, self.first_name)
b = Contact(self.phone_number, self.first_name)
c = Contact(self.phone_number, "")
d = Contact("", self.first_name)
e = Voice("", "unique_id", 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
async def test_send_contact_without_required(self, bot, chat_id):
with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
await bot.send_contact(chat_id=chat_id)
async def test_send_mutually_exclusive(self, bot, chat_id, contact):
with pytest.raises(ValueError, match="Not both"):
await bot.send_contact(
chat_id=chat_id,
contact=contact,
phone_number=contact.phone_number,
first_name=contact.first_name,
)
async def test_send_with_contact(self, monkeypatch, bot, chat_id, contact):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
@ -77,10 +123,10 @@ class TestContact:
return phone and first and last
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_contact(contact=contact, chat_id=chat_id)
assert message
assert await bot.send_contact(contact=contact, chat_id=chat_id)
@pytest.mark.flaky(3, 1)
class TestContactWithRequest(TestContactBase):
@pytest.mark.parametrize(
"default_bot,custom",
[
@ -114,54 +160,12 @@ class TestContact:
chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_contact_default_protect_content(self, chat_id, default_bot, contact):
protected = await default_bot.send_contact(chat_id, contact=contact)
assert protected.has_protected_content
unprotected = await default_bot.send_contact(
chat_id, contact=contact, protect_content=False
tasks = asyncio.gather(
default_bot.send_contact(chat_id, contact=contact),
default_bot.send_contact(chat_id, contact=contact, protect_content=False),
)
protected, unprotected = await tasks
assert protected.has_protected_content
assert not unprotected.has_protected_content
async def test_send_contact_without_required(self, bot, chat_id):
with pytest.raises(ValueError, match="Either contact or phone_number and first_name"):
await bot.send_contact(chat_id=chat_id)
async def test_send_mutually_exclusive(self, bot, chat_id, contact):
with pytest.raises(ValueError, match="Not both"):
await bot.send_contact(
chat_id=chat_id,
contact=contact,
phone_number=contact.phone_number,
first_name=contact.first_name,
)
def test_to_dict(self, contact):
contact_dict = contact.to_dict()
assert isinstance(contact_dict, dict)
assert contact_dict["phone_number"] == contact.phone_number
assert contact_dict["first_name"] == contact.first_name
assert contact_dict["last_name"] == contact.last_name
assert contact_dict["user_id"] == contact.user_id
def test_equality(self):
a = Contact(self.phone_number, self.first_name)
b = Contact(self.phone_number, self.first_name)
c = Contact(self.phone_number, "")
d = Contact("", self.first_name)
e = Voice("", "unique_id", 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -24,7 +24,6 @@ from warnings import filterwarnings
import pytest
from telegram import (
Bot,
CallbackQuery,
Chat,
ChosenInlineResult,
@ -45,7 +44,6 @@ from telegram.ext import (
CommandHandler,
ConversationHandler,
Defaults,
ExtBot,
InlineQueryHandler,
JobQueue,
MessageHandler,
@ -59,7 +57,7 @@ from telegram.ext import (
filters,
)
from telegram.warnings import PTBUserWarning
from tests.conftest import make_command_message
from tests.conftest import DictBot, make_bot, make_command_message
@pytest.fixture(scope="class")
@ -1255,7 +1253,7 @@ class TestConversationHandler:
await app.process_update(Update(update_id=2, message=brew_message))
assert handler.check_update(Update(0, message=pour_coffee_message))
# assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
await asyncio.sleep(0.7)
await asyncio.sleep(0.75)
assert handler.check_update(Update(0, message=start_message))
# assert handler.conversations.get((self.group.id, user1.id)) is None
@ -2113,7 +2111,7 @@ class TestConversationHandler:
@pytest.mark.parametrize("handler_block", [True, False, None])
@pytest.mark.parametrize("ext_bot", [True, False], ids=["ExtBot", "Bot"])
async def test_blocking_resolution_order(
self, bot, default_block, ch_block, handler_block, ext_bot
self, bot_info, default_block, ch_block, handler_block, ext_bot
):
event = asyncio.Event()
@ -2149,7 +2147,7 @@ class TestConversationHandler:
fallbacks=[fallback],
)
bot = ExtBot(bot.token, defaults=defaults) if ext_bot else Bot(bot.token)
bot = make_bot(bot_info, defaults=defaults) if ext_bot else DictBot(bot_info["token"])
app = ApplicationBuilder().bot(bot).build()
app.add_handler(conv_handler)
@ -2160,7 +2158,7 @@ class TestConversationHandler:
fallback_message.set_bot(bot)
# This loop makes sure that we test all of entry points, states handler & fallbacks
for message in [start_message, start_message, fallback_message]:
for message in [start_message, fallback_message]:
process_update_task = asyncio.create_task(
app.process_update(Update(0, message=message))
)

View file

@ -17,7 +17,6 @@
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import datetime as dtm
import os
import time
import pytest
@ -26,18 +25,22 @@ from telegram._utils import datetime as tg_dtm
from telegram.ext import Defaults
# sample time specification values categorised into absolute / delta / time-of-day
from tests.auxil.object_conversions import env_var_2_bool
from tests.conftest import TEST_WITH_OPT_DEPS
ABSOLUTE_TIME_SPECS = [
dtm.datetime.now(tz=dtm.timezone(dtm.timedelta(hours=-7))).replace(second=0, microsecond=0),
dtm.datetime.utcnow().replace(second=0, microsecond=0),
]
# We do not parametrize tests with these variables, since there's a tiny chance that there is an
# error while collecting the tests (happens when time goes from HH:59:00 -> HH+1:00:00) when we
# run the test suite with multiple workers
DELTA_TIME_SPECS = [dtm.timedelta(hours=3, seconds=42, milliseconds=2), 30, 7.5]
TIME_OF_DAY_TIME_SPECS = [
dtm.time(12, 42, tzinfo=dtm.timezone(dtm.timedelta(hours=-7))),
dtm.time(12, 42),
]
RELATIVE_TIME_SPECS = DELTA_TIME_SPECS + TIME_OF_DAY_TIME_SPECS
ABSOLUTE_TIME_SPECS = [
dtm.datetime.now(tz=dtm.timezone(dtm.timedelta(hours=-7))),
dtm.datetime.utcnow(),
]
TIME_SPECS = ABSOLUTE_TIME_SPECS + RELATIVE_TIME_SPECS
"""
@ -49,7 +52,6 @@ Because imports in pytest are intricate, we just run
with the TEST_WITH_OPT_DEPS=False environment variable in addition to the regular test suite.
"""
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
class TestDatetime:
@ -97,12 +99,15 @@ class TestDatetime:
with pytest.raises(ValueError):
tg_dtm.to_float_timestamp(dtm.datetime(2019, 11, 11), reference_timestamp=123)
@pytest.mark.parametrize("time_spec", DELTA_TIME_SPECS, ids=str)
def test_to_float_timestamp_delta(self, time_spec):
# see note on parametrization at the top of this file
def test_to_float_timestamp_delta(self):
"""Conversion from a 'delta' time specification to timestamp"""
reference_t = 0
delta = time_spec.total_seconds() if hasattr(time_spec, "total_seconds") else time_spec
assert tg_dtm.to_float_timestamp(time_spec, reference_t) == reference_t + delta
for i in DELTA_TIME_SPECS:
delta = i.total_seconds() if hasattr(i, "total_seconds") else i
assert (
tg_dtm.to_float_timestamp(i, reference_t) == reference_t + delta
), f"failed for {i}"
def test_to_float_timestamp_time_of_day(self):
"""Conversion from time-of-day specification to timestamp"""
@ -130,22 +135,24 @@ class TestDatetime:
ref_t + (-utc_offset.total_seconds() % (24 * 60 * 60))
)
@pytest.mark.parametrize("time_spec", RELATIVE_TIME_SPECS, ids=str)
def test_to_float_timestamp_default_reference(self, time_spec):
# see note on parametrization at the top of this file
def test_to_float_timestamp_default_reference(self):
"""The reference timestamp for relative time specifications should default to now"""
now = time.time()
assert tg_dtm.to_float_timestamp(time_spec) == pytest.approx(
tg_dtm.to_float_timestamp(time_spec, reference_timestamp=now)
)
for i in RELATIVE_TIME_SPECS:
now = time.time()
assert tg_dtm.to_float_timestamp(i) == pytest.approx(
tg_dtm.to_float_timestamp(i, reference_timestamp=now)
), f"Failed for {i}"
def test_to_float_timestamp_error(self):
with pytest.raises(TypeError, match="Defaults"):
tg_dtm.to_float_timestamp(Defaults())
@pytest.mark.parametrize("time_spec", TIME_SPECS, ids=str)
def test_to_timestamp(self, time_spec):
# see note on parametrization at the top of this file
def test_to_timestamp(self):
# delegate tests to `to_float_timestamp`
assert tg_dtm.to_timestamp(time_spec) == int(tg_dtm.to_float_timestamp(time_spec))
for i in TIME_SPECS:
assert tg_dtm.to_timestamp(i) == int(tg_dtm.to_float_timestamp(i)), f"Failed for {i}"
def test_to_timestamp_none(self):
# this 'convenience' behaviour has been left left for backwards compatibility

View file

@ -19,15 +19,12 @@
import datetime as dtm
import inspect
import os
import pytest
from telegram import User
from telegram.ext import Defaults
from tests.auxil.object_conversions import env_var_2_bool
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
from tests.conftest import TEST_WITH_OPT_DEPS
class TestDefault:

View file

@ -22,14 +22,16 @@ import pytest
from telegram import BotCommand, Dice
@pytest.fixture(scope="class", params=Dice.ALL_EMOJI)
@pytest.fixture(scope="module", params=Dice.ALL_EMOJI)
def dice(request):
return Dice(value=5, emoji=request.param)
class TestDice:
class TestDiceBase:
value = 4
class TestDiceWithoutRequest(TestDiceBase):
def test_slot_behaviour(self, dice, mro_slots):
for attr in dice.__slots__:
assert getattr(dice, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import os
from pathlib import Path
@ -35,18 +36,17 @@ from tests.conftest import data_file
@pytest.fixture(scope="function")
def document_file():
f = data_file("telegram.png").open("rb")
yield f
f.close()
with data_file("telegram.png").open("rb") as f:
yield f
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
async def document(bot, chat_id):
with data_file("telegram.png").open("rb") as f:
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
class TestDocument:
class TestDocumentBase:
caption = "DocumentTest - *Caption*"
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
file_size = 12948
@ -58,6 +58,8 @@ class TestDocument:
document_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
class TestDocumentWithoutRequest(TestDocumentBase):
def test_slot_behaviour(self, document, mro_slots):
for attr in document.__slots__:
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
@ -78,7 +80,141 @@ class TestDocument:
assert document.thumb.width == self.thumb_width
assert document.thumb.height == self.thumb_height
@pytest.mark.flaky(3, 1)
def test_de_json(self, bot, document):
json_dict = {
"file_id": self.document_file_id,
"file_unique_id": self.document_file_unique_id,
"thumb": document.thumb.to_dict(),
"file_name": self.file_name,
"mime_type": self.mime_type,
"file_size": self.file_size,
}
test_document = Document.de_json(json_dict, bot)
assert test_document.api_kwargs == {}
assert test_document.file_id == self.document_file_id
assert test_document.file_unique_id == self.document_file_unique_id
assert test_document.thumb == document.thumb
assert test_document.file_name == self.file_name
assert test_document.mime_type == self.mime_type
assert test_document.file_size == self.file_size
def test_to_dict(self, document):
document_dict = document.to_dict()
assert isinstance(document_dict, dict)
assert document_dict["file_id"] == document.file_id
assert document_dict["file_unique_id"] == document.file_unique_id
assert document_dict["file_name"] == document.file_name
assert document_dict["mime_type"] == document.mime_type
assert document_dict["file_size"] == document.file_size
def test_equality(self, document):
a = Document(document.file_id, document.file_unique_id)
b = Document("", document.file_unique_id)
d = Document("", "")
e = Voice(document.file_id, document.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_document(chat_id=chat_id)
@pytest.mark.parametrize("disable_content_type_detection", [True, False, None])
async def test_send_with_document(
self, monkeypatch, bot, chat_id, document, disable_content_type_detection
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.parameters
type_detection = (
data.get("disable_content_type_detection") == disable_content_type_detection
)
return data["document"] == document.file_id and type_detection
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_document(
document=document,
chat_id=chat_id,
disable_content_type_detection=disable_content_type_detection,
)
assert message
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("document") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("document"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_document(chat_id, file, thumb=file)
assert test_flag
finally:
bot._local_mode = False
async def test_get_file_instance_method(self, monkeypatch, document):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == document.file_id
assert check_shortcut_signature(Document.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(document.get_file, document.get_bot(), "get_file")
assert await check_defaults_handling(document.get_file, document.get_bot())
monkeypatch.setattr(document.get_bot(), "get_file", make_assertion)
assert await document.get_file()
class TestDocumentWithRequest(TestDocumentBase):
async def test_error_send_empty_file(self, bot, chat_id):
with open(os.devnull, "rb") as f:
with pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document=f)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document="")
async def test_get_and_download(self, bot, document, chat_id):
path = Path("telegram.png")
if path.is_file():
path.unlink()
new_file = await bot.get_file(document.file_id)
assert new_file.file_size == document.file_size
assert new_file.file_unique_id == document.file_unique_id
assert new_file.file_path.startswith("https://")
await new_file.download_to_drive("telegram.png")
assert path.is_file()
async def test_send_resend(self, bot, chat_id, document):
message = await bot.send_document(chat_id=chat_id, document=document.file_id)
assert message.document == document
async def test_send_all_args(self, bot, chat_id, document_file, document, thumb_file):
message = await bot.send_document(
chat_id,
@ -105,24 +241,6 @@ class TestDocument:
assert message.document.thumb.height == self.thumb_height
assert message.has_protected_content
@pytest.mark.flaky(3, 1)
async def test_get_and_download(self, bot, document):
path = Path("telegram.png")
if path.is_file():
path.unlink()
new_file = await bot.get_file(document.file_id)
assert new_file.file_size == document.file_size
assert new_file.file_id == document.file_id
assert new_file.file_unique_id == document.file_unique_id
assert new_file.file_path.startswith("https://")
await new_file.download_to_drive("telegram.png")
assert path.is_file()
@pytest.mark.flaky(3, 1)
async def test_send_url_gif_file(self, bot, chat_id):
message = await bot.send_document(chat_id, self.document_file_url)
@ -138,34 +256,16 @@ class TestDocument:
assert document.mime_type == "image/gif"
assert document.file_size == 3878
@pytest.mark.flaky(3, 1)
async def test_send_resend(self, bot, chat_id, document):
message = await bot.send_document(chat_id=chat_id, document=document.file_id)
assert message.document == document
@pytest.mark.parametrize("disable_content_type_detection", [True, False, None])
async def test_send_with_document(
self, monkeypatch, bot, chat_id, document, disable_content_type_detection
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.parameters
type_detection = (
data.get("disable_content_type_detection") == disable_content_type_detection
)
return data["document"] == document.file_id and type_detection
monkeypatch.setattr(bot.request, "post", make_assertion)
message = await bot.send_document(
document=document,
chat_id=chat_id,
disable_content_type_detection=disable_content_type_detection,
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_document_default_protect_content(self, chat_id, default_bot, document):
tasks = asyncio.gather(
default_bot.send_document(chat_id, document),
default_bot.send_document(chat_id, document, protect_content=False),
)
protected, unprotected = await tasks
assert protected.has_protected_content
assert not unprotected.has_protected_content
assert message
@pytest.mark.flaky(3, 1)
async def test_send_document_caption_entities(self, bot, chat_id, document):
test_string = "Italic Bold Code"
entities = [
@ -180,7 +280,6 @@ class TestDocument:
assert message.caption == test_string
assert message.caption_entities == tuple(entities)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_document_default_parse_mode_1(self, default_bot, chat_id, document):
test_string = "Italic Bold Code"
@ -190,7 +289,6 @@ class TestDocument:
assert message.caption_markdown == test_markdown_string
assert message.caption == test_string
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document):
test_markdown_string = "_Italic_ *Bold* `Code`"
@ -201,7 +299,6 @@ class TestDocument:
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_document_default_parse_mode_3(self, default_bot, chat_id, document):
test_markdown_string = "_Italic_ *Bold* `Code`"
@ -212,7 +309,6 @@ class TestDocument:
assert message.caption == test_markdown_string
assert message.caption_markdown == escape_markdown(test_markdown_string)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot,custom",
[
@ -245,106 +341,3 @@ class TestDocument:
await default_bot.send_document(
chat_id, document, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_document_default_protect_content(self, chat_id, default_bot, document):
protected = await default_bot.send_document(chat_id, document)
assert protected.has_protected_content
unprotected = await default_bot.send_document(chat_id, document, protect_content=False)
assert not unprotected.has_protected_content
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_document_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
bot._local_mode = local_mode
# For just test that the correct paths are passed as we have no local bot API set up
test_flag = False
file = data_file("telegram.jpg")
expected = file.as_uri()
async def make_assertion(_, data, *args, **kwargs):
nonlocal test_flag
if local_mode:
test_flag = data.get("document") == expected and data.get("thumb") == expected
else:
test_flag = isinstance(data.get("document"), InputFile) and isinstance(
data.get("thumb"), InputFile
)
monkeypatch.setattr(bot, "_post", make_assertion)
await bot.send_document(chat_id, file, thumb=file)
assert test_flag
finally:
bot._local_mode = False
def test_de_json(self, bot, document):
json_dict = {
"file_id": self.document_file_id,
"file_unique_id": self.document_file_unique_id,
"thumb": document.thumb.to_dict(),
"file_name": self.file_name,
"mime_type": self.mime_type,
"file_size": self.file_size,
}
test_document = Document.de_json(json_dict, bot)
assert test_document.api_kwargs == {}
assert test_document.file_id == self.document_file_id
assert test_document.file_unique_id == self.document_file_unique_id
assert test_document.thumb == document.thumb
assert test_document.file_name == self.file_name
assert test_document.mime_type == self.mime_type
assert test_document.file_size == self.file_size
def test_to_dict(self, document):
document_dict = document.to_dict()
assert isinstance(document_dict, dict)
assert document_dict["file_id"] == document.file_id
assert document_dict["file_unique_id"] == document.file_unique_id
assert document_dict["file_name"] == document.file_name
assert document_dict["mime_type"] == document.mime_type
assert document_dict["file_size"] == document.file_size
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, chat_id):
with open(os.devnull, "rb") as f:
with pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document=f)
@pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_document(chat_id=chat_id, document="")
async def test_error_send_without_required_args(self, bot, chat_id):
with pytest.raises(TypeError):
await bot.send_document(chat_id=chat_id)
async def test_get_file_instance_method(self, monkeypatch, document):
async def make_assertion(*_, **kwargs):
return kwargs["file_id"] == document.file_id
assert check_shortcut_signature(Document.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(document.get_file, document.get_bot(), "get_file")
assert await check_defaults_handling(document.get_file, document.get_bot())
monkeypatch.setattr(document.get_bot(), "get_file", make_assertion)
assert await document.get_file()
def test_equality(self, document):
a = Document(document.file_id, document.file_unique_id)
b = Document("", document.file_unique_id)
d = Document("", "")
e = Voice(document.file_id, document.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -22,20 +22,22 @@ import pytest
from telegram import EncryptedCredentials, PassportElementError
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def encrypted_credentials():
return EncryptedCredentials(
TestEncryptedCredentials.data,
TestEncryptedCredentials.hash,
TestEncryptedCredentials.secret,
TestEncryptedCredentialsBase.data,
TestEncryptedCredentialsBase.hash,
TestEncryptedCredentialsBase.secret,
)
class TestEncryptedCredentials:
class TestEncryptedCredentialsBase:
data = "data"
hash = "hash"
secret = "secret"
class TestEncryptedCredentialsWithoutRequest(TestEncryptedCredentialsBase):
def test_slot_behaviour(self, encrypted_credentials, mro_slots):
inst = encrypted_credentials
for attr in inst.__slots__:

View file

@ -22,22 +22,22 @@ import pytest
from telegram import EncryptedPassportElement, PassportElementError, PassportFile
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def encrypted_passport_element():
return EncryptedPassportElement(
TestEncryptedPassportElement.type_,
TestEncryptedPassportElementBase.type_,
"this is a hash",
data=TestEncryptedPassportElement.data,
phone_number=TestEncryptedPassportElement.phone_number,
email=TestEncryptedPassportElement.email,
files=TestEncryptedPassportElement.files,
front_side=TestEncryptedPassportElement.front_side,
reverse_side=TestEncryptedPassportElement.reverse_side,
selfie=TestEncryptedPassportElement.selfie,
data=TestEncryptedPassportElementBase.data,
phone_number=TestEncryptedPassportElementBase.phone_number,
email=TestEncryptedPassportElementBase.email,
files=TestEncryptedPassportElementBase.files,
front_side=TestEncryptedPassportElementBase.front_side,
reverse_side=TestEncryptedPassportElementBase.reverse_side,
selfie=TestEncryptedPassportElementBase.selfie,
)
class TestEncryptedPassportElement:
class TestEncryptedPassportElementBase:
type_ = "type"
hash = "this is a hash"
data = "data"
@ -48,6 +48,8 @@ class TestEncryptedPassportElement:
reverse_side = PassportFile("file_id", 50, 0, 25)
selfie = PassportFile("file_id", 50, 0, 25)
class TestEncryptedPassportElementWithoutRequest(TestEncryptedPassportElementBase):
def test_slot_behaviour(self, encrypted_passport_element, mro_slots):
inst = encrypted_passport_element
for attr in inst.__slots__:

View file

@ -27,20 +27,20 @@ from telegram.error import TelegramError
from tests.conftest import data_file
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def file(bot):
file = File(
TestFile.file_id,
TestFile.file_unique_id,
file_path=TestFile.file_path,
file_size=TestFile.file_size,
TestFileBase.file_id,
TestFileBase.file_unique_id,
file_path=TestFileBase.file_path,
file_size=TestFileBase.file_size,
)
file.set_bot(bot)
file._unfreeze()
return file
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def encrypted_file(bot):
# check https://github.com/python-telegram-bot/python-telegram-bot/wiki/\
# PTB-test-writing-knowledge-base#how-to-generate-encrypted-passport-files
@ -49,13 +49,18 @@ def encrypted_file(bot):
"Oq3G4sX+bKZthoyms1YlPqvWou9esb+z0Bi/KqQUG8s=",
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(TestFile.file_id, TestFile.file_unique_id, TestFile.file_size, TestFile.file_path)
ef = File(
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
TestFileBase.file_path,
)
ef.set_bot(bot)
ef.set_credentials(fc)
return ef
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def encrypted_local_file(bot):
# check encrypted_file() for the source of the fc values
fc = FileCredentials(
@ -63,9 +68,9 @@ def encrypted_local_file(bot):
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
)
ef = File(
TestFile.file_id,
TestFile.file_unique_id,
TestFile.file_size,
TestFileBase.file_id,
TestFileBase.file_unique_id,
TestFileBase.file_size,
file_path=str(data_file("image_encrypted.jpg")),
)
ef.set_bot(bot)
@ -73,19 +78,19 @@ def encrypted_local_file(bot):
return ef
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def local_file(bot):
file = File(
TestFile.file_id,
TestFile.file_unique_id,
TestFileBase.file_id,
TestFileBase.file_unique_id,
file_path=str(data_file("local_file.txt")),
file_size=TestFile.file_size,
file_size=TestFileBase.file_size,
)
file.set_bot(bot)
return file
class TestFile:
class TestFileBase:
file_id = "NOTVALIDDOESNOTMATTER"
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
file_path = (
@ -94,6 +99,8 @@ class TestFile:
file_size = 28232
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
class TestFileWithoutRequest(TestFileBase):
def test_slot_behaviour(self, file, mro_slots):
for attr in file.__slots__:
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
@ -123,10 +130,25 @@ class TestFile:
assert file_dict["file_path"] == file.file_path
assert file_dict["file_size"] == file.file_size
@pytest.mark.flaky(3, 1)
async def test_error_get_empty_file_id(self, bot):
with pytest.raises(TelegramError):
await bot.get_file(file_id="")
def test_equality(self, bot):
a = File(self.file_id, self.file_unique_id, bot)
b = File("", self.file_unique_id, bot)
c = File(self.file_id, self.file_unique_id, None)
d = File("", "", bot)
e = Voice(self.file_id, self.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
async def test_download(self, monkeypatch, file):
async def test(*args, **kwargs):
@ -140,9 +162,6 @@ class TestFile:
finally:
out_file.unlink()
async def test_download_local_file(self, local_file):
assert await local_file.download_to_drive() == Path(local_file.file_path)
@pytest.mark.parametrize(
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
)
@ -161,20 +180,6 @@ class TestFile:
os.close(file_handle)
custom_path.unlink()
@pytest.mark.parametrize(
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
)
async def test_download_custom_path_local_file(self, local_file, custom_path_type):
file_handle, custom_path = mkstemp()
custom_path = Path(custom_path)
try:
out_file = await local_file.download_to_drive(custom_path_type(custom_path))
assert out_file == custom_path
assert out_file.read_bytes() == self.file_content
finally:
os.close(file_handle)
custom_path.unlink()
async def test_download_no_filename(self, monkeypatch, file):
async def test(*args, **kwargs):
return self.file_content
@ -200,12 +205,6 @@ class TestFile:
custom_fobj.seek(0)
assert custom_fobj.read() == self.file_content
async def test_download_file_obj_local_file(self, local_file):
with TemporaryFile() as custom_fobj:
await local_file.download_to_memory(out=custom_fobj)
custom_fobj.seek(0)
assert custom_fobj.read() == self.file_content
async def test_download_bytearray(self, monkeypatch, file):
async def test(*args, **kwargs):
return self.file_content
@ -223,18 +222,6 @@ class TestFile:
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
async def test_download_bytearray_local_file(self, local_file):
# Check that a download to a newly allocated bytearray works.
buf = await local_file.download_as_bytearray()
assert buf == bytearray(self.file_content)
# Check that a download to a given bytearray works (extends the bytearray).
buf2 = buf[:]
buf3 = await local_file.download_as_bytearray(buf=buf2)
assert buf3 is buf2
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
async def test_download_encrypted(self, monkeypatch, bot, encrypted_file):
async def test(*args, **kwargs):
return data_file("image_encrypted.jpg").read_bytes()
@ -257,29 +244,6 @@ class TestFile:
custom_fobj.seek(0)
assert custom_fobj.read() == data_file("image_decrypted.jpg").read_bytes()
async def test_download_local_file_encrypted(self, encrypted_local_file):
out_file = await encrypted_local_file.download_to_drive()
try:
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
finally:
out_file.unlink()
@pytest.mark.parametrize(
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
)
async def test_download_custom_path_local_file_encrypted(
self, encrypted_local_file, custom_path_type
):
file_handle, custom_path = mkstemp()
custom_path = Path(custom_path)
try:
out_file = await encrypted_local_file.download_to_drive(custom_path_type(custom_path))
assert out_file == custom_path
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
finally:
os.close(file_handle)
custom_path.unlink()
async def test_download_file_obj_local_file_encrypted(self, monkeypatch, encrypted_local_file):
async def test(*args, **kwargs):
return data_file("image_encrypted.jpg").read_bytes()
@ -307,6 +271,70 @@ class TestFile:
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
class TestFileWithRequest(TestFileBase):
async def test_error_get_empty_file_id(self, bot):
with pytest.raises(TelegramError):
await bot.get_file(file_id="")
async def test_download_local_file(self, local_file):
assert await local_file.download_to_drive() == Path(local_file.file_path)
@pytest.mark.parametrize(
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
)
async def test_download_custom_path_local_file(self, local_file, custom_path_type):
file_handle, custom_path = mkstemp()
custom_path = Path(custom_path)
try:
out_file = await local_file.download_to_drive(custom_path_type(custom_path))
assert out_file == custom_path
assert out_file.read_bytes() == self.file_content
finally:
os.close(file_handle)
custom_path.unlink()
async def test_download_file_obj_local_file(self, local_file):
with TemporaryFile() as custom_fobj:
await local_file.download_to_memory(out=custom_fobj)
custom_fobj.seek(0)
assert custom_fobj.read() == self.file_content
@pytest.mark.parametrize(
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
)
async def test_download_custom_path_local_file_encrypted(
self, encrypted_local_file, custom_path_type
):
file_handle, custom_path = mkstemp()
custom_path = Path(custom_path)
try:
out_file = await encrypted_local_file.download_to_drive(custom_path_type(custom_path))
assert out_file == custom_path
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
finally:
os.close(file_handle)
custom_path.unlink()
async def test_download_local_file_encrypted(self, encrypted_local_file):
out_file = await encrypted_local_file.download_to_drive()
try:
assert out_file.read_bytes() == data_file("image_decrypted.jpg").read_bytes()
finally:
out_file.unlink()
async def test_download_bytearray_local_file(self, local_file):
# Check that a download to a newly allocated bytearray works.
buf = await local_file.download_as_bytearray()
assert buf == bytearray(self.file_content)
# Check that a download to a given bytearray works (extends the bytearray).
buf2 = buf[:]
buf3 = await local_file.download_as_bytearray(buf=buf2)
assert buf3 is buf2
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
async def test_download_bytearray_local_file_encrypted(self, encrypted_local_file):
# Check that a download to a newly allocated bytearray works.
buf = await encrypted_local_file.download_as_bytearray()
@ -318,23 +346,3 @@ class TestFile:
assert buf3 is buf2
assert buf2[len(buf) :] == buf
assert buf2[: len(buf)] == buf
def test_equality(self, bot):
a = File(self.file_id, self.file_unique_id, bot)
b = File("", self.file_unique_id, bot)
c = File(self.file_id, self.file_unique_id, None)
d = File("", "", bot)
e = Voice(self.file_id, self.file_unique_id, 0)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a == c
assert hash(a) == hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -22,30 +22,23 @@ import pytest
from telegram import ForceReply, ReplyKeyboardRemove
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def force_reply():
return ForceReply(
TestForceReply.selective,
TestForceReply.input_field_placeholder,
)
return ForceReply(TestForceReplyBase.selective, TestForceReplyBase.input_field_placeholder)
class TestForceReply:
class TestForceReplyBase:
force_reply = True
selective = True
input_field_placeholder = "force replies can be annoying if not used properly"
class TestForceReplyWithoutRequest(TestForceReplyBase):
def test_slot_behaviour(self, force_reply, mro_slots):
for attr in force_reply.__slots__:
assert getattr(force_reply, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(force_reply)) == len(set(mro_slots(force_reply))), "duplicate slot"
@pytest.mark.flaky(3, 1)
async def test_send_message_with_force_reply(self, bot, chat_id, force_reply):
message = await bot.send_message(chat_id, "text", reply_markup=force_reply)
assert message.text == "text"
def test_expected(self, force_reply):
assert force_reply.force_reply == self.force_reply
assert force_reply.selective == self.selective
@ -73,3 +66,9 @@ class TestForceReply:
assert a != d
assert hash(a) != hash(d)
class TestForceReplyWithRequest(TestForceReplyBase):
async def test_send_message_with_force_reply(self, bot, chat_id, force_reply):
message = await bot.send_message(chat_id, "text", reply_markup=force_reply)
assert message.text == "text"

View file

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import datetime
import pytest
@ -44,7 +45,7 @@ async def emoji_id(bot):
return first_sticker.custom_emoji_id
@pytest.fixture
@pytest.fixture(scope="module")
async def forum_topic_object(forum_group_id, emoji_id):
return ForumTopic(
message_thread_id=forum_group_id,
@ -54,7 +55,7 @@ async def forum_topic_object(forum_group_id, emoji_id):
)
@pytest.fixture
@pytest.fixture(scope="function")
async def real_topic(bot, emoji_id, forum_group_id):
result = await bot.create_forum_topic(
chat_id=forum_group_id,
@ -71,13 +72,12 @@ async def real_topic(bot, emoji_id, forum_group_id):
assert result is True, "Topic was not deleted"
class TestForumTopic:
class TestForumTopicWithoutRequest:
def test_slot_behaviour(self, mro_slots, forum_topic_object):
for attr in forum_topic_object.__slots__:
assert getattr(forum_topic_object, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(forum_topic_object)) == len(
set(mro_slots(forum_topic_object))
), "duplicate slot"
inst = forum_topic_object
for attr in inst.__slots__:
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
async def test_expected_values(self, emoji_id, forum_group_id, forum_topic_object):
assert forum_topic_object.message_thread_id == forum_group_id
@ -152,8 +152,7 @@ class TestForumTopic:
assert hash(a) != hash(e)
@pytest.mark.flaky(3, 1)
class TestForumMethods:
class TestForumMethodsWithRequest:
async def test_create_forum_topic(self, real_topic):
result = real_topic
assert isinstance(result, ForumTopic)
@ -237,22 +236,20 @@ class TestForumMethods:
async def test_unpin_all_forum_topic_messages(self, bot, forum_group_id, real_topic):
message_thread_id = real_topic.message_thread_id
pin_msg_tasks = set()
msgs = [
await (
await bot.send_message(
chat_id=forum_group_id, text=TEST_MSG_TEXT, message_thread_id=message_thread_id
)
).pin()
awaitables = {
bot.send_message(forum_group_id, TEST_MSG_TEXT, message_thread_id=message_thread_id)
for _ in range(2)
]
}
for coro in asyncio.as_completed(awaitables):
msg = await coro
pin_msg_tasks.add(asyncio.create_task(msg.pin()))
assert all(msgs) is True, "Message(s) were not pinned"
assert all([await task for task in pin_msg_tasks]) is True, "Message(s) were not pinned"
# We need 2 or more pinned msgs for this to work, else we get Chat_not_modified error
result = await bot.unpin_all_forum_topic_messages(
chat_id=forum_group_id, message_thread_id=message_thread_id
)
result = await bot.unpin_all_forum_topic_messages(forum_group_id, message_thread_id)
assert result is True, "Failed to unpin all the messages in forum topic"
async def test_edit_general_forum_topic(self, bot, forum_group_id):
@ -304,12 +301,12 @@ class TestForumMethods:
assert result is True, "Failed to reopen general forum topic"
@pytest.fixture
@pytest.fixture(scope="module")
def topic_created():
return ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR)
class TestForumTopicCreated:
class TestForumTopicCreatedWithoutRequest:
def test_slot_behaviour(self, topic_created, mro_slots):
for attr in topic_created.__slots__:
assert getattr(topic_created, attr, "err") != "err", f"got extra slot '{attr}'"
@ -358,7 +355,7 @@ class TestForumTopicCreated:
assert hash(a) != hash(d)
class TestForumTopicClosed:
class TestForumTopicClosedWithoutRequest:
def test_slot_behaviour(self, mro_slots):
action = ForumTopicClosed()
for attr in action.__slots__:
@ -376,7 +373,7 @@ class TestForumTopicClosed:
assert action_dict == {}
class TestForumTopicReopened:
class TestForumTopicReopenedWithoutRequest:
def test_slot_behaviour(self, mro_slots):
action = ForumTopicReopened()
for attr in action.__slots__:

View file

@ -22,21 +22,21 @@ import pytest
from telegram import Animation, Game, MessageEntity, PhotoSize
@pytest.fixture(scope="function")
@pytest.fixture(scope="module")
def game():
game = Game(
TestGame.title,
TestGame.description,
TestGame.photo,
text=TestGame.text,
text_entities=TestGame.text_entities,
animation=TestGame.animation,
TestGameBase.title,
TestGameBase.description,
TestGameBase.photo,
text=TestGameBase.text,
text_entities=TestGameBase.text_entities,
animation=TestGameBase.animation,
)
game._unfreeze()
return game
class TestGame:
class TestGameBase:
title = "Python-telegram-bot Test Game"
description = "description"
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
@ -47,6 +47,8 @@ class TestGame:
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
animation = Animation("blah", "unique_id", 320, 180, 1)
class TestGameWithoutRequest(TestGameBase):
def test_slot_behaviour(self, game, mro_slots):
for attr in game.__slots__:
assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'"
@ -95,20 +97,6 @@ class TestGame:
assert game_dict["text_entities"] == [game.text_entities[0].to_dict()]
assert game_dict["animation"] == game.animation.to_dict()
def test_parse_entity(self, game):
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
game.text_entities = [entity]
assert game.parse_text_entity(entity) == "http://google.com"
def test_parse_entities(self, game):
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
game.text_entities = [entity_2, entity]
assert game.parse_text_entities(MessageEntity.URL) == {entity: "http://google.com"}
assert game.parse_text_entities() == {entity: "http://google.com", entity_2: "h"}
def test_equality(self):
a = Game("title", "description", [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)])
b = Game(
@ -133,3 +121,17 @@ class TestGame:
assert a != d
assert hash(a) != hash(d)
def test_parse_entity(self, game):
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
game.text_entities = [entity]
assert game.parse_text_entity(entity) == "http://google.com"
def test_parse_entities(self, game):
entity = MessageEntity(type=MessageEntity.URL, offset=13, length=17)
entity_2 = MessageEntity(type=MessageEntity.BOLD, offset=13, length=1)
game.text_entities = [entity_2, entity]
assert game.parse_text_entities(MessageEntity.URL) == {entity: "http://google.com"}
assert game.parse_text_entities() == {entity: "http://google.com", entity_2: "h"}

View file

@ -22,25 +22,31 @@ import pytest
from telegram import GameHighScore, User
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def game_highscore():
return GameHighScore(
TestGameHighScore.position, TestGameHighScore.user, TestGameHighScore.score
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score
)
class TestGameHighScore:
class TestGameHighScoreBase:
position = 12
user = User(2, "test user", False)
score = 42
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase):
def test_slot_behaviour(self, game_highscore, mro_slots):
for attr in game_highscore.__slots__:
assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(game_highscore)) == len(set(mro_slots(game_highscore))), "same slot"
def test_de_json(self, bot):
json_dict = {"position": self.position, "user": self.user.to_dict(), "score": self.score}
json_dict = {
"position": self.position,
"user": self.user.to_dict(),
"score": self.score,
}
highscore = GameHighScore.de_json(json_dict, bot)
assert highscore.api_kwargs == {}

View file

@ -22,22 +22,22 @@ import pytest
from telegram import CallbackGame, InlineKeyboardButton, LoginUrl, WebAppInfo
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_keyboard_button():
return InlineKeyboardButton(
TestInlineKeyboardButton.text,
url=TestInlineKeyboardButton.url,
callback_data=TestInlineKeyboardButton.callback_data,
switch_inline_query=TestInlineKeyboardButton.switch_inline_query,
switch_inline_query_current_chat=TestInlineKeyboardButton.switch_inline_query_current_chat,
callback_game=TestInlineKeyboardButton.callback_game,
pay=TestInlineKeyboardButton.pay,
login_url=TestInlineKeyboardButton.login_url,
web_app=TestInlineKeyboardButton.web_app,
TestInlineKeyboardButtonBase.text,
url=TestInlineKeyboardButtonBase.url,
callback_data=TestInlineKeyboardButtonBase.callback_data,
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
switch_inline_query_current_chat=TestInlineKeyboardButtonBase.switch_inline_query_current_chat, # noqa: E501
callback_game=TestInlineKeyboardButtonBase.callback_game,
pay=TestInlineKeyboardButtonBase.pay,
login_url=TestInlineKeyboardButtonBase.login_url,
web_app=TestInlineKeyboardButtonBase.web_app,
)
class TestInlineKeyboardButton:
class TestInlineKeyboardButtonBase:
text = "text"
url = "url"
callback_data = "callback data"
@ -48,6 +48,8 @@ class TestInlineKeyboardButton:
login_url = LoginUrl("http://google.com")
web_app = WebAppInfo(url="https://example.com")
class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
def test_slot_behaviour(self, inline_keyboard_button, mro_slots):
inst = inline_keyboard_button
for attr in inst.__slots__:

View file

@ -28,12 +28,12 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_keyboard_markup():
return InlineKeyboardMarkup(TestInlineKeyboardMarkup.inline_keyboard)
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard)
class TestInlineKeyboardMarkup:
class TestInlineKeyboardMarkupBase:
inline_keyboard = [
[
InlineKeyboardButton(text="button1", callback_data="data1"),
@ -41,104 +41,14 @@ class TestInlineKeyboardMarkup:
]
]
class TestInlineKeyboardMarkupWithoutRequest(TestInlineKeyboardMarkupBase):
def test_slot_behaviour(self, inline_keyboard_markup, mro_slots):
inst = inline_keyboard_markup
for attr in inst.__slots__:
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
@pytest.mark.flaky(3, 1)
async def test_send_message_with_inline_keyboard_markup(
self, bot, chat_id, inline_keyboard_markup
):
message = await bot.send_message(
chat_id, "Testing InlineKeyboardMarkup", reply_markup=inline_keyboard_markup
)
assert message.text == "Testing InlineKeyboardMarkup"
def test_from_button(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text="button1", callback_data="data1")
).inline_keyboard
assert len(inline_keyboard_markup) == 1
assert len(inline_keyboard_markup[0]) == 1
def test_from_row(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_row(
[
InlineKeyboardButton(text="button1", callback_data="data1"),
InlineKeyboardButton(text="button1", callback_data="data1"),
]
).inline_keyboard
assert len(inline_keyboard_markup) == 1
assert len(inline_keyboard_markup[0]) == 2
def test_from_column(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_column(
[
InlineKeyboardButton(text="button1", callback_data="data1"),
InlineKeyboardButton(text="button1", callback_data="data1"),
]
).inline_keyboard
assert len(inline_keyboard_markup) == 2
assert len(inline_keyboard_markup[0]) == 1
assert len(inline_keyboard_markup[1]) == 1
def test_expected_values(self, inline_keyboard_markup):
assert inline_keyboard_markup.inline_keyboard == tuple(
tuple(row) for row in self.inline_keyboard
)
def test_wrong_keyboard_inputs(self):
with pytest.raises(ValueError):
InlineKeyboardMarkup(
[[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")]
)
with pytest.raises(ValueError):
InlineKeyboardMarkup("strings_are_not_allowed")
with pytest.raises(ValueError):
InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
with pytest.raises(ValueError):
InlineKeyboardMarkup(InlineKeyboardButton("b1", "1"))
with pytest.raises(ValueError):
InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]])
async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
async def make_assertion(
url,
data,
reply_to_message_id=None,
disable_notification=None,
reply_markup=None,
timeout=None,
**kwargs,
):
if reply_markup is not None:
markups = (
InlineKeyboardMarkup,
ReplyKeyboardMarkup,
ForceReply,
ReplyKeyboardRemove,
)
if isinstance(reply_markup, markups):
data["reply_markup"] = reply_markup.to_dict()
else:
data["reply_markup"] = reply_markup
assert bool("'switch_inline_query': ''" in str(data["reply_markup"]))
assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"]))
inline_keyboard_markup.inline_keyboard[0][0]._unfreeze()
inline_keyboard_markup.inline_keyboard[0][0].callback_data = None
inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = ""
inline_keyboard_markup.inline_keyboard[0][1]._unfreeze()
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ""
monkeypatch.setattr(bot, "_send_message", make_assertion)
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
def test_to_dict(self, inline_keyboard_markup):
inline_keyboard_markup_dict = inline_keyboard_markup.to_dict()
@ -233,3 +143,96 @@ class TestInlineKeyboardMarkup:
assert a != g
assert hash(a) != hash(g)
def test_from_button(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text="button1", callback_data="data1")
).inline_keyboard
assert len(inline_keyboard_markup) == 1
assert len(inline_keyboard_markup[0]) == 1
def test_from_row(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_row(
[
InlineKeyboardButton(text="button1", callback_data="data1"),
InlineKeyboardButton(text="button1", callback_data="data1"),
]
).inline_keyboard
assert len(inline_keyboard_markup) == 1
assert len(inline_keyboard_markup[0]) == 2
def test_from_column(self):
inline_keyboard_markup = InlineKeyboardMarkup.from_column(
[
InlineKeyboardButton(text="button1", callback_data="data1"),
InlineKeyboardButton(text="button1", callback_data="data1"),
]
).inline_keyboard
assert len(inline_keyboard_markup) == 2
assert len(inline_keyboard_markup[0]) == 1
assert len(inline_keyboard_markup[1]) == 1
def test_expected_values(self, inline_keyboard_markup):
assert inline_keyboard_markup.inline_keyboard == tuple(
tuple(row) for row in self.inline_keyboard
)
def test_wrong_keyboard_inputs(self):
with pytest.raises(ValueError):
InlineKeyboardMarkup(
[[InlineKeyboardButton("b1", "1")], InlineKeyboardButton("b2", "2")]
)
with pytest.raises(ValueError):
InlineKeyboardMarkup("strings_are_not_allowed")
with pytest.raises(ValueError):
InlineKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
with pytest.raises(ValueError):
InlineKeyboardMarkup(InlineKeyboardButton("b1", "1"))
with pytest.raises(ValueError):
InlineKeyboardMarkup([[[InlineKeyboardButton("only_2d_array_is_allowed", "1")]]])
async def test_expected_values_empty_switch(self, inline_keyboard_markup, bot, monkeypatch):
async def make_assertion(
url,
data,
reply_to_message_id=None,
disable_notification=None,
reply_markup=None,
timeout=None,
**kwargs,
):
if reply_markup is not None:
markups = (
InlineKeyboardMarkup,
ReplyKeyboardMarkup,
ForceReply,
ReplyKeyboardRemove,
)
if isinstance(reply_markup, markups):
data["reply_markup"] = reply_markup.to_dict()
else:
data["reply_markup"] = reply_markup
assert bool("'switch_inline_query': ''" in str(data["reply_markup"]))
assert bool("'switch_inline_query_current_chat': ''" in str(data["reply_markup"]))
inline_keyboard_markup.inline_keyboard[0][0]._unfreeze()
inline_keyboard_markup.inline_keyboard[0][0].callback_data = None
inline_keyboard_markup.inline_keyboard[0][0].switch_inline_query = ""
inline_keyboard_markup.inline_keyboard[0][1]._unfreeze()
inline_keyboard_markup.inline_keyboard[0][1].callback_data = None
inline_keyboard_markup.inline_keyboard[0][1].switch_inline_query_current_chat = ""
monkeypatch.setattr(bot, "_send_message", make_assertion)
await bot.send_message(123, "test", reply_markup=inline_keyboard_markup)
class TestInlineKeyborardMarkupWithRequest(TestInlineKeyboardMarkupBase):
async def test_send_message_with_inline_keyboard_markup(
self, bot, chat_id, inline_keyboard_markup
):
message = await bot.send_message(
chat_id, "Testing InlineKeyboardMarkup", reply_markup=inline_keyboard_markup
)
assert message.text == "Testing InlineKeyboardMarkup"

View file

@ -27,26 +27,28 @@ from tests.auxil.bot_method_checks import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query(bot):
ilq = InlineQuery(
TestInlineQuery.id_,
TestInlineQuery.from_user,
TestInlineQuery.query,
TestInlineQuery.offset,
location=TestInlineQuery.location,
TestInlineQueryBase.id_,
TestInlineQueryBase.from_user,
TestInlineQueryBase.query,
TestInlineQueryBase.offset,
location=TestInlineQueryBase.location,
)
ilq.set_bot(bot)
return ilq
class TestInlineQuery:
class TestInlineQueryBase:
id_ = 1234
from_user = User(1, "First name", False)
query = "query text"
offset = "offset"
location = Location(8.8, 53.1)
class TestInlineQueryWithoutRequest(TestInlineQueryBase):
def test_slot_behaviour(self, inline_query, mro_slots):
for attr in inline_query.__slots__:
assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'"
@ -79,34 +81,6 @@ class TestInlineQuery:
assert inline_query_dict["query"] == inline_query.query
assert inline_query_dict["offset"] == inline_query.offset
async def test_answer(self, monkeypatch, inline_query):
async def make_assertion(*_, **kwargs):
return kwargs["inline_query_id"] == inline_query.id
assert check_shortcut_signature(
InlineQuery.answer, Bot.answer_inline_query, ["inline_query_id"], ["auto_pagination"]
)
assert await check_shortcut_call(
inline_query.answer, inline_query.get_bot(), "answer_inline_query"
)
assert await check_defaults_handling(inline_query.answer, inline_query.get_bot())
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
assert await inline_query.answer(results=[])
async def test_answer_error(self, inline_query):
with pytest.raises(ValueError, match="mutually exclusive"):
await inline_query.answer(results=[], auto_pagination=True, current_offset="foobar")
async def test_answer_auto_pagination(self, monkeypatch, inline_query):
async def make_assertion(*_, **kwargs):
inline_query_id_matches = kwargs["inline_query_id"] == inline_query.id
offset_matches = kwargs.get("current_offset") == inline_query.offset
return offset_matches and inline_query_id_matches
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
assert await inline_query.answer(results=[], auto_pagination=True)
def test_equality(self):
a = InlineQuery(self.id_, User(1, "", False), "", "")
b = InlineQuery(self.id_, User(1, "", False), "", "")
@ -126,3 +100,31 @@ class TestInlineQuery:
assert a != e
assert hash(a) != hash(e)
async def test_answer_error(self, inline_query):
with pytest.raises(ValueError, match="mutually exclusive"):
await inline_query.answer(results=[], auto_pagination=True, current_offset="foobar")
async def test_answer(self, monkeypatch, inline_query):
async def make_assertion(*_, **kwargs):
return kwargs["inline_query_id"] == inline_query.id
assert check_shortcut_signature(
InlineQuery.answer, Bot.answer_inline_query, ["inline_query_id"], ["auto_pagination"]
)
assert await check_shortcut_call(
inline_query.answer, inline_query.get_bot(), "answer_inline_query"
)
assert await check_defaults_handling(inline_query.answer, inline_query.get_bot())
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
assert await inline_query.answer(results=[])
async def test_answer_auto_pagination(self, monkeypatch, inline_query):
async def make_assertion(*_, **kwargs):
inline_query_id_matches = kwargs["inline_query_id"] == inline_query.id
offset_matches = kwargs.get("current_offset") == inline_query.offset
return offset_matches and inline_query_id_matches
monkeypatch.setattr(inline_query.get_bot(), "answer_inline_query", make_assertion)
assert await inline_query.answer(results=[], auto_pagination=True)

View file

@ -28,23 +28,23 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_article():
return InlineQueryResultArticle(
TestInlineQueryResultArticle.id_,
TestInlineQueryResultArticle.title,
input_message_content=TestInlineQueryResultArticle.input_message_content,
reply_markup=TestInlineQueryResultArticle.reply_markup,
url=TestInlineQueryResultArticle.url,
hide_url=TestInlineQueryResultArticle.hide_url,
description=TestInlineQueryResultArticle.description,
thumb_url=TestInlineQueryResultArticle.thumb_url,
thumb_height=TestInlineQueryResultArticle.thumb_height,
thumb_width=TestInlineQueryResultArticle.thumb_width,
TestInlineQueryResultArticleBase.id_,
TestInlineQueryResultArticleBase.title,
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
url=TestInlineQueryResultArticleBase.url,
hide_url=TestInlineQueryResultArticleBase.hide_url,
description=TestInlineQueryResultArticleBase.description,
thumb_url=TestInlineQueryResultArticleBase.thumb_url,
thumb_height=TestInlineQueryResultArticleBase.thumb_height,
thumb_width=TestInlineQueryResultArticleBase.thumb_width,
)
class TestInlineQueryResultArticle:
class TestInlineQueryResultArticleBase:
id_ = "id"
type_ = "article"
title = "title"
@ -57,6 +57,8 @@ class TestInlineQueryResultArticle:
thumb_height = 10
thumb_width = 15
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase):
def test_slot_behaviour(self, inline_query_result_article, mro_slots, recwarn):
inst = inline_query_result_article
for attr in inst.__slots__:

View file

@ -29,23 +29,23 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_audio():
return InlineQueryResultAudio(
TestInlineQueryResultAudio.id_,
TestInlineQueryResultAudio.audio_url,
TestInlineQueryResultAudio.title,
performer=TestInlineQueryResultAudio.performer,
audio_duration=TestInlineQueryResultAudio.audio_duration,
caption=TestInlineQueryResultAudio.caption,
parse_mode=TestInlineQueryResultAudio.parse_mode,
caption_entities=TestInlineQueryResultAudio.caption_entities,
input_message_content=TestInlineQueryResultAudio.input_message_content,
reply_markup=TestInlineQueryResultAudio.reply_markup,
TestInlineQueryResultAudioBase.id_,
TestInlineQueryResultAudioBase.audio_url,
TestInlineQueryResultAudioBase.title,
performer=TestInlineQueryResultAudioBase.performer,
audio_duration=TestInlineQueryResultAudioBase.audio_duration,
caption=TestInlineQueryResultAudioBase.caption,
parse_mode=TestInlineQueryResultAudioBase.parse_mode,
caption_entities=TestInlineQueryResultAudioBase.caption_entities,
input_message_content=TestInlineQueryResultAudioBase.input_message_content,
reply_markup=TestInlineQueryResultAudioBase.reply_markup,
)
class TestInlineQueryResultAudio:
class TestInlineQueryResultAudioBase:
id_ = "id"
type_ = "audio"
audio_url = "audio url"
@ -58,6 +58,8 @@ class TestInlineQueryResultAudio:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultAudioWithoutRequest(TestInlineQueryResultAudioBase):
def test_slot_behaviour(self, inline_query_result_audio, mro_slots):
inst = inline_query_result_audio
for attr in inst.__slots__:

View file

@ -29,20 +29,20 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_audio():
return InlineQueryResultCachedAudio(
TestInlineQueryResultCachedAudio.id_,
TestInlineQueryResultCachedAudio.audio_file_id,
caption=TestInlineQueryResultCachedAudio.caption,
parse_mode=TestInlineQueryResultCachedAudio.parse_mode,
caption_entities=TestInlineQueryResultCachedAudio.caption_entities,
input_message_content=TestInlineQueryResultCachedAudio.input_message_content,
reply_markup=TestInlineQueryResultCachedAudio.reply_markup,
TestInlineQueryResultCachedAudioBase.id_,
TestInlineQueryResultCachedAudioBase.audio_file_id,
caption=TestInlineQueryResultCachedAudioBase.caption,
parse_mode=TestInlineQueryResultCachedAudioBase.parse_mode,
caption_entities=TestInlineQueryResultCachedAudioBase.caption_entities,
input_message_content=TestInlineQueryResultCachedAudioBase.input_message_content,
reply_markup=TestInlineQueryResultCachedAudioBase.reply_markup,
)
class TestInlineQueryResultCachedAudio:
class TestInlineQueryResultCachedAudioBase:
id_ = "id"
type_ = "audio"
audio_file_id = "audio file id"
@ -52,6 +52,8 @@ class TestInlineQueryResultCachedAudio:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedAudioWithoutRequest(TestInlineQueryResultCachedAudioBase):
def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots):
inst = inline_query_result_cached_audio
for attr in inst.__slots__:

View file

@ -29,22 +29,22 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_document():
return InlineQueryResultCachedDocument(
TestInlineQueryResultCachedDocument.id_,
TestInlineQueryResultCachedDocument.title,
TestInlineQueryResultCachedDocument.document_file_id,
caption=TestInlineQueryResultCachedDocument.caption,
parse_mode=TestInlineQueryResultCachedDocument.parse_mode,
caption_entities=TestInlineQueryResultCachedDocument.caption_entities,
description=TestInlineQueryResultCachedDocument.description,
input_message_content=TestInlineQueryResultCachedDocument.input_message_content,
reply_markup=TestInlineQueryResultCachedDocument.reply_markup,
TestInlineQueryResultCachedDocumentBase.id_,
TestInlineQueryResultCachedDocumentBase.title,
TestInlineQueryResultCachedDocumentBase.document_file_id,
caption=TestInlineQueryResultCachedDocumentBase.caption,
parse_mode=TestInlineQueryResultCachedDocumentBase.parse_mode,
caption_entities=TestInlineQueryResultCachedDocumentBase.caption_entities,
description=TestInlineQueryResultCachedDocumentBase.description,
input_message_content=TestInlineQueryResultCachedDocumentBase.input_message_content,
reply_markup=TestInlineQueryResultCachedDocumentBase.reply_markup,
)
class TestInlineQueryResultCachedDocument:
class TestInlineQueryResultCachedDocumentBase:
id_ = "id"
type_ = "document"
document_file_id = "document file id"
@ -56,6 +56,8 @@ class TestInlineQueryResultCachedDocument:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedDocumentWithoutRequest(TestInlineQueryResultCachedDocumentBase):
def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots):
inst = inline_query_result_cached_document
for attr in inst.__slots__:

View file

@ -28,21 +28,21 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_gif():
return InlineQueryResultCachedGif(
TestInlineQueryResultCachedGif.id_,
TestInlineQueryResultCachedGif.gif_file_id,
title=TestInlineQueryResultCachedGif.title,
caption=TestInlineQueryResultCachedGif.caption,
parse_mode=TestInlineQueryResultCachedGif.parse_mode,
caption_entities=TestInlineQueryResultCachedGif.caption_entities,
input_message_content=TestInlineQueryResultCachedGif.input_message_content,
reply_markup=TestInlineQueryResultCachedGif.reply_markup,
TestInlineQueryResultCachedGifBase.id_,
TestInlineQueryResultCachedGifBase.gif_file_id,
title=TestInlineQueryResultCachedGifBase.title,
caption=TestInlineQueryResultCachedGifBase.caption,
parse_mode=TestInlineQueryResultCachedGifBase.parse_mode,
caption_entities=TestInlineQueryResultCachedGifBase.caption_entities,
input_message_content=TestInlineQueryResultCachedGifBase.input_message_content,
reply_markup=TestInlineQueryResultCachedGifBase.reply_markup,
)
class TestInlineQueryResultCachedGif:
class TestInlineQueryResultCachedGifBase:
id_ = "id"
type_ = "gif"
gif_file_id = "gif file id"
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedGif:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase):
def test_slot_behaviour(self, inline_query_result_cached_gif, mro_slots):
inst = inline_query_result_cached_gif
for attr in inst.__slots__:

View file

@ -28,21 +28,21 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_mpeg4_gif():
return InlineQueryResultCachedMpeg4Gif(
TestInlineQueryResultCachedMpeg4Gif.id_,
TestInlineQueryResultCachedMpeg4Gif.mpeg4_file_id,
title=TestInlineQueryResultCachedMpeg4Gif.title,
caption=TestInlineQueryResultCachedMpeg4Gif.caption,
parse_mode=TestInlineQueryResultCachedMpeg4Gif.parse_mode,
caption_entities=TestInlineQueryResultCachedMpeg4Gif.caption_entities,
input_message_content=TestInlineQueryResultCachedMpeg4Gif.input_message_content,
reply_markup=TestInlineQueryResultCachedMpeg4Gif.reply_markup,
TestInlineQueryResultCachedMpeg4GifBase.id_,
TestInlineQueryResultCachedMpeg4GifBase.mpeg4_file_id,
title=TestInlineQueryResultCachedMpeg4GifBase.title,
caption=TestInlineQueryResultCachedMpeg4GifBase.caption,
parse_mode=TestInlineQueryResultCachedMpeg4GifBase.parse_mode,
caption_entities=TestInlineQueryResultCachedMpeg4GifBase.caption_entities,
input_message_content=TestInlineQueryResultCachedMpeg4GifBase.input_message_content,
reply_markup=TestInlineQueryResultCachedMpeg4GifBase.reply_markup,
)
class TestInlineQueryResultCachedMpeg4Gif:
class TestInlineQueryResultCachedMpeg4GifBase:
id_ = "id"
type_ = "mpeg4_gif"
mpeg4_file_id = "mpeg4 file id"
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedMpeg4Gif:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase):
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots):
inst = inline_query_result_cached_mpeg4_gif
for attr in inst.__slots__:

View file

@ -28,22 +28,22 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_photo():
return InlineQueryResultCachedPhoto(
TestInlineQueryResultCachedPhoto.id_,
TestInlineQueryResultCachedPhoto.photo_file_id,
title=TestInlineQueryResultCachedPhoto.title,
description=TestInlineQueryResultCachedPhoto.description,
caption=TestInlineQueryResultCachedPhoto.caption,
parse_mode=TestInlineQueryResultCachedPhoto.parse_mode,
caption_entities=TestInlineQueryResultCachedPhoto.caption_entities,
input_message_content=TestInlineQueryResultCachedPhoto.input_message_content,
reply_markup=TestInlineQueryResultCachedPhoto.reply_markup,
TestInlineQueryResultCachedPhotoBase.id_,
TestInlineQueryResultCachedPhotoBase.photo_file_id,
title=TestInlineQueryResultCachedPhotoBase.title,
description=TestInlineQueryResultCachedPhotoBase.description,
caption=TestInlineQueryResultCachedPhotoBase.caption,
parse_mode=TestInlineQueryResultCachedPhotoBase.parse_mode,
caption_entities=TestInlineQueryResultCachedPhotoBase.caption_entities,
input_message_content=TestInlineQueryResultCachedPhotoBase.input_message_content,
reply_markup=TestInlineQueryResultCachedPhotoBase.reply_markup,
)
class TestInlineQueryResultCachedPhoto:
class TestInlineQueryResultCachedPhotoBase:
id_ = "id"
type_ = "photo"
photo_file_id = "photo file id"
@ -55,6 +55,8 @@ class TestInlineQueryResultCachedPhoto:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase):
def test_slot_behaviour(self, inline_query_result_cached_photo, mro_slots):
inst = inline_query_result_cached_photo
for attr in inst.__slots__:

View file

@ -27,23 +27,25 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_sticker():
return InlineQueryResultCachedSticker(
TestInlineQueryResultCachedSticker.id_,
TestInlineQueryResultCachedSticker.sticker_file_id,
input_message_content=TestInlineQueryResultCachedSticker.input_message_content,
reply_markup=TestInlineQueryResultCachedSticker.reply_markup,
TestInlineQueryResultCachedStickerBase.id_,
TestInlineQueryResultCachedStickerBase.sticker_file_id,
input_message_content=TestInlineQueryResultCachedStickerBase.input_message_content,
reply_markup=TestInlineQueryResultCachedStickerBase.reply_markup,
)
class TestInlineQueryResultCachedSticker:
class TestInlineQueryResultCachedStickerBase:
id_ = "id"
type_ = "sticker"
sticker_file_id = "sticker file id"
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedStickerWithoutRequest(TestInlineQueryResultCachedStickerBase):
def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots):
inst = inline_query_result_cached_sticker
for attr in inst.__slots__:

View file

@ -28,22 +28,22 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_video():
return InlineQueryResultCachedVideo(
TestInlineQueryResultCachedVideo.id_,
TestInlineQueryResultCachedVideo.video_file_id,
TestInlineQueryResultCachedVideo.title,
caption=TestInlineQueryResultCachedVideo.caption,
parse_mode=TestInlineQueryResultCachedVideo.parse_mode,
caption_entities=TestInlineQueryResultCachedVideo.caption_entities,
description=TestInlineQueryResultCachedVideo.description,
input_message_content=TestInlineQueryResultCachedVideo.input_message_content,
reply_markup=TestInlineQueryResultCachedVideo.reply_markup,
TestInlineQueryResultCachedVideoBase.id_,
TestInlineQueryResultCachedVideoBase.video_file_id,
TestInlineQueryResultCachedVideoBase.title,
caption=TestInlineQueryResultCachedVideoBase.caption,
parse_mode=TestInlineQueryResultCachedVideoBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVideoBase.caption_entities,
description=TestInlineQueryResultCachedVideoBase.description,
input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup,
)
class TestInlineQueryResultCachedVideo:
class TestInlineQueryResultCachedVideoBase:
id_ = "id"
type_ = "video"
video_file_id = "video file id"
@ -55,6 +55,8 @@ class TestInlineQueryResultCachedVideo:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase):
def test_slot_behaviour(self, inline_query_result_cached_video, mro_slots):
inst = inline_query_result_cached_video
for attr in inst.__slots__:

View file

@ -28,21 +28,21 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_cached_voice():
return InlineQueryResultCachedVoice(
TestInlineQueryResultCachedVoice.id_,
TestInlineQueryResultCachedVoice.voice_file_id,
TestInlineQueryResultCachedVoice.title,
caption=TestInlineQueryResultCachedVoice.caption,
parse_mode=TestInlineQueryResultCachedVoice.parse_mode,
caption_entities=TestInlineQueryResultCachedVoice.caption_entities,
input_message_content=TestInlineQueryResultCachedVoice.input_message_content,
reply_markup=TestInlineQueryResultCachedVoice.reply_markup,
TestInlineQueryResultCachedVoiceBase.id_,
TestInlineQueryResultCachedVoiceBase.voice_file_id,
TestInlineQueryResultCachedVoiceBase.title,
caption=TestInlineQueryResultCachedVoiceBase.caption,
parse_mode=TestInlineQueryResultCachedVoiceBase.parse_mode,
caption_entities=TestInlineQueryResultCachedVoiceBase.caption_entities,
input_message_content=TestInlineQueryResultCachedVoiceBase.input_message_content,
reply_markup=TestInlineQueryResultCachedVoiceBase.reply_markup,
)
class TestInlineQueryResultCachedVoice:
class TestInlineQueryResultCachedVoiceBase:
id_ = "id"
type_ = "voice"
voice_file_id = "voice file id"
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedVoice:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultCachedVoiceWithoutRequest(TestInlineQueryResultCachedVoiceBase):
def test_slot_behaviour(self, inline_query_result_cached_voice, mro_slots):
inst = inline_query_result_cached_voice
for attr in inst.__slots__:

View file

@ -27,22 +27,22 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_contact():
return InlineQueryResultContact(
TestInlineQueryResultContact.id_,
TestInlineQueryResultContact.phone_number,
TestInlineQueryResultContact.first_name,
last_name=TestInlineQueryResultContact.last_name,
thumb_url=TestInlineQueryResultContact.thumb_url,
thumb_width=TestInlineQueryResultContact.thumb_width,
thumb_height=TestInlineQueryResultContact.thumb_height,
input_message_content=TestInlineQueryResultContact.input_message_content,
reply_markup=TestInlineQueryResultContact.reply_markup,
TestInlineQueryResultContactBase.id_,
TestInlineQueryResultContactBase.phone_number,
TestInlineQueryResultContactBase.first_name,
last_name=TestInlineQueryResultContactBase.last_name,
thumb_url=TestInlineQueryResultContactBase.thumb_url,
thumb_width=TestInlineQueryResultContactBase.thumb_width,
thumb_height=TestInlineQueryResultContactBase.thumb_height,
input_message_content=TestInlineQueryResultContactBase.input_message_content,
reply_markup=TestInlineQueryResultContactBase.reply_markup,
)
class TestInlineQueryResultContact:
class TestInlineQueryResultContactBase:
id_ = "id"
type_ = "contact"
phone_number = "phone_number"
@ -54,6 +54,8 @@ class TestInlineQueryResultContact:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultContactWithoutRequest(TestInlineQueryResultContactBase):
def test_slot_behaviour(self, inline_query_result_contact, mro_slots):
inst = inline_query_result_contact
for attr in inst.__slots__:

View file

@ -28,26 +28,26 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_document():
return InlineQueryResultDocument(
TestInlineQueryResultDocument.id_,
TestInlineQueryResultDocument.document_url,
TestInlineQueryResultDocument.title,
TestInlineQueryResultDocument.mime_type,
caption=TestInlineQueryResultDocument.caption,
parse_mode=TestInlineQueryResultDocument.parse_mode,
caption_entities=TestInlineQueryResultDocument.caption_entities,
description=TestInlineQueryResultDocument.description,
thumb_url=TestInlineQueryResultDocument.thumb_url,
thumb_width=TestInlineQueryResultDocument.thumb_width,
thumb_height=TestInlineQueryResultDocument.thumb_height,
input_message_content=TestInlineQueryResultDocument.input_message_content,
reply_markup=TestInlineQueryResultDocument.reply_markup,
TestInlineQueryResultDocumentBase.id_,
TestInlineQueryResultDocumentBase.document_url,
TestInlineQueryResultDocumentBase.title,
TestInlineQueryResultDocumentBase.mime_type,
caption=TestInlineQueryResultDocumentBase.caption,
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
description=TestInlineQueryResultDocumentBase.description,
thumb_url=TestInlineQueryResultDocumentBase.thumb_url,
thumb_width=TestInlineQueryResultDocumentBase.thumb_width,
thumb_height=TestInlineQueryResultDocumentBase.thumb_height,
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
)
class TestInlineQueryResultDocument:
class TestInlineQueryResultDocumentBase:
id_ = "id"
type_ = "document"
document_url = "document url"
@ -63,6 +63,8 @@ class TestInlineQueryResultDocument:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultDocumentWithoutRequest(TestInlineQueryResultDocumentBase):
def test_slot_behaviour(self, inline_query_result_document, mro_slots):
inst = inline_query_result_document
for attr in inst.__slots__:

View file

@ -26,21 +26,23 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_game():
return InlineQueryResultGame(
TestInlineQueryResultGame.id_,
TestInlineQueryResultGame.game_short_name,
reply_markup=TestInlineQueryResultGame.reply_markup,
TestInlineQueryResultGameBase.id_,
TestInlineQueryResultGameBase.game_short_name,
reply_markup=TestInlineQueryResultGameBase.reply_markup,
)
class TestInlineQueryResultGame:
class TestInlineQueryResultGameBase:
id_ = "id"
type_ = "game"
game_short_name = "game short name"
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultGameWithoutRequest(TestInlineQueryResultGameBase):
def test_slot_behaviour(self, inline_query_result_game, mro_slots):
inst = inline_query_result_game
for attr in inst.__slots__:

View file

@ -28,26 +28,26 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_gif():
return InlineQueryResultGif(
TestInlineQueryResultGif.id_,
TestInlineQueryResultGif.gif_url,
TestInlineQueryResultGif.thumb_url,
gif_width=TestInlineQueryResultGif.gif_width,
gif_height=TestInlineQueryResultGif.gif_height,
gif_duration=TestInlineQueryResultGif.gif_duration,
title=TestInlineQueryResultGif.title,
caption=TestInlineQueryResultGif.caption,
parse_mode=TestInlineQueryResultGif.parse_mode,
caption_entities=TestInlineQueryResultGif.caption_entities,
input_message_content=TestInlineQueryResultGif.input_message_content,
reply_markup=TestInlineQueryResultGif.reply_markup,
thumb_mime_type=TestInlineQueryResultGif.thumb_mime_type,
TestInlineQueryResultGifBase.id_,
TestInlineQueryResultGifBase.gif_url,
TestInlineQueryResultGifBase.thumb_url,
gif_width=TestInlineQueryResultGifBase.gif_width,
gif_height=TestInlineQueryResultGifBase.gif_height,
gif_duration=TestInlineQueryResultGifBase.gif_duration,
title=TestInlineQueryResultGifBase.title,
caption=TestInlineQueryResultGifBase.caption,
parse_mode=TestInlineQueryResultGifBase.parse_mode,
caption_entities=TestInlineQueryResultGifBase.caption_entities,
input_message_content=TestInlineQueryResultGifBase.input_message_content,
reply_markup=TestInlineQueryResultGifBase.reply_markup,
thumb_mime_type=TestInlineQueryResultGifBase.thumb_mime_type,
)
class TestInlineQueryResultGif:
class TestInlineQueryResultGifBase:
id_ = "id"
type_ = "gif"
gif_url = "gif url"
@ -63,6 +63,8 @@ class TestInlineQueryResultGif:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultGifWithoutRequest(TestInlineQueryResultGifBase):
def test_slot_behaviour(self, inline_query_result_gif, mro_slots):
inst = inline_query_result_gif
for attr in inst.__slots__:

View file

@ -27,26 +27,26 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_location():
return InlineQueryResultLocation(
TestInlineQueryResultLocation.id_,
TestInlineQueryResultLocation.latitude,
TestInlineQueryResultLocation.longitude,
TestInlineQueryResultLocation.title,
live_period=TestInlineQueryResultLocation.live_period,
thumb_url=TestInlineQueryResultLocation.thumb_url,
thumb_width=TestInlineQueryResultLocation.thumb_width,
thumb_height=TestInlineQueryResultLocation.thumb_height,
input_message_content=TestInlineQueryResultLocation.input_message_content,
reply_markup=TestInlineQueryResultLocation.reply_markup,
horizontal_accuracy=TestInlineQueryResultLocation.horizontal_accuracy,
heading=TestInlineQueryResultLocation.heading,
proximity_alert_radius=TestInlineQueryResultLocation.proximity_alert_radius,
TestInlineQueryResultLocationBase.id_,
TestInlineQueryResultLocationBase.latitude,
TestInlineQueryResultLocationBase.longitude,
TestInlineQueryResultLocationBase.title,
live_period=TestInlineQueryResultLocationBase.live_period,
thumb_url=TestInlineQueryResultLocationBase.thumb_url,
thumb_width=TestInlineQueryResultLocationBase.thumb_width,
thumb_height=TestInlineQueryResultLocationBase.thumb_height,
input_message_content=TestInlineQueryResultLocationBase.input_message_content,
reply_markup=TestInlineQueryResultLocationBase.reply_markup,
horizontal_accuracy=TestInlineQueryResultLocationBase.horizontal_accuracy,
heading=TestInlineQueryResultLocationBase.heading,
proximity_alert_radius=TestInlineQueryResultLocationBase.proximity_alert_radius,
)
class TestInlineQueryResultLocation:
class TestInlineQueryResultLocationBase:
id_ = "id"
type_ = "location"
latitude = 0.0
@ -62,6 +62,8 @@ class TestInlineQueryResultLocation:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultLocationWithoutRequest(TestInlineQueryResultLocationBase):
def test_slot_behaviour(self, inline_query_result_location, mro_slots):
inst = inline_query_result_location
for attr in inst.__slots__:

View file

@ -28,26 +28,26 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_mpeg4_gif():
return InlineQueryResultMpeg4Gif(
TestInlineQueryResultMpeg4Gif.id_,
TestInlineQueryResultMpeg4Gif.mpeg4_url,
TestInlineQueryResultMpeg4Gif.thumb_url,
mpeg4_width=TestInlineQueryResultMpeg4Gif.mpeg4_width,
mpeg4_height=TestInlineQueryResultMpeg4Gif.mpeg4_height,
mpeg4_duration=TestInlineQueryResultMpeg4Gif.mpeg4_duration,
title=TestInlineQueryResultMpeg4Gif.title,
caption=TestInlineQueryResultMpeg4Gif.caption,
parse_mode=TestInlineQueryResultMpeg4Gif.parse_mode,
caption_entities=TestInlineQueryResultMpeg4Gif.caption_entities,
input_message_content=TestInlineQueryResultMpeg4Gif.input_message_content,
reply_markup=TestInlineQueryResultMpeg4Gif.reply_markup,
thumb_mime_type=TestInlineQueryResultMpeg4Gif.thumb_mime_type,
TestInlineQueryResultMpeg4GifBase.id_,
TestInlineQueryResultMpeg4GifBase.mpeg4_url,
TestInlineQueryResultMpeg4GifBase.thumb_url,
mpeg4_width=TestInlineQueryResultMpeg4GifBase.mpeg4_width,
mpeg4_height=TestInlineQueryResultMpeg4GifBase.mpeg4_height,
mpeg4_duration=TestInlineQueryResultMpeg4GifBase.mpeg4_duration,
title=TestInlineQueryResultMpeg4GifBase.title,
caption=TestInlineQueryResultMpeg4GifBase.caption,
parse_mode=TestInlineQueryResultMpeg4GifBase.parse_mode,
caption_entities=TestInlineQueryResultMpeg4GifBase.caption_entities,
input_message_content=TestInlineQueryResultMpeg4GifBase.input_message_content,
reply_markup=TestInlineQueryResultMpeg4GifBase.reply_markup,
thumb_mime_type=TestInlineQueryResultMpeg4GifBase.thumb_mime_type,
)
class TestInlineQueryResultMpeg4Gif:
class TestInlineQueryResultMpeg4GifBase:
id_ = "id"
type_ = "mpeg4_gif"
mpeg4_url = "mpeg4 url"
@ -63,6 +63,8 @@ class TestInlineQueryResultMpeg4Gif:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultMpeg4GifWithoutRequest(TestInlineQueryResultMpeg4GifBase):
def test_slot_behaviour(self, inline_query_result_mpeg4_gif, mro_slots):
inst = inline_query_result_mpeg4_gif
for attr in inst.__slots__:

View file

@ -28,25 +28,25 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_photo():
return InlineQueryResultPhoto(
TestInlineQueryResultPhoto.id_,
TestInlineQueryResultPhoto.photo_url,
TestInlineQueryResultPhoto.thumb_url,
photo_width=TestInlineQueryResultPhoto.photo_width,
photo_height=TestInlineQueryResultPhoto.photo_height,
title=TestInlineQueryResultPhoto.title,
description=TestInlineQueryResultPhoto.description,
caption=TestInlineQueryResultPhoto.caption,
parse_mode=TestInlineQueryResultPhoto.parse_mode,
caption_entities=TestInlineQueryResultPhoto.caption_entities,
input_message_content=TestInlineQueryResultPhoto.input_message_content,
reply_markup=TestInlineQueryResultPhoto.reply_markup,
TestInlineQueryResultPhotoBase.id_,
TestInlineQueryResultPhotoBase.photo_url,
TestInlineQueryResultPhotoBase.thumb_url,
photo_width=TestInlineQueryResultPhotoBase.photo_width,
photo_height=TestInlineQueryResultPhotoBase.photo_height,
title=TestInlineQueryResultPhotoBase.title,
description=TestInlineQueryResultPhotoBase.description,
caption=TestInlineQueryResultPhotoBase.caption,
parse_mode=TestInlineQueryResultPhotoBase.parse_mode,
caption_entities=TestInlineQueryResultPhotoBase.caption_entities,
input_message_content=TestInlineQueryResultPhotoBase.input_message_content,
reply_markup=TestInlineQueryResultPhotoBase.reply_markup,
)
class TestInlineQueryResultPhoto:
class TestInlineQueryResultPhotoBase:
id_ = "id"
type_ = "photo"
photo_url = "photo url"
@ -62,6 +62,8 @@ class TestInlineQueryResultPhoto:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultPhotoWithoutRequest(TestInlineQueryResultPhotoBase):
def test_slot_behaviour(self, inline_query_result_photo, mro_slots):
inst = inline_query_result_photo
for attr in inst.__slots__:

View file

@ -27,27 +27,27 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_venue():
return InlineQueryResultVenue(
TestInlineQueryResultVenue.id_,
TestInlineQueryResultVenue.latitude,
TestInlineQueryResultVenue.longitude,
TestInlineQueryResultVenue.title,
TestInlineQueryResultVenue.address,
foursquare_id=TestInlineQueryResultVenue.foursquare_id,
foursquare_type=TestInlineQueryResultVenue.foursquare_type,
thumb_url=TestInlineQueryResultVenue.thumb_url,
thumb_width=TestInlineQueryResultVenue.thumb_width,
thumb_height=TestInlineQueryResultVenue.thumb_height,
input_message_content=TestInlineQueryResultVenue.input_message_content,
reply_markup=TestInlineQueryResultVenue.reply_markup,
google_place_id=TestInlineQueryResultVenue.google_place_id,
google_place_type=TestInlineQueryResultVenue.google_place_type,
TestInlineQueryResultVenueBase.id_,
TestInlineQueryResultVenueBase.latitude,
TestInlineQueryResultVenueBase.longitude,
TestInlineQueryResultVenueBase.title,
TestInlineQueryResultVenueBase.address,
foursquare_id=TestInlineQueryResultVenueBase.foursquare_id,
foursquare_type=TestInlineQueryResultVenueBase.foursquare_type,
thumb_url=TestInlineQueryResultVenueBase.thumb_url,
thumb_width=TestInlineQueryResultVenueBase.thumb_width,
thumb_height=TestInlineQueryResultVenueBase.thumb_height,
input_message_content=TestInlineQueryResultVenueBase.input_message_content,
reply_markup=TestInlineQueryResultVenueBase.reply_markup,
google_place_id=TestInlineQueryResultVenueBase.google_place_id,
google_place_type=TestInlineQueryResultVenueBase.google_place_type,
)
class TestInlineQueryResultVenue:
class TestInlineQueryResultVenueBase:
id_ = "id"
type_ = "venue"
latitude = "latitude"
@ -64,6 +64,8 @@ class TestInlineQueryResultVenue:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVenueWithoutRequest(TestInlineQueryResultVenueBase):
def test_slot_behaviour(self, inline_query_result_venue, mro_slots):
inst = inline_query_result_venue
for attr in inst.__slots__:

View file

@ -28,27 +28,27 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_video():
return InlineQueryResultVideo(
TestInlineQueryResultVideo.id_,
TestInlineQueryResultVideo.video_url,
TestInlineQueryResultVideo.mime_type,
TestInlineQueryResultVideo.thumb_url,
TestInlineQueryResultVideo.title,
video_width=TestInlineQueryResultVideo.video_width,
video_height=TestInlineQueryResultVideo.video_height,
video_duration=TestInlineQueryResultVideo.video_duration,
caption=TestInlineQueryResultVideo.caption,
parse_mode=TestInlineQueryResultVideo.parse_mode,
caption_entities=TestInlineQueryResultVideo.caption_entities,
description=TestInlineQueryResultVideo.description,
input_message_content=TestInlineQueryResultVideo.input_message_content,
reply_markup=TestInlineQueryResultVideo.reply_markup,
TestInlineQueryResultVideoBase.id_,
TestInlineQueryResultVideoBase.video_url,
TestInlineQueryResultVideoBase.mime_type,
TestInlineQueryResultVideoBase.thumb_url,
TestInlineQueryResultVideoBase.title,
video_width=TestInlineQueryResultVideoBase.video_width,
video_height=TestInlineQueryResultVideoBase.video_height,
video_duration=TestInlineQueryResultVideoBase.video_duration,
caption=TestInlineQueryResultVideoBase.caption,
parse_mode=TestInlineQueryResultVideoBase.parse_mode,
caption_entities=TestInlineQueryResultVideoBase.caption_entities,
description=TestInlineQueryResultVideoBase.description,
input_message_content=TestInlineQueryResultVideoBase.input_message_content,
reply_markup=TestInlineQueryResultVideoBase.reply_markup,
)
class TestInlineQueryResultVideo:
class TestInlineQueryResultVideoBase:
id_ = "id"
type_ = "video"
video_url = "video url"
@ -65,6 +65,8 @@ class TestInlineQueryResultVideo:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVideoWithoutRequest(TestInlineQueryResultVideoBase):
def test_slot_behaviour(self, inline_query_result_video, mro_slots):
inst = inline_query_result_video
for attr in inst.__slots__:

View file

@ -28,22 +28,22 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def inline_query_result_voice():
return InlineQueryResultVoice(
id=TestInlineQueryResultVoice.id_,
voice_url=TestInlineQueryResultVoice.voice_url,
title=TestInlineQueryResultVoice.title,
voice_duration=TestInlineQueryResultVoice.voice_duration,
caption=TestInlineQueryResultVoice.caption,
parse_mode=TestInlineQueryResultVoice.parse_mode,
caption_entities=TestInlineQueryResultVoice.caption_entities,
input_message_content=TestInlineQueryResultVoice.input_message_content,
reply_markup=TestInlineQueryResultVoice.reply_markup,
id=TestInlineQueryResultVoiceBase.id_,
voice_url=TestInlineQueryResultVoiceBase.voice_url,
title=TestInlineQueryResultVoiceBase.title,
voice_duration=TestInlineQueryResultVoiceBase.voice_duration,
caption=TestInlineQueryResultVoiceBase.caption,
parse_mode=TestInlineQueryResultVoiceBase.parse_mode,
caption_entities=TestInlineQueryResultVoiceBase.caption_entities,
input_message_content=TestInlineQueryResultVoiceBase.input_message_content,
reply_markup=TestInlineQueryResultVoiceBase.reply_markup,
)
class TestInlineQueryResultVoice:
class TestInlineQueryResultVoiceBase:
id_ = "id"
type_ = "voice"
voice_url = "voice url"
@ -55,6 +55,8 @@ class TestInlineQueryResultVoice:
input_message_content = InputTextMessageContent("input_message_content")
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
class TestInlineQueryResultVoiceWithoutRequest(TestInlineQueryResultVoiceBase):
def test_slot_behaviour(self, inline_query_result_voice, mro_slots):
inst = inline_query_result_voice
for attr in inst.__slots__:

View file

@ -21,20 +21,22 @@ import pytest
from telegram import InputContactMessageContent, User
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_contact_message_content():
return InputContactMessageContent(
TestInputContactMessageContent.phone_number,
TestInputContactMessageContent.first_name,
last_name=TestInputContactMessageContent.last_name,
TestInputContactMessageContentBase.phone_number,
TestInputContactMessageContentBase.first_name,
TestInputContactMessageContentBase.last_name,
)
class TestInputContactMessageContent:
class TestInputContactMessageContentBase:
phone_number = "phone number"
first_name = "first name"
last_name = "last name"
class TestInputContactMessageContentWithoutRequest(TestInputContactMessageContentBase):
def test_slot_behaviour(self, input_contact_message_content, mro_slots):
inst = input_contact_message_content
for attr in inst.__slots__:

View file

@ -26,12 +26,12 @@ from telegram import InputFile
from tests.conftest import data_file
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def png_file():
return data_file("game.png")
class TestInputFile:
class TestInputFileWithoutRequest:
def test_slot_behaviour(self, mro_slots):
inst = InputFile(BytesIO(b"blah"), filename="tg.jpg")
for attr in inst.__slots__:
@ -65,7 +65,7 @@ class TestInputFile:
assert input_file.attach_name is None
assert input_file.attach_uri is None
def test_mimetypes(self, caplog):
def test_mimetypes(self):
# Only test a few to make sure logic works okay
assert InputFile(data_file("telegram.jpg").open("rb")).mimetype == "image/jpeg"
# For some reason python can guess the type on macOS
@ -139,6 +139,8 @@ class TestInputFile:
== "blah.jpg"
)
class TestInputFileWithRequest:
async def test_send_bytes(self, bot, chat_id):
# We test this here and not at the respective test modules because it's not worth
# duplicating the test for the different methods

View file

@ -22,33 +22,33 @@ import pytest
from telegram import InputInvoiceMessageContent, InputTextMessageContent, LabeledPrice
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_invoice_message_content():
return InputInvoiceMessageContent(
title=TestInputInvoiceMessageContent.title,
description=TestInputInvoiceMessageContent.description,
payload=TestInputInvoiceMessageContent.payload,
provider_token=TestInputInvoiceMessageContent.provider_token,
currency=TestInputInvoiceMessageContent.currency,
prices=TestInputInvoiceMessageContent.prices,
max_tip_amount=TestInputInvoiceMessageContent.max_tip_amount,
suggested_tip_amounts=TestInputInvoiceMessageContent.suggested_tip_amounts,
provider_data=TestInputInvoiceMessageContent.provider_data,
photo_url=TestInputInvoiceMessageContent.photo_url,
photo_size=TestInputInvoiceMessageContent.photo_size,
photo_width=TestInputInvoiceMessageContent.photo_width,
photo_height=TestInputInvoiceMessageContent.photo_height,
need_name=TestInputInvoiceMessageContent.need_name,
need_phone_number=TestInputInvoiceMessageContent.need_phone_number,
need_email=TestInputInvoiceMessageContent.need_email,
need_shipping_address=TestInputInvoiceMessageContent.need_shipping_address,
send_phone_number_to_provider=TestInputInvoiceMessageContent.send_phone_number_to_provider,
send_email_to_provider=TestInputInvoiceMessageContent.send_email_to_provider,
is_flexible=TestInputInvoiceMessageContent.is_flexible,
title=TestInputInvoiceMessageContentBase.title,
description=TestInputInvoiceMessageContentBase.description,
payload=TestInputInvoiceMessageContentBase.payload,
provider_token=TestInputInvoiceMessageContentBase.provider_token,
currency=TestInputInvoiceMessageContentBase.currency,
prices=TestInputInvoiceMessageContentBase.prices,
max_tip_amount=TestInputInvoiceMessageContentBase.max_tip_amount,
suggested_tip_amounts=TestInputInvoiceMessageContentBase.suggested_tip_amounts,
provider_data=TestInputInvoiceMessageContentBase.provider_data,
photo_url=TestInputInvoiceMessageContentBase.photo_url,
photo_size=TestInputInvoiceMessageContentBase.photo_size,
photo_width=TestInputInvoiceMessageContentBase.photo_width,
photo_height=TestInputInvoiceMessageContentBase.photo_height,
need_name=TestInputInvoiceMessageContentBase.need_name,
need_phone_number=TestInputInvoiceMessageContentBase.need_phone_number,
need_email=TestInputInvoiceMessageContentBase.need_email,
need_shipping_address=TestInputInvoiceMessageContentBase.need_shipping_address,
send_phone_number_to_provider=TestInputInvoiceMessageContentBase.send_phone_number_to_provider, # noqa: E501
send_email_to_provider=TestInputInvoiceMessageContentBase.send_email_to_provider,
is_flexible=TestInputInvoiceMessageContentBase.is_flexible,
)
class TestInputInvoiceMessageContent:
class TestInputInvoiceMessageContentBase:
title = "invoice title"
description = "invoice description"
payload = "invoice payload"
@ -70,6 +70,8 @@ class TestInputInvoiceMessageContent:
send_email_to_provider = True
is_flexible = True
class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageContentBase):
def test_slot_behaviour(self, input_invoice_message_content, mro_slots):
inst = input_invoice_message_content
for attr in inst.__slots__:
@ -103,29 +105,7 @@ class TestInputInvoiceMessageContent:
assert input_invoice_message_content.send_email_to_provider == self.send_email_to_provider
assert input_invoice_message_content.is_flexible == self.is_flexible
def test_suggested_tip_amonuts_always_tuple(self):
input_invoice_message_content = InputInvoiceMessageContent(
title=self.title,
description=self.description,
payload=self.payload,
provider_token=self.provider_token,
currency=self.currency,
prices=self.prices,
max_tip_amount=self.max_tip_amount,
suggested_tip_amounts=self.suggested_tip_amounts,
provider_data=self.provider_data,
photo_url=self.photo_url,
photo_size=self.photo_size,
photo_width=self.photo_width,
photo_height=self.photo_height,
need_name=self.need_name,
need_phone_number=self.need_phone_number,
need_email=self.need_email,
need_shipping_address=self.need_shipping_address,
send_phone_number_to_provider=self.send_phone_number_to_provider,
send_email_to_provider=self.send_email_to_provider,
is_flexible=self.is_flexible,
)
def test_suggested_tip_amonuts_always_tuple(self, input_invoice_message_content):
assert isinstance(input_invoice_message_content.suggested_tip_amounts, tuple)
assert input_invoice_message_content.suggested_tip_amounts == tuple(
int(amount) for amount in self.suggested_tip_amounts

View file

@ -21,19 +21,19 @@ import pytest
from telegram import InputLocationMessageContent, Location
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_location_message_content():
return InputLocationMessageContent(
TestInputLocationMessageContent.latitude,
TestInputLocationMessageContent.longitude,
live_period=TestInputLocationMessageContent.live_period,
horizontal_accuracy=TestInputLocationMessageContent.horizontal_accuracy,
heading=TestInputLocationMessageContent.heading,
proximity_alert_radius=TestInputLocationMessageContent.proximity_alert_radius,
TestInputLocationMessageContentBase.latitude,
TestInputLocationMessageContentBase.longitude,
live_period=TestInputLocationMessageContentBase.live_period,
horizontal_accuracy=TestInputLocationMessageContentBase.horizontal_accuracy,
heading=TestInputLocationMessageContentBase.heading,
proximity_alert_radius=TestInputLocationMessageContentBase.proximity_alert_radius,
)
class TestInputLocationMessageContent:
class TestInputLocationMessageContentBase:
latitude = -23.691288
longitude = -46.788279
live_period = 80
@ -41,6 +41,8 @@ class TestInputLocationMessageContent:
heading = 90
proximity_alert_radius = 999
class TestInputLocationMessageContentWithoutRequest(TestInputLocationMessageContentBase):
def test_slot_behaviour(self, input_location_message_content, mro_slots):
inst = input_location_message_content
for attr in inst.__slots__:

View file

@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import copy
from collections.abc import Sequence
@ -56,86 +57,75 @@ from .test_photo import _photo, photo, photo_file, thumb # noqa: F401
from .test_video import video, video_file # noqa: F401
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_media_video(class_thumb_file):
return InputMediaVideo(
media=TestInputMediaVideo.media,
caption=TestInputMediaVideo.caption,
width=TestInputMediaVideo.width,
height=TestInputMediaVideo.height,
duration=TestInputMediaVideo.duration,
parse_mode=TestInputMediaVideo.parse_mode,
caption_entities=TestInputMediaVideo.caption_entities,
media=TestInputMediaVideoBase.media,
caption=TestInputMediaVideoBase.caption,
width=TestInputMediaVideoBase.width,
height=TestInputMediaVideoBase.height,
duration=TestInputMediaVideoBase.duration,
parse_mode=TestInputMediaVideoBase.parse_mode,
caption_entities=TestInputMediaVideoBase.caption_entities,
thumb=class_thumb_file,
supports_streaming=TestInputMediaVideo.supports_streaming,
has_spoiler=TestInputMediaVideo.has_spoiler,
supports_streaming=TestInputMediaVideoBase.supports_streaming,
has_spoiler=TestInputMediaVideoBase.has_spoiler,
)
@pytest.fixture(scope="class")
def input_media_photo(class_thumb_file):
@pytest.fixture(scope="module")
def input_media_photo():
return InputMediaPhoto(
media=TestInputMediaPhoto.media,
caption=TestInputMediaPhoto.caption,
parse_mode=TestInputMediaPhoto.parse_mode,
caption_entities=TestInputMediaPhoto.caption_entities,
has_spoiler=TestInputMediaPhoto.has_spoiler,
media=TestInputMediaPhotoBase.media,
caption=TestInputMediaPhotoBase.caption,
parse_mode=TestInputMediaPhotoBase.parse_mode,
caption_entities=TestInputMediaPhotoBase.caption_entities,
has_spoiler=TestInputMediaPhotoBase.has_spoiler,
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_media_animation(class_thumb_file):
return InputMediaAnimation(
media=TestInputMediaAnimation.media,
caption=TestInputMediaAnimation.caption,
parse_mode=TestInputMediaAnimation.parse_mode,
caption_entities=TestInputMediaAnimation.caption_entities,
width=TestInputMediaAnimation.width,
height=TestInputMediaAnimation.height,
media=TestInputMediaAnimationBase.media,
caption=TestInputMediaAnimationBase.caption,
parse_mode=TestInputMediaAnimationBase.parse_mode,
caption_entities=TestInputMediaAnimationBase.caption_entities,
width=TestInputMediaAnimationBase.width,
height=TestInputMediaAnimationBase.height,
thumb=class_thumb_file,
duration=TestInputMediaAnimation.duration,
has_spoiler=TestInputMediaAnimation.has_spoiler,
duration=TestInputMediaAnimationBase.duration,
has_spoiler=TestInputMediaAnimationBase.has_spoiler,
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_media_audio(class_thumb_file):
return InputMediaAudio(
media=TestInputMediaAudio.media,
caption=TestInputMediaAudio.caption,
duration=TestInputMediaAudio.duration,
performer=TestInputMediaAudio.performer,
title=TestInputMediaAudio.title,
media=TestInputMediaAudioBase.media,
caption=TestInputMediaAudioBase.caption,
duration=TestInputMediaAudioBase.duration,
performer=TestInputMediaAudioBase.performer,
title=TestInputMediaAudioBase.title,
thumb=class_thumb_file,
parse_mode=TestInputMediaAudio.parse_mode,
caption_entities=TestInputMediaAudio.caption_entities,
parse_mode=TestInputMediaAudioBase.parse_mode,
caption_entities=TestInputMediaAudioBase.caption_entities,
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_media_document(class_thumb_file):
return InputMediaDocument(
media=TestInputMediaDocument.media,
caption=TestInputMediaDocument.caption,
media=TestInputMediaDocumentBase.media,
caption=TestInputMediaDocumentBase.caption,
thumb=class_thumb_file,
parse_mode=TestInputMediaDocument.parse_mode,
caption_entities=TestInputMediaDocument.caption_entities,
disable_content_type_detection=TestInputMediaDocument.disable_content_type_detection,
parse_mode=TestInputMediaDocumentBase.parse_mode,
caption_entities=TestInputMediaDocumentBase.caption_entities,
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection,
)
class CustomSequence(Sequence):
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
def __len__(self):
return len(self.items)
class TestInputMediaVideo:
class TestInputMediaVideoBase:
type_ = "video"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -147,6 +137,8 @@ class TestInputMediaVideo:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
has_spoiler = True
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
def test_slot_behaviour(self, input_media_video, mro_slots):
inst = input_media_video
for attr in inst.__slots__:
@ -210,7 +202,7 @@ class TestInputMediaVideo:
assert input_media_video.thumb == data_file("telegram.jpg").as_uri()
class TestInputMediaPhoto:
class TestInputMediaPhotoBase:
type_ = "photo"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -218,6 +210,8 @@ class TestInputMediaPhoto:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
has_spoiler = True
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
def test_slot_behaviour(self, input_media_photo, mro_slots):
inst = input_media_photo
for attr in inst.__slots__:
@ -266,7 +260,7 @@ class TestInputMediaPhoto:
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
class TestInputMediaAnimation:
class TestInputMediaAnimationBase:
type_ = "animation"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -277,6 +271,8 @@ class TestInputMediaAnimation:
duration = 1
has_spoiler = True
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
def test_slot_behaviour(self, input_media_animation, mro_slots):
inst = input_media_animation
for attr in inst.__slots__:
@ -332,7 +328,7 @@ class TestInputMediaAnimation:
assert input_media_animation.thumb == data_file("telegram.jpg").as_uri()
class TestInputMediaAudio:
class TestInputMediaAudioBase:
type_ = "audio"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -342,6 +338,8 @@ class TestInputMediaAudio:
parse_mode = "HTML"
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
def test_slot_behaviour(self, input_media_audio, mro_slots):
inst = input_media_audio
for attr in inst.__slots__:
@ -401,7 +399,7 @@ class TestInputMediaAudio:
assert input_media_audio.thumb == data_file("telegram.jpg").as_uri()
class TestInputMediaDocument:
class TestInputMediaDocumentBase:
type_ = "document"
media = "NOTAREALFILEID"
caption = "My Caption"
@ -409,6 +407,8 @@ class TestInputMediaDocument:
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
disable_content_type_detection = True
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
def test_slot_behaviour(self, input_media_document, mro_slots):
inst = input_media_document
for attr in inst.__slots__:
@ -467,7 +467,7 @@ class TestInputMediaDocument:
assert input_media_document.thumb == data_file("telegram.jpg").as_uri()
@pytest.fixture(scope="function") # noqa: F811
@pytest.fixture(scope="module") # noqa: F811
def media_group(photo, thumb): # noqa: F811
return [
InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"),
@ -478,12 +478,12 @@ def media_group(photo, thumb): # noqa: F811
]
@pytest.fixture(scope="function") # noqa: F811
@pytest.fixture(scope="module") # noqa: F811
def media_group_no_caption_args(photo, thumb): # noqa: F811
return [InputMediaPhoto(photo), InputMediaPhoto(thumb), InputMediaPhoto(photo)]
@pytest.fixture(scope="function") # noqa: F811
@pytest.fixture(scope="module") # noqa: F811
def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811
return [
InputMediaPhoto(photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]),
@ -491,7 +491,7 @@ def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811
]
@pytest.fixture(scope="function") # noqa: F811
@pytest.fixture(scope="module") # noqa: F811
def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
return [
InputMediaPhoto(photo, parse_mode="Markdown"),
@ -499,32 +499,7 @@ def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
]
class TestSendMediaGroup:
@pytest.mark.flaky(3, 1)
async def test_send_media_group_photo(self, bot, chat_id, media_group):
messages = await bot.send_media_group(chat_id, media_group)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages))
assert all(
mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages
)
async def test_send_media_group_with_message_thread_id(
self, bot, real_topic, forum_group_id, media_group # noqa: F811
):
messages = await bot.send_media_group(
forum_group_id,
media_group,
message_thread_id=real_topic.message_thread_id,
)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(i.message_thread_id == real_topic.message_thread_id for i in messages)
class TestSendMediaGroupWithoutRequest:
async def test_send_media_group_throws_error_with_group_caption_and_individual_captions(
self,
bot,
@ -544,6 +519,143 @@ class TestSendMediaGroup:
):
await bot.send_media_group(chat_id, group, caption="foo")
async def test_send_media_group_custom_filename(
self,
bot,
chat_id,
photo_file, # noqa: F811
animation_file, # noqa: F811
audio_file, # noqa: F811
video_file, # noqa: F811
monkeypatch,
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
result = all(
field_tuple[0] == "custom_filename"
for field_tuple in request_data.multipart_data.values()
)
if result is True:
raise Exception("Test was successful")
monkeypatch.setattr(bot.request, "post", make_assertion)
media = [
InputMediaAnimation(animation_file, filename="custom_filename"),
InputMediaAudio(audio_file, filename="custom_filename"),
InputMediaPhoto(photo_file, filename="custom_filename"),
InputMediaVideo(video_file, filename="custom_filename"),
]
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, media)
async def test_send_media_group_with_thumbs(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(method, url, request_data: RequestData, *args, **kwargs):
nonlocal input_video
files = request_data.multipart_data
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumb=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, [input_video, input_video])
async def test_edit_message_media_with_thumb(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(
method: str, url: str, request_data: RequestData = None, *args, **kwargs
):
files = request_data.multipart_data
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumb=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video)
class CustomSequence(Sequence):
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
def __len__(self):
return len(self.items)
class TestSendMediaGroupWithRequest:
async def test_send_media_group_photo(self, bot, chat_id, media_group):
messages = await bot.send_media_group(chat_id, media_group)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
assert all(mes.caption == f"photo {idx+1}" for idx, mes in enumerate(messages))
assert all(
mes.caption_entities == (MessageEntity(MessageEntity.BOLD, 0, 5),) for mes in messages
)
async def test_send_media_group_new_files(
self, bot, chat_id, video_file, photo_file # noqa: F811
):
async def func():
return await bot.send_media_group(
chat_id,
[
InputMediaVideo(video_file),
InputMediaPhoto(photo_file),
InputMediaPhoto(data_file("telegram.jpg").read_bytes()),
],
)
messages = await expect_bad_request(
func, "Type of file mismatch", "Telegram did not accept the file."
)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
@pytest.mark.parametrize("sequence_type", [list, tuple, CustomSequence])
@pytest.mark.parametrize("bot_class", ["raw_bot", "ext_bot"])
async def test_send_media_group_different_sequences(
self, bot, chat_id, media_group, sequence_type, bot_class, raw_bot
):
"""Test that send_media_group accepts different sequence types. This test ensures that
Bot._insert_defaults works for arbitrary sequence types."""
bot = bot if bot_class == "ext_bot" else raw_bot
messages = await bot.send_media_group(chat_id, sequence_type(media_group))
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
async def test_send_media_group_with_message_thread_id(
self, bot, real_topic, forum_group_id, media_group # noqa: F811
):
messages = await bot.send_media_group(
forum_group_id,
media_group,
message_thread_id=real_topic.message_thread_id,
)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(i.message_thread_id == real_topic.message_thread_id for i in messages)
@pytest.mark.parametrize(
"caption, parse_mode, caption_entities",
[
@ -553,7 +665,6 @@ class TestSendMediaGroup:
("photo 1", None, [MessageEntity(MessageEntity.BOLD, 0, 5)]),
],
)
@pytest.mark.flaky(3, 1)
async def test_send_media_group_with_group_caption(
self,
bot,
@ -598,16 +709,15 @@ class TestSendMediaGroup:
assert all(mes.caption is None for mes in other_messages)
assert not any(mes.caption_entities for mes in other_messages)
@pytest.mark.flaky(3, 1)
async def test_send_media_group_all_args(self, bot, raw_bot, chat_id, media_group):
ext_bot = bot
for bot in (ext_bot, raw_bot):
# We need to test 1) below both the bot and raw_bot and setting this up with
# pytest.parametrize appears to be difficult ...
m1 = await bot.send_message(chat_id, text="test")
# We need to test 1) below both the bot and raw_bot and setting this up with
# pytest.parametrize appears to be difficult ...
aws = {b.send_message(chat_id, text="test") for b in (ext_bot, raw_bot)}
for msg_task in asyncio.as_completed(aws):
m1 = await msg_task
copied_media_group = copy.copy(media_group)
messages = await bot.send_media_group(
messages = await m1.get_bot().send_media_group(
chat_id,
media_group,
disable_notification=True,
@ -633,7 +743,6 @@ class TestSendMediaGroup:
)
assert all(mes.has_protected_content for mes in messages)
@pytest.mark.flaky(3, 1)
async def test_send_media_group_with_spoiler(
self, bot, chat_id, photo_file, video_file # noqa: F811
):
@ -649,97 +758,36 @@ class TestSendMediaGroup:
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
assert all(mes.has_media_spoiler for mes in messages)
@pytest.mark.flaky(3, 1)
async def test_send_media_group_custom_filename(
self,
bot,
chat_id,
photo_file, # noqa: F811
animation_file, # noqa: F811
audio_file, # noqa: F811
video_file, # noqa: F811
monkeypatch,
):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
result = all(
field_tuple[0] == "custom_filename"
for field_tuple in request_data.multipart_data.values()
async def test_edit_message_media(self, bot, raw_bot, chat_id, media_group):
ext_bot = bot
# We need to test 1) below both the bot and raw_bot and setting this up with
# pytest.parametrize appears to be difficult ...
aws = {b.send_media_group(chat_id, media_group) for b in (ext_bot, raw_bot)}
for msg_task in asyncio.as_completed(aws):
messages = await msg_task
cid = messages[-1].chat.id
mid = messages[-1].message_id
copied_media = copy.copy(media_group[0])
new_message = (
await messages[-1]
.get_bot()
.edit_message_media(chat_id=cid, message_id=mid, media=media_group[0])
)
if result is True:
raise Exception("Test was successful")
assert isinstance(new_message, Message)
monkeypatch.setattr(bot.request, "post", make_assertion)
# 1)
# make sure that the media was not modified
assert media_group[0].parse_mode == copied_media.parse_mode
media = [
InputMediaAnimation(animation_file, filename="custom_filename"),
InputMediaAudio(audio_file, filename="custom_filename"),
InputMediaPhoto(photo_file, filename="custom_filename"),
InputMediaVideo(video_file, filename="custom_filename"),
]
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, media)
async def test_send_media_group_with_thumbs(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(method, url, request_data: RequestData, *args, **kwargs):
files = request_data.multipart_data
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumb=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.send_media_group(chat_id, [input_video, input_video])
@pytest.mark.flaky(3, 1) # noqa: F811
async def test_send_media_group_new_files(
self,
bot,
chat_id,
video_file, # noqa: F811
photo_file, # noqa: F811
animation_file, # noqa: F811
):
async def func():
return await bot.send_media_group(
chat_id,
[
InputMediaVideo(video_file),
InputMediaPhoto(photo_file),
InputMediaPhoto(data_file("telegram.jpg").read_bytes()),
],
)
messages = await expect_bad_request(
func, "Type of file mismatch", "Telegram did not accept the file."
async def test_edit_message_media_new_file(self, bot, chat_id, media_group, thumb_file):
messages = await bot.send_media_group(chat_id, media_group)
cid = messages[-1].chat.id
mid = messages[-1].message_id
new_message = await bot.edit_message_media(
chat_id=cid, message_id=mid, media=InputMediaPhoto(thumb_file)
)
assert isinstance(new_message, Message)
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("sequence_type", [list, tuple, CustomSequence])
@pytest.mark.parametrize("bot_class", ["raw_bot", "ext_bot"])
async def test_send_media_group_different_sequences(
self, bot, chat_id, media_group, sequence_type, bot_class, raw_bot
):
"""Test that send_media_group accepts different sequence types. This test ensures that
Bot._insert_defaults works for arbitrary sequence types."""
bot = bot if bot_class == "ext_bot" else raw_bot
messages = await bot.send_media_group(chat_id, sequence_type(media_group))
assert isinstance(messages, tuple)
assert len(messages) == 3
assert all(isinstance(mes, Message) for mes in messages)
assert all(mes.media_group_id == messages[0].media_group_id for mes in messages)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot,custom",
[
@ -773,19 +821,18 @@ class TestSendMediaGroup:
chat_id, media_group, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_media_group_default_protect_content(
self, chat_id, media_group, default_bot
):
protected = await default_bot.send_media_group(chat_id, media_group)
assert all(msg.has_protected_content for msg in protected)
unprotected = await default_bot.send_media_group(
chat_id, media_group, protect_content=False
tasks = asyncio.gather(
default_bot.send_media_group(chat_id, media_group),
default_bot.send_media_group(chat_id, media_group, protect_content=False),
)
protected, unprotected = await tasks
assert all(msg.has_protected_content for msg in protected)
assert not all(msg.has_protected_content for msg in unprotected)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True)
async def test_send_media_group_default_parse_mode(
self, chat_id, media_group_no_caption_args, default_bot
@ -797,19 +844,21 @@ class TestSendMediaGroup:
# make sure no parse_mode was set as a side effect
assert not any(item.parse_mode for item in media_group_no_caption_args)
overridden_markdown_v2 = await default_bot.send_media_group(
chat_id,
media_group_no_caption_args.copy(),
caption="*photo* 1",
parse_mode=ParseMode.MARKDOWN_V2,
)
overridden_none = await default_bot.send_media_group(
chat_id,
media_group_no_caption_args.copy(),
caption="<b>photo</b> 1",
parse_mode=None,
tasks = asyncio.gather(
default_bot.send_media_group(
chat_id,
media_group_no_caption_args.copy(),
caption="*photo* 1",
parse_mode=ParseMode.MARKDOWN_V2,
),
default_bot.send_media_group(
chat_id,
media_group_no_caption_args.copy(),
caption="<b>photo</b> 1",
parse_mode=None,
),
)
overridden_markdown_v2, overridden_none = await tasks
# Make sure first message got the caption, which will lead to Telegram
# displaying its caption as group caption
@ -830,53 +879,6 @@ class TestSendMediaGroup:
assert all(mes.caption is None for mes in other_messages)
assert not any(mes.caption_entities for mes in other_messages)
@pytest.mark.flaky(3, 1)
async def test_edit_message_media(self, bot, raw_bot, chat_id, media_group):
ext_bot = bot
for bot in (ext_bot, raw_bot):
# We need to test 1) below both the bot and raw_bot and setting this up with
# pytest.parametrize appears to be difficult ...
messages = await bot.send_media_group(chat_id, media_group)
cid = messages[-1].chat.id
mid = messages[-1].message_id
copied_media = copy.copy(media_group[0])
new_message = await bot.edit_message_media(
chat_id=cid, message_id=mid, media=media_group[0]
)
assert isinstance(new_message, Message)
# 1)
# make sure that the media was not modified
assert media_group[0].parse_mode == copied_media.parse_mode
@pytest.mark.flaky(3, 1)
async def test_edit_message_media_new_file(self, bot, chat_id, media_group, thumb_file):
messages = await bot.send_media_group(chat_id, media_group)
cid = messages[-1].chat.id
mid = messages[-1].message_id
new_message = await bot.edit_message_media(
chat_id=cid, message_id=mid, media=InputMediaPhoto(thumb_file)
)
assert isinstance(new_message, Message)
async def test_edit_message_media_with_thumb(
self, bot, chat_id, video_file, photo_file, monkeypatch # noqa: F811
):
async def make_assertion(
method: str, url: str, request_data: RequestData = None, *args, **kwargs
):
files = request_data.multipart_data
video_check = files[input_video.media.attach_name] == input_video.media.field_tuple
thumb_check = files[input_video.thumb.attach_name] == input_video.thumb.field_tuple
result = video_check and thumb_check
raise Exception(f"Test was {'successful' if result else 'failing'}")
monkeypatch.setattr(bot.request, "_request_wrapper", make_assertion)
input_video = InputMediaVideo(video_file, thumb=photo_file)
with pytest.raises(Exception, match="Test was successful"):
await bot.edit_message_media(chat_id=chat_id, message_id=123, media=input_video)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True, ids=["HTML-Bot"]
)

View file

@ -22,22 +22,24 @@ from telegram import InputTextMessageContent, MessageEntity
from telegram.constants import ParseMode
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_text_message_content():
return InputTextMessageContent(
TestInputTextMessageContent.message_text,
parse_mode=TestInputTextMessageContent.parse_mode,
entities=TestInputTextMessageContent.entities,
disable_web_page_preview=TestInputTextMessageContent.disable_web_page_preview,
TestInputTextMessageContentBase.message_text,
parse_mode=TestInputTextMessageContentBase.parse_mode,
entities=TestInputTextMessageContentBase.entities,
disable_web_page_preview=TestInputTextMessageContentBase.disable_web_page_preview,
)
class TestInputTextMessageContent:
class TestInputTextMessageContentBase:
message_text = "*message text*"
parse_mode = ParseMode.MARKDOWN
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
disable_web_page_preview = True
class TestInputTextMessageContentWithoutRequest(TestInputTextMessageContentBase):
def test_slot_behaviour(self, input_text_message_content, mro_slots):
inst = input_text_message_content
for attr in inst.__slots__:

View file

@ -21,21 +21,21 @@ import pytest
from telegram import InputVenueMessageContent, Location
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def input_venue_message_content():
return InputVenueMessageContent(
TestInputVenueMessageContent.latitude,
TestInputVenueMessageContent.longitude,
TestInputVenueMessageContent.title,
TestInputVenueMessageContent.address,
foursquare_id=TestInputVenueMessageContent.foursquare_id,
foursquare_type=TestInputVenueMessageContent.foursquare_type,
google_place_id=TestInputVenueMessageContent.google_place_id,
google_place_type=TestInputVenueMessageContent.google_place_type,
TestInputVenueMessageContentBase.latitude,
TestInputVenueMessageContentBase.longitude,
TestInputVenueMessageContentBase.title,
TestInputVenueMessageContentBase.address,
foursquare_id=TestInputVenueMessageContentBase.foursquare_id,
foursquare_type=TestInputVenueMessageContentBase.foursquare_type,
google_place_id=TestInputVenueMessageContentBase.google_place_id,
google_place_type=TestInputVenueMessageContentBase.google_place_type,
)
class TestInputVenueMessageContent:
class TestInputVenueMessageContentBase:
latitude = 1.0
longitude = 2.0
title = "title"
@ -45,6 +45,8 @@ class TestInputVenueMessageContent:
google_place_id = "google place id"
google_place_type = "google place type"
class TestInputVenueMessageContentWithoutRequest(TestInputVenueMessageContentBase):
def test_slot_behaviour(self, input_venue_message_content, mro_slots):
inst = input_venue_message_content
for attr in inst.__slots__:

View file

@ -16,6 +16,8 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import pytest
from telegram import Invoice, LabeledPrice
@ -23,18 +25,18 @@ from telegram.error import BadRequest
from telegram.request import RequestData
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def invoice():
return Invoice(
TestInvoice.title,
TestInvoice.description,
TestInvoice.start_parameter,
TestInvoice.currency,
TestInvoice.total_amount,
TestInvoiceBase.title,
TestInvoiceBase.description,
TestInvoiceBase.start_parameter,
TestInvoiceBase.currency,
TestInvoiceBase.total_amount,
)
class TestInvoice:
class TestInvoiceBase:
payload = "payload"
prices = [LabeledPrice("Fish", 100), LabeledPrice("Fish Tax", 1000)]
provider_data = """{"test":"test"}"""
@ -46,6 +48,8 @@ class TestInvoice:
max_tip_amount = 42
suggested_tip_amounts = [13, 42]
class TestInvoiceWithoutRequest(TestInvoiceBase):
def test_slot_behaviour(self, invoice, mro_slots):
for attr in invoice.__slots__:
assert getattr(invoice, attr, "err") != "err", f"got extra slot '{attr}'"
@ -54,11 +58,11 @@ class TestInvoice:
def test_de_json(self, bot):
invoice_json = Invoice.de_json(
{
"title": TestInvoice.title,
"description": TestInvoice.description,
"start_parameter": TestInvoice.start_parameter,
"currency": TestInvoice.currency,
"total_amount": TestInvoice.total_amount,
"title": self.title,
"description": self.description,
"start_parameter": self.start_parameter,
"currency": self.currency,
"total_amount": self.total_amount,
},
bot,
)
@ -80,100 +84,12 @@ class TestInvoice:
assert invoice_dict["currency"] == invoice.currency
assert invoice_dict["total_amount"] == invoice.total_amount
@pytest.mark.flaky(3, 1)
async def test_send_required_args_only(self, bot, chat_id, provider_token):
message = await bot.send_invoice(
chat_id=chat_id,
title=self.title,
description=self.description,
payload=self.payload,
provider_token=provider_token,
currency=self.currency,
prices=self.prices,
)
assert message.invoice.currency == self.currency
assert message.invoice.start_parameter == ""
assert message.invoice.description == self.description
assert message.invoice.title == self.title
assert message.invoice.total_amount == self.total_amount
link = await bot.create_invoice_link(
title=self.title,
description=self.description,
payload=self.payload,
provider_token=provider_token,
currency=self.currency,
prices=self.prices,
)
assert isinstance(link, str)
assert link != ""
async def test_send_all_args_send_invoice(self, bot, chat_id, provider_token, monkeypatch):
message = await bot.send_invoice(
chat_id,
self.title,
self.description,
self.payload,
provider_token,
self.currency,
self.prices,
max_tip_amount=self.max_tip_amount,
suggested_tip_amounts=self.suggested_tip_amounts,
start_parameter=self.start_parameter,
provider_data=self.provider_data,
photo_url="https://raw.githubusercontent.com/"
"python-telegram-bot/logos/master/"
"logo/png/ptb-logo_240.png",
photo_size=240,
photo_width=240,
photo_height=240,
need_name=True,
need_phone_number=True,
need_email=True,
need_shipping_address=True,
send_phone_number_to_provider=True,
send_email_to_provider=True,
is_flexible=True,
disable_notification=True,
protect_content=True,
)
assert message.invoice.currency == self.currency
assert message.invoice.start_parameter == self.start_parameter
assert message.invoice.description == self.description
assert message.invoice.title == self.title
assert message.invoice.total_amount == self.total_amount
assert message.has_protected_content
# We do this next one as safety guard to make sure that we pass all of the optional
async def test_send_invoice_all_args_mock(self, bot, monkeypatch):
# We do this one as safety guard to make sure that we pass all of the optional
# parameters correctly because #2526 went unnoticed for 3 years …
async def make_assertion(*args, **_):
kwargs = args[1]
return (
kwargs["chat_id"] == "chat_id"
and kwargs["title"] == "title"
and kwargs["description"] == "description"
and kwargs["payload"] == "payload"
and kwargs["provider_token"] == "provider_token"
and kwargs["currency"] == "currency"
and kwargs["prices"] == self.prices
and kwargs["max_tip_amount"] == "max_tip_amount"
and kwargs["suggested_tip_amounts"] == "suggested_tip_amounts"
and kwargs["start_parameter"] == "start_parameter"
and kwargs["provider_data"] == "provider_data"
and kwargs["photo_url"] == "photo_url"
and kwargs["photo_size"] == "photo_size"
and kwargs["photo_width"] == "photo_width"
and kwargs["photo_height"] == "photo_height"
and kwargs["need_name"] == "need_name"
and kwargs["need_phone_number"] == "need_phone_number"
and kwargs["need_email"] == "need_email"
and kwargs["need_shipping_address"] == "need_shipping_address"
and kwargs["send_phone_number_to_provider"] == "send_phone_number_to_provider"
and kwargs["send_email_to_provider"] == "send_email_to_provider"
and kwargs["is_flexible"] == "is_flexible"
)
return all([kwargs[key] == key for key in kwargs])
monkeypatch.setattr(bot, "_send_message", make_assertion)
assert await bot.send_invoice(
@ -183,7 +99,7 @@ class TestInvoice:
payload="payload",
provider_token="provider_token",
currency="currency",
prices=self.prices,
prices="prices",
max_tip_amount="max_tip_amount",
suggested_tip_amounts="suggested_tip_amounts",
start_parameter="start_parameter",
@ -203,33 +119,10 @@ class TestInvoice:
protect_content=True,
)
async def test_send_all_args_create_invoice_link(
self, bot, chat_id, provider_token, monkeypatch
):
async def test_send_all_args_create_invoice_link(self, bot, monkeypatch):
async def make_assertion(*args, **_):
kwargs = args[1]
return (
kwargs["title"] == "title"
and kwargs["description"] == "description"
and kwargs["payload"] == "payload"
and kwargs["provider_token"] == "provider_token"
and kwargs["currency"] == "currency"
and kwargs["prices"] == self.prices
and kwargs["max_tip_amount"] == "max_tip_amount"
and kwargs["suggested_tip_amounts"] == "suggested_tip_amounts"
and kwargs["provider_data"] == "provider_data"
and kwargs["photo_url"] == "photo_url"
and kwargs["photo_size"] == "photo_size"
and kwargs["photo_width"] == "photo_width"
and kwargs["photo_height"] == "photo_height"
and kwargs["need_name"] == "need_name"
and kwargs["need_phone_number"] == "need_phone_number"
and kwargs["need_email"] == "need_email"
and kwargs["need_shipping_address"] == "need_shipping_address"
and kwargs["send_phone_number_to_provider"] == "send_phone_number_to_provider"
and kwargs["send_email_to_provider"] == "send_email_to_provider"
and kwargs["is_flexible"] == "is_flexible"
)
return all([kwargs[i] == i for i in kwargs])
monkeypatch.setattr(bot, "_post", make_assertion)
assert await bot.create_invoice_link(
@ -238,7 +131,7 @@ class TestInvoice:
payload="payload",
provider_token="provider_token",
currency="currency",
prices=self.prices,
prices="prices",
max_tip_amount="max_tip_amount",
suggested_tip_amounts="suggested_tip_amounts",
provider_data="provider_data",
@ -273,7 +166,75 @@ class TestInvoice:
start_parameter=self.start_parameter,
)
@pytest.mark.flaky(3, 1)
def test_equality(self):
a = Invoice("invoice", "desc", "start", "EUR", 7)
b = Invoice("invoice", "desc", "start", "EUR", 7)
c = Invoice("invoices", "description", "stop", "USD", 8)
d = LabeledPrice("label", 5)
assert a == b
assert hash(a) == hash(b)
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
class TestInvoiceWithRequest(TestInvoiceBase):
async def test_send_required_args_only(self, bot, chat_id, provider_token):
message = await bot.send_invoice(
chat_id=chat_id,
title=self.title,
description=self.description,
payload=self.payload,
provider_token=provider_token,
currency=self.currency,
prices=self.prices,
)
assert message.invoice.currency == self.currency
assert message.invoice.start_parameter == ""
assert message.invoice.description == self.description
assert message.invoice.title == self.title
assert message.invoice.total_amount == self.total_amount
link = await bot.create_invoice_link(
title=self.title,
description=self.description,
payload=self.payload,
provider_token=provider_token,
currency=self.currency,
prices=self.prices,
)
assert isinstance(link, str)
assert link != ""
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_invoice_default_protect_content(
self, chat_id, default_bot, provider_token
):
tasks = asyncio.gather(
*(
default_bot.send_invoice(
chat_id,
self.title,
self.description,
self.payload,
provider_token,
self.currency,
self.prices,
**kwargs,
)
for kwargs in ({}, {"protect_content": False})
)
)
protected, unprotected = await tasks
assert protected.has_protected_content
assert not unprotected.has_protected_content
@pytest.mark.parametrize(
"default_bot,custom",
[
@ -326,12 +287,8 @@ class TestInvoice:
reply_to_message_id=reply_to_message.message_id,
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_invoice_default_protect_content(
self, chat_id, default_bot, provider_token
):
protected = await default_bot.send_invoice(
async def test_send_all_args_send_invoice(self, bot, chat_id, provider_token):
message = await bot.send_invoice(
chat_id,
self.title,
self.description,
@ -339,31 +296,26 @@ class TestInvoice:
provider_token,
self.currency,
self.prices,
max_tip_amount=self.max_tip_amount,
suggested_tip_amounts=self.suggested_tip_amounts,
start_parameter=self.start_parameter,
provider_data=self.provider_data,
photo_url="https://raw.githubusercontent.com/"
"python-telegram-bot/logos/master/logo/png/ptb-logo_240.png",
photo_size=240,
photo_width=240,
photo_height=240,
need_name=True,
need_phone_number=True,
need_email=True,
need_shipping_address=True,
send_phone_number_to_provider=True,
send_email_to_provider=True,
is_flexible=True,
disable_notification=True,
protect_content=True,
)
assert protected.has_protected_content
unprotected = await default_bot.send_invoice(
chat_id,
self.title,
self.description,
self.payload,
provider_token,
self.currency,
self.prices,
protect_content=False,
)
assert not unprotected.has_protected_content
def test_equality(self):
a = Invoice("invoice", "desc", "start", "EUR", 7)
b = Invoice("invoice", "desc", "start", "EUR", 7)
c = Invoice("invoices", "description", "stop", "USD", 8)
d = LabeledPrice("label", 5)
assert a == b
assert hash(a) == hash(b)
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
for attr in message.invoice.__slots__:
assert getattr(message.invoice, attr) == getattr(self, attr)
assert message.has_protected_content

View file

@ -27,9 +27,7 @@ import time
import pytest
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue
from tests.auxil.object_conversions import env_var_2_bool
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
from tests.conftest import TEST_WITH_OPT_DEPS, make_bot
if TEST_WITH_OPT_DEPS:
import pytz
@ -46,7 +44,7 @@ class CustomContext(CallbackContext):
@pytest.fixture(scope="function")
async def job_queue(bot, app):
async def job_queue(app):
jq = JobQueue()
jq.set_application(app)
await jq.start()
@ -179,9 +177,9 @@ class TestJobQueue:
job_queue.run_repeating(
self.job_run_once, 0.5, first=dtm.datetime.now(timezone) + dtm.timedelta(seconds=0.2)
)
await asyncio.sleep(0.15)
await asyncio.sleep(0.05)
assert self.result == 0
await asyncio.sleep(0.2)
await asyncio.sleep(0.25)
assert self.result == 1
async def test_run_repeating_last(self, job_queue):
@ -192,7 +190,7 @@ class TestJobQueue:
assert self.result == 1
async def test_run_repeating_last_timezone(self, job_queue, timezone):
"""Test correct scheduling of job when passing a timezone-aware datetime as ``first``"""
"""Test correct scheduling of job when passing a timezone-aware datetime as ``last``"""
job_queue.run_repeating(
self.job_run_once, 0.25, last=dtm.datetime.now(timezone) + dtm.timedelta(seconds=0.4)
)
@ -244,6 +242,7 @@ class TestJobQueue:
j2 = job_queue.run_repeating(self.job_run_once, 0.2)
await asyncio.sleep(0.25)
assert self.result == 1
j1.schedule_removal()
j2.schedule_removal()
@ -273,8 +272,8 @@ class TestJobQueue:
await asyncio.sleep(0.3)
assert self.result == 1
async def test_in_application(self, bot):
app = ApplicationBuilder().token(bot.token).build()
async def test_in_application(self, bot_info):
app = ApplicationBuilder().bot(make_bot(bot_info)).build()
async with app:
assert not app.job_queue.scheduler.running
await app.start()
@ -311,7 +310,7 @@ class TestJobQueue:
# Testing running at a specific datetime
delta, now = dtm.timedelta(seconds=0.5), dtm.datetime.now(UTC)
when = now + delta
expected_time = (now + delta).timestamp()
expected_time = when.timestamp()
job_queue.run_once(self.job_datetime_tests, when)
await asyncio.sleep(0.6)
@ -444,8 +443,11 @@ class TestJobQueue:
callback = self.job_run_once
job1 = job_queue.run_once(callback, 10, name="name1")
await asyncio.sleep(0.03) # To stablize tests on windows
job2 = job_queue.run_once(callback, 10, name="name1")
await asyncio.sleep(0.03)
job3 = job_queue.run_once(callback, 10, name="name2")
await asyncio.sleep(0.03)
assert job_queue.jobs() == (job1, job2, job3)
assert job_queue.get_jobs_by_name("name1") == (job1, job2)
@ -453,9 +455,9 @@ class TestJobQueue:
async def test_job_run(self, app):
job = app.job_queue.run_repeating(self.job_run_once, 0.02)
await asyncio.sleep(0.05)
assert self.result == 0
await job.run(app)
await asyncio.sleep(0.05) # the job queue has not started yet
assert self.result == 0 # so the job will not run
await job.run(app) # but this will force it to run
assert self.result == 1
async def test_enable_disable_job(self, job_queue):
@ -569,7 +571,7 @@ class TestJobQueue:
)
job_queue.set_application(application)
def callback(context):
async def callback(context):
self.result = (
type(context),
context.user_data,
@ -603,8 +605,8 @@ class TestJobQueue:
if wait:
assert not task.done()
ready_event.set()
await asyncio.sleep(0.1)
await asyncio.sleep(0.1) # no CancelledError (see source of JobQueue.stop for details)
assert task.done()
else:
await asyncio.sleep(0.1)
await asyncio.sleep(0.1) # unfortunately we will get a CancelledError here
assert task.done()

View file

@ -28,20 +28,20 @@ from telegram import (
)
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def keyboard_button():
return KeyboardButton(
TestKeyboardButton.text,
request_location=TestKeyboardButton.request_location,
request_contact=TestKeyboardButton.request_contact,
request_poll=TestKeyboardButton.request_poll,
web_app=TestKeyboardButton.web_app,
request_chat=TestKeyboardButton.request_chat,
request_user=TestKeyboardButton.request_user,
TestKeyboardButtonBase.text,
request_location=TestKeyboardButtonBase.request_location,
request_contact=TestKeyboardButtonBase.request_contact,
request_poll=TestKeyboardButtonBase.request_poll,
web_app=TestKeyboardButtonBase.web_app,
request_chat=TestKeyboardButtonBase.request_chat,
request_user=TestKeyboardButtonBase.request_user,
)
class TestKeyboardButton:
class TestKeyboardButtonBase:
text = "text"
request_location = True
request_contact = True
@ -50,6 +50,8 @@ class TestKeyboardButton:
request_chat = KeyboardButtonRequestChat(1, True)
request_user = KeyboardButtonRequestUser(2)
class TestKeyboardButtonWithoutRequest(TestKeyboardButtonBase):
def test_slot_behaviour(self, keyboard_button, mro_slots):
inst = keyboard_button
for attr in inst.__slots__:

View file

@ -21,14 +21,16 @@ import pytest
from telegram import KeyboardButtonPollType, Poll
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def keyboard_button_poll_type():
return KeyboardButtonPollType(TestKeyboardButtonPollType.type)
return KeyboardButtonPollType(TestKeyboardButtonPollTypeBase.type)
class TestKeyboardButtonPollType:
class TestKeyboardButtonPollTypeBase:
type = Poll.QUIZ
class TestKeyboardButtonPollTypeWithoutRequest(TestKeyboardButtonPollTypeBase):
def test_slot_behaviour(self, keyboard_button_poll_type, mro_slots):
inst = keyboard_button_poll_type
for attr in inst.__slots__:

View file

@ -25,17 +25,19 @@ from telegram import ChatAdministratorRights, KeyboardButtonRequestChat, Keyboar
@pytest.fixture(scope="class")
def request_user():
return KeyboardButtonRequestUser(
TestKeyboardButtonRequestUser.request_id,
TestKeyboardButtonRequestUser.user_is_bot,
TestKeyboardButtonRequestUser.user_is_premium,
TestKeyboardButtonRequestUserBase.request_id,
TestKeyboardButtonRequestUserBase.user_is_bot,
TestKeyboardButtonRequestUserBase.user_is_premium,
)
class TestKeyboardButtonRequestUser:
class TestKeyboardButtonRequestUserBase:
request_id = 123
user_is_bot = True
user_is_premium = False
class TestKeyboardButtonRequestUserWithoutRequest(TestKeyboardButtonRequestUserBase):
def test_slot_behaviour(self, request_user, mro_slots):
for attr in request_user.__slots__:
assert getattr(request_user, attr, "err") != "err", f"got extra slot '{attr}'"
@ -78,18 +80,18 @@ class TestKeyboardButtonRequestUser:
@pytest.fixture(scope="class")
def request_chat():
return KeyboardButtonRequestChat(
TestKeyboardButtonRequestChat.request_id,
TestKeyboardButtonRequestChat.chat_is_channel,
TestKeyboardButtonRequestChat.chat_is_forum,
TestKeyboardButtonRequestChat.chat_has_username,
TestKeyboardButtonRequestChat.chat_is_created,
TestKeyboardButtonRequestChat.user_administrator_rights,
TestKeyboardButtonRequestChat.bot_administrator_rights,
TestKeyboardButtonRequestChat.bot_is_member,
TestKeyboardButtonRequestChatBase.request_id,
TestKeyboardButtonRequestChatBase.chat_is_channel,
TestKeyboardButtonRequestChatBase.chat_is_forum,
TestKeyboardButtonRequestChatBase.chat_has_username,
TestKeyboardButtonRequestChatBase.chat_is_created,
TestKeyboardButtonRequestChatBase.user_administrator_rights,
TestKeyboardButtonRequestChatBase.bot_administrator_rights,
TestKeyboardButtonRequestChatBase.bot_is_member,
)
class TestKeyboardButtonRequestChat:
class TestKeyboardButtonRequestChatBase:
request_id = 456
chat_is_channel = True
chat_is_forum = False
@ -103,6 +105,8 @@ class TestKeyboardButtonRequestChat:
)
bot_is_member = True
class TestKeyboardButtonRequestChatWithoutRequest(TestKeyboardButtonRequestChatBase):
def test_slot_behaviour(self, request_chat, mro_slots):
for attr in request_chat.__slots__:
assert getattr(request_chat, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -21,15 +21,17 @@ import pytest
from telegram import LabeledPrice, Location
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def labeled_price():
return LabeledPrice(TestLabeledPrice.label, TestLabeledPrice.amount)
return LabeledPrice(TestLabeledPriceBase.label, TestLabeledPriceBase.amount)
class TestLabeledPrice:
class TestLabeledPriceBase:
label = "label"
amount = 100
class TestLabeledPriceWithoutRequest(TestLabeledPriceBase):
def test_slot_behaviour(self, labeled_price, mro_slots):
inst = labeled_price
for attr in inst.__slots__:

View file

@ -16,6 +16,8 @@
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import pytest
from telegram import Location
@ -23,19 +25,19 @@ from telegram.error import BadRequest
from telegram.request import RequestData
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def location():
return Location(
latitude=TestLocation.latitude,
longitude=TestLocation.longitude,
horizontal_accuracy=TestLocation.horizontal_accuracy,
live_period=TestLocation.live_period,
heading=TestLocation.live_period,
proximity_alert_radius=TestLocation.proximity_alert_radius,
latitude=TestLocationBase.latitude,
longitude=TestLocationBase.longitude,
horizontal_accuracy=TestLocationBase.horizontal_accuracy,
live_period=TestLocationBase.live_period,
heading=TestLocationBase.live_period,
proximity_alert_radius=TestLocationBase.proximity_alert_radius,
)
class TestLocation:
class TestLocationBase:
latitude = -23.691288
longitude = -46.788279
horizontal_accuracy = 999
@ -43,6 +45,8 @@ class TestLocation:
heading = 90
proximity_alert_radius = 50
class TestLocationWithoutRequest(TestLocationBase):
def test_slot_behaviour(self, location, mro_slots):
for attr in location.__slots__:
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"
@ -50,12 +54,12 @@ class TestLocation:
def test_de_json(self, bot):
json_dict = {
"latitude": TestLocation.latitude,
"longitude": TestLocation.longitude,
"horizontal_accuracy": TestLocation.horizontal_accuracy,
"live_period": TestLocation.live_period,
"heading": TestLocation.heading,
"proximity_alert_radius": TestLocation.proximity_alert_radius,
"latitude": self.latitude,
"longitude": self.longitude,
"horizontal_accuracy": self.horizontal_accuracy,
"live_period": self.live_period,
"heading": self.heading,
"proximity_alert_radius": self.proximity_alert_radius,
}
location = Location.de_json(json_dict, bot)
assert location.api_kwargs == {}
@ -67,7 +71,139 @@ class TestLocation:
assert location.heading == self.heading
assert location.proximity_alert_radius == self.proximity_alert_radius
@pytest.mark.flaky(3, 1)
def test_to_dict(self, location):
location_dict = location.to_dict()
assert location_dict["latitude"] == location.latitude
assert location_dict["longitude"] == location.longitude
assert location_dict["horizontal_accuracy"] == location.horizontal_accuracy
assert location_dict["live_period"] == location.live_period
assert location["heading"] == location.heading
assert location["proximity_alert_radius"] == location.proximity_alert_radius
def test_equality(self):
a = Location(self.longitude, self.latitude)
b = Location(self.longitude, self.latitude)
d = Location(0, self.latitude)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != d
assert hash(a) != hash(d)
async def test_send_location_without_required(self, bot, chat_id):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.send_location(chat_id=chat_id)
async def test_edit_location_without_required(self, bot):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.edit_message_live_location(chat_id=2, message_id=3)
async def test_send_location_with_all_args(self, bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location)
async def test_edit_location_with_all_args(self, bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.edit_message_live_location(
chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location
)
# TODO: Needs improvement with in inline sent live location.
async def test_edit_live_inline_message(self, monkeypatch, bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
lat = data["latitude"] == str(location.latitude)
lon = data["longitude"] == str(location.longitude)
id_ = data["inline_message_id"] == "1234"
ha = data["horizontal_accuracy"] == "50"
heading = data["heading"] == "90"
prox_alert = data["proximity_alert_radius"] == "1000"
return lat and lon and id_ and ha and heading and prox_alert
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(
inline_message_id=1234,
location=location,
horizontal_accuracy=50,
heading=90,
proximity_alert_radius=1000,
)
# TODO: Needs improvement with in inline sent live location.
async def test_stop_live_inline_message(self, monkeypatch, bot):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
id_ = request_data.json_parameters["inline_message_id"] == "1234"
return id_
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.stop_message_live_location(inline_message_id=1234)
async def test_send_with_location(self, monkeypatch, bot, chat_id, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_location(location=location, chat_id=chat_id)
async def test_edit_live_location_with_location(self, monkeypatch, bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(None, None, location=location)
class TestLocationWithRequest:
@pytest.mark.parametrize(
"default_bot,custom",
[
({"allow_sending_without_reply": True}, None),
({"allow_sending_without_reply": False}, None),
({"allow_sending_without_reply": False}, True),
],
indirect=["default_bot"],
)
async def test_send_location_default_allow_sending_without_reply(
self, default_bot, chat_id, location, custom
):
reply_to_message = await default_bot.send_message(chat_id, "test")
await reply_to_message.delete()
if custom is not None:
message = await default_bot.send_location(
chat_id,
location=location,
allow_sending_without_reply=custom,
reply_to_message_id=reply_to_message.message_id,
)
assert message.reply_to_message is None
elif default_bot.defaults.allow_sending_without_reply:
message = await default_bot.send_location(
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
)
assert message.reply_to_message is None
else:
with pytest.raises(BadRequest, match="message not found"):
await default_bot.send_location(
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_location_default_protect_content(self, chat_id, default_bot, location):
tasks = asyncio.gather(
default_bot.send_location(chat_id, location=location),
default_bot.send_location(chat_id, location=location, protect_content=False),
)
protected, unprotected = await tasks
assert protected.has_protected_content
assert not unprotected.has_protected_content
@pytest.mark.xfail
async def test_send_live_location(self, bot, chat_id):
message = await bot.send_location(
@ -110,135 +246,3 @@ class TestLocation:
await bot.edit_message_live_location(
message.chat_id, message.message_id, latitude=52.223880, longitude=5.164306
)
# TODO: Needs improvement with in inline sent live location.
async def test_edit_live_inline_message(self, monkeypatch, bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
lat = data["latitude"] == str(location.latitude)
lon = data["longitude"] == str(location.longitude)
id_ = data["inline_message_id"] == "1234"
ha = data["horizontal_accuracy"] == "50"
heading = data["heading"] == "90"
prox_alert = data["proximity_alert_radius"] == "1000"
return lat and lon and id_ and ha and heading and prox_alert
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(
inline_message_id=1234,
location=location,
horizontal_accuracy=50,
heading=90,
proximity_alert_radius=1000,
)
# TODO: Needs improvement with in inline sent live location.
async def test_stop_live_inline_message(self, monkeypatch, bot):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
id_ = request_data.json_parameters["inline_message_id"] == "1234"
return id_
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.stop_message_live_location(inline_message_id=1234)
async def test_send_with_location(self, monkeypatch, bot, chat_id, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.send_location(location=location, chat_id=chat_id)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot,custom",
[
({"allow_sending_without_reply": True}, None),
({"allow_sending_without_reply": False}, None),
({"allow_sending_without_reply": False}, True),
],
indirect=["default_bot"],
)
async def test_send_location_default_allow_sending_without_reply(
self, default_bot, chat_id, location, custom
):
reply_to_message = await default_bot.send_message(chat_id, "test")
await reply_to_message.delete()
if custom is not None:
message = await default_bot.send_location(
chat_id,
location=location,
allow_sending_without_reply=custom,
reply_to_message_id=reply_to_message.message_id,
)
assert message.reply_to_message is None
elif default_bot.defaults.allow_sending_without_reply:
message = await default_bot.send_location(
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
)
assert message.reply_to_message is None
else:
with pytest.raises(BadRequest, match="message not found"):
await default_bot.send_location(
chat_id, location=location, reply_to_message_id=reply_to_message.message_id
)
@pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
async def test_send_location_default_protect_content(self, chat_id, default_bot, location):
protected = await default_bot.send_location(chat_id, location=location)
assert protected.has_protected_content
unprotected = await default_bot.send_location(
chat_id, location=location, protect_content=False
)
assert not unprotected.has_protected_content
async def test_edit_live_location_with_location(self, monkeypatch, bot, location):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
lat = request_data.json_parameters["latitude"] == str(location.latitude)
lon = request_data.json_parameters["longitude"] == str(location.longitude)
return lat and lon
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.edit_message_live_location(None, None, location=location)
async def test_send_location_without_required(self, bot, chat_id):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.send_location(chat_id=chat_id)
async def test_edit_location_without_required(self, bot):
with pytest.raises(ValueError, match="Either location or latitude and longitude"):
await bot.edit_message_live_location(chat_id=2, message_id=3)
async def test_send_location_with_all_args(self, bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.send_location(chat_id=1, latitude=2.5, longitude=4.6, location=location)
async def test_edit_location_with_all_args(self, bot, location):
with pytest.raises(ValueError, match="Not both"):
await bot.edit_message_live_location(
chat_id=1, message_id=7, latitude=2.5, longitude=4.6, location=location
)
def test_to_dict(self, location):
location_dict = location.to_dict()
assert location_dict["latitude"] == location.latitude
assert location_dict["longitude"] == location.longitude
assert location_dict["horizontal_accuracy"] == location.horizontal_accuracy
assert location_dict["live_period"] == location.live_period
assert location["heading"] == location.heading
assert location["proximity_alert_radius"] == location.proximity_alert_radius
def test_equality(self):
a = Location(self.longitude, self.latitude)
b = Location(self.longitude, self.latitude)
d = Location(0, self.latitude)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != d
assert hash(a) != hash(d)

View file

@ -21,22 +21,24 @@ import pytest
from telegram import LoginUrl
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def login_url():
return LoginUrl(
url=TestLoginUrl.url,
forward_text=TestLoginUrl.forward_text,
bot_username=TestLoginUrl.bot_username,
request_write_access=TestLoginUrl.request_write_access,
url=TestLoginUrlBase.url,
forward_text=TestLoginUrlBase.forward_text,
bot_username=TestLoginUrlBase.bot_username,
request_write_access=TestLoginUrlBase.request_write_access,
)
class TestLoginUrl:
class TestLoginUrlBase:
url = "http://www.google.com"
forward_text = "Send me forward!"
bot_username = "botname"
request_write_access = True
class TestLoginUrlWithoutRequest(TestLoginUrlBase):
def test_slot_behaviour(self, login_url, mro_slots):
for attr in login_url.__slots__:
assert getattr(login_url, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -31,7 +31,7 @@ from telegram import (
@pytest.fixture(
scope="class",
scope="module",
params=[
MenuButton.DEFAULT,
MenuButton.WEB_APP,
@ -43,7 +43,7 @@ def scope_type(request):
@pytest.fixture(
scope="class",
scope="module",
params=[
MenuButtonDefault,
MenuButtonCommands,
@ -60,7 +60,7 @@ def scope_class(request):
@pytest.fixture(
scope="class",
scope="module",
params=[
(MenuButtonDefault, MenuButton.DEFAULT),
(MenuButtonCommands, MenuButton.COMMANDS),
@ -76,24 +76,26 @@ def scope_class_and_type(request):
return request.param
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def menu_button(scope_class_and_type):
# We use de_json here so that we don't have to worry about which class gets which arguments
return scope_class_and_type[0].de_json(
dict(
type=scope_class_and_type[1],
text=TestMenuButton.text,
web_app=TestMenuButton.web_app.to_dict(),
text=TestMenuButtonselfBase.text,
web_app=TestMenuButtonselfBase.web_app.to_dict(),
),
bot=None,
)
# All the scope types are very similar, so we test everything via parametrization
class TestMenuButton:
class TestMenuButtonselfBase:
text = "button_text"
web_app = WebAppInfo(url="https://python-telegram-bot.org/web_app")
# All the scope types are very similar, so we test everything via parametrization
class TestMenuButtonWithoutRequest(TestMenuButtonselfBase):
def test_slot_behaviour(self, menu_button, mro_slots):
for attr in menu_button.__slots__:
assert getattr(menu_button, attr, "err") != "err", f"got extra slot '{attr}'"

View file

@ -66,13 +66,13 @@ from tests.auxil.bot_method_checks import (
from tests.test_passport import RAW_PASSPORT_DATA
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def message(bot):
message = Message(
message_id=TestMessage.id_,
date=TestMessage.date,
chat=copy(TestMessage.chat),
from_user=copy(TestMessage.from_user),
message_id=TestMessageBase.id_,
date=TestMessageBase.date,
chat=copy(TestMessageBase.chat),
from_user=copy(TestMessageBase.from_user),
)
message.set_bot(bot)
message._unfreeze()
@ -271,17 +271,17 @@ def message(bot):
)
def message_params(bot, request):
message = Message(
message_id=TestMessage.id_,
from_user=TestMessage.from_user,
date=TestMessage.date,
chat=TestMessage.chat,
message_id=TestMessageBase.id_,
from_user=TestMessageBase.from_user,
date=TestMessageBase.date,
chat=TestMessageBase.chat,
**request.param,
)
message.set_bot(bot)
return message
class TestMessage:
class TestMessageBase:
id_ = 1
from_user = User(2, "testuser", False)
date = datetime.utcnow()
@ -347,6 +347,13 @@ class TestMessage:
caption_entities=[MessageEntity(**e) for e in test_entities_v2],
)
class TestMessageWithoutRequest(TestMessageBase):
def test_slot_behaviour(self, message, mro_slots):
for attr in message.__slots__:
assert getattr(message, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(message)) == len(set(mro_slots(message))), "duplicate slot"
def test_all_possibilities_de_json_and_to_dict(self, bot, message_params):
new = Message.de_json(message_params.to_dict(), bot)
assert new.api_kwargs == {}
@ -358,10 +365,26 @@ class TestMessage:
for slot in new.__slots__:
assert not isinstance(new[slot], dict)
def test_slot_behaviour(self, message, mro_slots):
for attr in message.__slots__:
assert getattr(message, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(message)) == len(set(mro_slots(message))), "duplicate slot"
def test_equality(self):
id_ = 1
a = Message(id_, self.date, self.chat, from_user=self.from_user)
b = Message(id_, self.date, self.chat, from_user=self.from_user)
c = Message(id_, self.date, Chat(123, Chat.GROUP), from_user=User(0, "", False))
d = Message(0, self.date, self.chat, from_user=self.from_user)
e = Update(id_)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)
async def test_parse_entity(self):
text = (
@ -563,7 +586,7 @@ class TestMessage:
expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape")
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
message = Message(
1, self.from_user, self.date, self.chat, text=text, entities=[bold_entity]
1, self.date, self.chat, self.from_user, text=text, entities=[bold_entity]
)
assert expected == message.text_markdown
@ -1826,34 +1849,3 @@ class TestMessage:
monkeypatch.setattr(message.get_bot(), "unpin_all_forum_topic_messages", make_assertion)
assert await message.unpin_all_forum_topic_messages()
def test_equality(self):
id_ = 1
a = Message(
id_,
self.date,
self.chat,
from_user=self.from_user,
)
b = Message(
id_,
self.date,
self.chat,
from_user=self.from_user,
)
c = Message(id_, self.date, Chat(123, Chat.GROUP), from_user=User(0, "", False))
d = Message(0, self.date, self.chat, from_user=self.from_user)
e = Update(id_)
assert a == b
assert hash(a) == hash(b)
assert a is not b
assert a != c
assert hash(a) != hash(c)
assert a != d
assert hash(a) != hash(d)
assert a != e
assert hash(a) != hash(e)

View file

@ -19,7 +19,7 @@
from telegram import MessageAutoDeleteTimerChanged, VideoChatEnded
class TestMessageAutoDeleteTimerChanged:
class TestMessageAutoDeleteTimerChangedWithoutRequest:
message_auto_delete_time = 100
def test_slot_behaviour(self, mro_slots):

View file

@ -22,7 +22,7 @@ from telegram import MessageEntity, User
from telegram.constants import MessageEntityType
@pytest.fixture(scope="class", params=MessageEntity.ALL_TYPES)
@pytest.fixture(scope="module", params=MessageEntity.ALL_TYPES)
def message_entity(request):
type_ = request.param
url = None
@ -37,12 +37,14 @@ def message_entity(request):
return MessageEntity(type_, 1, 3, url=url, user=user, language=language)
class TestMessageEntity:
class TestMessageEntityBase:
type_ = "url"
offset = 1
length = 2
url = "url"
class TestMessageEntityWithoutRequest(TestMessageEntityBase):
def test_slot_behaviour(self, message_entity, mro_slots):
inst = message_entity
for attr in inst.__slots__:

View file

@ -20,12 +20,12 @@ import pytest
from telegram import MessageId, User
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def message_id():
return MessageId(message_id=TestMessageId.m_id)
return MessageId(message_id=TestMessageIdWithoutRequest.m_id)
class TestMessageId:
class TestMessageIdWithoutRequest:
m_id = 1234
def test_slot_behaviour(self, message_id, mro_slots):

View file

@ -26,30 +26,26 @@ Because imports in pytest are intricate, we just run
with the TEST_WITH_OPT_DEPS environment variable set to False in addition to the regular test suite
"""
import os
import pytest
from telegram import _bot as bot
from telegram._passport import credentials as credentials
from tests.auxil.object_conversions import env_var_2_bool
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
from tests.conftest import TEST_WITH_OPT_DEPS
@pytest.mark.skipif(
TEST_WITH_OPT_DEPS, reason="Only relevant if the optional dependency is not installed"
)
class TestNoPassport:
def test_bot_init(self, bot_info, monkeypatch):
class TestNoPassportWithoutRequest:
def test_bot_init(self, bot_info):
with pytest.raises(RuntimeError, match="passport"):
bot.Bot(bot_info["token"], private_key=1, private_key_password=2)
def test_credentials_decrypt(self, monkeypatch):
def test_credentials_decrypt(self):
with pytest.raises(RuntimeError, match="passport"):
credentials.decrypt(1, 1, 1)
def test_encrypted_credentials_decrypted_secret(self, monkeypatch):
def test_encrypted_credentials_decrypted_secret(self):
ec = credentials.EncryptedCredentials("data", "hash", "secret")
with pytest.raises(RuntimeError, match="passport"):
ec.decrypted_secret

View file

@ -21,22 +21,24 @@ import pytest
from telegram import OrderInfo, ShippingAddress
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def order_info():
return OrderInfo(
TestOrderInfo.name,
TestOrderInfo.phone_number,
TestOrderInfo.email,
TestOrderInfo.shipping_address,
TestOrderInfoBase.name,
TestOrderInfoBase.phone_number,
TestOrderInfoBase.email,
TestOrderInfoBase.shipping_address,
)
class TestOrderInfo:
class TestOrderInfoBase:
name = "name"
phone_number = "phone_number"
email = "email"
shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1")
class TestOrderInfoWithoutRequest(TestOrderInfoBase):
def test_slot_behaviour(self, order_info, mro_slots):
for attr in order_info.__slots__:
assert getattr(order_info, attr, "err") != "err", f"got extra slot '{attr}'"
@ -44,10 +46,10 @@ class TestOrderInfo:
def test_de_json(self, bot):
json_dict = {
"name": TestOrderInfo.name,
"phone_number": TestOrderInfo.phone_number,
"email": TestOrderInfo.email,
"shipping_address": TestOrderInfo.shipping_address.to_dict(),
"name": self.name,
"phone_number": self.phone_number,
"email": self.email,
"shipping_address": self.shipping_address.to_dict(),
}
order_info = OrderInfo.de_json(json_dict, bot)
assert order_info.api_kwargs == {}

View file

@ -128,7 +128,7 @@ RAW_PASSPORT_DATA = {
}
@pytest.fixture(scope="function")
@pytest.fixture(scope="module")
def all_passport_data():
return [
{
@ -214,12 +214,12 @@ def all_passport_data():
]
@pytest.fixture(scope="function")
@pytest.fixture(scope="module")
def passport_data(bot):
return PassportData.de_json(RAW_PASSPORT_DATA, bot=bot)
class TestPassport:
class TestPassportBase:
driver_license_selfie_file_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI"
driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12"
driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI"
@ -241,6 +241,8 @@ class TestPassport:
driver_license_selfie_credentials_file_hash = "Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI="
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
class TestPassportWithoutRequest(TestPassportBase):
def test_slot_behaviour(self, passport_data, mro_slots):
inst = passport_data
for attr in inst.__slots__:
@ -387,6 +389,37 @@ class TestPassport:
assert email.type == "email"
assert email.email == "fb3e3i47zt@dispostable.com"
def test_de_json_and_to_dict(self, bot):
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot)
assert passport_data.api_kwargs == {}
assert passport_data.to_dict() == RAW_PASSPORT_DATA
assert passport_data.decrypted_data
assert passport_data.to_dict() == RAW_PASSPORT_DATA
def test_equality(self, passport_data):
a = PassportData(passport_data.data, passport_data.credentials)
b = PassportData(passport_data.data, passport_data.credentials)
assert a == b
assert hash(a) == hash(b)
assert a is not b
new_pp_data = deepcopy(passport_data)
new_pp_data.credentials._unfreeze()
new_pp_data.credentials.hash = "NOTAPROPERHASH"
c = PassportData(new_pp_data.data, new_pp_data.credentials)
assert a != c
assert hash(a) != hash(c)
def test_bot_init_invalid_key(self, bot):
with pytest.raises(TypeError):
Bot(bot.token, private_key="Invalid key!")
with pytest.raises(ValueError):
Bot(bot.token, private_key=b"Invalid key!")
def test_all_types(self, passport_data, bot, all_passport_data):
credentials = passport_data.decrypted_credentials.to_dict()
@ -422,13 +455,6 @@ class TestPassport:
assert isinstance(new, PassportData)
assert new.decrypted_data
def test_bot_init_invalid_key(self, bot):
with pytest.raises(TypeError):
Bot(bot.token, private_key="Invalid key!")
with pytest.raises(ValueError):
Bot(bot.token, private_key=b"Invalid key!")
async def test_passport_data_okay_with_non_crypto_bot(self, bot):
async with make_bot(token=bot.token) as b:
assert PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
@ -506,26 +532,3 @@ class TestPassport:
],
)
assert message
def test_de_json_and_to_dict(self, bot):
passport_data = PassportData.de_json(RAW_PASSPORT_DATA, bot)
assert passport_data.api_kwargs == {}
assert passport_data.to_dict() == RAW_PASSPORT_DATA
assert passport_data.decrypted_data
assert passport_data.to_dict() == RAW_PASSPORT_DATA
def test_equality(self, passport_data):
a = PassportData(passport_data.data, passport_data.credentials)
b = PassportData(passport_data.data, passport_data.credentials)
assert a == b
assert hash(a) == hash(b)
assert a is not b
passport_data.credentials._unfreeze()
passport_data.credentials.hash = "NOTAPROPERHASH"
c = PassportData(passport_data.data, passport_data.credentials)
assert a != c
assert hash(a) != hash(c)

View file

@ -21,23 +21,25 @@ import pytest
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_data_field():
return PassportElementErrorDataField(
TestPassportElementErrorDataField.type_,
TestPassportElementErrorDataField.field_name,
TestPassportElementErrorDataField.data_hash,
TestPassportElementErrorDataField.message,
TestPassportElementErrorDataFieldBase.type_,
TestPassportElementErrorDataFieldBase.field_name,
TestPassportElementErrorDataFieldBase.data_hash,
TestPassportElementErrorDataFieldBase.message,
)
class TestPassportElementErrorDataField:
class TestPassportElementErrorDataFieldBase:
source = "data"
type_ = "test_type"
field_name = "test_field"
data_hash = "data_hash"
message = "Error message"
class TestPassportElementErrorDataFieldWithoutRequest(TestPassportElementErrorDataFieldBase):
def test_slot_behaviour(self, passport_element_error_data_field, mro_slots):
inst = passport_element_error_data_field
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorFile, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_file():
return PassportElementErrorFile(
TestPassportElementErrorFile.type_,
TestPassportElementErrorFile.file_hash,
TestPassportElementErrorFile.message,
TestPassportElementErrorFileBase.type_,
TestPassportElementErrorFileBase.file_hash,
TestPassportElementErrorFileBase.message,
)
class TestPassportElementErrorFile:
class TestPassportElementErrorFileBase:
source = "file"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorFileWithoutRequest(TestPassportElementErrorFileBase):
def test_slot_behaviour(self, passport_element_error_file, mro_slots):
inst = passport_element_error_file
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorFiles, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_files():
return PassportElementErrorFiles(
TestPassportElementErrorFiles.type_,
TestPassportElementErrorFiles.file_hashes,
TestPassportElementErrorFiles.message,
TestPassportElementErrorFilesBase.type_,
TestPassportElementErrorFilesBase.file_hashes,
TestPassportElementErrorFilesBase.message,
)
class TestPassportElementErrorFiles:
class TestPassportElementErrorFilesBase:
source = "files"
type_ = "test_type"
file_hashes = ["hash1", "hash2"]
message = "Error message"
class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesBase):
def test_slot_behaviour(self, passport_element_error_files, mro_slots):
inst = passport_element_error_files
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorFrontSide, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_front_side():
return PassportElementErrorFrontSide(
TestPassportElementErrorFrontSide.type_,
TestPassportElementErrorFrontSide.file_hash,
TestPassportElementErrorFrontSide.message,
TestPassportElementErrorFrontSideBase.type_,
TestPassportElementErrorFrontSideBase.file_hash,
TestPassportElementErrorFrontSideBase.message,
)
class TestPassportElementErrorFrontSide:
class TestPassportElementErrorFrontSideBase:
source = "front_side"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorFrontSideWithoutRequest(TestPassportElementErrorFrontSideBase):
def test_slot_behaviour(self, passport_element_error_front_side, mro_slots):
inst = passport_element_error_front_side
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorReverseSide, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_reverse_side():
return PassportElementErrorReverseSide(
TestPassportElementErrorReverseSide.type_,
TestPassportElementErrorReverseSide.file_hash,
TestPassportElementErrorReverseSide.message,
TestPassportElementErrorReverseSideBase.type_,
TestPassportElementErrorReverseSideBase.file_hash,
TestPassportElementErrorReverseSideBase.message,
)
class TestPassportElementErrorReverseSide:
class TestPassportElementErrorReverseSideBase:
source = "reverse_side"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorReverseSideWithoutRequest(TestPassportElementErrorReverseSideBase):
def test_slot_behaviour(self, passport_element_error_reverse_side, mro_slots):
inst = passport_element_error_reverse_side
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_selfie():
return PassportElementErrorSelfie(
TestPassportElementErrorSelfie.type_,
TestPassportElementErrorSelfie.file_hash,
TestPassportElementErrorSelfie.message,
TestPassportElementErrorSelfieBase.type_,
TestPassportElementErrorSelfieBase.file_hash,
TestPassportElementErrorSelfieBase.message,
)
class TestPassportElementErrorSelfie:
class TestPassportElementErrorSelfieBase:
source = "selfie"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorSelfieWithoutRequest(TestPassportElementErrorSelfieBase):
def test_slot_behaviour(self, passport_element_error_selfie, mro_slots):
inst = passport_element_error_selfie
for attr in inst.__slots__:

View file

@ -21,21 +21,25 @@ import pytest
from telegram import PassportElementErrorDataField, PassportElementErrorTranslationFile
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_translation_file():
return PassportElementErrorTranslationFile(
TestPassportElementErrorTranslationFile.type_,
TestPassportElementErrorTranslationFile.file_hash,
TestPassportElementErrorTranslationFile.message,
TestPassportElementErrorTranslationFileBase.type_,
TestPassportElementErrorTranslationFileBase.file_hash,
TestPassportElementErrorTranslationFileBase.message,
)
class TestPassportElementErrorTranslationFile:
class TestPassportElementErrorTranslationFileBase:
source = "translation_file"
type_ = "test_type"
file_hash = "file_hash"
message = "Error message"
class TestPassportElementErrorTranslationFileWithoutRequest(
TestPassportElementErrorTranslationFileBase
):
def test_slot_behaviour(self, passport_element_error_translation_file, mro_slots):
inst = passport_element_error_translation_file
for attr in inst.__slots__:

View file

@ -21,21 +21,25 @@ import pytest
from telegram import PassportElementErrorSelfie, PassportElementErrorTranslationFiles
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_translation_files():
return PassportElementErrorTranslationFiles(
TestPassportElementErrorTranslationFiles.type_,
TestPassportElementErrorTranslationFiles.file_hashes,
TestPassportElementErrorTranslationFiles.message,
TestPassportElementErrorTranslationFilesBase.type_,
TestPassportElementErrorTranslationFilesBase.file_hashes,
TestPassportElementErrorTranslationFilesBase.message,
)
class TestPassportElementErrorTranslationFiles:
class TestPassportElementErrorTranslationFilesBase:
source = "translation_files"
type_ = "test_type"
file_hashes = ["hash1", "hash2"]
message = "Error message"
class TestPassportElementErrorTranslationFilesWithoutRequest(
TestPassportElementErrorTranslationFilesBase
):
def test_slot_behaviour(self, passport_element_error_translation_files, mro_slots):
inst = passport_element_error_translation_files
for attr in inst.__slots__:

View file

@ -21,21 +21,23 @@ import pytest
from telegram import PassportElementErrorDataField, PassportElementErrorUnspecified
@pytest.fixture(scope="class")
@pytest.fixture(scope="module")
def passport_element_error_unspecified():
return PassportElementErrorUnspecified(
TestPassportElementErrorUnspecified.type_,
TestPassportElementErrorUnspecified.element_hash,
TestPassportElementErrorUnspecified.message,
TestPassportElementErrorUnspecifiedBase.type_,
TestPassportElementErrorUnspecifiedBase.element_hash,
TestPassportElementErrorUnspecifiedBase.message,
)
class TestPassportElementErrorUnspecified:
class TestPassportElementErrorUnspecifiedBase:
source = "unspecified"
type_ = "test_type"
element_hash = "element_hash"
message = "Error message"
class TestPassportElementErrorUnspecifiedWithoutRequest(TestPassportElementErrorUnspecifiedBase):
def test_slot_behaviour(self, passport_element_error_unspecified, mro_slots):
inst = passport_element_error_unspecified
for attr in inst.__slots__:

View file

@ -29,21 +29,23 @@ from tests.auxil.bot_method_checks import (
@pytest.fixture(scope="class")
def passport_file(bot):
pf = PassportFile(
file_id=TestPassportFile.file_id,
file_unique_id=TestPassportFile.file_unique_id,
file_size=TestPassportFile.file_size,
file_date=TestPassportFile.file_date,
file_id=TestPassportFileBase.file_id,
file_unique_id=TestPassportFileBase.file_unique_id,
file_size=TestPassportFileBase.file_size,
file_date=TestPassportFileBase.file_date,
)
pf.set_bot(bot)
return pf
class TestPassportFile:
class TestPassportFileBase:
file_id = "data"
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
file_size = 50
file_date = 1532879128
class TestPassportFileWithoutRequest(TestPassportFileBase):
def test_slot_behaviour(self, passport_file, mro_slots):
inst = passport_file
for attr in inst.__slots__:
@ -65,21 +67,6 @@ class TestPassportFile:
assert passport_file_dict["file_size"] == passport_file.file_size
assert passport_file_dict["file_date"] == passport_file.file_date
async def test_get_file_instance_method(self, monkeypatch, passport_file):
async def make_assertion(*_, **kwargs):
result = kwargs["file_id"] == passport_file.file_id
# we need to be a bit hacky here, b/c PF.get_file needs Bot.get_file to return a File
return File(file_id=result, file_unique_id=result)
assert check_shortcut_signature(PassportFile.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(
passport_file.get_file, passport_file.get_bot(), "get_file"
)
assert await check_defaults_handling(passport_file.get_file, passport_file.get_bot())
monkeypatch.setattr(passport_file.get_bot(), "get_file", make_assertion)
assert (await passport_file.get_file()).file_id == "True"
def test_equality(self):
a = PassportFile(self.file_id, self.file_unique_id, self.file_size, self.file_date)
b = PassportFile("", self.file_unique_id, self.file_size, self.file_date)
@ -99,3 +86,18 @@ class TestPassportFile:
assert a != e
assert hash(a) != hash(e)
async def test_get_file_instance_method(self, monkeypatch, passport_file):
async def make_assertion(*_, **kwargs):
result = kwargs["file_id"] == passport_file.file_id
# we need to be a bit hacky here, b/c PF.get_file needs Bot.get_file to return a File
return File(file_id=result, file_unique_id=result)
assert check_shortcut_signature(PassportFile.get_file, Bot.get_file, ["file_id"], [])
assert await check_shortcut_call(
passport_file.get_file, passport_file.get_bot(), "get_file"
)
assert await check_defaults_handling(passport_file.get_file, passport_file.get_bot())
monkeypatch.setattr(passport_file.get_bot(), "get_file", make_assertion)
assert (await passport_file.get_file()).file_id == "True"

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