diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst
index 6e5e2190b..6a21efc9f 100644
--- a/.github/CONTRIBUTING.rst
+++ b/.github/CONTRIBUTING.rst
@@ -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`.
-
- - Don’t 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
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d81f0e1f7..1d042053a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -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}
diff --git a/.github/workflows/type_completeness.yml b/.github/workflows/type_completeness.yml
index 5cc33f2e6..39d6b7146 100644
--- a/.github/workflows/type_completeness.yml
+++ b/.github/workflows/type_completeness.yml
@@ -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
diff --git a/docs/source/index.rst b/docs/source/index.rst
index c9e356dc7..adccdbef5 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -32,7 +32,6 @@
GitHub Repository
Telegram Channel
Telegram User Group
- contributing
coc
-
-
+ contributing
+ testing
diff --git a/docs/source/testing.rst b/docs/source/testing.rst
new file mode 100644
index 000000000..c32693c24
--- /dev/null
+++ b/docs/source/testing.rst
@@ -0,0 +1 @@
+.. include:: ../../tests/README.rst
\ No newline at end of file
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 10db73911..347a8ab0d 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -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
diff --git a/setup.cfg b/setup.cfg
index a0e0eb856..60c2cf574 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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]
diff --git a/tests/README.rst b/tests/README.rst
new file mode 100644
index 000000000..fb2ffe14b
--- /dev/null
+++ b/tests/README.rst
@@ -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
\ No newline at end of file
diff --git a/tests/bots.py b/tests/bots.py
index c5432dc49..70849d45a 100644
--- a/tests/bots.py
+++ b/tests/bots.py
@@ -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()}
diff --git a/tests/conftest.py b/tests/conftest.py
index d0f827a4c..4756931d2 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -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
diff --git a/tests/test_animation.py b/tests/test_animation.py
index 69c34c6a3..2a344f132 100644
--- a/tests/test_animation.py
+++ b/tests/test_animation.py
@@ -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)
diff --git a/tests/test_application.py b/tests/test_application.py
index a458f6ae7..b80c6a334 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -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":
diff --git a/tests/test_applicationbuilder.py b/tests/test_applicationbuilder.py
index e45fb90cd..754585e51 100644
--- a/tests/test_applicationbuilder.py
+++ b/tests/test_applicationbuilder.py
@@ -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")
diff --git a/tests/test_audio.py b/tests/test_audio.py
index 2a44dca0d..322e61f9e 100644
--- a/tests/test_audio.py
+++ b/tests/test_audio.py
@@ -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)
diff --git a/tests/test_basepersistence.py b/tests/test_basepersistence.py
index bb40fcf04..fbc70540d 100644
--- a/tests/test_basepersistence.py
+++ b/tests/test_basepersistence.py
@@ -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()
)
diff --git a/tests/test_bot.py b/tests/test_bot.py
index 28b946c8f..39e848922 100644
--- a/tests/test_bot.py
+++ b/tests/test_bot.py
@@ -24,7 +24,6 @@ import logging
import pickle
import re
import socket
-import sys
import time
from collections import defaultdict
@@ -77,7 +76,15 @@ from telegram.helpers import escape_markdown
from telegram.request import BaseRequest, HTTPXRequest, RequestData
from tests.auxil.bot_method_checks import check_defaults_handling
from tests.bots import FALLBACKS
-from tests.conftest import GITHUB_ACTION, data_file, expect_bad_request, make_bot
+from tests.conftest import (
+ GITHUB_ACTION,
+ TEST_WITH_OPT_DEPS,
+ DictBot,
+ DictExtBot,
+ data_file,
+ expect_bad_request,
+ make_bot,
+)
def to_camel_case(snake_str):
@@ -88,29 +95,22 @@ def to_camel_case(snake_str):
return components[0] + "".join(x.title() for x in components[1:])
-@pytest.fixture(scope="class")
-async def message(bot, chat_id):
- to_reply_to = await bot.send_message(
- chat_id, "Text", disable_web_page_preview=True, disable_notification=True
- )
+@pytest.fixture(scope="function")
+async def message(bot, chat_id): # mostly used in tests for edit_message
out = await bot.send_message(
- chat_id,
- "Text",
- reply_to_message_id=to_reply_to.message_id,
- disable_web_page_preview=True,
- disable_notification=True,
+ chat_id, "Text", disable_web_page_preview=True, disable_notification=True
)
out._unfreeze()
return out
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def media_message(bot, chat_id):
with data_file("telegram.ogg").open("rb") as f:
return await bot.send_voice(chat_id, voice=f, caption="my caption", read_timeout=10)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def chat_permissions():
return ChatPermissions(can_send_messages=False, can_change_info=False, can_invite_users=False)
@@ -126,7 +126,7 @@ def inline_results_callback(page=None):
return None
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def inline_results():
return inline_results_callback()
@@ -140,7 +140,7 @@ xfail = pytest.mark.xfail(
)
-def bot_methods(ext_bot=True):
+def bot_methods(ext_bot=True, include_camel_case=False):
arg_values = []
ids = []
non_api_methods = [
@@ -160,6 +160,8 @@ def bot_methods(ext_bot=True):
for name, attribute in inspect.getmembers(cls, predicate=inspect.isfunction):
if name.startswith("_") or name in non_api_methods:
continue
+ if not include_camel_case and any(x.isupper() for x in name):
+ continue
arg_values.append((cls, name, attribute))
ids.append(f"{cls.__name__}.{name}")
@@ -190,7 +192,7 @@ class InputMessageContentDWPP(InputMessageContent):
self.disable_web_page_preview = disable_web_page_preview
-class TestBot:
+class TestBotWithoutRequest:
"""
Most are executed on tg.ext.ExtBot, as that class only extends the functionality of tg.bot
@@ -198,16 +200,6 @@ class TestBot:
is tested in `test_callbackdatacache`
"""
- @staticmethod
- def localize(dt, tzinfo):
- try:
- return tzinfo.localize(dt)
- except AttributeError:
- # We get here if tzinfo is not a pytz timezone but a datetime.timezone class
- # This test class should never be run in if pytz is not installed, we intentionally
- # fail if this branch is ever reached.
- sys.exit(1)
-
test_flag = None
@pytest.fixture(scope="function", autouse=True)
@@ -221,14 +213,28 @@ class TestBot:
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_initialize_and_shutdown(self, bot, monkeypatch):
+ async def test_no_token_passed(self):
+ with pytest.raises(InvalidToken, match="You must pass the token"):
+ Bot("")
+
+ async def test_to_dict(self, bot):
+ to_dict_bot = bot.to_dict()
+
+ assert isinstance(to_dict_bot, dict)
+ assert to_dict_bot["id"] == bot.id
+ assert to_dict_bot["username"] == bot.username
+ assert to_dict_bot["first_name"] == bot.first_name
+ if bot.last_name:
+ assert to_dict_bot["last_name"] == bot.last_name
+
+ async def test_initialize_and_shutdown(self, bot: DictExtBot, monkeypatch):
async def initialize(*args, **kwargs):
self.test_flag = ["initialize"]
async def stop(*args, **kwargs):
self.test_flag.append("stop")
- temp_bot = Bot(token=bot.token)
+ temp_bot = DictBot(token=bot.token)
orig_stop = temp_bot.request.shutdown
try:
@@ -255,7 +261,7 @@ class TestBot:
monkeypatch.setattr(HTTPXRequest, "initialize", initialize)
monkeypatch.setattr(HTTPXRequest, "shutdown", shutdown)
- test_bot = Bot(bot.token)
+ test_bot = DictBot(bot.token)
await test_bot.initialize()
await test_bot.initialize()
await test_bot.initialize()
@@ -267,14 +273,6 @@ class TestBot:
assert self.received["init"] == 2
assert self.received["shutdown"] == 2
- async def test_multiple_init_cycles(self, bot):
- # nothing really to assert - this should just not fail
- test_bot = Bot(bot.token)
- async with test_bot:
- await test_bot.get_me()
- async with test_bot:
- await test_bot.get_me()
-
async def test_context_manager(self, monkeypatch, bot):
async def initialize():
self.test_flag = ["initialize"]
@@ -306,80 +304,24 @@ class TestBot:
assert self.test_flag == "stop"
- async def test_log_decorator(self, bot, caplog):
- # Second argument makes sure that we ignore logs from e.g. httpx
- with caplog.at_level(logging.DEBUG, logger="telegram"):
- await bot.get_me()
- # Only for stabilizing this test-
- if len(caplog.records) == 4:
- for idx, record in enumerate(caplog.records):
- print(record)
- if record.getMessage().startswith("Task was destroyed but it is pending"):
- caplog.records.pop(idx)
- if record.getMessage().startswith("Task exception was never retrieved"):
- caplog.records.pop(idx)
- assert len(caplog.records) == 3
- assert caplog.records[0].getMessage().startswith("Entering: get_me")
- assert caplog.records[-1].getMessage().startswith("Exiting: get_me")
+ async def test_equality(self):
+ async with make_bot(token=FALLBACKS[0]["token"]) as a, make_bot(
+ token=FALLBACKS[0]["token"]
+ ) as b, make_bot(token=FALLBACKS[1]["token"]) as c, Bot(token=FALLBACKS[0]["token"]) as d:
+ e = Update(123456789)
- @bot_methods(ext_bot=False)
- def test_api_methods_have_log_decorator(self, bot_class, bot_method_name, bot_method):
- """Check that all bot methods have the log decorator ..."""
- # not islower() skips the camelcase aliases
- if not bot_method_name.islower():
- return
- source = inspect.getsource(bot_method)
- assert (
- # Use re.match to only match at *the beginning* of the string
- re.match(rf"\s*\@\_log\s*async def {bot_method_name}", source)
- ), f"{bot_method_name} is missing the @_log decorator"
+ assert a == b
+ assert hash(a) == hash(b)
+ assert a is not b
- @pytest.mark.parametrize(
- "acd_in,maxsize",
- [(True, 1024), (False, 1024), (0, 0), (None, None)],
- )
- async def test_callback_data_maxsize(self, bot, acd_in, maxsize):
- async with ExtBot(bot.token, arbitrary_callback_data=acd_in) as acd_bot:
- if acd_in is not False:
- assert acd_bot.callback_data_cache.maxsize == maxsize
- else:
- assert acd_bot.callback_data_cache is None
+ assert a != c
+ assert hash(a) != hash(c)
- async def test_no_token_passed(self):
- with pytest.raises(InvalidToken, match="You must pass the token"):
- Bot("")
+ assert a != d
+ assert hash(a) != hash(d)
- async def test_invalid_token_server_response(self):
- with pytest.raises(InvalidToken, match="The token `12` was rejected by the server."):
- async with make_bot(token="12"):
- pass
-
- async def test_unknown_kwargs(self, bot, monkeypatch):
- async def post(url, request_data: RequestData, *args, **kwargs):
- data = request_data.json_parameters
- if not all([data["unknown_kwarg_1"] == "7", data["unknown_kwarg_2"] == "5"]):
- pytest.fail("got wrong parameters")
- return True
-
- monkeypatch.setattr(bot.request, "post", post)
- await bot.send_message(
- 123, "text", api_kwargs={"unknown_kwarg_1": 7, "unknown_kwarg_2": 5}
- )
-
- @pytest.mark.flaky(3, 1)
- async def test_get_me_and_properties(self, bot: Bot):
- get_me_bot = await bot.get_me()
-
- assert isinstance(get_me_bot, User)
- assert get_me_bot.id == bot.id
- assert get_me_bot.username == bot.username
- assert get_me_bot.first_name == bot.first_name
- assert get_me_bot.last_name == bot.last_name
- assert get_me_bot.name == bot.name
- assert get_me_bot.can_join_groups == bot.can_join_groups
- assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
- assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
- assert f"https://t.me/{get_me_bot.username}" == bot.link
+ assert a != e
+ assert hash(a) != hash(e)
@pytest.mark.parametrize(
"attribute",
@@ -403,35 +345,19 @@ class TestBot:
finally:
await bot.shutdown()
- async def test_equality(self):
- async with make_bot(token=FALLBACKS[0]["token"]) as a, make_bot(
- token=FALLBACKS[0]["token"]
- ) as b, make_bot(token=FALLBACKS[1]["token"]) as c, Bot(token=FALLBACKS[0]["token"]) as d:
- e = Update(123456789)
+ async def test_get_me_and_properties(self, bot):
+ get_me_bot = await ExtBot(bot.token).get_me()
- 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)
-
- @pytest.mark.flaky(3, 1)
- async def test_to_dict(self, bot):
- to_dict_bot = bot.to_dict()
-
- assert isinstance(to_dict_bot, dict)
- assert to_dict_bot["id"] == bot.id
- assert to_dict_bot["username"] == bot.username
- assert to_dict_bot["first_name"] == bot.first_name
- if bot.last_name:
- assert to_dict_bot["last_name"] == bot.last_name
+ assert isinstance(get_me_bot, User)
+ assert get_me_bot.id == bot.id
+ assert get_me_bot.username == bot.username
+ assert get_me_bot.first_name == bot.first_name
+ assert get_me_bot.last_name == bot.last_name
+ assert get_me_bot.name == bot.name
+ assert get_me_bot.can_join_groups == bot.can_join_groups
+ assert get_me_bot.can_read_all_group_messages == bot.can_read_all_group_messages
+ assert get_me_bot.supports_inline_queries == bot.supports_inline_queries
+ assert f"https://t.me/{get_me_bot.username}" == bot.link
def test_bot_pickling_error(self, bot):
with pytest.raises(pickle.PicklingError, match="Bot objects cannot be pickled"):
@@ -441,9 +367,68 @@ class TestBot:
with pytest.raises(TypeError, match="Bot objects cannot be deepcopied"):
copy.deepcopy(bot)
+ @bot_methods(ext_bot=False)
+ def test_api_methods_have_log_decorator(self, bot_class, bot_method_name, bot_method):
+ """Check that all bot methods have the log decorator ..."""
+ # not islower() skips the camelcase aliases
+ if not bot_method_name.islower():
+ return
+ source = inspect.getsource(bot_method)
+ assert (
+ # Use re.match to only match at *the beginning* of the string
+ re.match(rf"\s*\@\_log\s*async def {bot_method_name}", source)
+ ), f"{bot_method_name} is missing the @_log decorator"
+
+ async def test_log_decorator(self, bot: DictExtBot, caplog):
+ # Second argument makes sure that we ignore logs from e.g. httpx
+ with caplog.at_level(logging.DEBUG, logger="telegram"):
+ await ExtBot(bot.token).get_me()
+ # Only for stabilizing this test-
+ if len(caplog.records) == 4:
+ for idx, record in enumerate(caplog.records):
+ print(record)
+ if record.getMessage().startswith("Task was destroyed but it is pending"):
+ caplog.records.pop(idx)
+ if record.getMessage().startswith("Task exception was never retrieved"):
+ caplog.records.pop(idx)
+ assert len(caplog.records) == 3
+ assert caplog.records[0].getMessage().startswith("Entering: get_me")
+ assert caplog.records[-1].getMessage().startswith("Exiting: get_me")
+
+ @bot_methods()
+ def test_camel_case_aliases(self, bot_class, bot_method_name, bot_method):
+ camel_case_name = to_camel_case(bot_method_name)
+ camel_case_function = getattr(bot_class, camel_case_name, False)
+ assert camel_case_function is not False, f"{camel_case_name} not found"
+ assert camel_case_function is bot_method, f"{camel_case_name} is not {bot_method}"
+
+ @bot_methods()
+ def test_coroutine_functions(self, bot_class, bot_method_name, bot_method):
+ """Check that all bot methods are defined as async def ..."""
+ meth = getattr(bot_method, "__wrapped__", bot_method) # to unwrap the @_log decorator
+ assert inspect.iscoroutinefunction(meth), f"{bot_method_name} must be a coroutine function"
+
+ @bot_methods()
+ def test_api_kwargs_and_timeouts_present(self, bot_class, bot_method_name, bot_method):
+ """Check that all bot methods have `api_kwargs` and timeout params."""
+ param_names = inspect.signature(bot_method).parameters.keys()
+ params = ("pool_timeout", "read_timeout", "connect_timeout", "write_timeout", "api_kwargs")
+
+ for param in params:
+ assert param in param_names, f"{bot_method_name} is missing the parameter `{param}`"
+
+ rate_arg = "rate_limit_args"
+ if bot_method_name.replace("_", "").lower() != "getupdates" and bot_class is ExtBot:
+ assert rate_arg in param_names, f"{bot_method} is missing the parameter `{rate_arg}`"
+
@bot_methods(ext_bot=False)
async def test_defaults_handling(
- self, bot_class, bot_method_name, bot_method, bot, raw_bot, monkeypatch
+ self,
+ bot_class,
+ bot_method_name: str,
+ bot_method,
+ bot: DictExtBot,
+ raw_bot: DictBot,
):
"""
Here we check that the bot methods handle tg.ext.Defaults correctly. This has two parts:
@@ -513,473 +498,18 @@ class TestBot:
param.kind == ext_signature.parameters[param_name].kind
), f"Wrong parameter kind for parameter {param_name} of method {name}"
- @pytest.mark.flaky(3, 1)
- async def test_forward_message(self, bot, chat_id, message):
- forward_message = await bot.forward_message(
- chat_id, from_chat_id=chat_id, message_id=message.message_id
+ async def test_unknown_kwargs(self, bot, monkeypatch):
+ async def post(url, request_data: RequestData, *args, **kwargs):
+ data = request_data.json_parameters
+ if not all([data["unknown_kwarg_1"] == "7", data["unknown_kwarg_2"] == "5"]):
+ pytest.fail("got wrong parameters")
+ return True
+
+ monkeypatch.setattr(bot.request, "post", post)
+ await bot.send_message(
+ 123, "text", api_kwargs={"unknown_kwarg_1": 7, "unknown_kwarg_2": 5}
)
- assert forward_message.text == message.text
- assert forward_message.forward_from.username == message.from_user.username
- assert isinstance(forward_message.forward_date, dtm.datetime)
-
- async def test_forward_protected_message(self, bot, message, chat_id):
- to_forward_protected = await bot.send_message(
- chat_id, "cant forward me", protect_content=True
- )
- assert to_forward_protected.has_protected_content
-
- with pytest.raises(BadRequest, match="can't be forwarded"):
- await to_forward_protected.forward(chat_id)
-
- to_forward_unprotected = await bot.send_message(
- chat_id, "forward me", protect_content=False
- )
- assert not to_forward_unprotected.has_protected_content
- forwarded_but_now_protected = await to_forward_unprotected.forward(
- chat_id, protect_content=True
- )
- assert forwarded_but_now_protected.has_protected_content
- with pytest.raises(BadRequest, match="can't be forwarded"):
- await forwarded_but_now_protected.forward(chat_id)
-
- @pytest.mark.flaky(3, 1)
- async def test_delete_message(self, bot, chat_id):
- message = await bot.send_message(chat_id, text="will be deleted")
- await asyncio.sleep(2)
-
- assert await bot.delete_message(chat_id=chat_id, message_id=message.message_id) is True
-
- @pytest.mark.flaky(3, 1)
- async def test_delete_message_old_message(self, bot, chat_id):
- with pytest.raises(BadRequest):
- # Considering that the first message is old enough
- await bot.delete_message(chat_id=chat_id, message_id=1)
-
- # send_photo, send_audio, send_document, send_sticker, send_video, send_voice, send_video_note,
- # send_media_group and send_animation are tested in their respective test modules. No need to
- # duplicate here.
-
- @pytest.mark.flaky(3, 1)
- async def test_send_venue(self, bot, chat_id):
- longitude = -46.788279
- latitude = -23.691288
- title = "title"
- address = "address"
- foursquare_id = "foursquare id"
- foursquare_type = "foursquare type"
- google_place_id = "google_place id"
- google_place_type = "google_place type"
-
- message = await bot.send_venue(
- chat_id=chat_id,
- title=title,
- address=address,
- latitude=latitude,
- longitude=longitude,
- foursquare_id=foursquare_id,
- foursquare_type=foursquare_type,
- protect_content=True,
- )
-
- assert message.venue
- assert message.venue.title == title
- assert message.venue.address == address
- assert message.venue.location.latitude == latitude
- assert message.venue.location.longitude == longitude
- assert message.venue.foursquare_id == foursquare_id
- assert message.venue.foursquare_type == foursquare_type
- assert message.venue.google_place_id is None
- assert message.venue.google_place_type is None
- assert message.has_protected_content
-
- message = await bot.send_venue(
- chat_id=chat_id,
- title=title,
- address=address,
- latitude=latitude,
- longitude=longitude,
- google_place_id=google_place_id,
- google_place_type=google_place_type,
- protect_content=True,
- )
-
- assert message.venue
- assert message.venue.title == title
- assert message.venue.address == address
- assert message.venue.location.latitude == latitude
- assert message.venue.location.longitude == longitude
- assert message.venue.google_place_id == google_place_id
- assert message.venue.google_place_type == google_place_type
- assert message.venue.foursquare_id is None
- assert message.venue.foursquare_type is None
- assert message.has_protected_content
-
- @pytest.mark.flaky(3, 1)
- async def test_send_contact(self, bot, chat_id):
- phone_number = "+11234567890"
- first_name = "Leandro"
- last_name = "Toledo"
- message = await bot.send_contact(
- chat_id=chat_id,
- phone_number=phone_number,
- first_name=first_name,
- last_name=last_name,
- protect_content=True,
- )
-
- assert message.contact
- assert message.contact.phone_number == phone_number
- assert message.contact.first_name == first_name
- assert message.contact.last_name == last_name
- assert message.has_protected_content
-
- # TODO: Add bot to group to test polls too
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize(
- "reply_markup",
- [
- None,
- InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="data")
- ),
- InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="data")
- ).to_dict(),
- ],
- )
- async def test_send_and_stop_poll(self, bot, super_group_id, reply_markup):
- question = "Is this a test?"
- answers = ["Yes", "No", "Maybe"]
- message = await bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- is_anonymous=False,
- allows_multiple_answers=True,
- read_timeout=60,
- protect_content=True,
- )
-
- assert message.poll
- assert message.poll.question == question
- assert message.poll.options[0].text == answers[0]
- assert message.poll.options[1].text == answers[1]
- assert message.poll.options[2].text == answers[2]
- assert not message.poll.is_anonymous
- assert message.poll.allows_multiple_answers
- assert not message.poll.is_closed
- assert message.poll.type == Poll.REGULAR
- assert message.has_protected_content
-
- # Since only the poll and not the complete message is returned, we can't check that the
- # reply_markup is correct. So we just test that sending doesn't give an error.
- poll = await bot.stop_poll(
- chat_id=super_group_id,
- message_id=message.message_id,
- reply_markup=reply_markup,
- read_timeout=60,
- )
- assert isinstance(poll, Poll)
- assert poll.is_closed
- assert poll.options[0].text == answers[0]
- assert poll.options[0].voter_count == 0
- assert poll.options[1].text == answers[1]
- assert poll.options[1].voter_count == 0
- assert poll.options[2].text == answers[2]
- assert poll.options[2].voter_count == 0
- assert poll.question == question
- assert poll.total_voter_count == 0
-
- explanation = "[Here is a link](https://google.com)"
- explanation_entities = [
- MessageEntity(MessageEntity.TEXT_LINK, 0, 14, url="https://google.com")
- ]
- message_quiz = await bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- type=Poll.QUIZ,
- correct_option_id=2,
- is_closed=True,
- explanation=explanation,
- explanation_parse_mode=ParseMode.MARKDOWN_V2,
- )
- assert message_quiz.poll.correct_option_id == 2
- assert message_quiz.poll.type == Poll.QUIZ
- assert message_quiz.poll.is_closed
- assert message_quiz.poll.explanation == "Here is a link"
- assert message_quiz.poll.explanation_entities == tuple(explanation_entities)
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize(
- ["open_period", "close_date"], [(5, None), (None, True)], ids=["open_period", "close_date"]
- )
- async def test_send_open_period(self, bot, super_group_id, open_period, close_date):
- question = "Is this a test?"
- answers = ["Yes", "No", "Maybe"]
- reply_markup = InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="data")
- )
-
- if close_date:
- close_date = dtm.datetime.utcnow() + dtm.timedelta(seconds=5.05)
-
- message = await bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- is_anonymous=False,
- allows_multiple_answers=True,
- read_timeout=60,
- open_period=open_period,
- close_date=close_date,
- )
- await asyncio.sleep(5.1)
- new_message = await bot.edit_message_reply_markup(
- chat_id=super_group_id,
- message_id=message.message_id,
- reply_markup=reply_markup,
- read_timeout=60,
- )
- assert new_message.poll.id == message.poll.id
- assert new_message.poll.is_closed
-
- @pytest.mark.flaky(3, 1)
- async def test_send_close_date_default_tz(self, tz_bot, super_group_id):
- question = "Is this a test?"
- answers = ["Yes", "No", "Maybe"]
- reply_markup = InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="data")
- )
-
- aware_close_date = dtm.datetime.now(tz=tz_bot.defaults.tzinfo) + dtm.timedelta(seconds=5)
- close_date = aware_close_date.replace(tzinfo=None)
-
- msg = await tz_bot.send_poll( # The timezone returned from this is always converted to UTC
- chat_id=super_group_id,
- question=question,
- options=answers,
- close_date=close_date,
- read_timeout=60,
- )
- msg.poll._unfreeze()
- # Sometimes there can be a few seconds delay, so don't let the test fail due to that-
- msg.poll.close_date = msg.poll.close_date.astimezone(aware_close_date.tzinfo)
- assert abs(msg.poll.close_date - aware_close_date) <= dtm.timedelta(seconds=5)
-
- await asyncio.sleep(5.1)
-
- new_message = await tz_bot.edit_message_reply_markup(
- chat_id=super_group_id,
- message_id=msg.message_id,
- reply_markup=reply_markup,
- read_timeout=60,
- )
- assert new_message.poll.id == msg.poll.id
- assert new_message.poll.is_closed
-
- @pytest.mark.flaky(3, 1)
- async def test_send_poll_explanation_entities(self, bot, chat_id):
- 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_poll(
- chat_id,
- "question",
- options=["a", "b"],
- correct_option_id=0,
- type=Poll.QUIZ,
- explanation=test_string,
- explanation_entities=entities,
- )
-
- assert message.poll.explanation == test_string
- assert message.poll.explanation_entities == tuple(entities)
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
- async def test_send_poll_default_parse_mode(self, default_bot, super_group_id):
- explanation = "Italic Bold Code"
- explanation_markdown = "_Italic_ *Bold* `Code`"
- question = "Is this a test?"
- answers = ["Yes", "No", "Maybe"]
-
- message = await default_bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- type=Poll.QUIZ,
- correct_option_id=2,
- is_closed=True,
- explanation=explanation_markdown,
- )
- assert message.poll.explanation == explanation
- assert message.poll.explanation_entities == (
- MessageEntity(MessageEntity.ITALIC, 0, 6),
- MessageEntity(MessageEntity.BOLD, 7, 4),
- MessageEntity(MessageEntity.CODE, 12, 4),
- )
-
- message = await default_bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- type=Poll.QUIZ,
- correct_option_id=2,
- is_closed=True,
- explanation=explanation_markdown,
- explanation_parse_mode=None,
- )
- assert message.poll.explanation == explanation_markdown
- assert message.poll.explanation_entities == ()
-
- message = await default_bot.send_poll(
- chat_id=super_group_id,
- question=question,
- options=answers,
- type=Poll.QUIZ,
- correct_option_id=2,
- is_closed=True,
- explanation=explanation_markdown,
- explanation_parse_mode="HTML",
- )
- assert message.poll.explanation == explanation_markdown
- assert message.poll.explanation_entities == ()
-
- @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_poll_default_allow_sending_without_reply(
- self, default_bot, chat_id, custom
- ):
- question = "Is this a test?"
- answers = ["Yes", "No", "Maybe"]
- 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_poll(
- chat_id,
- question=question,
- options=answers,
- 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_poll(
- chat_id,
- question=question,
- options=answers,
- 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_poll(
- chat_id,
- question=question,
- options=answers,
- 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_poll_default_protect_content(self, chat_id, default_bot):
- protected_poll = await default_bot.send_poll(chat_id, "Test", ["1", "2"])
- assert protected_poll.has_protected_content
- unprotect_poll = await default_bot.send_poll(
- chat_id, "test", ["1", "2"], protect_content=False
- )
- assert not unprotect_poll.has_protected_content
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI + [None])
- async def test_send_dice(self, bot, chat_id, emoji):
- message = await bot.send_dice(chat_id, emoji=emoji, protect_content=True)
-
- assert message.dice
- assert message.has_protected_content
- if emoji is None:
- assert message.dice.emoji == Dice.DICE
- else:
- assert message.dice.emoji == emoji
-
- @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_dice_default_allow_sending_without_reply(
- self, default_bot, chat_id, 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_dice(
- chat_id,
- 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_dice(
- chat_id,
- 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_dice(
- chat_id, 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_dice_default_protect_content(self, chat_id, default_bot):
- protected_dice = await default_bot.send_dice(chat_id)
- assert protected_dice.has_protected_content
- unprotected_dice = await default_bot.send_dice(chat_id, protect_content=False)
- assert not unprotected_dice.has_protected_content
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("chat_action", list(ChatAction))
- async def test_send_chat_action(self, bot, chat_id, chat_action):
- assert await bot.send_chat_action(chat_id, chat_action)
-
- async def test_wrong_chat_action(self, bot, chat_id):
- with pytest.raises(BadRequest, match="Wrong parameter action"):
- await bot.send_chat_action(chat_id, "unknown action")
-
- async def test_send_chat_action_all_args(self, bot, chat_id, provider_token, monkeypatch):
- async def make_assertion(*args, **_):
- kwargs = args[1]
- return (
- kwargs["chat_id"] == chat_id
- and kwargs["action"] == "action"
- and kwargs["message_thread_id"] == 1
- )
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- assert await bot.send_chat_action(chat_id, "action", 1)
-
- @pytest.mark.asyncio
async def test_answer_web_app_query(self, bot, raw_bot, monkeypatch):
params = False
@@ -1024,7 +554,6 @@ class TestBot:
== copied_result.input_message_content.parse_mode
)
- @pytest.mark.asyncio
@pytest.mark.parametrize(
"default_bot",
[{"parse_mode": "Markdown", "disable_web_page_preview": True}],
@@ -1380,12 +909,6 @@ class TestBot:
copied_results[idx].input_message_content, "disable_web_page_preview", None
)
- async def test_answer_inline_query_current_offset_error(self, bot, inline_results):
- with pytest.raises(ValueError, match=("`current_offset` and `next_offset`")):
- await bot.answer_inline_query(
- 1234, results=inline_results, next_offset=42, current_offset=51
- )
-
@pytest.mark.parametrize(
"current_offset,num_results,id_offset,expected_next_offset",
[
@@ -1476,23 +999,12 @@ class TestBot:
1234, results=inline_results_callback, current_offset=6
)
- @pytest.mark.flaky(3, 1)
- async def test_get_user_profile_photos(self, bot, chat_id):
- user_profile_photos = await bot.get_user_profile_photos(chat_id)
-
- assert user_profile_photos.photos[0][0].file_size == 5403
-
- @pytest.mark.flaky(3, 1)
- async def test_get_one_user_profile_photo(self, bot, chat_id):
- user_profile_photos = await bot.get_user_profile_photos(chat_id, offset=0, limit=1)
- assert user_profile_photos.photos[0][0].file_size == 5403
-
# get_file is tested multiple times in the test_*media* modules.
# Here we only test the behaviour for bot apis in local mode
async def test_get_file_local_mode(self, bot, monkeypatch):
path = str(data_file("game.gif"))
- async def _post(*args, **kwargs):
+ async def make_assertion(*args, **kwargs):
return {
"file_id": None,
"file_unique_id": None,
@@ -1500,12 +1012,11 @@ class TestBot:
"file_path": path,
}
- monkeypatch.setattr(bot, "_post", _post)
+ monkeypatch.setattr(bot, "_post", make_assertion)
resulting_path = (await bot.get_file("file_id")).file_path
assert bot.token not in resulting_path
assert resulting_path == path
- monkeypatch.delattr(bot, "_post")
# TODO: Needs improvement. No feasible way to test until bots can add members.
async def test_ban_chat_member(self, monkeypatch, bot):
@@ -1524,7 +1035,6 @@ class TestBot:
assert await bot.ban_chat_member(2, 32, until_date=until)
assert await bot.ban_chat_member(2, 32, until_date=1577887200)
assert await bot.ban_chat_member(2, 32, revoke_messages=True)
- monkeypatch.delattr(bot.request, "post")
async def test_ban_chat_member_default_tz(self, monkeypatch, tz_bot):
until = dtm.datetime(2020, 1, 11, 16, 13)
@@ -1553,7 +1063,6 @@ class TestBot:
monkeypatch.setattr(bot.request, "post", make_assertion)
assert await bot.ban_chat_sender_chat(2, 32)
- monkeypatch.delattr(bot.request, "post")
# TODO: Needs improvement.
@pytest.mark.parametrize("only_if_banned", [True, False, None])
@@ -1620,261 +1129,6 @@ class TestBot:
23, text="answer", show_alert=True, url="no_url", cache_time=1
)
- @pytest.mark.flaky(3, 1)
- async def test_edit_message_text(self, bot, message):
- message = await bot.edit_message_text(
- text="new_text",
- chat_id=message.chat_id,
- message_id=message.message_id,
- parse_mode="HTML",
- disable_web_page_preview=True,
- )
-
- assert message.text == "new_text"
-
- @pytest.mark.flaky(3, 1)
- async def test_edit_message_text_entities(self, bot, message):
- test_string = "Italic Bold Code"
- entities = [
- MessageEntity(MessageEntity.ITALIC, 0, 6),
- MessageEntity(MessageEntity.ITALIC, 7, 4),
- MessageEntity(MessageEntity.ITALIC, 12, 4),
- ]
- message = await bot.edit_message_text(
- text=test_string,
- chat_id=message.chat_id,
- message_id=message.message_id,
- entities=entities,
- )
-
- assert message.text == test_string
- assert message.entities == tuple(entities)
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
- async def test_edit_message_text_default_parse_mode(self, default_bot, message):
- test_string = "Italic Bold Code"
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.edit_message_text(
- text=test_markdown_string,
- chat_id=message.chat_id,
- message_id=message.message_id,
- disable_web_page_preview=True,
- )
- assert message.text_markdown == test_markdown_string
- assert message.text == test_string
-
- message = await default_bot.edit_message_text(
- text=test_markdown_string,
- chat_id=message.chat_id,
- message_id=message.message_id,
- parse_mode=None,
- disable_web_page_preview=True,
- )
- assert message.text == test_markdown_string
- assert message.text_markdown == escape_markdown(test_markdown_string)
-
- message = await default_bot.edit_message_text(
- text=test_markdown_string,
- chat_id=message.chat_id,
- message_id=message.message_id,
- disable_web_page_preview=True,
- )
- message = await default_bot.edit_message_text(
- text=test_markdown_string,
- chat_id=message.chat_id,
- message_id=message.message_id,
- parse_mode="HTML",
- disable_web_page_preview=True,
- )
- assert message.text == test_markdown_string
- assert message.text_markdown == escape_markdown(test_markdown_string)
-
- @pytest.mark.skip(reason="need reference to an inline message")
- async def test_edit_message_text_inline(self):
- pass
-
- @pytest.mark.flaky(3, 1)
- async def test_edit_message_caption(self, bot, media_message):
- message = await bot.edit_message_caption(
- caption="new_caption",
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- )
-
- assert message.caption == "new_caption"
-
- @pytest.mark.flaky(3, 1)
- async def test_edit_message_caption_entities(self, bot, media_message):
- test_string = "Italic Bold Code"
- entities = [
- MessageEntity(MessageEntity.ITALIC, 0, 6),
- MessageEntity(MessageEntity.ITALIC, 7, 4),
- MessageEntity(MessageEntity.ITALIC, 12, 4),
- ]
- message = await bot.edit_message_caption(
- caption=test_string,
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- caption_entities=entities,
- )
-
- assert message.caption == test_string
- assert message.caption_entities == tuple(entities)
-
- # edit_message_media is tested in test_inputmedia
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
- async def test_edit_message_caption_default_parse_mode(self, default_bot, media_message):
- test_string = "Italic Bold Code"
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.edit_message_caption(
- caption=test_markdown_string,
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- )
- assert message.caption_markdown == test_markdown_string
- assert message.caption == test_string
-
- message = await default_bot.edit_message_caption(
- caption=test_markdown_string,
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- parse_mode=None,
- )
- assert message.caption == test_markdown_string
- assert message.caption_markdown == escape_markdown(test_markdown_string)
-
- message = await default_bot.edit_message_caption(
- caption=test_markdown_string,
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- )
- message = await default_bot.edit_message_caption(
- caption=test_markdown_string,
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- parse_mode="HTML",
- )
- assert message.caption == test_markdown_string
- assert message.caption_markdown == escape_markdown(test_markdown_string)
-
- @pytest.mark.flaky(3, 1)
- async def test_edit_message_caption_with_parse_mode(self, bot, media_message):
- message = await bot.edit_message_caption(
- caption="new *caption*",
- parse_mode="Markdown",
- chat_id=media_message.chat_id,
- message_id=media_message.message_id,
- )
-
- assert message.caption == "new caption"
-
- @pytest.mark.skip(reason="need reference to an inline message")
- async def test_edit_message_caption_inline(self):
- pass
-
- @pytest.mark.flaky(3, 1)
- async def test_edit_reply_markup(self, bot, message):
- new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text="test", callback_data="1")]])
- message = await bot.edit_message_reply_markup(
- chat_id=message.chat_id, message_id=message.message_id, reply_markup=new_markup
- )
-
- assert message is not True
-
- @pytest.mark.skip(reason="need reference to an inline message")
- async def test_edit_reply_markup_inline(self):
- pass
-
- # TODO: Actually send updates to the test bot so this can be tested properly
- @pytest.mark.flaky(3, 1)
- async def test_get_updates(self, bot):
- await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
- updates = await bot.get_updates(timeout=1)
-
- assert isinstance(updates, tuple)
- if updates:
- assert isinstance(updates[0], Update)
-
- async def test_get_updates_invalid_callback_data(self, cdc_bot, monkeypatch):
- bot = cdc_bot
-
- async def post(*args, **kwargs):
- return [
- Update(
- 17,
- callback_query=CallbackQuery(
- id=1,
- from_user=None,
- chat_instance=123,
- data="invalid data",
- message=Message(
- 1,
- from_user=User(1, "", False),
- date=dtm.datetime.utcnow(),
- chat=Chat(1, ""),
- text="Webhook",
- ),
- ),
- ).to_dict()
- ]
-
- try:
- await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
- monkeypatch.setattr(BaseRequest, "post", post)
- updates = await bot.get_updates(timeout=1)
-
- assert isinstance(updates, tuple)
- assert len(updates) == 1
- assert isinstance(updates[0].callback_query.data, InvalidCallbackData)
-
- finally:
- # Reset b/c bots scope is session
- bot.callback_data_cache.clear_callback_data()
- bot.callback_data_cache.clear_callback_queries()
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("use_ip", [True, False])
- # local file path as file_input is tested below in test_set_webhook_params
- @pytest.mark.parametrize("file_input", ["bytes", "file_handle"])
- async def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot, use_ip, file_input):
- url = "https://python-telegram-bot.org/test/webhook"
- # Get the ip address of the website - dynamically just in case it ever changes
- ip = socket.gethostbyname("python-telegram-bot.org")
- max_connections = 7
- allowed_updates = ["message"]
- file_input = (
- data_file("sslcert.pem").read_bytes()
- if file_input == "bytes"
- else data_file("sslcert.pem").open("rb")
- )
- await bot.set_webhook(
- url,
- max_connections=max_connections,
- allowed_updates=allowed_updates,
- ip_address=ip if use_ip else None,
- certificate=file_input if use_ip else None,
- )
-
- await asyncio.sleep(1)
- live_info = await bot.get_webhook_info()
- assert live_info.url == url
- assert live_info.max_connections == max_connections
- assert live_info.allowed_updates == tuple(allowed_updates)
- assert live_info.ip_address == ip
- assert live_info.has_custom_certificate == use_ip
-
- await bot.delete_webhook()
- await asyncio.sleep(1)
- info = await bot.get_webhook_info()
- assert info.url == ""
- assert info.ip_address is None
- assert info.has_custom_certificate is False
-
@pytest.mark.parametrize("drop_pending_updates", [True, False])
async def test_set_webhook_delete_webhook_drop_pending_updates(
self, bot, drop_pending_updates, monkeypatch
@@ -1934,207 +1188,6 @@ class TestBot:
"SoSecretToken",
)
- @pytest.mark.flaky(3, 1)
- async def test_leave_chat(self, bot):
- with pytest.raises(BadRequest, match="Chat not found"):
- await bot.leave_chat(-123456)
-
- with pytest.raises(NetworkError, match="Chat not found"):
- await bot.leave_chat(-123456)
-
- @pytest.mark.flaky(3, 1)
- async def test_get_chat(self, bot, super_group_id):
- chat = await bot.get_chat(super_group_id)
-
- assert chat.type == "supergroup"
- assert chat.title == f">>> telegram.Bot(test) @{bot.username}"
- assert chat.id == int(super_group_id)
-
- @pytest.mark.flaky(3, 1)
- async def test_get_chat_administrators(self, bot, channel_id):
- admins = await bot.get_chat_administrators(channel_id)
- assert isinstance(admins, tuple)
-
- for a in admins:
- assert a.status in ("administrator", "creator")
-
- @pytest.mark.flaky(3, 1)
- async def test_get_chat_member_count(self, bot, channel_id):
- count = await bot.get_chat_member_count(channel_id)
- assert isinstance(count, int)
- assert count > 3
-
- @pytest.mark.flaky(3, 1)
- async def test_get_chat_member(self, bot, channel_id, chat_id):
- chat_member = await bot.get_chat_member(channel_id, chat_id)
-
- assert chat_member.status == "administrator"
- assert chat_member.user.first_name == "PTB"
- assert chat_member.user.last_name == "Test user"
-
- @pytest.mark.skip(reason="Not implemented since we need a supergroup with many members")
- async def test_set_chat_sticker_set(self):
- pass
-
- @pytest.mark.skip(reason="Not implemented since we need a supergroup with many members")
- async def test_delete_chat_sticker_set(self):
- pass
-
- @pytest.mark.flaky(3, 1)
- async def test_send_game(self, bot, chat_id):
- game_short_name = "test_game"
- message = await bot.send_game(chat_id, game_short_name, protect_content=True)
-
- assert message.game
- assert message.game.description == (
- "A no-op test game, for python-telegram-bot bot framework testing."
- )
- assert message.game.animation.file_id != ""
- # We added some test bots later and for some reason the file size is not the same for them
- # so we accept three different sizes here. Shouldn't be too much of
- assert message.game.photo[0].file_size in [851, 4928, 850]
- assert message.has_protected_content
-
- @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_game_default_allow_sending_without_reply(
- self, default_bot, chat_id, custom
- ):
- game_short_name = "test_game"
- 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_game(
- chat_id,
- game_short_name,
- 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_game(
- chat_id,
- game_short_name,
- 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_game(
- chat_id, game_short_name, reply_to_message_id=reply_to_message.message_id
- )
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize(
- "default_bot,val",
- [({"protect_content": True}, True), ({"protect_content": False}, None)],
- indirect=["default_bot"],
- )
- async def test_send_game_default_protect_content(self, default_bot, chat_id, val):
- protected = await default_bot.send_game(chat_id, "test_game", protect_content=val)
- assert protected.has_protected_content is val
-
- @xfail
- async def test_set_game_score_1(self, bot, chat_id):
- # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
- # First, test setting a score.
- game_short_name = "test_game"
- game = await bot.send_game(chat_id, game_short_name)
-
- message = await bot.set_game_score(
- user_id=chat_id,
- score=BASE_GAME_SCORE, # Score value is relevant for other set_game_score_* tests!
- chat_id=game.chat_id,
- message_id=game.message_id,
- )
-
- assert message.game.description == game.game.description
- assert message.game.photo[0].file_size == game.game.photo[0].file_size
- assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
- assert message.game.text != game.game.text
-
- @xfail
- async def test_set_game_score_2(self, bot, chat_id):
- # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
- # Test setting a score higher than previous
- game_short_name = "test_game"
- game = await bot.send_game(chat_id, game_short_name)
-
- score = BASE_GAME_SCORE + 1
-
- message = await bot.set_game_score(
- user_id=chat_id,
- score=score,
- chat_id=game.chat_id,
- message_id=game.message_id,
- disable_edit_message=True,
- )
-
- assert message.game.description == game.game.description
- assert message.game.photo[0].file_size == game.game.photo[0].file_size
- assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
- assert message.game.text == game.game.text
-
- @xfail
- async def test_set_game_score_3(self, bot, chat_id):
- # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
- # Test setting a score lower than previous (should raise error)
- game_short_name = "test_game"
- game = await bot.send_game(chat_id, game_short_name)
-
- score = BASE_GAME_SCORE # Even a score equal to previous raises an error.
-
- with pytest.raises(BadRequest, match="Bot_score_not_modified"):
- await bot.set_game_score(
- user_id=chat_id, score=score, chat_id=game.chat_id, message_id=game.message_id
- )
-
- @xfail
- async def test_set_game_score_4(self, bot, chat_id):
- # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
- # Test force setting a lower score
- game_short_name = "test_game"
- game = await bot.send_game(chat_id, game_short_name)
- await asyncio.sleep(1.5)
-
- score = BASE_GAME_SCORE - 10
-
- message = await bot.set_game_score(
- user_id=chat_id,
- score=score,
- chat_id=game.chat_id,
- message_id=game.message_id,
- force=True,
- )
-
- assert message.game.description == game.game.description
- assert message.game.photo[0].file_size == game.game.photo[0].file_size
- assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
-
- # For some reason the returned message doesn't contain the updated score. need to fetch
- # the game again... (the service message is also absent when running the test suite)
- game2 = await bot.send_game(chat_id, game_short_name)
- assert str(score) in game2.game.text
-
- @xfail
- async def test_get_game_high_scores(self, bot, chat_id):
- # We need a game to get the scores for
- game_short_name = "test_game"
- game = await bot.send_game(chat_id, game_short_name)
- high_scores = await bot.get_game_high_scores(chat_id, game.chat_id, game.message_id)
- # We assume that the other game score tests ran within 20 sec
- assert high_scores[0].score == BASE_GAME_SCORE - 10
-
- # send_invoice and create_invoice_link is tested in test_invoice
-
# TODO: Needs improvement. Need incoming shipping queries to test
async def test_answer_shipping_query_ok(self, monkeypatch, bot):
# For now just test that our internals pass the correct data
@@ -2223,7 +1276,1272 @@ class TestBot:
channel_id, 95205500, chat_permissions, until_date=until_timestamp
)
- @pytest.mark.flaky(3, 1)
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_set_chat_photo_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
+ self.test_flag = False
+ file = data_file("telegram.jpg")
+ expected = file.as_uri()
+
+ async def make_assertion(_, data, *args, **kwargs):
+ if local_mode:
+ self.test_flag = data.get("photo") == expected
+ else:
+ self.test_flag = isinstance(data.get("photo"), InputFile)
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.set_chat_photo(chat_id, file)
+ assert self.test_flag
+ finally:
+ bot._local_mode = False
+
+ async def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id):
+ # Use BaseException that's not a subclass of Exception such that
+ # OkException should not be caught anywhere
+ class OkException(BaseException):
+ pass
+
+ timeout = 42
+
+ async def do_request(*args, **kwargs):
+ obj = kwargs.get("read_timeout")
+ if obj == timeout:
+ raise OkException
+
+ return 200, b'{"ok": true, "result": []}'
+
+ monkeypatch.setattr(bot.request, "do_request", do_request)
+
+ # Test file uploading
+ with pytest.raises(OkException):
+ await bot.send_photo(
+ chat_id, data_file("telegram.jpg").open("rb"), read_timeout=timeout
+ )
+
+ # Test JSON submission
+ with pytest.raises(OkException):
+ await bot.get_chat_administrators(chat_id, read_timeout=timeout)
+
+ async def test_timeout_propagation_implicit(self, monkeypatch, bot, chat_id):
+ # Use BaseException that's not a subclass of Exception such that
+ # OkException should not be caught anywhere
+ class OkException(BaseException):
+ pass
+
+ async def do_request(*args, **kwargs):
+ obj = kwargs.get("write_timeout")
+ if obj == 20:
+ raise OkException
+
+ return 200, b'{"ok": true, "result": []}'
+
+ monkeypatch.setattr(bot.request, "do_request", do_request)
+
+ # Test file uploading
+ with pytest.raises(OkException):
+ await bot.send_photo(chat_id, data_file("telegram.jpg").open("rb"))
+
+ async def test_log_out(self, monkeypatch, bot):
+ # We don't actually make a request as to not break the test setup
+ async def assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters == {} and url.split("/")[-1] == "logOut"
+
+ monkeypatch.setattr(bot.request, "post", assertion)
+
+ assert await bot.log_out()
+
+ async def test_close(self, monkeypatch, bot):
+ # We don't actually make a request as to not break the test setup
+ async def assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters == {} and url.split("/")[-1] == "close"
+
+ monkeypatch.setattr(bot.request, "post", assertion)
+
+ assert await bot.close()
+
+ @pytest.mark.parametrize("json_keyboard", [True, False])
+ @pytest.mark.parametrize("caption", ["Test", "", None])
+ async def test_copy_message(
+ self, monkeypatch, bot, chat_id, media_message, json_keyboard, caption
+ ):
+ keyboard = InlineKeyboardMarkup(
+ [[InlineKeyboardButton(text="test", callback_data="test2")]]
+ )
+
+ async def post(url, request_data: RequestData, *args, **kwargs):
+ data = request_data.parameters
+ if not all(
+ [
+ data["chat_id"] == chat_id,
+ data["from_chat_id"] == chat_id,
+ data["message_id"] == media_message.message_id,
+ data.get("caption") == caption,
+ data["parse_mode"] == ParseMode.HTML,
+ data["reply_to_message_id"] == media_message.message_id,
+ data["reply_markup"] == keyboard.to_json()
+ if json_keyboard
+ else keyboard.to_dict(),
+ data["disable_notification"] is True,
+ data["caption_entities"]
+ == [MessageEntity(MessageEntity.BOLD, 0, 4).to_dict()],
+ data["protect_content"] is True,
+ data["message_thread_id"] == 1,
+ ]
+ ):
+ pytest.fail("I got wrong parameters in post")
+ return data
+
+ monkeypatch.setattr(bot.request, "post", post)
+ await bot.copy_message(
+ chat_id,
+ from_chat_id=chat_id,
+ message_id=media_message.message_id,
+ caption=caption,
+ caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 4)],
+ parse_mode=ParseMode.HTML,
+ reply_to_message_id=media_message.message_id,
+ reply_markup=keyboard.to_json() if json_keyboard else keyboard,
+ disable_notification=True,
+ protect_content=True,
+ message_thread_id=1,
+ )
+
+ # In the following tests we check that get_updates inserts callback data correctly if necessary
+ # The same must be done in the webhook updater. This is tested over at test_updater.py, but
+ # here we test more extensively.
+
+ @pytest.mark.parametrize(
+ "acd_in,maxsize",
+ [(True, 1024), (False, 1024), (0, 0), (None, None)],
+ )
+ async def test_callback_data_maxsize(self, bot_info, acd_in, maxsize):
+ async with make_bot(bot_info, arbitrary_callback_data=acd_in) as acd_bot:
+ if acd_in is not False:
+ assert acd_bot.callback_data_cache.maxsize == maxsize
+ else:
+ assert acd_bot.callback_data_cache is None
+
+ async def test_arbitrary_callback_data_no_insert(self, monkeypatch, cdc_bot):
+ """Updates that don't need insertion shouldn't fail obviously"""
+ bot = cdc_bot
+
+ async def post(*args, **kwargs):
+ update = Update(
+ 17,
+ poll=Poll(
+ "42",
+ "question",
+ options=[PollOption("option", 0)],
+ total_voter_count=0,
+ is_closed=False,
+ is_anonymous=True,
+ type=Poll.REGULAR,
+ allows_multiple_answers=False,
+ ),
+ )
+ return [update.to_dict()]
+
+ try:
+ monkeypatch.setattr(BaseRequest, "post", post)
+ updates = await bot.get_updates(timeout=1)
+
+ assert len(updates) == 1
+ assert updates[0].update_id == 17
+ assert updates[0].poll.id == "42"
+ finally:
+ bot.callback_data_cache.clear_callback_data()
+ bot.callback_data_cache.clear_callback_queries()
+
+ @pytest.mark.parametrize(
+ "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"]
+ )
+ async def test_arbitrary_callback_data_pinned_message_reply_to_message(
+ self, cdc_bot, monkeypatch, message_type
+ ):
+ bot = cdc_bot
+
+ reply_markup = InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="callback_data")
+ )
+
+ message = Message(
+ 1,
+ dtm.datetime.utcnow(),
+ None,
+ reply_markup=bot.callback_data_cache.process_keyboard(reply_markup),
+ )
+ message._unfreeze()
+ # We do to_dict -> de_json to make sure those aren't the same objects
+ message.pinned_message = Message.de_json(message.to_dict(), bot)
+
+ async def post(*args, **kwargs):
+ update = Update(
+ 17,
+ **{
+ message_type: Message(
+ 1,
+ dtm.datetime.utcnow(),
+ None,
+ pinned_message=message,
+ reply_to_message=Message.de_json(message.to_dict(), bot),
+ )
+ },
+ )
+ return [update.to_dict()]
+
+ try:
+ monkeypatch.setattr(BaseRequest, "post", post)
+ updates = await bot.get_updates(timeout=1)
+
+ assert isinstance(updates, tuple)
+ assert len(updates) == 1
+
+ effective_message = updates[0][message_type]
+ assert (
+ effective_message.reply_to_message.reply_markup.inline_keyboard[0][0].callback_data
+ == "callback_data"
+ )
+ assert (
+ effective_message.pinned_message.reply_markup.inline_keyboard[0][0].callback_data
+ == "callback_data"
+ )
+
+ pinned_message = effective_message.reply_to_message.pinned_message
+ assert (
+ pinned_message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data"
+ )
+
+ finally:
+ bot.callback_data_cache.clear_callback_data()
+ bot.callback_data_cache.clear_callback_queries()
+
+ async def test_get_updates_invalid_callback_data(self, cdc_bot, monkeypatch):
+ bot = cdc_bot
+
+ async def post(*args, **kwargs):
+ return [
+ Update(
+ 17,
+ callback_query=CallbackQuery(
+ id=1,
+ from_user=None,
+ chat_instance=123,
+ data="invalid data",
+ message=Message(
+ 1,
+ from_user=User(1, "", False),
+ date=dtm.datetime.utcnow(),
+ chat=Chat(1, ""),
+ text="Webhook",
+ ),
+ ),
+ ).to_dict()
+ ]
+
+ try:
+ monkeypatch.setattr(BaseRequest, "post", post)
+ updates = await bot.get_updates(timeout=1)
+
+ assert isinstance(updates, tuple)
+ assert len(updates) == 1
+ assert isinstance(updates[0].callback_query.data, InvalidCallbackData)
+
+ finally:
+ # Reset b/c bots scope is session
+ bot.callback_data_cache.clear_callback_data()
+ bot.callback_data_cache.clear_callback_queries()
+
+ # TODO: Needs improvement. We need incoming inline query to test answer.
+ async def test_replace_callback_data_answer_inline_query(self, monkeypatch, cdc_bot, chat_id):
+ bot = cdc_bot
+
+ # For now just test that our internals pass the correct data
+ async def make_assertion(
+ endpoint,
+ data=None,
+ *args,
+ **kwargs,
+ ):
+ inline_keyboard = data["results"][0]["reply_markup"].inline_keyboard
+ assertion_1 = inline_keyboard[0][1] == no_replace_button
+ assertion_2 = inline_keyboard[0][0] != replace_button
+ keyboard, button = (
+ inline_keyboard[0][0].callback_data[:32],
+ inline_keyboard[0][0].callback_data[32:],
+ )
+ assertion_3 = (
+ bot.callback_data_cache._keyboard_data[keyboard].button_data[button]
+ == "replace_test"
+ )
+ assertion_4 = data["results"][1].reply_markup is None
+ return assertion_1 and assertion_2 and assertion_3 and assertion_4
+
+ try:
+ replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test")
+ no_replace_button = InlineKeyboardButton(
+ text="no_replace", url="http://python-telegram-bot.org/"
+ )
+ reply_markup = InlineKeyboardMarkup.from_row(
+ [
+ replace_button,
+ no_replace_button,
+ ]
+ )
+
+ bot.username # call this here so `bot.get_me()` won't be called after mocking
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ results = [
+ InlineQueryResultArticle(
+ "11", "first", InputTextMessageContent("first"), reply_markup=reply_markup
+ ),
+ InlineQueryResultVoice(
+ "22",
+ "https://python-telegram-bot.org/static/testfiles/telegram.ogg",
+ title="second",
+ ),
+ ]
+
+ assert await bot.answer_inline_query(chat_id, results=results)
+
+ finally:
+ bot.callback_data_cache.clear_callback_data()
+ bot.callback_data_cache.clear_callback_queries()
+
+ @pytest.mark.parametrize(
+ "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"]
+ )
+ @pytest.mark.parametrize("self_sender", [True, False])
+ async def test_arbitrary_callback_data_via_bot(
+ self, cdc_bot, monkeypatch, self_sender, message_type
+ ):
+ bot = cdc_bot
+ reply_markup = InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="callback_data")
+ )
+
+ reply_markup = bot.callback_data_cache.process_keyboard(reply_markup)
+ message = Message(
+ 1,
+ dtm.datetime.utcnow(),
+ None,
+ reply_markup=reply_markup,
+ via_bot=bot.bot if self_sender else User(1, "first", False),
+ )
+
+ async def post(*args, **kwargs):
+ return [Update(17, **{message_type: message}).to_dict()]
+
+ try:
+ monkeypatch.setattr(BaseRequest, "post", post)
+ updates = await bot.get_updates(timeout=1)
+
+ assert isinstance(updates, tuple)
+ assert len(updates) == 1
+
+ message = updates[0][message_type]
+ if self_sender:
+ assert message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data"
+ else:
+ assert (
+ message.reply_markup.inline_keyboard[0][0].callback_data
+ == reply_markup.inline_keyboard[0][0].callback_data
+ )
+ finally:
+ bot.callback_data_cache.clear_callback_data()
+ bot.callback_data_cache.clear_callback_queries()
+
+
+class TestBotWithRequest:
+ """
+ Most are executed on tg.ext.ExtBot, as that class only extends the functionality of tg.bot
+
+ Behavior for init of ExtBot with missing optional dependency cachetools (for CallbackDataCache)
+ is tested in `test_callbackdatacache`
+ """
+
+ async def test_invalid_token_server_response(self):
+ with pytest.raises(InvalidToken, match="The token `12` was rejected by the server."):
+ async with ExtBot(token="12"):
+ pass
+
+ async def test_multiple_init_cycles(self, bot):
+ # nothing really to assert - this should just not fail
+ test_bot = Bot(bot.token)
+ async with test_bot:
+ await test_bot.get_me()
+ async with test_bot:
+ await test_bot.get_me()
+
+ async def test_forward_message(self, bot, chat_id, message):
+ forward_message = await bot.forward_message(
+ chat_id, from_chat_id=chat_id, message_id=message.message_id
+ )
+
+ assert forward_message.text == message.text
+ assert forward_message.forward_from.username == message.from_user.username
+ assert isinstance(forward_message.forward_date, dtm.datetime)
+
+ async def test_forward_protected_message(self, bot, chat_id):
+ tasks = asyncio.gather(
+ bot.send_message(chat_id, "cant forward me", protect_content=True),
+ bot.send_message(chat_id, "forward me", protect_content=False),
+ )
+ to_forward_protected, to_forward_unprotected = await tasks
+
+ assert to_forward_protected.has_protected_content
+ assert not to_forward_unprotected.has_protected_content
+
+ forwarded_but_now_protected = await to_forward_unprotected.forward(
+ chat_id, protect_content=True
+ )
+ assert forwarded_but_now_protected.has_protected_content
+
+ tasks = asyncio.gather(
+ to_forward_protected.forward(chat_id),
+ forwarded_but_now_protected.forward(chat_id),
+ return_exceptions=True,
+ )
+ result = await tasks
+ assert all("can't be forwarded" in str(exc) for exc in result)
+
+ async def test_delete_message(self, bot, chat_id):
+ message = await bot.send_message(chat_id, text="will be deleted")
+ await asyncio.sleep(2)
+
+ assert await bot.delete_message(chat_id=chat_id, message_id=message.message_id) is True
+
+ async def test_delete_message_old_message(self, bot, chat_id):
+ with pytest.raises(BadRequest):
+ # Considering that the first message is old enough
+ await bot.delete_message(chat_id=chat_id, message_id=1)
+
+ # send_photo, send_audio, send_document, send_sticker, send_video, send_voice, send_video_note,
+ # send_media_group and send_animation are tested in their respective test modules. No need to
+ # duplicate here.
+
+ async def test_send_venue(self, bot, chat_id):
+ longitude = -46.788279
+ latitude = -23.691288
+ title = "title"
+ address = "address"
+ foursquare_id = "foursquare id"
+ foursquare_type = "foursquare type"
+ google_place_id = "google_place id"
+ google_place_type = "google_place type"
+
+ tasks = asyncio.gather(
+ *(
+ bot.send_venue(
+ chat_id=chat_id,
+ title=title,
+ address=address,
+ latitude=latitude,
+ longitude=longitude,
+ protect_content=True,
+ **i,
+ )
+ for i in (
+ {"foursquare_id": foursquare_id, "foursquare_type": foursquare_type},
+ {"google_place_id": google_place_id, "google_place_type": google_place_type},
+ )
+ ),
+ )
+
+ message, message2 = await tasks
+ assert message.venue
+ assert message.venue.title == title
+ assert message.venue.address == address
+ assert message.venue.location.latitude == latitude
+ assert message.venue.location.longitude == longitude
+ assert message.venue.foursquare_id == foursquare_id
+ assert message.venue.foursquare_type == foursquare_type
+ assert message.venue.google_place_id is None
+ assert message.venue.google_place_type is None
+ assert message.has_protected_content
+
+ assert message2.venue
+ assert message2.venue.title == title
+ assert message2.venue.address == address
+ assert message2.venue.location.latitude == latitude
+ assert message2.venue.location.longitude == longitude
+ assert message2.venue.google_place_id == google_place_id
+ assert message2.venue.google_place_type == google_place_type
+ assert message2.venue.foursquare_id is None
+ assert message2.venue.foursquare_type is None
+ assert message2.has_protected_content
+
+ async def test_send_contact(self, bot, chat_id):
+ phone_number = "+11234567890"
+ first_name = "Leandro"
+ last_name = "Toledo"
+ message = await bot.send_contact(
+ chat_id=chat_id,
+ phone_number=phone_number,
+ first_name=first_name,
+ last_name=last_name,
+ protect_content=True,
+ )
+
+ assert message.contact
+ assert message.contact.phone_number == phone_number
+ assert message.contact.first_name == first_name
+ assert message.contact.last_name == last_name
+ assert message.has_protected_content
+
+ async def test_send_chat_action_all_args(self, bot, chat_id, monkeypatch):
+ async def make_assertion(*args, **_):
+ kwargs = args[1]
+ return (
+ kwargs["chat_id"] == chat_id
+ and kwargs["action"] == "action"
+ and kwargs["message_thread_id"] == 1
+ )
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ assert await bot.send_chat_action(chat_id, "action", 1)
+
+ # TODO: Add bot to group to test polls too
+ @pytest.mark.parametrize(
+ "reply_markup",
+ [
+ None,
+ InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="data")
+ ),
+ InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="data")
+ ).to_dict(),
+ ],
+ )
+ async def test_send_and_stop_poll(self, bot, super_group_id, reply_markup):
+ question = "Is this a test?"
+ answers = ["Yes", "No", "Maybe"]
+ explanation = "[Here is a link](https://google.com)"
+ explanation_entities = [
+ MessageEntity(MessageEntity.TEXT_LINK, 0, 14, url="https://google.com")
+ ]
+
+ poll_task = asyncio.create_task(
+ bot.send_poll(
+ chat_id=super_group_id,
+ question=question,
+ options=answers,
+ is_anonymous=False,
+ allows_multiple_answers=True,
+ read_timeout=60,
+ protect_content=True,
+ )
+ )
+ quiz_task = asyncio.create_task(
+ bot.send_poll(
+ chat_id=super_group_id,
+ question=question,
+ options=answers,
+ type=Poll.QUIZ,
+ correct_option_id=2,
+ is_closed=True,
+ explanation=explanation,
+ explanation_parse_mode=ParseMode.MARKDOWN_V2,
+ )
+ )
+
+ message = await poll_task
+ assert message.poll
+ assert message.poll.question == question
+ assert message.poll.options[0].text == answers[0]
+ assert message.poll.options[1].text == answers[1]
+ assert message.poll.options[2].text == answers[2]
+ assert not message.poll.is_anonymous
+ assert message.poll.allows_multiple_answers
+ assert not message.poll.is_closed
+ assert message.poll.type == Poll.REGULAR
+ assert message.has_protected_content
+
+ # Since only the poll and not the complete message is returned, we can't check that the
+ # reply_markup is correct. So we just test that sending doesn't give an error.
+ poll = await bot.stop_poll(
+ chat_id=super_group_id,
+ message_id=message.message_id,
+ reply_markup=reply_markup,
+ read_timeout=60,
+ )
+ assert isinstance(poll, Poll)
+ assert poll.is_closed
+ assert poll.options[0].text == answers[0]
+ assert poll.options[0].voter_count == 0
+ assert poll.options[1].text == answers[1]
+ assert poll.options[1].voter_count == 0
+ assert poll.options[2].text == answers[2]
+ assert poll.options[2].voter_count == 0
+ assert poll.question == question
+ assert poll.total_voter_count == 0
+
+ message_quiz = await quiz_task
+ assert message_quiz.poll.correct_option_id == 2
+ assert message_quiz.poll.type == Poll.QUIZ
+ assert message_quiz.poll.is_closed
+ assert message_quiz.poll.explanation == "Here is a link"
+ assert message_quiz.poll.explanation_entities == tuple(explanation_entities)
+ assert poll_task.done() and quiz_task.done()
+
+ @pytest.mark.parametrize(
+ ["open_period", "close_date"], [(5, None), (None, True)], ids=["open_period", "close_date"]
+ )
+ async def test_send_open_period(self, bot, super_group_id, open_period, close_date):
+ question = "Is this a test?"
+ answers = ["Yes", "No", "Maybe"]
+ reply_markup = InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="data")
+ )
+
+ if close_date:
+ close_date = dtm.datetime.utcnow() + dtm.timedelta(seconds=5.05)
+
+ message = await bot.send_poll(
+ chat_id=super_group_id,
+ question=question,
+ options=answers,
+ is_anonymous=False,
+ allows_multiple_answers=True,
+ read_timeout=60,
+ open_period=open_period,
+ close_date=close_date,
+ )
+ await asyncio.sleep(5.1)
+ new_message = await bot.edit_message_reply_markup(
+ chat_id=super_group_id,
+ message_id=message.message_id,
+ reply_markup=reply_markup,
+ read_timeout=60,
+ )
+ assert new_message.poll.id == message.poll.id
+ assert new_message.poll.is_closed
+
+ async def test_send_close_date_default_tz(self, tz_bot, super_group_id):
+ question = "Is this a test?"
+ answers = ["Yes", "No", "Maybe"]
+ reply_markup = InlineKeyboardMarkup.from_button(
+ InlineKeyboardButton(text="text", callback_data="data")
+ )
+
+ aware_close_date = dtm.datetime.now(tz=tz_bot.defaults.tzinfo) + dtm.timedelta(seconds=5)
+ close_date = aware_close_date.replace(tzinfo=None)
+
+ msg = await tz_bot.send_poll( # The timezone returned from this is always converted to UTC
+ chat_id=super_group_id,
+ question=question,
+ options=answers,
+ close_date=close_date,
+ read_timeout=60,
+ )
+ msg.poll._unfreeze()
+ # Sometimes there can be a few seconds delay, so don't let the test fail due to that-
+ msg.poll.close_date = msg.poll.close_date.astimezone(aware_close_date.tzinfo)
+ assert abs(msg.poll.close_date - aware_close_date) <= dtm.timedelta(seconds=5)
+
+ await asyncio.sleep(5.1)
+
+ new_message = await tz_bot.edit_message_reply_markup(
+ chat_id=super_group_id,
+ message_id=msg.message_id,
+ reply_markup=reply_markup,
+ read_timeout=60,
+ )
+ assert new_message.poll.id == msg.poll.id
+ assert new_message.poll.is_closed
+
+ async def test_send_poll_explanation_entities(self, bot, chat_id):
+ 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_poll(
+ chat_id,
+ "question",
+ options=["a", "b"],
+ correct_option_id=0,
+ type=Poll.QUIZ,
+ explanation=test_string,
+ explanation_entities=entities,
+ )
+
+ assert message.poll.explanation == test_string
+ assert message.poll.explanation_entities == tuple(entities)
+
+ @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
+ async def test_send_poll_default_parse_mode(self, default_bot, super_group_id):
+ explanation = "Italic Bold Code"
+ explanation_markdown = "_Italic_ *Bold* `Code`"
+ question = "Is this a test?"
+ answers = ["Yes", "No", "Maybe"]
+
+ tasks = asyncio.gather(
+ *(
+ default_bot.send_poll(
+ chat_id=super_group_id,
+ question=question,
+ options=answers,
+ type=Poll.QUIZ,
+ correct_option_id=2,
+ is_closed=True,
+ explanation=explanation_markdown,
+ **i,
+ )
+ for i in ({}, {"explanation_parse_mode": None}, {"explanation_parse_mode": "HTML"})
+ ),
+ )
+ message1, message2, message3 = await tasks
+ assert message1.poll.explanation == explanation
+ assert message1.poll.explanation_entities == (
+ MessageEntity(MessageEntity.ITALIC, 0, 6),
+ MessageEntity(MessageEntity.BOLD, 7, 4),
+ MessageEntity(MessageEntity.CODE, 12, 4),
+ )
+
+ assert message2.poll.explanation == explanation_markdown
+ assert message2.poll.explanation_entities == ()
+
+ assert message3.poll.explanation == explanation_markdown
+ assert message3.poll.explanation_entities == ()
+
+ @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_poll_default_allow_sending_without_reply(
+ self, default_bot, chat_id, custom
+ ):
+ question = "Is this a test?"
+ answers = ["Yes", "No", "Maybe"]
+ 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_poll(
+ chat_id,
+ question=question,
+ options=answers,
+ 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_poll(
+ chat_id,
+ question=question,
+ options=answers,
+ 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_poll(
+ chat_id,
+ question=question,
+ options=answers,
+ reply_to_message_id=reply_to_message.message_id,
+ )
+
+ @pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
+ async def test_send_poll_default_protect_content(self, chat_id, default_bot):
+ tasks = asyncio.gather(
+ default_bot.send_poll(chat_id, "Test", ["1", "2"]),
+ default_bot.send_poll(chat_id, "test", ["1", "2"], protect_content=False),
+ )
+ protected_poll, unprotect_poll = await tasks
+ assert protected_poll.has_protected_content
+ assert not unprotect_poll.has_protected_content
+
+ @pytest.mark.parametrize("emoji", Dice.ALL_EMOJI + [None])
+ async def test_send_dice(self, bot, chat_id, emoji):
+ message = await bot.send_dice(chat_id, emoji=emoji, protect_content=True)
+
+ assert message.dice
+ assert message.has_protected_content
+ if emoji is None:
+ assert message.dice.emoji == Dice.DICE
+ else:
+ assert message.dice.emoji == emoji
+
+ @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_dice_default_allow_sending_without_reply(
+ self, default_bot, chat_id, 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_dice(
+ chat_id,
+ 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_dice(
+ chat_id,
+ 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_dice(
+ chat_id, reply_to_message_id=reply_to_message.message_id
+ )
+
+ @pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
+ async def test_send_dice_default_protect_content(self, chat_id, default_bot):
+ tasks = asyncio.gather(
+ default_bot.send_dice(chat_id), default_bot.send_dice(chat_id, protect_content=False)
+ )
+ protected_dice, unprotected_dice = await tasks
+ assert protected_dice.has_protected_content
+ assert not unprotected_dice.has_protected_content
+
+ @pytest.mark.parametrize("chat_action", list(ChatAction))
+ async def test_send_chat_action(self, bot, chat_id, chat_action):
+ assert await bot.send_chat_action(chat_id, chat_action)
+
+ async def test_wrong_chat_action(self, bot, chat_id):
+ with pytest.raises(BadRequest, match="Wrong parameter action"):
+ await bot.send_chat_action(chat_id, "unknown action")
+
+ async def test_answer_inline_query_current_offset_error(self, bot, inline_results):
+ with pytest.raises(ValueError, match=("`current_offset` and `next_offset`")):
+ await bot.answer_inline_query(
+ 1234, results=inline_results, next_offset=42, current_offset=51
+ )
+
+ async def test_get_user_profile_photos(self, bot, chat_id):
+ user_profile_photos = await bot.get_user_profile_photos(chat_id)
+ assert user_profile_photos.photos[0][0].file_size == 5403
+
+ async def test_get_one_user_profile_photo(self, bot, chat_id):
+ user_profile_photos = await bot.get_user_profile_photos(chat_id, offset=0, limit=1)
+ assert user_profile_photos.total_count == 1
+ assert user_profile_photos.photos[0][0].file_size == 5403
+
+ async def test_edit_message_text(self, bot, message):
+ message = await bot.edit_message_text(
+ text="new_text",
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ parse_mode="HTML",
+ disable_web_page_preview=True,
+ )
+
+ assert message.text == "new_text"
+
+ async def test_edit_message_text_entities(self, bot, message):
+ test_string = "Italic Bold Code"
+ entities = [
+ MessageEntity(MessageEntity.ITALIC, 0, 6),
+ MessageEntity(MessageEntity.ITALIC, 7, 4),
+ MessageEntity(MessageEntity.ITALIC, 12, 4),
+ ]
+ message = await bot.edit_message_text(
+ text=test_string,
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ entities=entities,
+ )
+
+ assert message.text == test_string
+ assert message.entities == tuple(entities)
+
+ @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
+ async def test_edit_message_text_default_parse_mode(self, default_bot, message):
+ test_string = "Italic Bold Code"
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.edit_message_text(
+ text=test_markdown_string,
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ disable_web_page_preview=True,
+ )
+ assert message.text_markdown == test_markdown_string
+ assert message.text == test_string
+
+ message = await default_bot.edit_message_text(
+ text=test_markdown_string,
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ parse_mode=None,
+ disable_web_page_preview=True,
+ )
+ assert message.text == test_markdown_string
+ assert message.text_markdown == escape_markdown(test_markdown_string)
+
+ message = await default_bot.edit_message_text(
+ text=test_markdown_string,
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ disable_web_page_preview=True,
+ )
+ message = await default_bot.edit_message_text(
+ text=test_markdown_string,
+ chat_id=message.chat_id,
+ message_id=message.message_id,
+ parse_mode="HTML",
+ disable_web_page_preview=True,
+ )
+ assert message.text == test_markdown_string
+ assert message.text_markdown == escape_markdown(test_markdown_string)
+
+ @pytest.mark.skip(reason="need reference to an inline message")
+ async def test_edit_message_text_inline(self):
+ pass
+
+ async def test_edit_message_caption(self, bot, media_message):
+ message = await bot.edit_message_caption(
+ caption="new_caption",
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ )
+
+ assert message.caption == "new_caption"
+
+ async def test_edit_message_caption_entities(self, bot, media_message):
+ test_string = "Italic Bold Code"
+ entities = [
+ MessageEntity(MessageEntity.ITALIC, 0, 6),
+ MessageEntity(MessageEntity.ITALIC, 7, 4),
+ MessageEntity(MessageEntity.ITALIC, 12, 4),
+ ]
+ message = await bot.edit_message_caption(
+ caption=test_string,
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ caption_entities=entities,
+ )
+
+ assert message.caption == test_string
+ assert message.caption_entities == tuple(entities)
+
+ # edit_message_media is tested in test_inputmedia
+
+ @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
+ async def test_edit_message_caption_default_parse_mode(self, default_bot, media_message):
+ test_string = "Italic Bold Code"
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.edit_message_caption(
+ caption=test_markdown_string,
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ )
+ assert message.caption_markdown == test_markdown_string
+ assert message.caption == test_string
+
+ message = await default_bot.edit_message_caption(
+ caption=test_markdown_string,
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ parse_mode=None,
+ )
+ assert message.caption == test_markdown_string
+ assert message.caption_markdown == escape_markdown(test_markdown_string)
+
+ message = await default_bot.edit_message_caption(
+ caption=test_markdown_string,
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ )
+ message = await default_bot.edit_message_caption(
+ caption=test_markdown_string,
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ parse_mode="HTML",
+ )
+ assert message.caption == test_markdown_string
+ assert message.caption_markdown == escape_markdown(test_markdown_string)
+
+ async def test_edit_message_caption_with_parse_mode(self, bot, media_message):
+ message = await bot.edit_message_caption(
+ caption="new *caption*",
+ parse_mode="Markdown",
+ chat_id=media_message.chat_id,
+ message_id=media_message.message_id,
+ )
+
+ assert message.caption == "new caption"
+
+ @pytest.mark.skip(reason="need reference to an inline message")
+ async def test_edit_message_caption_inline(self):
+ pass
+
+ async def test_edit_reply_markup(self, bot, message):
+ new_markup = InlineKeyboardMarkup([[InlineKeyboardButton(text="test", callback_data="1")]])
+ message = await bot.edit_message_reply_markup(
+ chat_id=message.chat_id, message_id=message.message_id, reply_markup=new_markup
+ )
+
+ assert message is not True
+
+ @pytest.mark.skip(reason="need reference to an inline message")
+ async def test_edit_reply_markup_inline(self):
+ pass
+
+ @pytest.mark.xdist_group("getUpdates_and_webhook")
+ # TODO: Actually send updates to the test bot so this can be tested properly
+ async def test_get_updates(self, bot):
+ await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
+ updates = await bot.get_updates(timeout=1)
+
+ assert isinstance(updates, tuple)
+ if updates:
+ assert isinstance(updates[0], Update)
+
+ @pytest.mark.xdist_group("getUpdates_and_webhook")
+ @pytest.mark.parametrize("use_ip", [True, False])
+ # local file path as file_input is tested below in test_set_webhook_params
+ @pytest.mark.parametrize("file_input", ["bytes", "file_handle"])
+ async def test_set_webhook_get_webhook_info_and_delete_webhook(self, bot, use_ip, file_input):
+ url = "https://python-telegram-bot.org/test/webhook"
+ # Get the ip address of the website - dynamically just in case it ever changes
+ ip = socket.gethostbyname("python-telegram-bot.org")
+ max_connections = 7
+ allowed_updates = ["message"]
+ file_input = (
+ data_file("sslcert.pem").read_bytes()
+ if file_input == "bytes"
+ else data_file("sslcert.pem").open("rb")
+ )
+ await bot.set_webhook(
+ url,
+ max_connections=max_connections,
+ allowed_updates=allowed_updates,
+ ip_address=ip if use_ip else None,
+ certificate=file_input if use_ip else None,
+ )
+
+ await asyncio.sleep(1)
+ live_info = await bot.get_webhook_info()
+ assert live_info.url == url
+ assert live_info.max_connections == max_connections
+ assert live_info.allowed_updates == tuple(allowed_updates)
+ assert live_info.ip_address == ip
+ assert live_info.has_custom_certificate == use_ip
+
+ await bot.delete_webhook()
+ await asyncio.sleep(1)
+ info = await bot.get_webhook_info()
+ assert info.url == ""
+ assert info.ip_address is None
+ assert info.has_custom_certificate is False
+
+ async def test_leave_chat(self, bot):
+ with pytest.raises(BadRequest, match="Chat not found"):
+ await bot.leave_chat(-123456)
+
+ with pytest.raises(NetworkError, match="Chat not found"):
+ await bot.leave_chat(-123456)
+
+ async def test_get_chat(self, bot, super_group_id):
+ chat = await bot.get_chat(super_group_id)
+ assert chat.type == "supergroup"
+ assert chat.title == f">>> telegram.Bot(test) @{bot.username}"
+ assert chat.id == int(super_group_id)
+
+ async def test_get_chat_administrators(self, bot, channel_id):
+ admins = await bot.get_chat_administrators(channel_id)
+ assert isinstance(admins, tuple)
+
+ for a in admins:
+ assert a.status in ("administrator", "creator")
+
+ async def test_get_chat_member_count(self, bot, channel_id):
+ count = await bot.get_chat_member_count(channel_id)
+ assert isinstance(count, int)
+ assert count > 3
+
+ async def test_get_chat_member(self, bot, channel_id, chat_id):
+ chat_member = await bot.get_chat_member(channel_id, chat_id)
+
+ assert chat_member.status == "administrator"
+ assert chat_member.user.first_name == "PTB"
+ assert chat_member.user.last_name == "Test user"
+
+ @pytest.mark.skip(reason="Not implemented since we need a supergroup with many members")
+ async def test_set_chat_sticker_set(self):
+ pass
+
+ @pytest.mark.skip(reason="Not implemented since we need a supergroup with many members")
+ async def test_delete_chat_sticker_set(self):
+ pass
+
+ async def test_send_game(self, bot, chat_id):
+ game_short_name = "test_game"
+ message = await bot.send_game(chat_id, game_short_name, protect_content=True)
+
+ assert message.game
+ assert message.game.description == (
+ "A no-op test game, for python-telegram-bot bot framework testing."
+ )
+ assert message.game.animation.file_id != ""
+ # We added some test bots later and for some reason the file size is not the same for them
+ # so we accept three different sizes here. Shouldn't be too much of
+ assert message.game.photo[0].file_size in [851, 4928, 850]
+ assert message.has_protected_content
+
+ @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_game_default_allow_sending_without_reply(
+ self, default_bot, chat_id, custom
+ ):
+ game_short_name = "test_game"
+ 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_game(
+ chat_id,
+ game_short_name,
+ 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_game(
+ chat_id,
+ game_short_name,
+ 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_game(
+ chat_id, game_short_name, reply_to_message_id=reply_to_message.message_id
+ )
+
+ @pytest.mark.parametrize(
+ "default_bot,val",
+ [({"protect_content": True}, True), ({"protect_content": False}, None)],
+ indirect=["default_bot"],
+ )
+ async def test_send_game_default_protect_content(self, default_bot, chat_id, val):
+ protected = await default_bot.send_game(chat_id, "test_game", protect_content=val)
+ assert protected.has_protected_content is val
+
+ @pytest.mark.xdist_group("game")
+ @xfail
+ async def test_set_game_score_1(self, bot, chat_id):
+ # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
+ # First, test setting a score.
+ game_short_name = "test_game"
+ game = await bot.send_game(chat_id, game_short_name)
+
+ message = await bot.set_game_score(
+ user_id=chat_id,
+ score=BASE_GAME_SCORE, # Score value is relevant for other set_game_score_* tests!
+ chat_id=game.chat_id,
+ message_id=game.message_id,
+ )
+
+ assert message.game.description == game.game.description
+ assert message.game.photo[0].file_size == game.game.photo[0].file_size
+ assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
+ assert message.game.text != game.game.text
+
+ @pytest.mark.xdist_group("game")
+ @xfail
+ async def test_set_game_score_2(self, bot, chat_id):
+ # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
+ # Test setting a score higher than previous
+ game_short_name = "test_game"
+ game = await bot.send_game(chat_id, game_short_name)
+
+ score = BASE_GAME_SCORE + 1
+
+ message = await bot.set_game_score(
+ user_id=chat_id,
+ score=score,
+ chat_id=game.chat_id,
+ message_id=game.message_id,
+ disable_edit_message=True,
+ )
+
+ assert message.game.description == game.game.description
+ assert message.game.photo[0].file_size == game.game.photo[0].file_size
+ assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
+ assert message.game.text == game.game.text
+
+ @pytest.mark.xdist_group("game")
+ @xfail
+ async def test_set_game_score_3(self, bot, chat_id):
+ # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
+ # Test setting a score lower than previous (should raise error)
+ game_short_name = "test_game"
+ game = await bot.send_game(chat_id, game_short_name)
+
+ score = BASE_GAME_SCORE # Even a score equal to previous raises an error.
+
+ with pytest.raises(BadRequest, match="Bot_score_not_modified"):
+ await bot.set_game_score(
+ user_id=chat_id, score=score, chat_id=game.chat_id, message_id=game.message_id
+ )
+
+ @pytest.mark.xdist_group("game")
+ @xfail
+ async def test_set_game_score_4(self, bot, chat_id):
+ # NOTE: numbering of methods assures proper order between test_set_game_scoreX methods
+ # Test force setting a lower score
+ game_short_name = "test_game"
+ game = await bot.send_game(chat_id, game_short_name)
+ await asyncio.sleep(1.5)
+
+ score = BASE_GAME_SCORE - 10
+
+ message = await bot.set_game_score(
+ user_id=chat_id,
+ score=score,
+ chat_id=game.chat_id,
+ message_id=game.message_id,
+ force=True,
+ )
+
+ assert message.game.description == game.game.description
+ assert message.game.photo[0].file_size == game.game.photo[0].file_size
+ assert message.game.animation.file_unique_id == game.game.animation.file_unique_id
+
+ # For some reason the returned message doesn't contain the updated score. need to fetch
+ # the game again... (the service message is also absent when running the test suite)
+ game2 = await bot.send_game(chat_id, game_short_name)
+ assert str(score) in game2.game.text
+
+ @pytest.mark.xdist_group("game")
+ @xfail
+ async def test_get_game_high_scores(self, bot, chat_id):
+ # We need a game to get the scores for
+ game_short_name = "test_game"
+ game = await bot.send_game(chat_id, game_short_name)
+ high_scores = await bot.get_game_high_scores(chat_id, game.chat_id, game.message_id)
+ # We assume that the other game score tests ran within 20 sec
+ assert high_scores[0].score == BASE_GAME_SCORE - 10
+
+ # send_invoice and create_invoice_link is tested in test_invoice
async def test_promote_chat_member(self, bot, channel_id, monkeypatch):
# TODO: Add bot to supergroup so this can be tested properly / give bot perms
with pytest.raises(BadRequest, match="Not enough rights"):
@@ -2282,14 +2600,12 @@ class TestBot:
can_manage_topics=12,
)
- @pytest.mark.flaky(3, 1)
async def test_export_chat_invite_link(self, bot, channel_id):
# Each link is unique apparently
invite_link = await bot.export_chat_invite_link(channel_id)
assert isinstance(invite_link, str)
assert invite_link != ""
- @pytest.mark.flaky(3, 1)
async def test_edit_revoke_chat_invite_link_passing_link_objects(self, bot, channel_id):
invite_link = await bot.create_chat_invite_link(chat_id=channel_id)
assert invite_link.name is None
@@ -2307,7 +2623,6 @@ class TestBot:
assert revoked_link.is_revoked is True
assert revoked_link.name == "some_name"
- @pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("creates_join_request", [True, False])
@pytest.mark.parametrize("name", [None, "name"])
async def test_create_chat_invite_link_basics(
@@ -2330,7 +2645,7 @@ class TestBot:
)
assert revoked_link.is_revoked
- @pytest.mark.flaky(3, 1)
+ @pytest.mark.skipif(not TEST_WITH_OPT_DEPS, reason="This test's implementation requires pytz")
@pytest.mark.parametrize("datetime", argvalues=[True, False], ids=["datetime", "integer"])
async def test_advanced_chat_invite_links(self, bot, channel_id, datetime):
# we are testing this all in one function in order to save api calls
@@ -2338,7 +2653,7 @@ class TestBot:
add_seconds = dtm.timedelta(0, 70)
time_in_future = timestamp + add_seconds
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
- aware_time_in_future = self.localize(time_in_future, UTC)
+ aware_time_in_future = UTC.localize(time_in_future)
invite_link = await bot.create_chat_invite_link(
channel_id, expire_date=expire_time, member_limit=10
@@ -2351,7 +2666,7 @@ class TestBot:
add_seconds = dtm.timedelta(0, 80)
time_in_future = timestamp + add_seconds
expire_time = time_in_future if datetime else to_timestamp(time_in_future)
- aware_time_in_future = self.localize(time_in_future, UTC)
+ aware_time_in_future = UTC.localize(time_in_future)
edited_invite_link = await bot.edit_chat_invite_link(
channel_id,
@@ -2385,7 +2700,6 @@ class TestBot:
assert revoked_invite_link.invite_link == invite_link.invite_link
assert revoked_invite_link.is_revoked
- @pytest.mark.flaky(3, 1)
async def test_advanced_chat_invite_links_default_tzinfo(self, tz_bot, channel_id):
# we are testing this all in one function in order to save api calls
add_seconds = dtm.timedelta(0, 70)
@@ -2434,7 +2748,6 @@ class TestBot:
assert revoked_invite_link.invite_link == invite_link.invite_link
assert revoked_invite_link.is_revoked
- @pytest.mark.flaky(3, 1)
async def test_approve_chat_join_request(self, bot, chat_id, channel_id):
# TODO: Need incoming join request to properly test
# Since we can't create join requests on the fly, we just tests the call to TG
@@ -2442,7 +2755,6 @@ class TestBot:
with pytest.raises(BadRequest, match="User_already_participant"):
await bot.approve_chat_join_request(chat_id=channel_id, user_id=chat_id)
- @pytest.mark.flaky(3, 1)
async def test_decline_chat_join_request(self, bot, chat_id, channel_id):
# TODO: Need incoming join request to properly test
# Since we can't create join requests on the fly, we just tests the call to TG
@@ -2453,7 +2765,6 @@ class TestBot:
with pytest.raises(BadRequest, match="User_already_participant|Hide_requester_missing"):
await bot.decline_chat_join_request(chat_id=channel_id, user_id=chat_id)
- @pytest.mark.flaky(3, 1)
async def test_set_chat_photo(self, bot, channel_id):
async def func():
assert await bot.set_chat_photo(channel_id, f)
@@ -2463,88 +2774,57 @@ class TestBot:
func, "Type of file mismatch", "Telegram did not accept the file."
)
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_set_chat_photo_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("photo") == expected
- else:
- test_flag = isinstance(data.get("photo"), InputFile)
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.set_chat_photo(chat_id, file)
- assert test_flag
- finally:
- bot._local_mode = False
-
- @pytest.mark.flaky(3, 1)
async def test_delete_chat_photo(self, bot, channel_id):
async def func():
assert await bot.delete_chat_photo(channel_id)
await expect_bad_request(func, "Chat_not_modified", "Chat photo was not set.")
- @pytest.mark.flaky(3, 1)
async def test_set_chat_title(self, bot, channel_id):
assert await bot.set_chat_title(channel_id, ">>> telegram.Bot() - Tests")
- @pytest.mark.flaky(3, 1)
async def test_set_chat_description(self, bot, channel_id):
assert await bot.set_chat_description(channel_id, "Time: " + str(time.time()))
- @pytest.mark.flaky(3, 1)
async def test_pin_and_unpin_message(self, bot, super_group_id):
- message1 = await bot.send_message(super_group_id, text="test_pin_message_1")
- message2 = await bot.send_message(super_group_id, text="test_pin_message_2")
- message3 = await bot.send_message(super_group_id, text="test_pin_message_3")
+ messages = [] # contains the Messages we sent
+ pinned_messages_tasks = set() # contains the asyncio.Tasks that pin the messages
- assert await bot.pin_chat_message(
- chat_id=super_group_id,
- message_id=message1.message_id,
- disable_notification=True,
- read_timeout=10,
- )
- await asyncio.sleep(1)
+ # Let's send 3 messages so we can pin them
+ awaitables = {bot.send_message(super_group_id, f"test_pin_message_{i}") for i in range(3)}
- await bot.pin_chat_message(
- chat_id=super_group_id,
- message_id=message2.message_id,
- disable_notification=True,
- read_timeout=10,
- )
- await bot.pin_chat_message(
- chat_id=super_group_id,
- message_id=message3.message_id,
- disable_notification=True,
- read_timeout=10,
- )
- await asyncio.sleep(1)
+ # We will pin the messages immediately after sending them
+ for sending_msg in asyncio.as_completed(awaitables): # as_completed sends the messages
+ msg = await sending_msg
+ coro = bot.pin_chat_message(super_group_id, msg.message_id, True, read_timeout=10)
+ pinned_messages_tasks.add(asyncio.create_task(coro)) # start pinning the message
+ messages.append(msg)
- chat = await bot.get_chat(super_group_id)
- assert chat.pinned_message == message3
+ assert len(messages) == 3 # Check if we sent 3 messages
- assert await bot.unpin_chat_message(
- super_group_id,
- message_id=message2.message_id,
- read_timeout=10,
- )
- assert await bot.unpin_chat_message(
- super_group_id,
- read_timeout=10,
- )
+ assert all([await i for i in pinned_messages_tasks]) # Check if we pinned 3 messages
+ assert all([i.done() for i in pinned_messages_tasks]) # Check if all tasks are done
- assert await bot.unpin_all_chat_messages(
- super_group_id,
- read_timeout=10,
+ chat = await bot.get_chat(super_group_id) # get the chat to check the pinned message
+ assert chat.pinned_message in messages
+
+ # Determine which message is not the most recently pinned
+ for old_pin_msg in messages:
+ if chat.pinned_message != old_pin_msg:
+ break
+
+ # Test unpinning our messages
+ tasks = asyncio.gather(
+ bot.unpin_chat_message( # unpins any message except the most recent
+ chat_id=super_group_id, # because we don't want to accidentally unpin the same msg
+ message_id=old_pin_msg.message_id, # twice
+ read_timeout=10,
+ ),
+ bot.unpin_chat_message(chat_id=super_group_id, read_timeout=10), # unpins most recent
)
+ assert all(await tasks)
+ assert all([i.done() for i in tasks])
+ assert await bot.unpin_all_chat_messages(super_group_id, read_timeout=10)
# get_sticker_set, upload_sticker_file, create_new_sticker_set, add_sticker_to_set,
# set_sticker_position_in_set, delete_sticker_from_set and get_custom_emoji_stickers
@@ -2553,53 +2833,6 @@ class TestBot:
# get_forum_topic_icon_stickers, edit_forum_topic, general_forum etc...
# are tested in the test_forum module.
- async def test_timeout_propagation_explicit(self, monkeypatch, bot, chat_id):
- # Use BaseException that's not a subclass of Exception such that
- # OkException should not be caught anywhere
- class OkException(BaseException):
- pass
-
- timeout = 42
-
- async def do_request(*args, **kwargs):
- obj = kwargs.get("read_timeout")
- if obj == timeout:
- raise OkException
-
- return 200, b'{"ok": true, "result": []}'
-
- monkeypatch.setattr(bot.request, "do_request", do_request)
-
- # Test file uploading
- with pytest.raises(OkException):
- await bot.send_photo(
- chat_id, data_file("telegram.jpg").open("rb"), read_timeout=timeout
- )
-
- # Test JSON submission
- with pytest.raises(OkException):
- await bot.get_chat_administrators(chat_id, read_timeout=timeout)
-
- async def test_timeout_propagation_implicit(self, monkeypatch, bot, chat_id):
- # Use BaseException that's not a subclass of Exception such that
- # OkException should not be caught anywhere
- class OkException(BaseException):
- pass
-
- async def do_request(*args, **kwargs):
- obj = kwargs.get("write_timeout")
- if obj == 20:
- raise OkException
-
- return 200, b'{"ok": true, "result": []}'
-
- monkeypatch.setattr(bot.request, "do_request", do_request)
-
- # Test file uploading
- with pytest.raises(OkException):
- await bot.send_photo(chat_id, data_file("telegram.jpg").open("rb"))
-
- @pytest.mark.flaky(3, 1)
async def test_send_message_entities(self, bot, chat_id):
test_string = "Italic Bold Code Spoiler"
entities = [
@@ -2612,34 +2845,37 @@ class TestBot:
assert message.text == test_string
assert message.entities == tuple(entities)
- @pytest.mark.flaky(3, 1)
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
async def test_send_message_default_parse_mode(self, default_bot, chat_id):
test_string = "Italic Bold Code"
test_markdown_string = "_Italic_ *Bold* `Code`"
- message = await default_bot.send_message(chat_id, test_markdown_string)
- assert message.text_markdown == test_markdown_string
- assert message.text == test_string
+ tasks = asyncio.gather(
+ *(
+ default_bot.send_message(chat_id, test_markdown_string, **i)
+ for i in ({}, {"parse_mode": None}, {"parse_mode": "HTML"})
+ )
+ )
+ msg1, msg2, msg3 = await tasks
+ assert msg1.text_markdown == test_markdown_string
+ assert msg1.text == test_string
- message = await default_bot.send_message(chat_id, test_markdown_string, parse_mode=None)
- assert message.text == test_markdown_string
- assert message.text_markdown == escape_markdown(test_markdown_string)
+ assert msg2.text == test_markdown_string
+ assert msg2.text_markdown == escape_markdown(test_markdown_string)
- message = await default_bot.send_message(chat_id, test_markdown_string, parse_mode="HTML")
- assert message.text == test_markdown_string
- assert message.text_markdown == escape_markdown(test_markdown_string)
+ assert msg3.text == test_markdown_string
+ assert msg3.text_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_message_default_protect_content(self, default_bot, chat_id):
- to_check = await default_bot.send_message(chat_id, "test")
+ tasks = asyncio.gather(
+ default_bot.send_message(chat_id, "test"),
+ default_bot.send_message(chat_id, "test", protect_content=False),
+ )
+ to_check, no_protect = await tasks
assert to_check.has_protected_content
-
- no_protect = await default_bot.send_message(chat_id, "test", protect_content=False)
assert not no_protect.has_protected_content
- @pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot,custom",
[
@@ -2673,17 +2909,16 @@ class TestBot:
chat_id, "test", reply_to_message_id=reply_to_message.message_id
)
- @pytest.mark.asyncio
async def test_get_set_my_default_administrator_rights(self, bot):
# Test that my default administrator rights for group are as all False
- await bot.set_my_default_administrator_rights()
+ assert await bot.set_my_default_administrator_rights() # clear any set rights
my_admin_rights_grp = await bot.get_my_default_administrator_rights()
assert isinstance(my_admin_rights_grp, ChatAdministratorRights)
assert all(not getattr(my_admin_rights_grp, at) for at in my_admin_rights_grp.__slots__)
# Test setting my default admin rights for channel
my_rights = ChatAdministratorRights.all_rights()
- await bot.set_my_default_administrator_rights(my_rights, for_channels=True)
+ assert await bot.set_my_default_administrator_rights(my_rights, for_channels=True)
my_admin_rights_ch = await bot.get_my_default_administrator_rights(for_channels=True)
assert my_admin_rights_ch.can_invite_users is my_rights.can_invite_users
# tg bug? is_anonymous is False despite setting it True for channels:
@@ -2699,7 +2934,6 @@ class TestBot:
assert my_admin_rights_ch.can_pin_messages is None # Not returned for channels
assert my_admin_rights_ch.can_manage_topics is None # Not returned for channels
- @pytest.mark.asyncio
async def test_get_set_chat_menu_button(self, bot, chat_id):
# Test our chat menu button is commands-
menu_button = await bot.get_chat_menu_button()
@@ -2709,21 +2943,20 @@ class TestBot:
# Test setting our chat menu button to Webapp.
my_menu = MenuButtonWebApp("click me!", WebAppInfo("https://telegram.org/"))
- await bot.set_chat_menu_button(chat_id=chat_id, menu_button=my_menu)
+ assert await bot.set_chat_menu_button(chat_id=chat_id, menu_button=my_menu)
menu_button = await bot.get_chat_menu_button(chat_id)
assert isinstance(menu_button, MenuButtonWebApp)
assert menu_button.type == MenuButtonType.WEB_APP
assert menu_button.text == my_menu.text
assert menu_button.web_app.url == my_menu.web_app.url
- await bot.set_chat_menu_button(chat_id=chat_id, menu_button=MenuButtonDefault())
+ assert await bot.set_chat_menu_button(chat_id=chat_id, menu_button=MenuButtonDefault())
menu_button = await bot.get_chat_menu_button(chat_id=chat_id)
assert isinstance(menu_button, MenuButtonDefault)
- @pytest.mark.flaky(3, 1)
async def test_set_and_get_my_commands(self, bot):
commands = [BotCommand("cmd1", "descr1"), ["cmd2", "descr2"]]
- await bot.set_my_commands([])
+ assert await bot.set_my_commands([])
assert await bot.get_my_commands() == ()
assert await bot.set_my_commands(commands)
@@ -2731,7 +2964,6 @@ class TestBot:
assert bc.command == f"cmd{i+1}"
assert bc.description == f"descr{i+1}"
- @pytest.mark.flaky(3, 1)
async def test_get_set_delete_my_commands_with_scope(self, bot, super_group_id, chat_id):
group_cmds = [BotCommand("group_cmd", "visible to this supergroup only")]
private_cmds = [BotCommand("private_cmd", "visible to this private chat only")]
@@ -2739,26 +2971,32 @@ class TestBot:
private_scope = BotCommandScopeChat(chat_id)
# Set supergroup command list with lang code and check if the same can be returned from api
- await bot.set_my_commands(group_cmds, scope=group_scope, language_code="en")
+ assert await bot.set_my_commands(group_cmds, scope=group_scope, language_code="en")
gotten_group_cmds = await bot.get_my_commands(scope=group_scope, language_code="en")
assert len(gotten_group_cmds) == len(group_cmds)
assert gotten_group_cmds[0].command == group_cmds[0].command
# Set private command list and check if same can be returned from the api
- await bot.set_my_commands(private_cmds, scope=private_scope)
+ assert await bot.set_my_commands(private_cmds, scope=private_scope)
gotten_private_cmd = await bot.get_my_commands(scope=private_scope)
assert len(gotten_private_cmd) == len(private_cmds)
assert gotten_private_cmd[0].command == private_cmds[0].command
# Delete command list from that supergroup and private chat-
- await bot.delete_my_commands(private_scope)
- await bot.delete_my_commands(group_scope, "en")
+ tasks = asyncio.gather(
+ bot.delete_my_commands(private_scope),
+ bot.delete_my_commands(group_scope, "en"),
+ )
+ assert all(await tasks)
# Check if its been deleted-
- deleted_priv_cmds = await bot.get_my_commands(scope=private_scope)
- deleted_grp_cmds = await bot.get_my_commands(scope=group_scope, language_code="en")
+ tasks = asyncio.gather(
+ bot.get_my_commands(private_scope),
+ bot.get_my_commands(group_scope, "en"),
+ )
+ deleted_priv_cmds, deleted_grp_cmds = await tasks
assert len(deleted_grp_cmds) == 0 == len(group_cmds) - 1
assert len(deleted_priv_cmds) == 0 == len(private_cmds) - 1
@@ -2766,73 +3004,6 @@ class TestBot:
await bot.delete_my_commands() # Delete commands from default scope
assert len(await bot.get_my_commands()) == 0
- async def test_log_out(self, monkeypatch, bot):
- # We don't actually make a request as to not break the test setup
- async def assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters == {} and url.split("/")[-1] == "logOut"
-
- monkeypatch.setattr(bot.request, "post", assertion)
-
- assert await bot.log_out()
-
- async def test_close(self, monkeypatch, bot):
- # We don't actually make a request as to not break the test setup
- async def assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters == {} and url.split("/")[-1] == "close"
-
- monkeypatch.setattr(bot.request, "post", assertion)
-
- assert await bot.close()
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("json_keyboard", [True, False])
- @pytest.mark.parametrize("caption", ["Test", "", None])
- async def test_copy_message(
- self, monkeypatch, bot, chat_id, media_message, json_keyboard, caption
- ):
- keyboard = InlineKeyboardMarkup(
- [[InlineKeyboardButton(text="test", callback_data="test2")]]
- )
-
- async def post(url, request_data: RequestData, *args, **kwargs):
- data = request_data.parameters
- if not all(
- [
- data["chat_id"] == chat_id,
- data["from_chat_id"] == chat_id,
- data["message_id"] == media_message.message_id,
- data.get("caption") == caption,
- data["parse_mode"] == ParseMode.HTML,
- data["reply_to_message_id"] == media_message.message_id,
- data["reply_markup"] == keyboard.to_json()
- if json_keyboard
- else keyboard.to_dict(),
- data["disable_notification"] is True,
- data["caption_entities"]
- == [MessageEntity(MessageEntity.BOLD, 0, 4).to_dict()],
- data["protect_content"] is True,
- data["message_thread_id"] == 1,
- ]
- ):
- pytest.fail("I got wrong parameters in post")
- return data
-
- monkeypatch.setattr(bot.request, "post", post)
- await bot.copy_message(
- chat_id,
- from_chat_id=chat_id,
- message_id=media_message.message_id,
- caption=caption,
- caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 4)],
- parse_mode=ParseMode.HTML,
- reply_to_message_id=media_message.message_id,
- reply_markup=keyboard.to_json() if json_keyboard else keyboard,
- disable_notification=True,
- protect_content=True,
- message_thread_id=1,
- )
-
- @pytest.mark.flaky(3, 1)
async def test_copy_message_without_reply(self, bot, chat_id, media_message):
keyboard = InlineKeyboardMarkup(
[[InlineKeyboardButton(text="test", callback_data="test2")]]
@@ -2858,7 +3029,6 @@ class TestBot:
assert len(message.caption_entities) == 1
assert message.reply_markup == keyboard
- @pytest.mark.flaky(3, 1)
@pytest.mark.parametrize(
"default_bot",
[
@@ -2899,6 +3069,7 @@ class TestBot:
else:
assert len(message.caption_entities) == 0
+ # Continue testing arbitrary callback data here with actual requests:
async def test_replace_callback_data_send_message(self, cdc_bot, chat_id):
bot = cdc_bot
@@ -2989,63 +3160,7 @@ class TestBot:
bot.callback_data_cache.clear_callback_data()
bot.callback_data_cache.clear_callback_queries()
- # TODO: Needs improvement. We need incoming inline query to test answer.
- async def test_replace_callback_data_answer_inline_query(self, monkeypatch, cdc_bot, chat_id):
- bot = cdc_bot
-
- # For now just test that our internals pass the correct data
- async def make_assertion(
- endpoint,
- data=None,
- *args,
- **kwargs,
- ):
- inline_keyboard = data["results"][0]["reply_markup"].inline_keyboard
- assertion_1 = inline_keyboard[0][1] == no_replace_button
- assertion_2 = inline_keyboard[0][0] != replace_button
- keyboard, button = (
- inline_keyboard[0][0].callback_data[:32],
- inline_keyboard[0][0].callback_data[32:],
- )
- assertion_3 = (
- bot.callback_data_cache._keyboard_data[keyboard].button_data[button]
- == "replace_test"
- )
- assertion_4 = data["results"][1].reply_markup is None
- return assertion_1 and assertion_2 and assertion_3 and assertion_4
-
- try:
- replace_button = InlineKeyboardButton(text="replace", callback_data="replace_test")
- no_replace_button = InlineKeyboardButton(
- text="no_replace", url="http://python-telegram-bot.org/"
- )
- reply_markup = InlineKeyboardMarkup.from_row(
- [
- replace_button,
- no_replace_button,
- ]
- )
-
- bot.username # call this here so `bot.get_me()` won't be called after mocking
- monkeypatch.setattr(bot, "_post", make_assertion)
- results = [
- InlineQueryResultArticle(
- "11", "first", InputTextMessageContent("first"), reply_markup=reply_markup
- ),
- InlineQueryResultVoice(
- "22",
- "https://python-telegram-bot.org/static/testfiles/telegram.ogg",
- title="second",
- ),
- ]
-
- assert await bot.answer_inline_query(chat_id, results=results)
-
- finally:
- bot.callback_data_cache.clear_callback_data()
- bot.callback_data_cache.clear_callback_queries()
-
- async def test_get_chat_arbitrary_callback_data(self, super_group_id, cdc_bot):
+ async def test_get_chat_arbitrary_callback_data(self, channel_id, cdc_bot):
bot = cdc_bot
try:
@@ -3054,7 +3169,7 @@ class TestBot:
)
message = await bot.send_message(
- super_group_id, text="get_chat_arbitrary_callback_data", reply_markup=reply_markup
+ channel_id, text="get_chat_arbitrary_callback_data", reply_markup=reply_markup
)
await message.pin()
@@ -3062,110 +3177,10 @@ class TestBot:
data = list(bot.callback_data_cache._keyboard_data[keyboard].button_data.values())[0]
assert data == "callback_data"
- chat = await bot.get_chat(super_group_id)
+ chat = await bot.get_chat(channel_id)
assert chat.pinned_message == message
assert chat.pinned_message.reply_markup == reply_markup
- finally:
- bot.callback_data_cache.clear_callback_data()
- bot.callback_data_cache.clear_callback_queries()
- await bot.unpin_all_chat_messages(super_group_id)
-
- # In the following tests we check that get_updates inserts callback data correctly if necessary
- # The same must be done in the webhook updater. This is tested over at test_updater.py, but
- # here we test more extensively.
-
- async def test_arbitrary_callback_data_no_insert(self, monkeypatch, cdc_bot):
- """Updates that don't need insertion shouldn't fail obviously"""
- bot = cdc_bot
-
- async def post(*args, **kwargs):
- update = Update(
- 17,
- poll=Poll(
- "42",
- "question",
- options=[PollOption("option", 0)],
- total_voter_count=0,
- is_closed=False,
- is_anonymous=True,
- type=Poll.REGULAR,
- allows_multiple_answers=False,
- ),
- )
- return [update.to_dict()]
-
- try:
- monkeypatch.setattr(BaseRequest, "post", post)
- await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
- updates = await bot.get_updates(timeout=1)
-
- assert len(updates) == 1
- assert updates[0].update_id == 17
- assert updates[0].poll.id == "42"
- finally:
- bot.callback_data_cache.clear_callback_data()
- bot.callback_data_cache.clear_callback_queries()
-
- @pytest.mark.parametrize(
- "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"]
- )
- async def test_arbitrary_callback_data_pinned_message_reply_to_message(
- self, super_group_id, cdc_bot, monkeypatch, message_type
- ):
- bot = cdc_bot
-
- reply_markup = InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="callback_data")
- )
-
- message = Message(
- 1,
- dtm.datetime.utcnow(),
- None,
- reply_markup=bot.callback_data_cache.process_keyboard(reply_markup),
- )
- message._unfreeze()
- # We do to_dict -> de_json to make sure those aren't the same objects
- message.pinned_message = Message.de_json(message.to_dict(), bot)
-
- async def post(*args, **kwargs):
- update = Update(
- 17,
- **{
- message_type: Message(
- 1,
- dtm.datetime.utcnow(),
- None,
- pinned_message=message,
- reply_to_message=Message.de_json(message.to_dict(), bot),
- )
- },
- )
- return [update.to_dict()]
-
- try:
- monkeypatch.setattr(BaseRequest, "post", post)
- await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
- updates = await bot.get_updates(timeout=1)
-
- assert isinstance(updates, tuple)
- assert len(updates) == 1
-
- effective_message = updates[0][message_type]
- assert (
- effective_message.reply_to_message.reply_markup.inline_keyboard[0][0].callback_data
- == "callback_data"
- )
- assert (
- effective_message.pinned_message.reply_markup.inline_keyboard[0][0].callback_data
- == "callback_data"
- )
-
- pinned_message = effective_message.reply_to_message.pinned_message
- assert (
- pinned_message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data"
- )
-
+ assert await message.unpin() # (not placed in finally block since msg can be unbound)
finally:
bot.callback_data_cache.clear_callback_data()
bot.callback_data_cache.clear_callback_queries()
@@ -3185,92 +3200,3 @@ class TestBot:
finally:
bot.callback_data_cache.clear_callback_data()
bot.callback_data_cache.clear_callback_queries()
-
- @pytest.mark.parametrize(
- "message_type", ["channel_post", "edited_channel_post", "message", "edited_message"]
- )
- @pytest.mark.parametrize("self_sender", [True, False])
- async def test_arbitrary_callback_data_via_bot(
- self, super_group_id, cdc_bot, monkeypatch, self_sender, message_type
- ):
- bot = cdc_bot
- reply_markup = InlineKeyboardMarkup.from_button(
- InlineKeyboardButton(text="text", callback_data="callback_data")
- )
-
- reply_markup = bot.callback_data_cache.process_keyboard(reply_markup)
- message = Message(
- 1,
- dtm.datetime.utcnow(),
- None,
- reply_markup=reply_markup,
- via_bot=bot.bot if self_sender else User(1, "first", False),
- )
-
- async def post(*args, **kwargs):
- return [Update(17, **{message_type: message}).to_dict()]
-
- try:
- monkeypatch.setattr(BaseRequest, "post", post)
- await bot.delete_webhook() # make sure there is no webhook set if webhook tests failed
- updates = await bot.get_updates(timeout=1)
-
- assert isinstance(updates, tuple)
- assert len(updates) == 1
-
- message = updates[0][message_type]
- if self_sender:
- assert message.reply_markup.inline_keyboard[0][0].callback_data == "callback_data"
- else:
- assert (
- message.reply_markup.inline_keyboard[0][0].callback_data
- == reply_markup.inline_keyboard[0][0].callback_data
- )
- finally:
- bot.callback_data_cache.clear_callback_data()
- bot.callback_data_cache.clear_callback_queries()
-
- @bot_methods()
- def test_camel_case_aliases(self, bot_class, bot_method_name, bot_method):
- camel_case_name = to_camel_case(bot_method_name)
- camel_case_function = getattr(bot_class, camel_case_name, False)
- assert camel_case_function is not False, f"{camel_case_name} not found"
- assert camel_case_function is bot_method, f"{camel_case_name} is not {bot_method}"
-
- @bot_methods()
- def test_coroutine_functions(self, bot_class, bot_method_name, bot_method):
- """Check that all bot methods are defined as async def ..."""
- # not islower() skips the camelcase aliases
- if not bot_method_name.islower():
- return
- # unfortunately `inspect.iscoroutinefunction` doesn't do the trick directly,
- # as the @_log decorator interferes
- source = "".join(inspect.getsourcelines(bot_method)[0])
- assert (
- f"async def {bot_method_name}" in source
- ), f"{bot_method_name} should be a coroutine function"
-
- @bot_methods()
- def test_api_kwargs_and_timeouts_present(self, bot_class, bot_method_name, bot_method):
- """Check that all bot methods have `api_kwargs` and timeout params."""
- param_names = inspect.signature(bot_method).parameters.keys()
- assert (
- "pool_timeout" in param_names
- ), f"{bot_method_name} is missing the parameter `pool_timeout`"
- assert (
- "read_timeout" in param_names
- ), f"{bot_method_name} is missing the parameter `read_timeout`"
- assert (
- "connect_timeout" in param_names
- ), f"{bot_method_name} is missing the parameter `connect_timeout`"
- assert (
- "write_timeout" in param_names
- ), f"{bot_method_name} is missing the parameter `write_timeout`"
- assert (
- "api_kwargs" in param_names
- ), f"{bot_method_name} is missing the parameter `api_kwargs`"
-
- if bot_class is ExtBot and bot_method_name.replace("_", "").lower() != "getupdates":
- assert (
- "rate_limit_args" in param_names
- ), f"{bot_method_name} of ExtBot is missing the parameter `rate_limit_args`"
diff --git a/tests/test_botcommand.py b/tests/test_botcommand.py
index e35441037..80b529042 100644
--- a/tests/test_botcommand.py
+++ b/tests/test_botcommand.py
@@ -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"
diff --git a/tests/test_botcommandscope.py b/tests/test_botcommandscope.py
index 8843bc6f9..78472f076 100644
--- a/tests/test_botcommandscope.py
+++ b/tests/test_botcommandscope.py
@@ -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}'"
diff --git a/tests/test_callbackdatacache.py b/tests/test_callbackdatacache.py
index 9b9981f4f..d2bfc4f38 100644
--- a/tests/test_callbackdatacache.py
+++ b/tests/test_callbackdatacache.py
@@ -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",
diff --git a/tests/test_callbackquery.py b/tests/test_callbackquery.py
index c97f1e276..f13b6c6f3 100644
--- a/tests/test_callbackquery.py
+++ b/tests/test_callbackquery.py
@@ -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)
diff --git a/tests/test_chat.py b/tests/test_chat.py
index f32cc60b4..e60ea3ea1 100644
--- a/tests/test_chat.py
+++ b/tests/test_chat.py
@@ -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)
diff --git a/tests/test_chatadministratorrights.py b/tests/test_chatadministratorrights.py
index f6044248e..310fe9f4e 100644
--- a/tests/test_chatadministratorrights.py
+++ b/tests/test_chatadministratorrights.py
@@ -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__:
diff --git a/tests/test_chatinvitelink.py b/tests/test_chatinvitelink.py
index 276b3856f..66d9ed4dd 100644
--- a/tests/test_chatinvitelink.py
+++ b/tests/test_chatinvitelink.py
@@ -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}'"
diff --git a/tests/test_chatjoinrequest.py b/tests/test_chatjoinrequest.py
index b4abed795..9c9f5b3fe 100644
--- a/tests/test_chatjoinrequest.py
+++ b/tests/test_chatjoinrequest.py
@@ -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__:
diff --git a/tests/test_chatlocation.py b/tests/test_chatlocation.py
index 42be03f0e..391e41297 100644
--- a/tests/test_chatlocation.py
+++ b/tests/test_chatlocation.py
@@ -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__:
diff --git a/tests/test_chatmember.py b/tests/test_chatmember.py
index 8aeb37765..ae2680417 100644
--- a/tests/test_chatmember.py
+++ b/tests/test_chatmember.py
@@ -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__:
diff --git a/tests/test_chatmemberupdated.py b/tests/test_chatmemberupdated.py
index 0268d0359..4045d4090 100644
--- a/tests/test_chatmemberupdated.py
+++ b/tests/test_chatmemberupdated.py
@@ -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__:
diff --git a/tests/test_chatpermissions.py b/tests/test_chatpermissions.py
index 584506ec6..1afd5a602 100644
--- a/tests/test_chatpermissions.py
+++ b/tests/test_chatpermissions.py
@@ -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__:
diff --git a/tests/test_chatphoto.py b/tests/test_chatphoto.py
index 3b6c8cb95..d2d4ad4fa 100644
--- a/tests/test_chatphoto.py
+++ b/tests/test_chatphoto.py
@@ -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)
diff --git a/tests/test_choseninlineresult.py b/tests/test_choseninlineresult.py
index c49790a5c..dc9ea7ca1 100644
--- a/tests/test_choseninlineresult.py
+++ b/tests/test_choseninlineresult.py
@@ -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__:
diff --git a/tests/test_constants.py b/tests/test_constants.py
index eb271300e..f69199ceb 100644
--- a/tests/test_constants.py
+++ b/tests/test_constants.py
@@ -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)
diff --git a/tests/test_contact.py b/tests/test_contact.py
index 32cefb97c..9160474ef 100644
--- a/tests/test_contact.py
+++ b/tests/test_contact.py
@@ -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)
diff --git a/tests/test_conversationhandler.py b/tests/test_conversationhandler.py
index 3e3e01033..8f873e001 100644
--- a/tests/test_conversationhandler.py
+++ b/tests/test_conversationhandler.py
@@ -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))
)
diff --git a/tests/test_datetime.py b/tests/test_datetime.py
index 431b05530..1d7d0e190 100644
--- a/tests/test_datetime.py
+++ b/tests/test_datetime.py
@@ -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
diff --git a/tests/test_defaults.py b/tests/test_defaults.py
index 565ba4b6f..88048031b 100644
--- a/tests/test_defaults.py
+++ b/tests/test_defaults.py
@@ -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:
diff --git a/tests/test_dice.py b/tests/test_dice.py
index bfef75cf3..182b3276c 100644
--- a/tests/test_dice.py
+++ b/tests/test_dice.py
@@ -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}'"
diff --git a/tests/test_document.py b/tests/test_document.py
index 0cdf56099..93574a231 100644
--- a/tests/test_document.py
+++ b/tests/test_document.py
@@ -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)
diff --git a/tests/test_encryptedcredentials.py b/tests/test_encryptedcredentials.py
index 6845749a4..ad9345458 100644
--- a/tests/test_encryptedcredentials.py
+++ b/tests/test_encryptedcredentials.py
@@ -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__:
diff --git a/tests/test_encryptedpassportelement.py b/tests/test_encryptedpassportelement.py
index 32c9badd9..a4f938e9f 100644
--- a/tests/test_encryptedpassportelement.py
+++ b/tests/test_encryptedpassportelement.py
@@ -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__:
diff --git a/tests/test_file.py b/tests/test_file.py
index 5ac195c5a..30c75b878 100644
--- a/tests/test_file.py
+++ b/tests/test_file.py
@@ -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)
diff --git a/tests/test_forcereply.py b/tests/test_forcereply.py
index e847fbcd7..18822a33f 100644
--- a/tests/test_forcereply.py
+++ b/tests/test_forcereply.py
@@ -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"
diff --git a/tests/test_forum.py b/tests/test_forum.py
index 5cbadc018..67001ae6c 100644
--- a/tests/test_forum.py
+++ b/tests/test_forum.py
@@ -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__:
diff --git a/tests/test_game.py b/tests/test_game.py
index a2ee9eae5..1ad8f4d16 100644
--- a/tests/test_game.py
+++ b/tests/test_game.py
@@ -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"}
diff --git a/tests/test_gamehighscore.py b/tests/test_gamehighscore.py
index 4d27698cb..1556a67d4 100644
--- a/tests/test_gamehighscore.py
+++ b/tests/test_gamehighscore.py
@@ -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 == {}
diff --git a/tests/test_inlinekeyboardbutton.py b/tests/test_inlinekeyboardbutton.py
index e1478c9e9..d9783dcd0 100644
--- a/tests/test_inlinekeyboardbutton.py
+++ b/tests/test_inlinekeyboardbutton.py
@@ -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__:
diff --git a/tests/test_inlinekeyboardmarkup.py b/tests/test_inlinekeyboardmarkup.py
index 7f0301d21..8e9a7e33c 100644
--- a/tests/test_inlinekeyboardmarkup.py
+++ b/tests/test_inlinekeyboardmarkup.py
@@ -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"
diff --git a/tests/test_inlinequery.py b/tests/test_inlinequery.py
index c4cd2d673..7b79a998f 100644
--- a/tests/test_inlinequery.py
+++ b/tests/test_inlinequery.py
@@ -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)
diff --git a/tests/test_inlinequeryresultarticle.py b/tests/test_inlinequeryresultarticle.py
index de8698201..1d4ebce80 100644
--- a/tests/test_inlinequeryresultarticle.py
+++ b/tests/test_inlinequeryresultarticle.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultaudio.py b/tests/test_inlinequeryresultaudio.py
index 0ae44c799..fd5fb1570 100644
--- a/tests/test_inlinequeryresultaudio.py
+++ b/tests/test_inlinequeryresultaudio.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedaudio.py b/tests/test_inlinequeryresultcachedaudio.py
index 9ce63db5c..672346613 100644
--- a/tests/test_inlinequeryresultcachedaudio.py
+++ b/tests/test_inlinequeryresultcachedaudio.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcacheddocument.py b/tests/test_inlinequeryresultcacheddocument.py
index e98c3dbe3..6b47eb7e7 100644
--- a/tests/test_inlinequeryresultcacheddocument.py
+++ b/tests/test_inlinequeryresultcacheddocument.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedgif.py b/tests/test_inlinequeryresultcachedgif.py
index edc29abf4..34f26df73 100644
--- a/tests/test_inlinequeryresultcachedgif.py
+++ b/tests/test_inlinequeryresultcachedgif.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedmpeg4gif.py b/tests/test_inlinequeryresultcachedmpeg4gif.py
index 12bb20934..37a47e11b 100644
--- a/tests/test_inlinequeryresultcachedmpeg4gif.py
+++ b/tests/test_inlinequeryresultcachedmpeg4gif.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedphoto.py b/tests/test_inlinequeryresultcachedphoto.py
index 4bc11f640..209bc9061 100644
--- a/tests/test_inlinequeryresultcachedphoto.py
+++ b/tests/test_inlinequeryresultcachedphoto.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedsticker.py b/tests/test_inlinequeryresultcachedsticker.py
index 866057df9..d41e0506b 100644
--- a/tests/test_inlinequeryresultcachedsticker.py
+++ b/tests/test_inlinequeryresultcachedsticker.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedvideo.py b/tests/test_inlinequeryresultcachedvideo.py
index bbf76087a..2ee368c40 100644
--- a/tests/test_inlinequeryresultcachedvideo.py
+++ b/tests/test_inlinequeryresultcachedvideo.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcachedvoice.py b/tests/test_inlinequeryresultcachedvoice.py
index c12c73a81..c1625b2db 100644
--- a/tests/test_inlinequeryresultcachedvoice.py
+++ b/tests/test_inlinequeryresultcachedvoice.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultcontact.py b/tests/test_inlinequeryresultcontact.py
index 4d61b8253..2b418e952 100644
--- a/tests/test_inlinequeryresultcontact.py
+++ b/tests/test_inlinequeryresultcontact.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultdocument.py b/tests/test_inlinequeryresultdocument.py
index 1ca01ff4b..b1221788e 100644
--- a/tests/test_inlinequeryresultdocument.py
+++ b/tests/test_inlinequeryresultdocument.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultgame.py b/tests/test_inlinequeryresultgame.py
index 963b130ae..f7c6ae757 100644
--- a/tests/test_inlinequeryresultgame.py
+++ b/tests/test_inlinequeryresultgame.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultgif.py b/tests/test_inlinequeryresultgif.py
index c25b18aa1..c50fd6b2d 100644
--- a/tests/test_inlinequeryresultgif.py
+++ b/tests/test_inlinequeryresultgif.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultlocation.py b/tests/test_inlinequeryresultlocation.py
index ba8d4a34e..d15688e34 100644
--- a/tests/test_inlinequeryresultlocation.py
+++ b/tests/test_inlinequeryresultlocation.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultmpeg4gif.py b/tests/test_inlinequeryresultmpeg4gif.py
index f40dea630..ae1210c89 100644
--- a/tests/test_inlinequeryresultmpeg4gif.py
+++ b/tests/test_inlinequeryresultmpeg4gif.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultphoto.py b/tests/test_inlinequeryresultphoto.py
index 5edbccca2..e6944f3b3 100644
--- a/tests/test_inlinequeryresultphoto.py
+++ b/tests/test_inlinequeryresultphoto.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultvenue.py b/tests/test_inlinequeryresultvenue.py
index 5f02526c2..7592bfdf0 100644
--- a/tests/test_inlinequeryresultvenue.py
+++ b/tests/test_inlinequeryresultvenue.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultvideo.py b/tests/test_inlinequeryresultvideo.py
index 79499381f..3e8edf76e 100644
--- a/tests/test_inlinequeryresultvideo.py
+++ b/tests/test_inlinequeryresultvideo.py
@@ -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__:
diff --git a/tests/test_inlinequeryresultvoice.py b/tests/test_inlinequeryresultvoice.py
index 12316b74d..8ce2f42db 100644
--- a/tests/test_inlinequeryresultvoice.py
+++ b/tests/test_inlinequeryresultvoice.py
@@ -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__:
diff --git a/tests/test_inputcontactmessagecontent.py b/tests/test_inputcontactmessagecontent.py
index 107852c54..cd47a2b18 100644
--- a/tests/test_inputcontactmessagecontent.py
+++ b/tests/test_inputcontactmessagecontent.py
@@ -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__:
diff --git a/tests/test_inputfile.py b/tests/test_inputfile.py
index d9db96d51..b215c907f 100644
--- a/tests/test_inputfile.py
+++ b/tests/test_inputfile.py
@@ -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
diff --git a/tests/test_inputinvoicemessagecontent.py b/tests/test_inputinvoicemessagecontent.py
index 045675512..d7d207575 100644
--- a/tests/test_inputinvoicemessagecontent.py
+++ b/tests/test_inputinvoicemessagecontent.py
@@ -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
diff --git a/tests/test_inputlocationmessagecontent.py b/tests/test_inputlocationmessagecontent.py
index 003269c9f..a9e776cb6 100644
--- a/tests/test_inputlocationmessagecontent.py
+++ b/tests/test_inputlocationmessagecontent.py
@@ -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__:
diff --git a/tests/test_inputmedia.py b/tests/test_inputmedia.py
index a998dea60..585e3a581 100644
--- a/tests/test_inputmedia.py
+++ b/tests/test_inputmedia.py
@@ -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="photo 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="photo 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"]
)
diff --git a/tests/test_inputtextmessagecontent.py b/tests/test_inputtextmessagecontent.py
index 97af5019d..13d53796f 100644
--- a/tests/test_inputtextmessagecontent.py
+++ b/tests/test_inputtextmessagecontent.py
@@ -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__:
diff --git a/tests/test_inputvenuemessagecontent.py b/tests/test_inputvenuemessagecontent.py
index cdad5f61b..2837249d1 100644
--- a/tests/test_inputvenuemessagecontent.py
+++ b/tests/test_inputvenuemessagecontent.py
@@ -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__:
diff --git a/tests/test_invoice.py b/tests/test_invoice.py
index a4476ce4c..cc2a0b2fa 100644
--- a/tests/test_invoice.py
+++ b/tests/test_invoice.py
@@ -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
diff --git a/tests/test_jobqueue.py b/tests/test_jobqueue.py
index 6bd4244e7..9376cbb12 100644
--- a/tests/test_jobqueue.py
+++ b/tests/test_jobqueue.py
@@ -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()
diff --git a/tests/test_keyboardbutton.py b/tests/test_keyboardbutton.py
index 7d508973c..9e82aed1b 100644
--- a/tests/test_keyboardbutton.py
+++ b/tests/test_keyboardbutton.py
@@ -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__:
diff --git a/tests/test_keyboardbuttonpolltype.py b/tests/test_keyboardbuttonpolltype.py
index 85bc19e82..56404a213 100644
--- a/tests/test_keyboardbuttonpolltype.py
+++ b/tests/test_keyboardbuttonpolltype.py
@@ -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__:
diff --git a/tests/test_keyboardbuttonrequest.py b/tests/test_keyboardbuttonrequest.py
index a59faba31..f298ba55f 100644
--- a/tests/test_keyboardbuttonrequest.py
+++ b/tests/test_keyboardbuttonrequest.py
@@ -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}'"
diff --git a/tests/test_labeledprice.py b/tests/test_labeledprice.py
index 95b1c1332..e5c61c51b 100644
--- a/tests/test_labeledprice.py
+++ b/tests/test_labeledprice.py
@@ -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__:
diff --git a/tests/test_location.py b/tests/test_location.py
index 30045559a..06ac34c4d 100644
--- a/tests/test_location.py
+++ b/tests/test_location.py
@@ -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)
diff --git a/tests/test_loginurl.py b/tests/test_loginurl.py
index f33ff3b38..09fd40569 100644
--- a/tests/test_loginurl.py
+++ b/tests/test_loginurl.py
@@ -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}'"
diff --git a/tests/test_menubutton.py b/tests/test_menubutton.py
index a72ea0bd7..091bbdb2d 100644
--- a/tests/test_menubutton.py
+++ b/tests/test_menubutton.py
@@ -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}'"
diff --git a/tests/test_message.py b/tests/test_message.py
index a840560f2..ce8417358 100644
--- a/tests/test_message.py
+++ b/tests/test_message.py
@@ -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)
diff --git a/tests/test_messageautodeletetimerchanged.py b/tests/test_messageautodeletetimerchanged.py
index 40b48736f..31a3ecd67 100644
--- a/tests/test_messageautodeletetimerchanged.py
+++ b/tests/test_messageautodeletetimerchanged.py
@@ -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):
diff --git a/tests/test_messageentity.py b/tests/test_messageentity.py
index 0e7b42bba..40faeff5b 100644
--- a/tests/test_messageentity.py
+++ b/tests/test_messageentity.py
@@ -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__:
diff --git a/tests/test_messageid.py b/tests/test_messageid.py
index f96a4afe8..f10250b3d 100644
--- a/tests/test_messageid.py
+++ b/tests/test_messageid.py
@@ -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):
diff --git a/tests/test_no_passport.py b/tests/test_no_passport.py
index 08c273ab6..9648422db 100644
--- a/tests/test_no_passport.py
+++ b/tests/test_no_passport.py
@@ -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
diff --git a/tests/test_orderinfo.py b/tests/test_orderinfo.py
index 946512022..1700e2d2d 100644
--- a/tests/test_orderinfo.py
+++ b/tests/test_orderinfo.py
@@ -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 == {}
diff --git a/tests/test_passport.py b/tests/test_passport.py
index 2dbc4d917..09e1b076e 100644
--- a/tests/test_passport.py
+++ b/tests/test_passport.py
@@ -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)
diff --git a/tests/test_passportelementerrordatafield.py b/tests/test_passportelementerrordatafield.py
index 16f7b5255..82b179395 100644
--- a/tests/test_passportelementerrordatafield.py
+++ b/tests/test_passportelementerrordatafield.py
@@ -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__:
diff --git a/tests/test_passportelementerrorfile.py b/tests/test_passportelementerrorfile.py
index 16642bd0b..c47ac4a37 100644
--- a/tests/test_passportelementerrorfile.py
+++ b/tests/test_passportelementerrorfile.py
@@ -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__:
diff --git a/tests/test_passportelementerrorfiles.py b/tests/test_passportelementerrorfiles.py
index e2b00f603..7e6301b00 100644
--- a/tests/test_passportelementerrorfiles.py
+++ b/tests/test_passportelementerrorfiles.py
@@ -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__:
diff --git a/tests/test_passportelementerrorfrontside.py b/tests/test_passportelementerrorfrontside.py
index b624b5bdf..d45a4a603 100644
--- a/tests/test_passportelementerrorfrontside.py
+++ b/tests/test_passportelementerrorfrontside.py
@@ -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__:
diff --git a/tests/test_passportelementerrorreverseside.py b/tests/test_passportelementerrorreverseside.py
index cd4918cb1..1808e63df 100644
--- a/tests/test_passportelementerrorreverseside.py
+++ b/tests/test_passportelementerrorreverseside.py
@@ -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__:
diff --git a/tests/test_passportelementerrorselfie.py b/tests/test_passportelementerrorselfie.py
index 13495f736..892e5a769 100644
--- a/tests/test_passportelementerrorselfie.py
+++ b/tests/test_passportelementerrorselfie.py
@@ -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__:
diff --git a/tests/test_passportelementerrortranslationfile.py b/tests/test_passportelementerrortranslationfile.py
index 56fe393e2..71930bc0f 100644
--- a/tests/test_passportelementerrortranslationfile.py
+++ b/tests/test_passportelementerrortranslationfile.py
@@ -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__:
diff --git a/tests/test_passportelementerrortranslationfiles.py b/tests/test_passportelementerrortranslationfiles.py
index b965bfa68..09493c4ee 100644
--- a/tests/test_passportelementerrortranslationfiles.py
+++ b/tests/test_passportelementerrortranslationfiles.py
@@ -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__:
diff --git a/tests/test_passportelementerrorunspecified.py b/tests/test_passportelementerrorunspecified.py
index 949131754..4c80b3db3 100644
--- a/tests/test_passportelementerrorunspecified.py
+++ b/tests/test_passportelementerrorunspecified.py
@@ -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__:
diff --git a/tests/test_passportfile.py b/tests/test_passportfile.py
index 8e310a45e..4b5837fcd 100644
--- a/tests/test_passportfile.py
+++ b/tests/test_passportfile.py
@@ -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"
diff --git a/tests/test_photo.py b/tests/test_photo.py
index fc9acbdca..bed123f63 100644
--- a/tests/test_photo.py
+++ b/tests/test_photo.py
@@ -15,6 +15,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 io import BytesIO
from pathlib import Path
@@ -35,34 +36,32 @@ from tests.conftest import data_file, expect_bad_request
@pytest.fixture(scope="function")
def photo_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="class")
+@pytest.fixture(scope="module")
async def _photo(bot, chat_id):
async def func():
with data_file("telegram.jpg").open("rb") as f:
- photo = (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
- return photo
+ return (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
return await expect_bad_request(
func, "Type of file mismatch", "Telegram did not accept the file."
)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def thumb(_photo):
return _photo[0]
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def photo(_photo):
return _photo[-1]
-class TestPhoto:
+class TestPhotoBase:
width = 800
height = 800
caption = "PhotoTest - *Caption*"
@@ -71,6 +70,8 @@ class TestPhoto:
# so we accept three different sizes here. Shouldn't be too much
file_size = [29176, 27662]
+
+class TestPhotoWithoutRequest(TestPhotoBase):
def test_slot_behaviour(self, photo, mro_slots):
for attr in photo.__slots__:
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -98,328 +99,6 @@ class TestPhoto:
assert thumb.height == 90
assert thumb.file_size == 1477
- @pytest.mark.flaky(3, 1)
- async def test_send_photo_all_args(self, bot, chat_id, photo_file, thumb, photo):
- message = await bot.send_photo(
- chat_id,
- photo_file,
- caption=self.caption,
- disable_notification=False,
- protect_content=True,
- parse_mode="Markdown",
- has_spoiler=True,
- )
-
- assert isinstance(message.photo[-2], PhotoSize)
- assert isinstance(message.photo[-2].file_id, str)
- assert isinstance(message.photo[-2].file_unique_id, str)
- assert message.photo[-2].file_id != ""
- assert message.photo[-2].file_unique_id != ""
-
- assert isinstance(message.photo[-1], PhotoSize)
- assert isinstance(message.photo[-1].file_id, str)
- assert isinstance(message.photo[-1].file_unique_id, str)
- assert message.photo[-1].file_id != ""
- assert message.photo[-1].file_unique_id != ""
-
- assert message.caption == TestPhoto.caption.replace("*", "")
- assert message.has_protected_content
- assert message.has_media_spoiler
-
- @pytest.mark.flaky(3, 1)
- async def test_send_photo_custom_filename(self, bot, chat_id, photo_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_photo(chat_id, photo_file, filename="custom_filename")
-
- @pytest.mark.flaky(3, 1)
- async def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file, thumb, photo):
- message = await bot.send_photo(
- chat_id, photo_file, caption=self.caption, parse_mode="Markdown"
- )
- assert isinstance(message.photo[-2], PhotoSize)
- assert isinstance(message.photo[-2].file_id, str)
- assert isinstance(message.photo[-2].file_unique_id, str)
- assert message.photo[-2].file_id != ""
- assert message.photo[-2].file_unique_id != ""
-
- assert isinstance(message.photo[-1], PhotoSize)
- assert isinstance(message.photo[-1].file_id, str)
- assert isinstance(message.photo[-1].file_unique_id, str)
- assert message.photo[-1].file_id != ""
- assert message.photo[-1].file_unique_id != ""
-
- assert message.caption == TestPhoto.caption.replace("*", "")
- assert len(message.caption_entities) == 1
-
- @pytest.mark.flaky(3, 1)
- async def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file, thumb, photo):
- message = await bot.send_photo(
- chat_id, photo_file, caption=self.caption, parse_mode="HTML"
- )
- assert isinstance(message.photo[-2], PhotoSize)
- assert isinstance(message.photo[-2].file_id, str)
- assert isinstance(message.photo[-2].file_unique_id, str)
- assert message.photo[-2].file_id != ""
- assert message.photo[-2].file_unique_id != ""
-
- assert isinstance(message.photo[-1], PhotoSize)
- assert isinstance(message.photo[-1].file_id, str)
- assert isinstance(message.photo[-1].file_unique_id, str)
- assert message.photo[-1].file_id != ""
- assert message.photo[-1].file_unique_id != ""
-
- assert message.caption == TestPhoto.caption.replace("", "").replace("", "")
- assert len(message.caption_entities) == 1
-
- @pytest.mark.flaky(3, 1)
- async def test_send_photo_caption_entities(self, bot, chat_id, photo_file, thumb, photo):
- 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_photo(
- chat_id, photo_file, 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_photo_default_parse_mode_1(
- self, default_bot, chat_id, photo_file, thumb, photo
- ):
- test_string = "Italic Bold Code"
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_photo(chat_id, photo_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_photo_default_parse_mode_2(
- self, default_bot, chat_id, photo_file, thumb, photo
- ):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_photo(
- chat_id, photo_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_photo_default_parse_mode_3(
- self, default_bot, chat_id, photo_file, thumb, photo
- ):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_photo(
- chat_id, photo_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_photo_default_protect_content(self, chat_id, default_bot, photo):
- protected = await default_bot.send_photo(chat_id, photo)
- assert protected.has_protected_content
- unprotected = await default_bot.send_photo(chat_id, photo, protect_content=False)
- assert not unprotected.has_protected_content
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_send_photo_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("photo") == expected
- else:
- test_flag = isinstance(data.get("photo"), InputFile)
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.send_photo(chat_id, file)
- assert test_flag
- 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_photo_default_allow_sending_without_reply(
- self, default_bot, chat_id, photo_file, thumb, photo, 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_photo(
- chat_id,
- photo_file,
- 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_photo(
- chat_id, photo_file, 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_photo(
- chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
- )
-
- @pytest.mark.flaky(3, 1)
- async def test_get_and_download(self, bot, photo):
- path = Path("telegram.jpg")
- if path.is_file():
- path.unlink()
-
- new_file = await bot.getFile(photo.file_id)
-
- assert new_file.file_size == photo.file_size
- assert new_file.file_unique_id == photo.file_unique_id
- assert new_file.file_path.startswith("https://") is True
-
- await new_file.download_to_drive("telegram.jpg")
-
- assert path.is_file()
-
- @pytest.mark.flaky(3, 1)
- async def test_send_url_jpg_file(self, bot, chat_id, thumb, photo):
- message = await bot.send_photo(chat_id, photo=self.photo_file_url)
-
- assert isinstance(message.photo[-2], PhotoSize)
- assert isinstance(message.photo[-2].file_id, str)
- assert isinstance(message.photo[-2].file_unique_id, str)
- assert message.photo[-2].file_id != ""
- assert message.photo[-2].file_unique_id != ""
-
- assert isinstance(message.photo[-1], PhotoSize)
- assert isinstance(message.photo[-1].file_id, str)
- assert isinstance(message.photo[-1].file_unique_id, str)
- assert message.photo[-1].file_id != ""
- assert message.photo[-1].file_unique_id != ""
-
- @pytest.mark.flaky(3, 1)
- async def test_send_url_png_file(self, bot, chat_id):
- message = await bot.send_photo(
- photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
- )
-
- photo = message.photo[-1]
-
- assert isinstance(photo, PhotoSize)
- assert isinstance(photo.file_id, str)
- assert isinstance(photo.file_unique_id, str)
- assert photo.file_id != ""
- assert photo.file_unique_id != ""
-
- @pytest.mark.flaky(3, 1)
- async def test_send_url_gif_file(self, bot, chat_id):
- message = await bot.send_photo(
- photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
- )
-
- photo = message.photo[-1]
-
- assert isinstance(photo, PhotoSize)
- assert isinstance(photo.file_id, str)
- assert isinstance(photo.file_unique_id, str)
- assert photo.file_id != ""
- assert photo.file_unique_id != ""
-
- @pytest.mark.flaky(3, 1)
- async def test_send_file_unicode_filename(self, bot, chat_id):
- """
- Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/1202
- """
- with data_file("测试.png").open("rb") as f:
- message = await bot.send_photo(photo=f, chat_id=chat_id)
-
- photo = message.photo[-1]
-
- assert isinstance(photo, PhotoSize)
- assert isinstance(photo.file_id, str)
- assert isinstance(photo.file_unique_id, str)
- assert photo.file_id != ""
- assert photo.file_unique_id != ""
-
- @pytest.mark.flaky(3, 1)
- async def test_send_bytesio_jpg_file(self, bot, chat_id):
- filepath = data_file("telegram_no_standard_header.jpg")
-
- # raw image bytes
- raw_bytes = BytesIO(filepath.read_bytes())
- input_file = InputFile(raw_bytes)
- assert input_file.mimetype == "application/octet-stream"
-
- # raw image bytes with name info
- raw_bytes = BytesIO(filepath.read_bytes())
- raw_bytes.name = str(filepath)
- input_file = InputFile(raw_bytes)
- assert input_file.mimetype == "image/jpeg"
-
- # send raw photo
- raw_bytes = BytesIO(filepath.read_bytes())
- message = await bot.send_photo(chat_id, photo=raw_bytes)
- photo = message.photo[-1]
- assert isinstance(photo.file_id, str)
- assert isinstance(photo.file_unique_id, str)
- assert photo.file_id != ""
- assert photo.file_unique_id != ""
- assert isinstance(photo, PhotoSize)
- assert photo.width == 1280
- assert photo.height == 720
- assert photo.file_size == 33372
-
- async def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo):
- async def make_assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters["photo"] == photo.file_id
-
- monkeypatch.setattr(bot.request, "post", make_assertion)
- message = await bot.send_photo(photo=photo, chat_id=chat_id)
- assert message
-
- @pytest.mark.flaky(3, 1)
- async def test_resend(self, bot, chat_id, photo, thumb):
- message = await bot.send_photo(chat_id=chat_id, photo=photo.file_id)
-
- assert isinstance(message.photo[-2], PhotoSize)
- assert isinstance(message.photo[-2].file_id, str)
- assert isinstance(message.photo[-2].file_unique_id, str)
- assert message.photo[-2].file_id != ""
- assert message.photo[-2].file_unique_id != ""
-
- assert isinstance(message.photo[-1], PhotoSize)
- assert isinstance(message.photo[-1].file_id, str)
- assert isinstance(message.photo[-1].file_unique_id, str)
- assert message.photo[-1].file_id != ""
- assert message.photo[-1].file_unique_id != ""
-
def test_de_json(self, bot, photo):
json_dict = {
"file_id": photo.file_id,
@@ -447,31 +126,6 @@ class TestPhoto:
assert photo_dict["height"] == photo.height
assert photo_dict["file_size"] == photo.file_size
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb"))
-
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file_id(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_photo(chat_id=chat_id, photo="")
-
- async def test_error_without_required_args(self, bot, chat_id):
- with pytest.raises(TypeError):
- await bot.send_photo(chat_id=chat_id)
-
- async def test_get_file_instance_method(self, monkeypatch, photo):
- async def make_assertion(*_, **kwargs):
- return kwargs["file_id"] == photo.file_id
-
- assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ["file_id"], [])
- assert await check_shortcut_call(photo.get_file, photo.get_bot(), "get_file")
- assert await check_defaults_handling(photo.get_file, photo.get_bot())
-
- monkeypatch.setattr(photo.get_bot(), "get_file", make_assertion)
- assert await photo.get_file()
-
def test_equality(self, photo):
a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height)
b = PhotoSize("", photo.file_unique_id, self.width, self.height)
@@ -499,3 +153,315 @@ class TestPhoto:
assert a != e
assert hash(a) != hash(e)
+
+ async def test_error_without_required_args(self, bot, chat_id):
+ with pytest.raises(TypeError):
+ await bot.send_photo(chat_id=chat_id)
+
+ async def test_send_photo_custom_filename(self, bot, chat_id, photo_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_photo(chat_id, photo_file, filename="custom_filename")
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_send_photo_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("photo") == expected
+ else:
+ test_flag = isinstance(data.get("photo"), InputFile)
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.send_photo(chat_id, file)
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ async def test_send_with_photosize(self, monkeypatch, bot, chat_id, photo):
+ async def make_assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters["photo"] == photo.file_id
+
+ monkeypatch.setattr(bot.request, "post", make_assertion)
+ assert await bot.send_photo(photo=photo, chat_id=chat_id)
+
+ async def test_get_file_instance_method(self, monkeypatch, photo):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["file_id"] == photo.file_id
+
+ assert check_shortcut_signature(PhotoSize.get_file, Bot.get_file, ["file_id"], [])
+ assert await check_shortcut_call(photo.get_file, photo.get_bot(), "get_file")
+ assert await check_defaults_handling(photo.get_file, photo.get_bot())
+
+ monkeypatch.setattr(photo.get_bot(), "get_file", make_assertion)
+ assert await photo.get_file()
+
+
+class TestPhotoWithRequest(TestPhotoBase):
+ async def test_send_photo_all_args(self, bot, chat_id, photo_file):
+ message = await bot.send_photo(
+ chat_id,
+ photo_file,
+ caption=self.caption,
+ disable_notification=False,
+ protect_content=True,
+ parse_mode="Markdown",
+ has_spoiler=True,
+ )
+
+ assert isinstance(message.photo[-2], PhotoSize)
+ assert isinstance(message.photo[-2].file_id, str)
+ assert isinstance(message.photo[-2].file_unique_id, str)
+ assert message.photo[-2].file_id != ""
+ assert message.photo[-2].file_unique_id != ""
+
+ assert isinstance(message.photo[-1], PhotoSize)
+ assert isinstance(message.photo[-1].file_id, str)
+ assert isinstance(message.photo[-1].file_unique_id, str)
+ assert message.photo[-1].file_id != ""
+ assert message.photo[-1].file_unique_id != ""
+
+ assert message.caption == self.caption.replace("*", "")
+ assert message.has_protected_content
+ assert message.has_media_spoiler
+
+ async def test_send_photo_parse_mode_markdown(self, bot, chat_id, photo_file):
+ message = await bot.send_photo(
+ chat_id, photo_file, caption=self.caption, parse_mode="Markdown"
+ )
+ assert isinstance(message.photo[-2], PhotoSize)
+ assert isinstance(message.photo[-2].file_id, str)
+ assert isinstance(message.photo[-2].file_unique_id, str)
+ assert message.photo[-2].file_id != ""
+ assert message.photo[-2].file_unique_id != ""
+
+ assert isinstance(message.photo[-1], PhotoSize)
+ assert isinstance(message.photo[-1].file_id, str)
+ assert isinstance(message.photo[-1].file_unique_id, str)
+ assert message.photo[-1].file_id != ""
+ assert message.photo[-1].file_unique_id != ""
+
+ assert message.caption == self.caption.replace("*", "")
+ assert len(message.caption_entities) == 1
+
+ async def test_send_photo_parse_mode_html(self, bot, chat_id, photo_file):
+ message = await bot.send_photo(
+ chat_id, photo_file, caption=self.caption, parse_mode="HTML"
+ )
+ assert isinstance(message.photo[-2], PhotoSize)
+ assert isinstance(message.photo[-2].file_id, str)
+ assert isinstance(message.photo[-2].file_unique_id, str)
+ assert message.photo[-2].file_id != ""
+ assert message.photo[-2].file_unique_id != ""
+
+ assert isinstance(message.photo[-1], PhotoSize)
+ assert isinstance(message.photo[-1].file_id, str)
+ assert isinstance(message.photo[-1].file_unique_id, str)
+ assert message.photo[-1].file_id != ""
+ assert message.photo[-1].file_unique_id != ""
+
+ assert message.caption == self.caption.replace("", "").replace("", "")
+ assert len(message.caption_entities) == 1
+
+ async def test_send_photo_caption_entities(self, bot, chat_id, photo_file):
+ 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_photo(
+ chat_id, photo_file, 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_photo_default_parse_mode_1(self, default_bot, chat_id, photo_file):
+ test_string = "Italic Bold Code"
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_photo(chat_id, photo_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_photo_default_parse_mode_2(self, default_bot, chat_id, photo_file):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_photo(
+ chat_id, photo_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_photo_default_parse_mode_3(self, default_bot, chat_id, photo_file):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_photo(
+ chat_id, photo_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_photo_default_protect_content(self, chat_id, default_bot, photo):
+ tasks = asyncio.gather(
+ default_bot.send_photo(chat_id, photo),
+ default_bot.send_photo(chat_id, photo, protect_content=False),
+ )
+ protected, unprotected = await tasks
+ assert protected.has_protected_content
+ assert not unprotected.has_protected_content
+
+ @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_photo_default_allow_sending_without_reply(
+ self, default_bot, chat_id, photo_file, 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_photo(
+ chat_id,
+ photo_file,
+ 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_photo(
+ chat_id, photo_file, 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_photo(
+ chat_id, photo_file, reply_to_message_id=reply_to_message.message_id
+ )
+
+ async def test_get_and_download(self, bot, photo):
+ path = Path("telegram.jpg")
+ if path.is_file():
+ path.unlink()
+
+ new_file = await bot.getFile(photo.file_id)
+
+ assert new_file.file_size == photo.file_size
+ assert new_file.file_unique_id == photo.file_unique_id
+ assert new_file.file_path.startswith("https://") is True
+
+ await new_file.download_to_drive("telegram.jpg")
+
+ assert path.is_file()
+
+ async def test_send_url_jpg_file(self, bot, chat_id):
+ message = await bot.send_photo(chat_id, photo=self.photo_file_url)
+
+ assert isinstance(message.photo[-2], PhotoSize)
+ assert isinstance(message.photo[-2].file_id, str)
+ assert isinstance(message.photo[-2].file_unique_id, str)
+ assert message.photo[-2].file_id != ""
+ assert message.photo[-2].file_unique_id != ""
+
+ assert isinstance(message.photo[-1], PhotoSize)
+ assert isinstance(message.photo[-1].file_id, str)
+ assert isinstance(message.photo[-1].file_unique_id, str)
+ assert message.photo[-1].file_id != ""
+ assert message.photo[-1].file_unique_id != ""
+
+ async def test_send_url_png_file(self, bot, chat_id):
+ message = await bot.send_photo(
+ photo="http://dummyimage.com/600x400/000/fff.png&text=telegram", chat_id=chat_id
+ )
+
+ photo = message.photo[-1]
+
+ assert isinstance(photo, PhotoSize)
+ assert isinstance(photo.file_id, str)
+ assert isinstance(photo.file_unique_id, str)
+ assert photo.file_id != ""
+ assert photo.file_unique_id != ""
+
+ async def test_send_file_unicode_filename(self, bot, chat_id):
+ """
+ Regression test for https://github.com/python-telegram-bot/python-telegram-bot/issues/1202
+ """
+ with data_file("测试.png").open("rb") as f:
+ message = await bot.send_photo(photo=f, chat_id=chat_id)
+
+ photo = message.photo[-1]
+
+ assert isinstance(photo, PhotoSize)
+ assert isinstance(photo.file_id, str)
+ assert isinstance(photo.file_unique_id, str)
+ assert photo.file_id != ""
+ assert photo.file_unique_id != ""
+
+ async def test_send_bytesio_jpg_file(self, bot, chat_id):
+ filepath = data_file("telegram_no_standard_header.jpg")
+
+ # raw image bytes
+ raw_bytes = BytesIO(filepath.read_bytes())
+ input_file = InputFile(raw_bytes)
+ assert input_file.mimetype == "application/octet-stream"
+
+ # raw image bytes with name info
+ raw_bytes = BytesIO(filepath.read_bytes())
+ raw_bytes.name = str(filepath)
+ input_file = InputFile(raw_bytes)
+ assert input_file.mimetype == "image/jpeg"
+
+ # send raw photo
+ raw_bytes = BytesIO(filepath.read_bytes())
+ message = await bot.send_photo(chat_id, photo=raw_bytes)
+ photo = message.photo[-1]
+ assert isinstance(photo.file_id, str)
+ assert isinstance(photo.file_unique_id, str)
+ assert photo.file_id != ""
+ assert photo.file_unique_id != ""
+ assert isinstance(photo, PhotoSize)
+ assert photo.width == 1280
+ assert photo.height == 720
+ assert photo.file_size == 33372
+
+ async def test_resend(self, bot, chat_id, photo):
+ message = await bot.send_photo(chat_id=chat_id, photo=photo.file_id)
+
+ assert isinstance(message.photo[-2], PhotoSize)
+ assert isinstance(message.photo[-2].file_id, str)
+ assert isinstance(message.photo[-2].file_unique_id, str)
+ assert message.photo[-2].file_id != ""
+ assert message.photo[-2].file_unique_id != ""
+
+ assert isinstance(message.photo[-1], PhotoSize)
+ assert isinstance(message.photo[-1].file_id, str)
+ assert isinstance(message.photo[-1].file_unique_id, str)
+ assert message.photo[-1].file_id != ""
+ assert message.photo[-1].file_unique_id != ""
+
+ async def test_error_send_empty_file(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_photo(chat_id=chat_id, photo=open(os.devnull, "rb"))
+
+ async def test_error_send_empty_file_id(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_photo(chat_id=chat_id, photo="")
diff --git a/tests/test_poll.py b/tests/test_poll.py
index 8a98a22a3..3fc49f46d 100644
--- a/tests/test_poll.py
+++ b/tests/test_poll.py
@@ -24,17 +24,19 @@ from telegram._utils.datetime import to_timestamp
from telegram.constants import PollType
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def poll_option():
- out = PollOption(text=TestPollOption.text, voter_count=TestPollOption.voter_count)
+ out = PollOption(text=TestPollOptionBase.text, voter_count=TestPollOptionBase.voter_count)
out._unfreeze()
return out
-class TestPollOption:
+class TestPollOptionBase:
text = "test option"
voter_count = 3
+
+class TestPollOptionWithoutRequest(TestPollOptionBase):
def test_slot_behaviour(self, poll_option, mro_slots):
for attr in poll_option.__slots__:
assert getattr(poll_option, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -75,18 +77,20 @@ class TestPollOption:
assert hash(a) != hash(e)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def poll_answer():
return PollAnswer(
- poll_id=TestPollAnswer.poll_id, user=TestPollAnswer.user, option_ids=TestPollAnswer.poll_id
+ TestPollAnswerBase.poll_id, TestPollAnswerBase.user, TestPollAnswerBase.poll_id
)
-class TestPollAnswer:
+class TestPollAnswerBase:
poll_id = "id"
user = User(1, "", False)
option_ids = [2]
+
+class TestPollAnswerWithoutRequest(TestPollAnswerBase):
def test_de_json(self):
json_dict = {
"poll_id": self.poll_id,
@@ -128,27 +132,27 @@ class TestPollAnswer:
assert hash(a) != hash(e)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def poll():
poll = Poll(
- TestPoll.id_,
- TestPoll.question,
- TestPoll.options,
- TestPoll.total_voter_count,
- TestPoll.is_closed,
- TestPoll.is_anonymous,
- TestPoll.type,
- TestPoll.allows_multiple_answers,
- explanation=TestPoll.explanation,
- explanation_entities=TestPoll.explanation_entities,
- open_period=TestPoll.open_period,
- close_date=TestPoll.close_date,
+ TestPollBase.id_,
+ TestPollBase.question,
+ TestPollBase.options,
+ TestPollBase.total_voter_count,
+ TestPollBase.is_closed,
+ TestPollBase.is_anonymous,
+ TestPollBase.type,
+ TestPollBase.allows_multiple_answers,
+ explanation=TestPollBase.explanation,
+ explanation_entities=TestPollBase.explanation_entities,
+ open_period=TestPollBase.open_period,
+ close_date=TestPollBase.close_date,
)
poll._unfreeze()
return poll
-class TestPoll:
+class TestPollBase:
id_ = "id"
question = "Test?"
options = [PollOption("test", 10), PollOption("test2", 11)]
@@ -165,6 +169,8 @@ class TestPoll:
open_period = 42
close_date = datetime.now(timezone.utc)
+
+class TestPollWithoutRequest(TestPollBase):
def test_de_json(self, bot):
json_dict = {
"id": self.id_,
@@ -218,6 +224,21 @@ class TestPoll:
assert poll_dict["open_period"] == poll.open_period
assert poll_dict["close_date"] == to_timestamp(poll.close_date)
+ def test_equality(self):
+ a = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True)
+ b = Poll(123, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True)
+ c = Poll(456, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True)
+ d = PollOption("Text", 1)
+
+ assert a == b
+ assert hash(a) == hash(b)
+
+ assert a != c
+ assert hash(a) != hash(c)
+
+ assert a != d
+ assert hash(a) != hash(d)
+
def test_enum_init(self):
poll = Poll(
type="foo",
@@ -267,18 +288,3 @@ class TestPoll:
assert poll.parse_explanation_entities(MessageEntity.URL) == {entity: "http://google.com"}
assert poll.parse_explanation_entities() == {entity: "http://google.com", entity_2: "h"}
-
- def test_equality(self):
- a = Poll(123, "question", ["O1", "O2"], 1, False, True, Poll.REGULAR, True)
- b = Poll(123, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True)
- c = Poll(456, "question", ["o1", "o2"], 1, True, False, Poll.REGULAR, True)
- d = PollOption("Text", 1)
-
- assert a == b
- assert hash(a) == hash(b)
-
- assert a != c
- assert hash(a) != hash(c)
-
- assert a != d
- assert hash(a) != hash(d)
diff --git a/tests/test_precheckoutquery.py b/tests/test_precheckoutquery.py
index acff440b7..61cb8a3e2 100644
--- a/tests/test_precheckoutquery.py
+++ b/tests/test_precheckoutquery.py
@@ -27,22 +27,22 @@ from tests.auxil.bot_method_checks import (
)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def pre_checkout_query(bot):
pcq = PreCheckoutQuery(
- TestPreCheckoutQuery.id_,
- TestPreCheckoutQuery.from_user,
- TestPreCheckoutQuery.currency,
- TestPreCheckoutQuery.total_amount,
- TestPreCheckoutQuery.invoice_payload,
- shipping_option_id=TestPreCheckoutQuery.shipping_option_id,
- order_info=TestPreCheckoutQuery.order_info,
+ TestPreCheckoutQueryBase.id_,
+ TestPreCheckoutQueryBase.from_user,
+ TestPreCheckoutQueryBase.currency,
+ TestPreCheckoutQueryBase.total_amount,
+ TestPreCheckoutQueryBase.invoice_payload,
+ shipping_option_id=TestPreCheckoutQueryBase.shipping_option_id,
+ order_info=TestPreCheckoutQueryBase.order_info,
)
pcq.set_bot(bot)
return pcq
-class TestPreCheckoutQuery:
+class TestPreCheckoutQueryBase:
id_ = 5
invoice_payload = "invoice_payload"
shipping_option_id = "shipping_option_id"
@@ -51,6 +51,8 @@ class TestPreCheckoutQuery:
from_user = User(0, "", False)
order_info = OrderInfo()
+
+class TestPreCheckoutQueryWithoutRequest(TestPreCheckoutQueryBase):
def test_slot_behaviour(self, pre_checkout_query, mro_slots):
inst = pre_checkout_query
for attr in inst.__slots__:
@@ -91,27 +93,6 @@ class TestPreCheckoutQuery:
assert pre_checkout_query_dict["from"] == pre_checkout_query.from_user.to_dict()
assert pre_checkout_query_dict["order_info"] == pre_checkout_query.order_info.to_dict()
- async def test_answer(self, monkeypatch, pre_checkout_query):
- async def make_assertion(*_, **kwargs):
- return kwargs["pre_checkout_query_id"] == pre_checkout_query.id
-
- assert check_shortcut_signature(
- PreCheckoutQuery.answer, Bot.answer_pre_checkout_query, ["pre_checkout_query_id"], []
- )
- assert await check_shortcut_call(
- pre_checkout_query.answer,
- pre_checkout_query.get_bot(),
- "answer_pre_checkout_query",
- )
- assert await check_defaults_handling(
- pre_checkout_query.answer, pre_checkout_query.get_bot()
- )
-
- monkeypatch.setattr(
- pre_checkout_query.get_bot(), "answer_pre_checkout_query", make_assertion
- )
- assert await pre_checkout_query.answer(ok=True)
-
def test_equality(self):
a = PreCheckoutQuery(
self.id_, self.from_user, self.currency, self.total_amount, self.invoice_payload
@@ -137,3 +118,24 @@ class TestPreCheckoutQuery:
assert a != e
assert hash(a) != hash(e)
+
+ async def test_answer(self, monkeypatch, pre_checkout_query):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["pre_checkout_query_id"] == pre_checkout_query.id
+
+ assert check_shortcut_signature(
+ PreCheckoutQuery.answer, Bot.answer_pre_checkout_query, ["pre_checkout_query_id"], []
+ )
+ assert await check_shortcut_call(
+ pre_checkout_query.answer,
+ pre_checkout_query.get_bot(),
+ "answer_pre_checkout_query",
+ )
+ assert await check_defaults_handling(
+ pre_checkout_query.answer, pre_checkout_query.get_bot()
+ )
+
+ monkeypatch.setattr(
+ pre_checkout_query.get_bot(), "answer_pre_checkout_query", make_assertion
+ )
+ assert await pre_checkout_query.answer(ok=True)
diff --git a/tests/test_proximityalerttriggered.py b/tests/test_proximityalerttriggered.py
index e47eb63a7..456c72b36 100644
--- a/tests/test_proximityalerttriggered.py
+++ b/tests/test_proximityalerttriggered.py
@@ -21,20 +21,22 @@ import pytest
from telegram import BotCommand, ProximityAlertTriggered, User
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def proximity_alert_triggered():
return ProximityAlertTriggered(
- traveler=TestProximityAlertTriggered.traveler,
- watcher=TestProximityAlertTriggered.watcher,
- distance=TestProximityAlertTriggered.distance,
+ TestProximityAlertTriggeredBase.traveler,
+ TestProximityAlertTriggeredBase.watcher,
+ TestProximityAlertTriggeredBase.distance,
)
-class TestProximityAlertTriggered:
+class TestProximityAlertTriggeredBase:
traveler = User(1, "foo", False)
watcher = User(2, "bar", False)
distance = 42
+
+class TestProximityAlertTriggeredWithoutRequest(TestProximityAlertTriggeredBase):
def test_slot_behaviour(self, proximity_alert_triggered, mro_slots):
inst = proximity_alert_triggered
for attr in inst.__slots__:
diff --git a/tests/test_ratelimiter.py b/tests/test_ratelimiter.py
index 1f888f112..f610e0b6e 100644
--- a/tests/test_ratelimiter.py
+++ b/tests/test_ratelimiter.py
@@ -36,9 +36,7 @@ from telegram.constants import ParseMode
from telegram.error import RetryAfter
from telegram.ext import AIORateLimiter, BaseRateLimiter, Defaults, ExtBot
from telegram.request import BaseRequest, RequestData
-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(
diff --git a/tests/test_replykeyboardmarkup.py b/tests/test_replykeyboardmarkup.py
index 0f3badfa1..5b97fda65 100644
--- a/tests/test_replykeyboardmarkup.py
+++ b/tests/test_replykeyboardmarkup.py
@@ -22,81 +22,32 @@ import pytest
from telegram import InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def reply_keyboard_markup():
return ReplyKeyboardMarkup(
- TestReplyKeyboardMarkup.keyboard,
- resize_keyboard=TestReplyKeyboardMarkup.resize_keyboard,
- one_time_keyboard=TestReplyKeyboardMarkup.one_time_keyboard,
- selective=TestReplyKeyboardMarkup.selective,
- is_persistent=TestReplyKeyboardMarkup.is_persistent,
+ TestReplyKeyboardMarkupBase.keyboard,
+ resize_keyboard=TestReplyKeyboardMarkupBase.resize_keyboard,
+ one_time_keyboard=TestReplyKeyboardMarkupBase.one_time_keyboard,
+ selective=TestReplyKeyboardMarkupBase.selective,
+ is_persistent=TestReplyKeyboardMarkupBase.is_persistent,
)
-class TestReplyKeyboardMarkup:
+class TestReplyKeyboardMarkupBase:
keyboard = [[KeyboardButton("button1"), KeyboardButton("button2")]]
resize_keyboard = True
one_time_keyboard = True
selective = True
is_persistent = True
+
+class TestReplyKeyboardMarkupWithoutRequest(TestReplyKeyboardMarkupBase):
def test_slot_behaviour(self, reply_keyboard_markup, mro_slots):
inst = reply_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_reply_keyboard_markup(
- self, bot, chat_id, reply_keyboard_markup
- ):
- message = await bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_markup)
-
- assert message.text == "Text"
-
- @pytest.mark.flaky(3, 1)
- async def test_send_message_with_data_markup(self, bot, chat_id):
- message = await bot.send_message(
- chat_id, "text 2", reply_markup={"keyboard": [["1", "2"]]}
- )
-
- assert message.text == "text 2"
-
- def test_from_button(self):
- reply_keyboard_markup = ReplyKeyboardMarkup.from_button(
- KeyboardButton(text="button1")
- ).keyboard
- assert len(reply_keyboard_markup) == 1
- assert len(reply_keyboard_markup[0]) == 1
-
- reply_keyboard_markup = ReplyKeyboardMarkup.from_button("button1").keyboard
- assert len(reply_keyboard_markup) == 1
- assert len(reply_keyboard_markup[0]) == 1
-
- def test_from_row(self):
- reply_keyboard_markup = ReplyKeyboardMarkup.from_row(
- [KeyboardButton(text="button1"), KeyboardButton(text="button2")]
- ).keyboard
- assert len(reply_keyboard_markup) == 1
- assert len(reply_keyboard_markup[0]) == 2
-
- reply_keyboard_markup = ReplyKeyboardMarkup.from_row(["button1", "button2"]).keyboard
- assert len(reply_keyboard_markup) == 1
- assert len(reply_keyboard_markup[0]) == 2
-
- def test_from_column(self):
- reply_keyboard_markup = ReplyKeyboardMarkup.from_column(
- [KeyboardButton(text="button1"), KeyboardButton(text="button2")]
- ).keyboard
- assert len(reply_keyboard_markup) == 2
- assert len(reply_keyboard_markup[0]) == 1
- assert len(reply_keyboard_markup[1]) == 1
-
- reply_keyboard_markup = ReplyKeyboardMarkup.from_column(["button1", "button2"]).keyboard
- assert len(reply_keyboard_markup) == 2
- assert len(reply_keyboard_markup[0]) == 1
- assert len(reply_keyboard_markup[1]) == 1
-
def test_expected_values(self, reply_keyboard_markup):
assert isinstance(reply_keyboard_markup.keyboard, tuple)
assert all(isinstance(row, tuple) for row in reply_keyboard_markup.keyboard)
@@ -107,18 +58,6 @@ class TestReplyKeyboardMarkup:
assert reply_keyboard_markup.selective == self.selective
assert reply_keyboard_markup.is_persistent == self.is_persistent
- def test_wrong_keyboard_inputs(self):
- with pytest.raises(ValueError):
- ReplyKeyboardMarkup([["button1"], 1])
- with pytest.raises(ValueError):
- ReplyKeyboardMarkup("strings_are_not_allowed")
- with pytest.raises(ValueError):
- ReplyKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
- with pytest.raises(ValueError):
- ReplyKeyboardMarkup(KeyboardButton("button1"))
- with pytest.raises(ValueError):
- ReplyKeyboardMarkup([[["button1"]]])
-
def test_to_dict(self, reply_keyboard_markup):
reply_keyboard_markup_dict = reply_keyboard_markup.to_dict()
@@ -165,3 +104,66 @@ class TestReplyKeyboardMarkup:
assert a != f
assert hash(a) != hash(f)
+
+ def test_wrong_keyboard_inputs(self):
+ with pytest.raises(ValueError):
+ ReplyKeyboardMarkup([["button1"], 1])
+ with pytest.raises(ValueError):
+ ReplyKeyboardMarkup("strings_are_not_allowed")
+ with pytest.raises(ValueError):
+ ReplyKeyboardMarkup(["strings_are_not_allowed_in_the_rows_either"])
+ with pytest.raises(ValueError):
+ ReplyKeyboardMarkup(KeyboardButton("button1"))
+ with pytest.raises(ValueError):
+ ReplyKeyboardMarkup([[["button1"]]])
+
+ def test_from_button(self):
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_button(
+ KeyboardButton(text="button1")
+ ).keyboard
+ assert len(reply_keyboard_markup) == 1
+ assert len(reply_keyboard_markup[0]) == 1
+
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_button("button1").keyboard
+ assert len(reply_keyboard_markup) == 1
+ assert len(reply_keyboard_markup[0]) == 1
+
+ def test_from_row(self):
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_row(
+ [KeyboardButton(text="button1"), KeyboardButton(text="button2")]
+ ).keyboard
+ assert len(reply_keyboard_markup) == 1
+ assert len(reply_keyboard_markup[0]) == 2
+
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_row(["button1", "button2"]).keyboard
+ assert len(reply_keyboard_markup) == 1
+ assert len(reply_keyboard_markup[0]) == 2
+
+ def test_from_column(self):
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_column(
+ [KeyboardButton(text="button1"), KeyboardButton(text="button2")]
+ ).keyboard
+ assert len(reply_keyboard_markup) == 2
+ assert len(reply_keyboard_markup[0]) == 1
+ assert len(reply_keyboard_markup[1]) == 1
+
+ reply_keyboard_markup = ReplyKeyboardMarkup.from_column(["button1", "button2"]).keyboard
+ assert len(reply_keyboard_markup) == 2
+ assert len(reply_keyboard_markup[0]) == 1
+ assert len(reply_keyboard_markup[1]) == 1
+
+
+class TestReplyKeyboardMarkupWithRequest(TestReplyKeyboardMarkupBase):
+ async def test_send_message_with_reply_keyboard_markup(
+ self, bot, chat_id, reply_keyboard_markup
+ ):
+ message = await bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_markup)
+
+ assert message.text == "Text"
+
+ async def test_send_message_with_data_markup(self, bot, chat_id):
+ message = await bot.send_message(
+ chat_id, "text 2", reply_markup={"keyboard": [["1", "2"]]}
+ )
+
+ assert message.text == "text 2"
diff --git a/tests/test_replykeyboardremove.py b/tests/test_replykeyboardremove.py
index fad6330b3..3845f5fc5 100644
--- a/tests/test_replykeyboardremove.py
+++ b/tests/test_replykeyboardremove.py
@@ -21,29 +21,23 @@ import pytest
from telegram import ReplyKeyboardRemove
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def reply_keyboard_remove():
- return ReplyKeyboardRemove(selective=TestReplyKeyboardRemove.selective)
+ return ReplyKeyboardRemove(selective=TestReplyKeyboardRemoveBase.selective)
-class TestReplyKeyboardRemove:
+class TestReplyKeyboardRemoveBase:
remove_keyboard = True
selective = True
+
+class TestReplyKeyboardRemoveWithoutRequest(TestReplyKeyboardRemoveBase):
def test_slot_behaviour(self, reply_keyboard_remove, mro_slots):
inst = reply_keyboard_remove
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_reply_keyboard_remove(
- self, bot, chat_id, reply_keyboard_remove
- ):
- message = await bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_remove)
-
- assert message.text == "Text"
-
def test_expected_values(self, reply_keyboard_remove):
assert reply_keyboard_remove.remove_keyboard == self.remove_keyboard
assert reply_keyboard_remove.selective == self.selective
@@ -55,3 +49,11 @@ class TestReplyKeyboardRemove:
reply_keyboard_remove_dict["remove_keyboard"] == reply_keyboard_remove.remove_keyboard
)
assert reply_keyboard_remove_dict["selective"] == reply_keyboard_remove.selective
+
+
+class TestReplyKeyboardRemoveWithRequest(TestReplyKeyboardRemoveBase):
+ async def test_send_message_with_reply_keyboard_remove(
+ self, bot, chat_id, reply_keyboard_remove
+ ):
+ message = await bot.send_message(chat_id, "Text", reply_markup=reply_keyboard_remove)
+ assert message.text == "Text"
diff --git a/tests/test_request.py b/tests/test_request.py
index 0b773d545..a81d3b1e4 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -20,7 +20,6 @@
implementations for BaseRequest and we want to test HTTPXRequest anyway."""
import asyncio
import json
-import os
from collections import defaultdict
from dataclasses import dataclass
from http import HTTPStatus
@@ -43,9 +42,9 @@ from telegram.error import (
)
from telegram.request._httpxrequest import HTTPXRequest
-from .auxil.object_conversions import env_var_2_bool
+from .conftest import TEST_WITH_OPT_DEPS
-# We only need the first fixture, but it uses the others, so pytest needs us to import them as well
+# We only need mixed_rqs fixture, but it uses the others, so pytest needs us to import them as well
from .test_requestdata import ( # noqa: F401
file_params,
input_media_photo,
@@ -72,9 +71,6 @@ async def httpx_request():
yield rq
-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"
)
@@ -84,14 +80,14 @@ class TestNoSocks:
HTTPXRequest(proxy_url="socks5://foo")
-class TestRequest:
+class TestRequestWithoutRequest:
test_flag = None
@pytest.fixture(autouse=True)
def reset(self):
self.test_flag = None
- async def test_init_import_errors(self, bot, monkeypatch):
+ async def test_init_import_errors(self, monkeypatch):
"""Makes sure that import errors are forwarded - related to TestNoSocks above"""
def __init__(self, *args, **kwargs):
@@ -325,7 +321,7 @@ class TestRequest:
assert self.test_flag == (1, 2, 3, 4)
-class TestHTTPXRequest:
+class TestHTTPXRequestWithoutRequest:
test_flag = None
@pytest.fixture(autouse=True)
@@ -595,8 +591,9 @@ class TestHTTPXRequest:
httpx_request.do_request(method="GET", url="URL"),
)
- @pytest.mark.flaky(3, 1)
- async def test_do_request_wait_for_pool(self, monkeypatch, httpx_request):
+
+class TestHTTPXRequestWithRequest:
+ async def test_do_request_wait_for_pool(self, httpx_request):
"""The pool logic is buried rather deeply in httpxcore, so we make actual requests here
instead of mocking"""
task_1 = asyncio.create_task(
diff --git a/tests/test_requestdata.py b/tests/test_requestdata.py
index 7a92cbec9..ad45e063e 100644
--- a/tests/test_requestdata.py
+++ b/tests/test_requestdata.py
@@ -108,28 +108,28 @@ def file_rqs(file_params) -> RequestData:
)
-@pytest.fixture()
+@pytest.fixture(scope="module")
def mixed_params(file_params, simple_params) -> Dict[str, Any]:
both = file_params.copy()
both.update(simple_params)
return both
-@pytest.fixture()
+@pytest.fixture(scope="module")
def mixed_jsons(file_jsons, simple_jsons) -> Dict[str, Any]:
both = file_jsons.copy()
both.update(simple_jsons)
return both
-@pytest.fixture()
+@pytest.fixture(scope="module")
def mixed_rqs(mixed_params) -> RequestData:
return RequestData(
[RequestParameter.from_input(key, value) for key, value in mixed_params.items()]
)
-class TestRequestData:
+class TestRequestDataWithoutRequest:
def test_slot_behaviour(self, simple_rqs, mro_slots):
for attr in simple_rqs.__slots__:
assert getattr(simple_rqs, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -201,7 +201,7 @@ class TestRequestData:
assert file_rqs.multipart_data == expected
assert mixed_rqs.multipart_data == expected
- def test_url_encoding(self, monkeypatch):
+ def test_url_encoding(self):
data = RequestData(
[
RequestParameter.from_input("chat_id", 123),
diff --git a/tests/test_requestparameter.py b/tests/test_requestparameter.py
index f7b7e4b8a..12c55c007 100644
--- a/tests/test_requestparameter.py
+++ b/tests/test_requestparameter.py
@@ -27,7 +27,13 @@ from telegram.request._requestparameter import RequestParameter
from tests.conftest import data_file
-class TestRequestParameter:
+class TestRequestParameterWithoutRequest:
+ def test_slot_behaviour(self, mro_slots):
+ inst = RequestParameter("name", "value", [1, 2])
+ 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"
+
def test_init(self):
request_parameter = RequestParameter("name", "value", [1, 2])
assert request_parameter.name == "name"
@@ -39,12 +45,6 @@ class TestRequestParameter:
assert request_parameter.value == "value"
assert request_parameter.input_files is None
- def test_slot_behaviour(self, mro_slots):
- inst = RequestParameter("name", "value", [1, 2])
- 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.parametrize(
"value, expected",
[
diff --git a/tests/test_sentwebappmessage.py b/tests/test_sentwebappmessage.py
index a1a3761f2..4304ab6bf 100644
--- a/tests/test_sentwebappmessage.py
+++ b/tests/test_sentwebappmessage.py
@@ -22,16 +22,16 @@ import pytest
from telegram import SentWebAppMessage
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def sent_web_app_message():
- return SentWebAppMessage(
- inline_message_id=TestSentWebAppMessage.inline_message_id,
- )
+ return SentWebAppMessage(inline_message_id=TestSentWebAppMessageBase.inline_message_id)
-class TestSentWebAppMessage:
+class TestSentWebAppMessageBase:
inline_message_id = "123"
+
+class TestSentWebAppMessageWithoutRequest(TestSentWebAppMessageBase):
def test_slot_behaviour(self, sent_web_app_message, mro_slots):
inst = sent_web_app_message
for attr in inst.__slots__:
diff --git a/tests/test_shared.py b/tests/test_shared.py
index 621d6753e..0d7da3b53 100644
--- a/tests/test_shared.py
+++ b/tests/test_shared.py
@@ -25,15 +25,17 @@ from telegram import ChatShared, UserShared
@pytest.fixture(scope="class")
def user_shared():
return UserShared(
- TestUserShared.request_id,
- TestUserShared.user_id,
+ TestUserSharedBase.request_id,
+ TestUserSharedBase.user_id,
)
-class TestUserShared:
+class TestUserSharedBase:
request_id = 789
user_id = 101112
+
+class TestUserSharedWithoutRequest(TestUserSharedBase):
def test_slot_behaviour(self, user_shared, mro_slots):
for attr in user_shared.__slots__:
assert getattr(user_shared, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -77,15 +79,17 @@ class TestUserShared:
@pytest.fixture(scope="class")
def chat_shared():
return ChatShared(
- TestChatShared.request_id,
- TestChatShared.chat_id,
+ TestChatSharedBase.request_id,
+ TestChatSharedBase.chat_id,
)
-class TestChatShared:
+class TestChatSharedBase:
request_id = 131415
chat_id = 161718
+
+class TestChatSharedWithoutRequest(TestChatSharedBase):
def test_slot_behaviour(self, chat_shared, mro_slots):
for attr in chat_shared.__slots__:
assert getattr(chat_shared, attr, "err") != "err", f"got extra slot '{attr}'"
diff --git a/tests/test_shippingaddress.py b/tests/test_shippingaddress.py
index 6b3fa53f1..439af4625 100644
--- a/tests/test_shippingaddress.py
+++ b/tests/test_shippingaddress.py
@@ -21,19 +21,19 @@ import pytest
from telegram import ShippingAddress
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def shipping_address():
return ShippingAddress(
- TestShippingAddress.country_code,
- TestShippingAddress.state,
- TestShippingAddress.city,
- TestShippingAddress.street_line1,
- TestShippingAddress.street_line2,
- TestShippingAddress.post_code,
+ TestShippingAddressBase.country_code,
+ TestShippingAddressBase.state,
+ TestShippingAddressBase.city,
+ TestShippingAddressBase.street_line1,
+ TestShippingAddressBase.street_line2,
+ TestShippingAddressBase.post_code,
)
-class TestShippingAddress:
+class TestShippingAddressBase:
country_code = "GB"
state = "state"
city = "London"
@@ -41,6 +41,8 @@ class TestShippingAddress:
street_line2 = "street_line2"
post_code = "WC1"
+
+class TestShippingAddressWithoutRequest(TestShippingAddressBase):
def test_slot_behaviour(self, shipping_address, mro_slots):
inst = shipping_address
for attr in inst.__slots__:
@@ -98,10 +100,20 @@ class TestShippingAddress:
"", self.state, self.city, self.street_line1, self.street_line2, self.post_code
)
d2 = ShippingAddress(
- self.country_code, "", self.city, self.street_line1, self.street_line2, self.post_code
+ self.country_code,
+ "",
+ self.city,
+ self.street_line1,
+ self.street_line2,
+ self.post_code,
)
d3 = ShippingAddress(
- self.country_code, self.state, "", self.street_line1, self.street_line2, self.post_code
+ self.country_code,
+ self.state,
+ "",
+ self.street_line1,
+ self.street_line2,
+ self.post_code,
)
d4 = ShippingAddress(
self.country_code, self.state, self.city, "", self.street_line2, self.post_code
diff --git a/tests/test_shippingoption.py b/tests/test_shippingoption.py
index e9241ce37..05832e931 100644
--- a/tests/test_shippingoption.py
+++ b/tests/test_shippingoption.py
@@ -21,18 +21,20 @@ import pytest
from telegram import LabeledPrice, ShippingOption, Voice
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def shipping_option():
return ShippingOption(
- TestShippingOption.id_, TestShippingOption.title, TestShippingOption.prices
+ TestShippingOptionBase.id_, TestShippingOptionBase.title, TestShippingOptionBase.prices
)
-class TestShippingOption:
+class TestShippingOptionBase:
id_ = "id"
title = "title"
prices = [LabeledPrice("Fish Container", 100), LabeledPrice("Premium Fish Container", 1000)]
+
+class TestShippingOptionWithoutRequest(TestShippingOptionBase):
def test_slot_behaviour(self, shipping_option, mro_slots):
inst = shipping_option
for attr in inst.__slots__:
diff --git a/tests/test_shippingquery.py b/tests/test_shippingquery.py
index 061c0f6e8..485511918 100644
--- a/tests/test_shippingquery.py
+++ b/tests/test_shippingquery.py
@@ -27,24 +27,26 @@ from tests.auxil.bot_method_checks import (
)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def shipping_query(bot):
sq = ShippingQuery(
- TestShippingQuery.id_,
- TestShippingQuery.from_user,
- TestShippingQuery.invoice_payload,
- TestShippingQuery.shipping_address,
+ TestShippingQueryBase.id_,
+ TestShippingQueryBase.from_user,
+ TestShippingQueryBase.invoice_payload,
+ TestShippingQueryBase.shipping_address,
)
sq.set_bot(bot)
return sq
-class TestShippingQuery:
+class TestShippingQueryBase:
id_ = "5"
invoice_payload = "invoice_payload"
from_user = User(0, "", False)
shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1")
+
+class TestShippingQueryWithoutRequest(TestShippingQueryBase):
def test_slot_behaviour(self, shipping_query, mro_slots):
inst = shipping_query
for attr in inst.__slots__:
@@ -53,10 +55,10 @@ class TestShippingQuery:
def test_de_json(self, bot):
json_dict = {
- "id": TestShippingQuery.id_,
- "invoice_payload": TestShippingQuery.invoice_payload,
- "from": TestShippingQuery.from_user.to_dict(),
- "shipping_address": TestShippingQuery.shipping_address.to_dict(),
+ "id": self.id_,
+ "invoice_payload": self.invoice_payload,
+ "from": self.from_user.to_dict(),
+ "shipping_address": self.shipping_address.to_dict(),
}
shipping_query = ShippingQuery.de_json(json_dict, bot)
assert shipping_query.api_kwargs == {}
@@ -76,21 +78,6 @@ class TestShippingQuery:
assert shipping_query_dict["from"] == shipping_query.from_user.to_dict()
assert shipping_query_dict["shipping_address"] == shipping_query.shipping_address.to_dict()
- async def test_answer(self, monkeypatch, shipping_query):
- async def make_assertion(*_, **kwargs):
- return kwargs["shipping_query_id"] == shipping_query.id
-
- assert check_shortcut_signature(
- ShippingQuery.answer, Bot.answer_shipping_query, ["shipping_query_id"], []
- )
- assert await check_shortcut_call(
- shipping_query.answer, shipping_query._bot, "answer_shipping_query"
- )
- assert await check_defaults_handling(shipping_query.answer, shipping_query._bot)
-
- monkeypatch.setattr(shipping_query._bot, "answer_shipping_query", make_assertion)
- assert await shipping_query.answer(ok=True)
-
def test_equality(self):
a = ShippingQuery(self.id_, self.from_user, self.invoice_payload, self.shipping_address)
b = ShippingQuery(self.id_, self.from_user, self.invoice_payload, self.shipping_address)
@@ -110,3 +97,18 @@ class TestShippingQuery:
assert a != e
assert hash(a) != hash(e)
+
+ async def test_answer(self, monkeypatch, shipping_query):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["shipping_query_id"] == shipping_query.id
+
+ assert check_shortcut_signature(
+ ShippingQuery.answer, Bot.answer_shipping_query, ["shipping_query_id"], []
+ )
+ assert await check_shortcut_call(
+ shipping_query.answer, shipping_query._bot, "answer_shipping_query"
+ )
+ assert await check_defaults_handling(shipping_query.answer, shipping_query._bot)
+
+ monkeypatch.setattr(shipping_query._bot, "answer_shipping_query", make_assertion)
+ assert await shipping_query.answer(ok=True)
diff --git a/tests/test_sticker.py b/tests/test_sticker.py
index cc29c87eb..b4c1c2ecd 100644
--- a/tests/test_sticker.py
+++ b/tests/test_sticker.py
@@ -39,7 +39,7 @@ def sticker_file():
yield file
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def sticker(bot, chat_id):
with data_file("telegram.webp").open("rb") as f:
return (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker
@@ -51,7 +51,7 @@ def animated_sticker_file():
yield f
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def animated_sticker(bot, chat_id):
with data_file("telegram_animated_sticker.tgs").open("rb") as f:
return (await bot.send_sticker(chat_id, sticker=f, read_timeout=50)).sticker
@@ -63,13 +63,13 @@ def video_sticker_file():
yield f
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def video_sticker(bot, chat_id):
with data_file("telegram_video_sticker.webm").open("rb") as f:
return bot.send_sticker(chat_id, sticker=f, timeout=50).sticker
-class TestSticker:
+class TestStickerBase:
# sticker_file_url = 'https://python-telegram-bot.org/static/testfiles/telegram.webp'
# Serving sticker from gh since our server sends wrong content_type
sticker_file_url = (
@@ -94,7 +94,9 @@ class TestSticker:
premium_animation = File("this_is_an_id", "this_is_an_unique_id")
- def test_slot_behaviour(self, sticker, mro_slots, recwarn):
+
+class TestStickerWithoutRequest(TestStickerBase):
+ def test_slot_behaviour(self, sticker, mro_slots):
for attr in sticker.__slots__:
assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(sticker)) == len(set(mro_slots(sticker))), "duplicate slot"
@@ -125,91 +127,19 @@ class TestSticker:
# we need to be a premium TG user to send a premium sticker, so the below is not tested
# assert sticker.premium_animation == self.premium_animation
- @pytest.mark.flaky(3, 1)
- async def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
- message = await bot.send_sticker(
- chat_id, sticker=sticker_file, disable_notification=False, protect_content=True
- )
+ def test_to_dict(self, sticker):
+ sticker_dict = sticker.to_dict()
- assert isinstance(message.sticker, Sticker)
- assert isinstance(message.sticker.file_id, str)
- assert isinstance(message.sticker.file_unique_id, str)
- assert message.sticker.file_id != ""
- assert message.sticker.file_unique_id != ""
- assert message.sticker.width == sticker.width
- assert message.sticker.height == sticker.height
- assert message.sticker.is_animated == sticker.is_animated
- assert message.sticker.is_video == sticker.is_video
- assert message.sticker.file_size == sticker.file_size
- assert message.sticker.type == sticker.type
- assert message.has_protected_content
- # we need to be a premium TG user to send a premium sticker, so the below is not tested
- # assert message.sticker.premium_animation == sticker.premium_animation
-
- assert isinstance(message.sticker.thumb, PhotoSize)
- assert isinstance(message.sticker.thumb.file_id, str)
- assert isinstance(message.sticker.thumb.file_unique_id, str)
- assert message.sticker.thumb.file_id != ""
- assert message.sticker.thumb.file_unique_id != ""
- assert message.sticker.thumb.width == sticker.thumb.width
- assert message.sticker.thumb.height == sticker.thumb.height
- assert message.sticker.thumb.file_size == sticker.thumb.file_size
-
- @pytest.mark.flaky(3, 1)
- async def test_get_and_download(self, bot, sticker):
- path = Path("telegram.webp")
- if path.is_file():
- path.unlink()
-
- new_file = await bot.get_file(sticker.file_id)
-
- assert new_file.file_size == sticker.file_size
- assert new_file.file_id == sticker.file_id
- assert new_file.file_unique_id == sticker.file_unique_id
- assert new_file.file_path.startswith("https://")
-
- await new_file.download_to_drive("telegram.webp")
-
- assert path.is_file()
-
- @pytest.mark.flaky(3, 1)
- async def test_resend(self, bot, chat_id, sticker):
- message = await bot.send_sticker(chat_id=chat_id, sticker=sticker.file_id)
-
- assert message.sticker == sticker
-
- @pytest.mark.flaky(3, 1)
- async def test_send_on_server_emoji(self, bot, chat_id):
- server_file_id = "CAADAQADHAADyIsGAAFZfq1bphjqlgI"
- message = await bot.send_sticker(chat_id=chat_id, sticker=server_file_id)
- sticker = message.sticker
- assert sticker.emoji == self.emoji
-
- @pytest.mark.flaky(3, 1)
- async def test_send_from_url(self, bot, chat_id):
- message = await bot.send_sticker(chat_id=chat_id, sticker=self.sticker_file_url)
- sticker = message.sticker
-
- assert isinstance(message.sticker, Sticker)
- assert isinstance(message.sticker.file_id, str)
- assert isinstance(message.sticker.file_unique_id, str)
- assert message.sticker.file_id != ""
- assert message.sticker.file_unique_id != ""
- assert message.sticker.width == sticker.width
- assert message.sticker.height == sticker.height
- assert message.sticker.is_animated == sticker.is_animated
- assert message.sticker.is_video == sticker.is_video
- assert message.sticker.file_size == sticker.file_size
- assert message.sticker.type == sticker.type
-
- assert isinstance(message.sticker.thumb, PhotoSize)
- assert isinstance(message.sticker.thumb.file_id, str)
- assert isinstance(message.sticker.thumb.file_unique_id, str)
- assert message.sticker.thumb.file_id != ""
- assert message.sticker.thumb.file_unique_id != ""
- assert message.sticker.thumb.width == sticker.thumb.width
- assert message.sticker.thumb.height == sticker.thumb.height
- assert message.sticker.thumb.file_size == sticker.thumb.file_size
+ assert isinstance(sticker_dict, dict)
+ assert sticker_dict["file_id"] == sticker.file_id
+ assert sticker_dict["file_unique_id"] == sticker.file_unique_id
+ assert sticker_dict["width"] == sticker.width
+ assert sticker_dict["height"] == sticker.height
+ assert sticker_dict["is_animated"] == sticker.is_animated
+ assert sticker_dict["is_video"] == sticker.is_video
+ assert sticker_dict["file_size"] == sticker.file_size
+ assert sticker_dict["thumb"] == sticker.thumb.to_dict()
+ assert sticker_dict["type"] == sticker.type
def test_de_json(self, bot, sticker):
json_dict = {
@@ -242,135 +172,6 @@ class TestSticker:
assert json_sticker.type == self.type
assert json_sticker.custom_emoji_id == self.custom_emoji_id
- async def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
- async def make_assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters["sticker"] == sticker.file_id
-
- monkeypatch.setattr(bot.request, "post", make_assertion)
- message = await bot.send_sticker(sticker=sticker, chat_id=chat_id)
- assert message
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_send_sticker_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("sticker") == expected
- else:
- test_flag = isinstance(data.get("sticker"), InputFile)
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.send_sticker(chat_id, 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_sticker_default_allow_sending_without_reply(
- self, default_bot, chat_id, sticker, 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_sticker(
- chat_id,
- sticker,
- 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_sticker(
- chat_id, sticker, 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_sticker(
- chat_id, sticker, 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_sticker_default_protect_content(self, chat_id, sticker, default_bot):
- protected = await default_bot.send_sticker(chat_id, sticker)
- assert protected.has_protected_content
- unprotected = await default_bot.send_sticker(chat_id, sticker, protect_content=False)
- assert not unprotected.has_protected_content
-
- def test_to_dict(self, sticker):
- sticker_dict = sticker.to_dict()
-
- assert isinstance(sticker_dict, dict)
- assert sticker_dict["file_id"] == sticker.file_id
- assert sticker_dict["file_unique_id"] == sticker.file_unique_id
- assert sticker_dict["width"] == sticker.width
- assert sticker_dict["height"] == sticker.height
- assert sticker_dict["is_animated"] == sticker.is_animated
- assert sticker_dict["is_video"] == sticker.is_video
- assert sticker_dict["file_size"] == sticker.file_size
- assert sticker_dict["thumb"] == sticker.thumb.to_dict()
- assert sticker_dict["type"] == sticker.type
-
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_sticker(chat_id, open(os.devnull, "rb"))
-
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file_id(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_sticker(chat_id, "")
-
- async def test_error_without_required_args(self, bot, chat_id):
- with pytest.raises(TypeError):
- await bot.send_sticker(chat_id)
-
- @pytest.mark.flaky(3, 1)
- async def test_premium_animation(self, bot):
- # testing animation sucks a bit since we can't create a premium sticker. What we can do is
- # get a sticker set which includes a premium sticker and check that specific one.
- premium_sticker_set = await bot.get_sticker_set("Flame")
- # the first one to appear here is a sticker with unique file id of AQADOBwAAifPOElr
- # this could change in the future ofc.
- premium_sticker = premium_sticker_set.stickers[20]
- assert premium_sticker.premium_animation.file_unique_id == "AQADOBwAAifPOElr"
- assert isinstance(premium_sticker.premium_animation.file_id, str)
- assert premium_sticker.premium_animation.file_id != ""
- premium_sticker_dict = {
- "file_unique_id": "AQADOBwAAifPOElr",
- "file_id": premium_sticker.premium_animation.file_id,
- "file_size": premium_sticker.premium_animation.file_size,
- }
- assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict
-
- @pytest.mark.flaky(3, 1)
- async def test_custom_emoji(self, bot):
- # testing custom emoji stickers is as much of an annoyance as the premium animation, see
- # in test_premium_animation
- custom_emoji_set = await bot.get_sticker_set("PTBStaticEmojiTestPack")
- # the first one to appear here is a sticker with unique file id of AQADjBsAAkKD0Uty
- # this could change in the future ofc.
- custom_emoji_sticker = custom_emoji_set.stickers[0]
- assert custom_emoji_sticker.custom_emoji_id == "6046140249875156202"
-
def test_equality(self, sticker):
a = Sticker(
sticker.file_id,
@@ -429,6 +230,216 @@ class TestSticker:
assert a != e
assert hash(a) != hash(e)
+ async def test_error_without_required_args(self, bot, chat_id):
+ with pytest.raises(TypeError):
+ await bot.send_sticker(chat_id)
+
+ async def test_send_with_sticker(self, monkeypatch, bot, chat_id, sticker):
+ async def make_assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters["sticker"] == sticker.file_id
+
+ monkeypatch.setattr(bot.request, "post", make_assertion)
+ assert await bot.send_sticker(sticker=sticker, chat_id=chat_id)
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_send_sticker_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("sticker") == expected
+ else:
+ test_flag = isinstance(data.get("sticker"), InputFile)
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.send_sticker(chat_id, file)
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+
+class TestStickerWithRequest(TestStickerBase):
+ async def test_send_all_args(self, bot, chat_id, sticker_file, sticker):
+ message = await bot.send_sticker(
+ chat_id, sticker=sticker_file, disable_notification=False, protect_content=True
+ )
+
+ assert isinstance(message.sticker, Sticker)
+ assert isinstance(message.sticker.file_id, str)
+ assert isinstance(message.sticker.file_unique_id, str)
+ assert message.sticker.file_id != ""
+ assert message.sticker.file_unique_id != ""
+ assert message.sticker.width == sticker.width
+ assert message.sticker.height == sticker.height
+ assert message.sticker.is_animated == sticker.is_animated
+ assert message.sticker.is_video == sticker.is_video
+ assert message.sticker.file_size == sticker.file_size
+ assert message.sticker.type == sticker.type
+ assert message.has_protected_content
+ # we need to be a premium TG user to send a premium sticker, so the below is not tested
+ # assert message.sticker.premium_animation == sticker.premium_animation
+
+ assert isinstance(message.sticker.thumb, PhotoSize)
+ assert isinstance(message.sticker.thumb.file_id, str)
+ assert isinstance(message.sticker.thumb.file_unique_id, str)
+ assert message.sticker.thumb.file_id != ""
+ assert message.sticker.thumb.file_unique_id != ""
+ assert message.sticker.thumb.width == sticker.thumb.width
+ assert message.sticker.thumb.height == sticker.thumb.height
+ assert message.sticker.thumb.file_size == sticker.thumb.file_size
+
+ async def test_get_and_download(self, bot, sticker, chat_id):
+ path = Path("telegram.webp")
+ if path.is_file():
+ path.unlink()
+
+ new_file = await bot.get_file(sticker.file_id)
+
+ assert new_file.file_size == sticker.file_size
+ assert new_file.file_unique_id == sticker.file_unique_id
+ assert new_file.file_path.startswith("https://")
+
+ await new_file.download_to_drive("telegram.webp")
+
+ assert path.is_file()
+
+ async def test_resend(self, bot, chat_id, sticker):
+ message = await bot.send_sticker(chat_id=chat_id, sticker=sticker.file_id)
+
+ assert message.sticker == sticker
+
+ async def test_send_on_server_emoji(self, bot, chat_id):
+ server_file_id = "CAADAQADHAADyIsGAAFZfq1bphjqlgI"
+ message = await bot.send_sticker(chat_id=chat_id, sticker=server_file_id)
+ sticker = message.sticker
+ assert sticker.emoji == self.emoji
+
+ async def test_send_from_url(self, bot, chat_id):
+ message = await bot.send_sticker(chat_id=chat_id, sticker=self.sticker_file_url)
+ sticker = message.sticker
+
+ assert isinstance(message.sticker, Sticker)
+ assert isinstance(message.sticker.file_id, str)
+ assert isinstance(message.sticker.file_unique_id, str)
+ assert message.sticker.file_id != ""
+ assert message.sticker.file_unique_id != ""
+ assert message.sticker.width == sticker.width
+ assert message.sticker.height == sticker.height
+ assert message.sticker.is_animated == sticker.is_animated
+ assert message.sticker.is_video == sticker.is_video
+ assert message.sticker.file_size == sticker.file_size
+ assert message.sticker.type == sticker.type
+
+ assert isinstance(message.sticker.thumb, PhotoSize)
+ assert isinstance(message.sticker.thumb.file_id, str)
+ assert isinstance(message.sticker.thumb.file_unique_id, str)
+ assert message.sticker.thumb.file_id != ""
+ assert message.sticker.thumb.file_unique_id != ""
+ assert message.sticker.thumb.width == sticker.thumb.width
+ assert message.sticker.thumb.height == sticker.thumb.height
+ assert message.sticker.thumb.file_size == sticker.thumb.file_size
+
+ @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_sticker_default_allow_sending_without_reply(
+ self, default_bot, chat_id, sticker, 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_sticker(
+ chat_id,
+ sticker,
+ 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_sticker(
+ chat_id, sticker, 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_sticker(
+ chat_id, sticker, reply_to_message_id=reply_to_message.message_id
+ )
+
+ @pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
+ async def test_send_sticker_default_protect_content(self, chat_id, sticker, default_bot):
+ tasks = asyncio.gather(
+ default_bot.send_sticker(chat_id, sticker),
+ default_bot.send_sticker(chat_id, sticker, protect_content=False),
+ )
+ protected, unprotected = await tasks
+ assert protected.has_protected_content
+ assert not unprotected.has_protected_content
+
+ async def test_premium_animation(self, bot):
+ # testing animation sucks a bit since we can't create a premium sticker. What we can do is
+ # get a sticker set which includes a premium sticker and check that specific one.
+ premium_sticker_set = await bot.get_sticker_set("Flame")
+ # the first one to appear here is a sticker with unique file id of AQADOBwAAifPOElr
+ # this could change in the future ofc.
+ premium_sticker = premium_sticker_set.stickers[20]
+ assert premium_sticker.premium_animation.file_unique_id == "AQADOBwAAifPOElr"
+ assert isinstance(premium_sticker.premium_animation.file_id, str)
+ assert premium_sticker.premium_animation.file_id != ""
+ premium_sticker_dict = {
+ "file_unique_id": "AQADOBwAAifPOElr",
+ "file_id": premium_sticker.premium_animation.file_id,
+ "file_size": premium_sticker.premium_animation.file_size,
+ }
+ assert premium_sticker.premium_animation.to_dict() == premium_sticker_dict
+
+ async def test_custom_emoji(self, bot):
+ # testing custom emoji stickers is as much of an annoyance as the premium animation, see
+ # in test_premium_animation
+ custom_emoji_set = await bot.get_sticker_set("PTBStaticEmojiTestPack")
+ # the first one to appear here is a sticker with unique file id of AQADjBsAAkKD0Uty
+ # this could change in the future ofc.
+ custom_emoji_sticker = custom_emoji_set.stickers[0]
+ assert custom_emoji_sticker.custom_emoji_id == "6046140249875156202"
+
+ async def test_custom_emoji_sticker(self, bot):
+ # we use the same ID as in test_custom_emoji
+ emoji_sticker_list = await bot.get_custom_emoji_stickers(["6046140249875156202"])
+ assert emoji_sticker_list[0].emoji == "😎"
+ assert emoji_sticker_list[0].height == 100
+ assert emoji_sticker_list[0].width == 100
+ assert not emoji_sticker_list[0].is_animated
+ assert not emoji_sticker_list[0].is_video
+ assert emoji_sticker_list[0].set_name == "PTBStaticEmojiTestPack"
+ assert emoji_sticker_list[0].type == Sticker.CUSTOM_EMOJI
+ assert emoji_sticker_list[0].custom_emoji_id == "6046140249875156202"
+ assert emoji_sticker_list[0].thumb.width == 100
+ assert emoji_sticker_list[0].thumb.height == 100
+ assert emoji_sticker_list[0].thumb.file_size == 3614
+ assert emoji_sticker_list[0].thumb.file_unique_id == "AQAD6gwAAoY06FNy"
+ assert emoji_sticker_list[0].file_size == 3678
+ assert emoji_sticker_list[0].file_unique_id == "AgAD6gwAAoY06FM"
+
+ async def test_error_send_empty_file(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_sticker(chat_id, open(os.devnull, "rb"))
+
+ async def test_error_send_empty_file_id(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_sticker(chat_id, "")
+
@pytest.fixture(scope="function")
async def sticker_set(bot):
@@ -478,7 +489,7 @@ def sticker_set_thumb_file():
yield file
-class TestStickerSet:
+class TestStickerSetBase:
title = "Test stickers"
is_animated = True
is_video = True
@@ -487,6 +498,14 @@ class TestStickerSet:
sticker_type = Sticker.REGULAR
contains_masks = True
+
+class TestStickerSetWithoutRequest(TestStickerSetBase):
+ def test_slot_behaviour(self, mro_slots):
+ inst = StickerSet("this", "is", True, self.stickers, True, "not")
+ 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"
+
def test_de_json(self, bot, sticker):
name = f"test_by_{bot.username}"
json_dict = {
@@ -510,82 +529,6 @@ class TestStickerSet:
assert sticker_set.sticker_type == self.sticker_type
assert sticker_set.api_kwargs == {"contains_masks": self.contains_masks}
- async def test_create_sticker_set(
- self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file
- ):
- """Creates the sticker set (if needed) which is required for tests. Make sure that this
- test comes before the tests that actually use the sticker sets!
- """
- test_by = f"test_by_{bot.username}"
- for sticker_set in [test_by, f"animated_{test_by}", f"video_{test_by}"]:
- try:
- await bot.get_sticker_set(sticker_set)
- except BadRequest as e:
- if not e.message == "Stickerset_invalid":
- raise e
-
- if sticker_set.startswith(test_by):
- s = await bot.create_new_sticker_set(
- chat_id,
- name=sticker_set,
- title="Sticker Test",
- png_sticker=sticker_file,
- emojis="😄",
- )
- assert s
- elif sticker_set.startswith("animated"):
- a = await bot.create_new_sticker_set(
- chat_id,
- name=sticker_set,
- title="Animated Test",
- tgs_sticker=animated_sticker_file,
- emojis="😄",
- )
- assert a
- elif sticker_set.startswith("video"):
- v = await bot.create_new_sticker_set(
- chat_id,
- name=sticker_set,
- title="Video Test",
- webm_sticker=video_sticker_file,
- emojis="🤔",
- )
- assert v
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_1_png(self, bot, chat_id, sticker_file):
- with data_file("telegram_sticker.png").open("rb") as f:
- # chat_id was hardcoded as 95205500 but it stopped working for some reason
- file = await bot.upload_sticker_file(chat_id, f)
- assert file
- assert await bot.add_sticker_to_set(
- chat_id, f"test_by_{bot.username}", png_sticker=file.file_id, emojis="😄"
- )
- # Also test with file input and mask
- assert await bot.add_sticker_to_set(
- chat_id,
- f"test_by_{bot.username}",
- png_sticker=sticker_file,
- emojis="😄",
- mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2),
- )
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_1_tgs(self, bot, chat_id):
- assert await bot.add_sticker_to_set(
- chat_id,
- f"animated_test_by_{bot.username}",
- tgs_sticker=data_file("telegram_animated_sticker.tgs").open("rb"),
- emojis="😄",
- )
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_1_webm(self, bot, chat_id):
- with data_file("telegram_video_sticker.webm").open("rb") as f:
- assert await bot.add_sticker_to_set(
- chat_id, f"video_test_by_{bot.username}", webm_sticker=f, emojis="🤔"
- )
-
def test_sticker_set_to_dict(self, sticker_set):
sticker_set_dict = sticker_set.to_dict()
@@ -598,215 +541,6 @@ class TestStickerSet:
assert sticker_set_dict["thumb"] == sticker_set.thumb.to_dict()
assert sticker_set_dict["sticker_type"] == sticker_set.sticker_type
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_2_png(self, bot, sticker_set):
- file_id = sticker_set.stickers[0].file_id
- assert await bot.set_sticker_position_in_set(file_id, 1)
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_2_tgs(self, bot, animated_sticker_set):
- file_id = animated_sticker_set.stickers[0].file_id
- assert await bot.set_sticker_position_in_set(file_id, 1)
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_2_webm(self, bot, video_sticker_set):
- file_id = video_sticker_set.stickers[0].file_id
- assert await bot.set_sticker_position_in_set(file_id, 1)
-
- @pytest.mark.flaky(3, 1)
- async def test_bot_methods_3_png(self, bot, chat_id, sticker_set_thumb_file):
- assert await bot.set_sticker_set_thumb(
- f"test_by_{bot.username}", chat_id, sticker_set_thumb_file
- )
-
- @pytest.mark.flaky(10, 1)
- async def test_bot_methods_3_tgs(
- self, bot, chat_id, animated_sticker_file, animated_sticker_set
- ):
- await asyncio.sleep(1)
- animated_test = f"animated_test_by_{bot.username}"
- assert await bot.set_sticker_set_thumb(animated_test, chat_id, animated_sticker_file)
- file_id = animated_sticker_set.stickers[-1].file_id
- # also test with file input and mask
- assert await bot.set_sticker_set_thumb(animated_test, chat_id, file_id)
-
- # TODO: Try the below by creating a custom .webm and not by downloading another pack's thumb
- @pytest.mark.skip(
- "Skipped for now since Telegram throws a 'File is too big' error "
- "regardless of the .webm file size."
- )
- def test_bot_methods_3_webm(self, bot, chat_id, video_sticker_file, video_sticker_set):
- pass
-
- @pytest.mark.flaky(10, 1)
- async def test_bot_methods_4_png(self, bot, sticker_set):
- await asyncio.sleep(1)
- file_id = sticker_set.stickers[-1].file_id
- assert await bot.delete_sticker_from_set(file_id)
-
- @pytest.mark.flaky(10, 1)
- async def test_bot_methods_4_tgs(self, bot, animated_sticker_set):
- await asyncio.sleep(1)
- file_id = animated_sticker_set.stickers[-1].file_id
- assert await bot.delete_sticker_from_set(file_id)
-
- @pytest.mark.flaky(10, 1)
- async def test_bot_methods_4_webm(self, bot, video_sticker_set):
- await asyncio.sleep(1)
- file_id = video_sticker_set.stickers[-1].file_id
- assert await bot.delete_sticker_from_set(file_id)
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_upload_sticker_file_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("png_sticker") == expected
- else:
- test_flag = isinstance(data.get("png_sticker"), InputFile)
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.upload_sticker_file(chat_id, file)
- assert test_flag
- monkeypatch.delattr(bot, "_post")
- finally:
- bot._local_mode = False
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_create_new_sticker_set_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("png_sticker") == expected
- and data.get("tgs_sticker") == expected
- and data.get("webm_sticker") == expected
- )
- else:
- test_flag = (
- isinstance(data.get("png_sticker"), InputFile)
- and isinstance(data.get("tgs_sticker"), InputFile)
- and isinstance(data.get("webm_sticker"), InputFile)
- )
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.create_new_sticker_set(
- chat_id,
- "name",
- "title",
- "emoji",
- png_sticker=file,
- tgs_sticker=file,
- webm_sticker=file,
- )
- assert test_flag
- monkeypatch.delattr(bot, "_post")
- finally:
- bot._local_mode = False
-
- async def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
- async def make_assertion(_, data, *args, **kwargs):
- assert data["user_id"] == chat_id
- assert data["name"] == "name"
- assert data["title"] == "title"
- assert data["emojis"] == "emoji"
- assert data["mask_position"] == mask_position
- assert data["png_sticker"] == "wow.png"
- assert data["tgs_sticker"] == "wow.tgs"
- assert data["webm_sticker"] == "wow.webm"
- assert data["sticker_type"] == Sticker.MASK
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.create_new_sticker_set(
- chat_id,
- "name",
- "title",
- "emoji",
- mask_position=mask_position,
- png_sticker="wow.png",
- tgs_sticker="wow.tgs",
- webm_sticker="wow.webm",
- sticker_type=Sticker.MASK,
- )
- monkeypatch.delattr(bot, "_post")
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_add_sticker_to_set_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("png_sticker") == expected and data.get("tgs_sticker") == expected
- )
- else:
- test_flag = isinstance(data.get("png_sticker"), InputFile) and isinstance(
- data.get("tgs_sticker"), InputFile
- )
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.add_sticker_to_set(
- chat_id, "name", "emoji", png_sticker=file, tgs_sticker=file
- )
- assert test_flag
- monkeypatch.delattr(bot, "_post")
- finally:
- bot._local_mode = False
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_set_sticker_set_thumb_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("thumb") == expected
- else:
- test_flag = isinstance(data.get("thumb"), InputFile)
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.set_sticker_set_thumb("name", chat_id, thumb=file)
- assert test_flag
- monkeypatch.delattr(bot, "_post")
- finally:
- bot._local_mode = False
-
- async def test_get_file_instance_method(self, monkeypatch, sticker):
- async def make_assertion(*_, **kwargs):
- return kwargs["file_id"] == sticker.file_id
-
- assert check_shortcut_signature(Sticker.get_file, Bot.get_file, ["file_id"], [])
- assert await check_shortcut_call(sticker.get_file, sticker.get_bot(), "get_file")
- assert await check_defaults_handling(sticker.get_file, sticker.get_bot())
-
- monkeypatch.setattr(sticker.get_bot(), "get_file", make_assertion)
- assert await sticker.get_file()
-
def test_equality(self):
a = StickerSet(
self.name,
@@ -848,23 +582,316 @@ class TestStickerSet:
assert a != e
assert hash(a) != hash(e)
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_upload_sticker_file_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()
-@pytest.fixture(scope="class")
+ async def make_assertion(_, data, *args, **kwargs):
+ nonlocal test_flag
+ if local_mode:
+ test_flag = data.get("png_sticker") == expected
+ else:
+ test_flag = isinstance(data.get("png_sticker"), InputFile)
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.upload_sticker_file(chat_id, file)
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_create_new_sticker_set_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("png_sticker") == expected
+ and data.get("tgs_sticker") == expected
+ and data.get("webm_sticker") == expected
+ )
+ else:
+ test_flag = (
+ isinstance(data.get("png_sticker"), InputFile)
+ and isinstance(data.get("tgs_sticker"), InputFile)
+ and isinstance(data.get("webm_sticker"), InputFile)
+ )
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.create_new_sticker_set(
+ chat_id,
+ "name",
+ "title",
+ "emoji",
+ png_sticker=file,
+ tgs_sticker=file,
+ webm_sticker=file,
+ )
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ async def test_create_new_sticker_all_params(self, monkeypatch, bot, chat_id, mask_position):
+ async def make_assertion(_, data, *args, **kwargs):
+ assert data["user_id"] == chat_id
+ assert data["name"] == "name"
+ assert data["title"] == "title"
+ assert data["emojis"] == "emoji"
+ assert data["mask_position"] == mask_position
+ assert data["png_sticker"] == "wow.png"
+ assert data["tgs_sticker"] == "wow.tgs"
+ assert data["webm_sticker"] == "wow.webm"
+ assert data["sticker_type"] == Sticker.MASK
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.create_new_sticker_set(
+ chat_id,
+ "name",
+ "title",
+ "emoji",
+ mask_position=mask_position,
+ png_sticker="wow.png",
+ tgs_sticker="wow.tgs",
+ webm_sticker="wow.webm",
+ sticker_type=Sticker.MASK,
+ )
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_add_sticker_to_set_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("png_sticker") == expected and data.get("tgs_sticker") == expected
+ )
+ else:
+ test_flag = isinstance(data.get("png_sticker"), InputFile) and isinstance(
+ data.get("tgs_sticker"), InputFile
+ )
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.add_sticker_to_set(
+ chat_id, "name", "emoji", png_sticker=file, tgs_sticker=file
+ )
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_set_sticker_set_thumb_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("thumb") == expected
+ else:
+ test_flag = isinstance(data.get("thumb"), InputFile)
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.set_sticker_set_thumb("name", chat_id, thumb=file)
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ async def test_get_file_instance_method(self, monkeypatch, sticker):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["file_id"] == sticker.file_id
+
+ assert check_shortcut_signature(Sticker.get_file, Bot.get_file, ["file_id"], [])
+ assert await check_shortcut_call(sticker.get_file, sticker.get_bot(), "get_file")
+ assert await check_defaults_handling(sticker.get_file, sticker.get_bot())
+
+ monkeypatch.setattr(sticker.get_bot(), "get_file", make_assertion)
+ assert await sticker.get_file()
+
+
+@pytest.mark.xdist_group("stickerset")
+class TestStickerSetWithRequest:
+ async def test_create_sticker_set(
+ self, bot, chat_id, sticker_file, animated_sticker_file, video_sticker_file
+ ):
+ """Creates the sticker set (if needed) which is required for tests. Make sure that this
+ test comes before the tests that actually use the sticker sets!
+ """
+ test_by = f"test_by_{bot.username}"
+ for sticker_set in [test_by, f"animated_{test_by}", f"video_{test_by}"]:
+ try:
+ ss = await bot.get_sticker_set(sticker_set)
+ assert isinstance(ss, StickerSet)
+ except BadRequest as e:
+ if not e.message == "Stickerset_invalid":
+ raise e
+
+ if sticker_set.startswith(test_by):
+ s = await bot.create_new_sticker_set(
+ chat_id,
+ name=sticker_set,
+ title="Sticker Test",
+ png_sticker=sticker_file,
+ emojis="😄",
+ )
+ assert s
+ elif sticker_set.startswith("animated"):
+ a = await bot.create_new_sticker_set(
+ chat_id,
+ name=sticker_set,
+ title="Animated Test",
+ tgs_sticker=animated_sticker_file,
+ emojis="😄",
+ )
+ assert a
+ elif sticker_set.startswith("video"):
+ v = await bot.create_new_sticker_set(
+ chat_id,
+ name=sticker_set,
+ title="Video Test",
+ webm_sticker=video_sticker_file,
+ emojis="🤔",
+ )
+ assert v
+
+ async def test_bot_methods_1_png(self, bot, chat_id, sticker_file):
+ with data_file("telegram_sticker.png").open("rb") as f:
+ # chat_id was hardcoded as 95205500 but it stopped working for some reason
+ file = await bot.upload_sticker_file(chat_id, f)
+ assert file
+
+ await asyncio.sleep(1)
+ tasks = asyncio.gather(
+ bot.add_sticker_to_set(
+ chat_id, f"test_by_{bot.username}", png_sticker=file.file_id, emojis="😄"
+ ),
+ bot.add_sticker_to_set( # Also test with file input and mask
+ chat_id,
+ f"test_by_{bot.username}",
+ png_sticker=sticker_file,
+ emojis="😄",
+ mask_position=MaskPosition(MaskPosition.EYES, -1, 1, 2),
+ ),
+ )
+ assert all(await tasks)
+
+ async def test_bot_methods_1_tgs(self, bot, chat_id):
+ await asyncio.sleep(1)
+ assert await bot.add_sticker_to_set(
+ chat_id,
+ f"animated_test_by_{bot.username}",
+ tgs_sticker=data_file("telegram_animated_sticker.tgs").open("rb"),
+ emojis="😄",
+ )
+
+ async def test_bot_methods_1_webm(self, bot, chat_id):
+ await asyncio.sleep(1)
+ with data_file("telegram_video_sticker.webm").open("rb") as f:
+ assert await bot.add_sticker_to_set(
+ chat_id, f"video_test_by_{bot.username}", webm_sticker=f, emojis="🤔"
+ )
+
+ async def test_bot_methods_2_png(self, bot, sticker_set):
+ await asyncio.sleep(1)
+ file_id = sticker_set.stickers[0].file_id
+ assert await bot.set_sticker_position_in_set(file_id, 1)
+
+ async def test_bot_methods_2_tgs(self, bot, animated_sticker_set):
+ await asyncio.sleep(1)
+ file_id = animated_sticker_set.stickers[0].file_id
+ assert await bot.set_sticker_position_in_set(file_id, 1)
+
+ async def test_bot_methods_2_webm(self, bot, video_sticker_set):
+ await asyncio.sleep(1)
+ file_id = video_sticker_set.stickers[0].file_id
+ assert await bot.set_sticker_position_in_set(file_id, 1)
+
+ async def test_bot_methods_3_png(self, bot, chat_id, sticker_set_thumb_file):
+ await asyncio.sleep(1)
+ assert await bot.set_sticker_set_thumb(
+ f"test_by_{bot.username}", chat_id, sticker_set_thumb_file
+ )
+
+ async def test_bot_methods_3_tgs(
+ self, bot, chat_id, animated_sticker_file, animated_sticker_set
+ ):
+ await asyncio.sleep(1)
+ animated_test = f"animated_test_by_{bot.username}"
+ file_id = animated_sticker_set.stickers[-1].file_id
+ tasks = asyncio.gather(
+ bot.set_sticker_set_thumb(animated_test, chat_id, animated_sticker_file),
+ bot.set_sticker_set_thumb(animated_test, chat_id, file_id),
+ )
+ assert all(await tasks)
+
+ # TODO: Try the below by creating a custom .webm and not by downloading another pack's thumb
+ @pytest.mark.skip(
+ "Skipped for now since Telegram throws a 'File is too big' error "
+ "regardless of the .webm file size."
+ )
+ def test_bot_methods_3_webm(self, bot, chat_id, video_sticker_file, video_sticker_set):
+ pass
+
+ async def test_bot_methods_4_png(self, bot, sticker_set):
+ await asyncio.sleep(1)
+ file_id = sticker_set.stickers[-1].file_id
+ assert await bot.delete_sticker_from_set(file_id)
+
+ async def test_bot_methods_4_tgs(self, bot, animated_sticker_set):
+ await asyncio.sleep(1)
+ file_id = animated_sticker_set.stickers[-1].file_id
+ assert await bot.delete_sticker_from_set(file_id)
+
+ async def test_bot_methods_4_webm(self, bot, video_sticker_set):
+ await asyncio.sleep(1)
+ file_id = video_sticker_set.stickers[-1].file_id
+ assert await bot.delete_sticker_from_set(file_id)
+
+
+@pytest.fixture(scope="module")
def mask_position():
return MaskPosition(
- TestMaskPosition.point,
- TestMaskPosition.x_shift,
- TestMaskPosition.y_shift,
- TestMaskPosition.scale,
+ TestMaskPositionBase.point,
+ TestMaskPositionBase.x_shift,
+ TestMaskPositionBase.y_shift,
+ TestMaskPositionBase.scale,
)
-class TestMaskPosition:
+class TestMaskPositionBase:
point = MaskPosition.EYES
x_shift = -1
y_shift = 1
scale = 2
+
+class TestMaskPositionWithoutRequest(TestMaskPositionBase):
+ def test_slot_behaviour(self, mask_position, mro_slots):
+ inst = mask_position
+ 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"
+
def test_mask_position_de_json(self, bot):
json_dict = {
"point": self.point,
@@ -908,23 +935,3 @@ class TestMaskPosition:
assert a != e
assert hash(a) != hash(e)
-
-
-class TestGetCustomEmojiSticker:
- async def test_custom_emoji_sticker(self, bot):
- # we use the same ID as in test_custom_emoji
- emoji_sticker_list = await bot.get_custom_emoji_stickers(["6046140249875156202"])
- assert emoji_sticker_list[0].emoji == "😎"
- assert emoji_sticker_list[0].height == 100
- assert emoji_sticker_list[0].width == 100
- assert not emoji_sticker_list[0].is_animated
- assert not emoji_sticker_list[0].is_video
- assert emoji_sticker_list[0].set_name == "PTBStaticEmojiTestPack"
- assert emoji_sticker_list[0].type == Sticker.CUSTOM_EMOJI
- assert emoji_sticker_list[0].custom_emoji_id == "6046140249875156202"
- assert emoji_sticker_list[0].thumb.width == 100
- assert emoji_sticker_list[0].thumb.height == 100
- assert emoji_sticker_list[0].thumb.file_size == 3614
- assert emoji_sticker_list[0].thumb.file_unique_id == "AQAD6gwAAoY06FNy"
- assert emoji_sticker_list[0].file_size == 3678
- assert emoji_sticker_list[0].file_unique_id == "AgAD6gwAAoY06FM"
diff --git a/tests/test_successfulpayment.py b/tests/test_successfulpayment.py
index b3e927aed..80dce68fa 100644
--- a/tests/test_successfulpayment.py
+++ b/tests/test_successfulpayment.py
@@ -21,20 +21,20 @@ import pytest
from telegram import OrderInfo, SuccessfulPayment
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def successful_payment():
return SuccessfulPayment(
- TestSuccessfulPayment.currency,
- TestSuccessfulPayment.total_amount,
- TestSuccessfulPayment.invoice_payload,
- TestSuccessfulPayment.telegram_payment_charge_id,
- TestSuccessfulPayment.provider_payment_charge_id,
- shipping_option_id=TestSuccessfulPayment.shipping_option_id,
- order_info=TestSuccessfulPayment.order_info,
+ TestSuccessfulPaymentBase.currency,
+ TestSuccessfulPaymentBase.total_amount,
+ TestSuccessfulPaymentBase.invoice_payload,
+ TestSuccessfulPaymentBase.telegram_payment_charge_id,
+ TestSuccessfulPaymentBase.provider_payment_charge_id,
+ shipping_option_id=TestSuccessfulPaymentBase.shipping_option_id,
+ order_info=TestSuccessfulPaymentBase.order_info,
)
-class TestSuccessfulPayment:
+class TestSuccessfulPaymentBase:
invoice_payload = "invoice_payload"
shipping_option_id = "shipping_option_id"
currency = "EUR"
@@ -43,6 +43,8 @@ class TestSuccessfulPayment:
telegram_payment_charge_id = "telegram_payment_charge_id"
provider_payment_charge_id = "provider_payment_charge_id"
+
+class TestSuccessfulPaymentWithoutRequest(TestSuccessfulPaymentBase):
def test_slot_behaviour(self, successful_payment, mro_slots):
inst = successful_payment
for attr in inst.__slots__:
diff --git a/tests/test_update.py b/tests/test_update.py
index b8429be41..5fcc7db1b 100644
--- a/tests/test_update.py
+++ b/tests/test_update.py
@@ -97,22 +97,25 @@ all_types = (
ids = all_types + ("callback_query_without_message",)
-@pytest.fixture(params=params, ids=ids)
+@pytest.fixture(scope="module", params=params, ids=ids)
def update(request):
- return Update(update_id=TestUpdate.update_id, **request.param)
+ return Update(update_id=TestUpdateBase.update_id, **request.param)
-class TestUpdate:
+class TestUpdateBase:
update_id = 868573637
- def test_slot_behaviour(self, update, mro_slots):
+
+class TestUpdateWithoutRequest(TestUpdateBase):
+ def test_slot_behaviour(self, mro_slots):
+ update = Update(self.update_id)
for attr in update.__slots__:
assert getattr(update, attr, "err") != "err", f"got extra slot '{attr}'"
assert len(mro_slots(update)) == len(set(mro_slots(update))), "duplicate slot"
@pytest.mark.parametrize("paramdict", argvalues=params, ids=ids)
def test_de_json(self, bot, paramdict):
- json_dict = {"update_id": TestUpdate.update_id}
+ json_dict = {"update_id": self.update_id}
# Convert the single update 'item' to a dict of that item and apply it to the json_dict
json_dict.update({k: v.to_dict() for k, v in paramdict.items()})
update = Update.de_json(json_dict, bot)
@@ -142,6 +145,26 @@ class TestUpdate:
if getattr(update, _type) is not None:
assert update_dict[_type] == getattr(update, _type).to_dict()
+ def test_equality(self):
+ a = Update(self.update_id, message=message)
+ b = Update(self.update_id, message=message)
+ c = Update(self.update_id)
+ d = Update(0, message=message)
+ e = User(self.update_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_effective_chat(self, update):
# Test that it's sometimes None per docstring
chat = update.effective_chat
@@ -188,23 +211,3 @@ class TestUpdate:
assert eff_message.message_id == message.message_id
else:
assert eff_message is None
-
- def test_equality(self):
- a = Update(self.update_id, message=message)
- b = Update(self.update_id, message=message)
- c = Update(self.update_id)
- d = Update(0, message=message)
- e = User(self.update_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)
diff --git a/tests/test_updater.py b/tests/test_updater.py
index 9b70ac161..35fbd8665 100644
--- a/tests/test_updater.py
+++ b/tests/test_updater.py
@@ -18,7 +18,6 @@
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import logging
-import os
from collections import defaultdict
from http import HTTPStatus
from pathlib import Path
@@ -31,8 +30,8 @@ from telegram._utils.defaultvalue import DEFAULT_NONE
from telegram.error import InvalidToken, RetryAfter, TelegramError, TimedOut
from telegram.ext import ExtBot, InvalidCallbackData, Updater
from telegram.request import HTTPXRequest
-from tests.auxil.object_conversions import env_var_2_bool
from tests.conftest import (
+ TEST_WITH_OPT_DEPS,
DictBot,
data_file,
make_bot,
@@ -41,8 +40,6 @@ from tests.conftest import (
send_webhook_message,
)
-TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
-
if TEST_WITH_OPT_DEPS:
from telegram.ext._utils.webhookhandler import WebhookServer
@@ -532,7 +529,7 @@ class TestUpdater:
# that depends on this distinction works
if ext_bot and not isinstance(updater.bot, ExtBot):
updater.bot = ExtBot(updater.bot.token)
- if not ext_bot and not type(updater.bot) is Bot:
+ if not ext_bot and type(updater.bot) is not Bot:
updater.bot = DictBot(updater.bot.token)
async def delete_webhook(*args, **kwargs):
diff --git a/tests/test_user.py b/tests/test_user.py
index 64b3aa8de..6ea946b9d 100644
--- a/tests/test_user.py
+++ b/tests/test_user.py
@@ -27,44 +27,44 @@ from tests.auxil.bot_method_checks import (
)
-@pytest.fixture(scope="function")
+@pytest.fixture(scope="module")
def json_dict():
return {
- "id": TestUser.id_,
- "is_bot": TestUser.is_bot,
- "first_name": TestUser.first_name,
- "last_name": TestUser.last_name,
- "username": TestUser.username,
- "language_code": TestUser.language_code,
- "can_join_groups": TestUser.can_join_groups,
- "can_read_all_group_messages": TestUser.can_read_all_group_messages,
- "supports_inline_queries": TestUser.supports_inline_queries,
- "is_premium": TestUser.is_premium,
- "added_to_attachment_menu": TestUser.added_to_attachment_menu,
+ "id": TestUserBase.id_,
+ "is_bot": TestUserBase.is_bot,
+ "first_name": TestUserBase.first_name,
+ "last_name": TestUserBase.last_name,
+ "username": TestUserBase.username,
+ "language_code": TestUserBase.language_code,
+ "can_join_groups": TestUserBase.can_join_groups,
+ "can_read_all_group_messages": TestUserBase.can_read_all_group_messages,
+ "supports_inline_queries": TestUserBase.supports_inline_queries,
+ "is_premium": TestUserBase.is_premium,
+ "added_to_attachment_menu": TestUserBase.added_to_attachment_menu,
}
@pytest.fixture(scope="function")
def user(bot):
user = User(
- id=TestUser.id_,
- first_name=TestUser.first_name,
- is_bot=TestUser.is_bot,
- last_name=TestUser.last_name,
- username=TestUser.username,
- language_code=TestUser.language_code,
- can_join_groups=TestUser.can_join_groups,
- can_read_all_group_messages=TestUser.can_read_all_group_messages,
- supports_inline_queries=TestUser.supports_inline_queries,
- is_premium=TestUser.is_premium,
- added_to_attachment_menu=TestUser.added_to_attachment_menu,
+ id=TestUserBase.id_,
+ first_name=TestUserBase.first_name,
+ is_bot=TestUserBase.is_bot,
+ last_name=TestUserBase.last_name,
+ username=TestUserBase.username,
+ language_code=TestUserBase.language_code,
+ can_join_groups=TestUserBase.can_join_groups,
+ can_read_all_group_messages=TestUserBase.can_read_all_group_messages,
+ supports_inline_queries=TestUserBase.supports_inline_queries,
+ is_premium=TestUserBase.is_premium,
+ added_to_attachment_menu=TestUserBase.added_to_attachment_menu,
)
user.set_bot(bot)
user._unfreeze()
return user
-class TestUser:
+class TestUserBase:
id_ = 1
is_bot = True
first_name = "first\u2022name"
@@ -77,6 +77,8 @@ class TestUser:
is_premium = True
added_to_attachment_menu = False
+
+class TestUserWithoutRequest(TestUserBase):
def test_slot_behaviour(self, user, mro_slots):
for attr in user.__slots__:
assert getattr(user, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -98,42 +100,41 @@ class TestUser:
assert user.is_premium == self.is_premium
assert user.added_to_attachment_menu == self.added_to_attachment_menu
- def test_de_json_without_username(self, json_dict, bot):
- del json_dict["username"]
+ def test_to_dict(self, user):
+ user_dict = user.to_dict()
- user = User.de_json(json_dict, bot)
- assert user.api_kwargs == {}
+ assert isinstance(user_dict, dict)
+ assert user_dict["id"] == user.id
+ assert user_dict["is_bot"] == user.is_bot
+ assert user_dict["first_name"] == user.first_name
+ assert user_dict["last_name"] == user.last_name
+ assert user_dict["username"] == user.username
+ assert user_dict["language_code"] == user.language_code
+ assert user_dict["can_join_groups"] == user.can_join_groups
+ assert user_dict["can_read_all_group_messages"] == user.can_read_all_group_messages
+ assert user_dict["supports_inline_queries"] == user.supports_inline_queries
+ assert user_dict["is_premium"] == user.is_premium
+ assert user_dict["added_to_attachment_menu"] == user.added_to_attachment_menu
- assert user.id == self.id_
- assert user.is_bot == self.is_bot
- assert user.first_name == self.first_name
- assert user.last_name == self.last_name
- assert user.username is None
- assert user.language_code == self.language_code
- assert user.can_join_groups == self.can_join_groups
- assert user.can_read_all_group_messages == self.can_read_all_group_messages
- assert user.supports_inline_queries == self.supports_inline_queries
- assert user.is_premium == self.is_premium
- assert user.added_to_attachment_menu == self.added_to_attachment_menu
+ def test_equality(self):
+ a = User(self.id_, self.first_name, self.is_bot, self.last_name)
+ b = User(self.id_, self.first_name, self.is_bot, self.last_name)
+ c = User(self.id_, self.first_name, self.is_bot)
+ d = User(0, self.first_name, self.is_bot, self.last_name)
+ e = Update(self.id_)
- def test_de_json_without_username_and_last_name(self, json_dict, bot):
- del json_dict["username"]
- del json_dict["last_name"]
+ assert a == b
+ assert hash(a) == hash(b)
+ assert a is not b
- user = User.de_json(json_dict, bot)
- assert user.api_kwargs == {}
+ assert a == c
+ assert hash(a) == hash(c)
- assert user.id == self.id_
- assert user.is_bot == self.is_bot
- assert user.first_name == self.first_name
- assert user.last_name is None
- assert user.username is None
- assert user.language_code == self.language_code
- assert user.can_join_groups == self.can_join_groups
- assert user.can_read_all_group_messages == self.can_read_all_group_messages
- assert user.supports_inline_queries == self.supports_inline_queries
- assert user.is_premium == self.is_premium
- assert user.added_to_attachment_menu == self.added_to_attachment_menu
+ assert a != d
+ assert hash(a) != hash(d)
+
+ assert a != e
+ assert hash(a) != hash(e)
def test_name(self, user):
assert user.name == "@username"
@@ -560,23 +561,3 @@ class TestUser:
"the\\{name\\>\u2022", user.id
)
assert user.mention_markdown_v2(user.username) == expected.format(user.username, user.id)
-
- def test_equality(self):
- a = User(self.id_, self.first_name, self.is_bot, self.last_name)
- b = User(self.id_, self.first_name, self.is_bot, self.last_name)
- c = User(self.id_, self.first_name, self.is_bot)
- d = User(0, self.first_name, self.is_bot, self.last_name)
- e = Update(self.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)
diff --git a/tests/test_userprofilephotos.py b/tests/test_userprofilephotos.py
index 7fb7c593c..da179e53e 100644
--- a/tests/test_userprofilephotos.py
+++ b/tests/test_userprofilephotos.py
@@ -19,7 +19,7 @@
from telegram import PhotoSize, UserProfilePhotos
-class TestUserProfilePhotos:
+class TestUserProfilePhotosBase:
total_count = 2
photos = [
[
@@ -32,6 +32,8 @@ class TestUserProfilePhotos:
],
]
+
+class TestUserProfilePhotosWithoutRequest(TestUserProfilePhotosBase):
def test_slot_behaviour(self, mro_slots):
inst = UserProfilePhotos(self.total_count, self.photos)
for attr in inst.__slots__:
diff --git a/tests/test_venue.py b/tests/test_venue.py
index d0360ef9f..55944c7b0 100644
--- a/tests/test_venue.py
+++ b/tests/test_venue.py
@@ -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, Venue
@@ -23,20 +25,20 @@ from telegram.error import BadRequest
from telegram.request import RequestData
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def venue():
return Venue(
- TestVenue.location,
- TestVenue.title,
- TestVenue.address,
- foursquare_id=TestVenue.foursquare_id,
- foursquare_type=TestVenue.foursquare_type,
- google_place_id=TestVenue.google_place_id,
- google_place_type=TestVenue.google_place_type,
+ TestVenueBase.location,
+ TestVenueBase.title,
+ TestVenueBase.address,
+ foursquare_id=TestVenueBase.foursquare_id,
+ foursquare_type=TestVenueBase.foursquare_type,
+ google_place_id=TestVenueBase.google_place_id,
+ google_place_type=TestVenueBase.google_place_type,
)
-class TestVenue:
+class TestVenueBase:
location = Location(longitude=-46.788279, latitude=-23.691288)
title = "title"
address = "address"
@@ -45,6 +47,8 @@ class TestVenue:
google_place_id = "google place id"
google_place_type = "google place type"
+
+class TestVenueWithoutRequest(TestVenueBase):
def test_slot_behaviour(self, venue, mro_slots):
for attr in venue.__slots__:
assert getattr(venue, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -52,13 +56,13 @@ class TestVenue:
def test_de_json(self, bot):
json_dict = {
- "location": TestVenue.location.to_dict(),
- "title": TestVenue.title,
- "address": TestVenue.address,
- "foursquare_id": TestVenue.foursquare_id,
- "foursquare_type": TestVenue.foursquare_type,
- "google_place_id": TestVenue.google_place_id,
- "google_place_type": TestVenue.google_place_type,
+ "location": self.location.to_dict(),
+ "title": self.title,
+ "address": self.address,
+ "foursquare_id": self.foursquare_id,
+ "foursquare_type": self.foursquare_type,
+ "google_place_id": self.google_place_id,
+ "google_place_type": self.google_place_type,
}
venue = Venue.de_json(json_dict, bot)
assert venue.api_kwargs == {}
@@ -71,6 +75,53 @@ class TestVenue:
assert venue.google_place_id == self.google_place_id
assert venue.google_place_type == self.google_place_type
+ def test_to_dict(self, venue):
+ venue_dict = venue.to_dict()
+
+ assert isinstance(venue_dict, dict)
+ assert venue_dict["location"] == venue.location.to_dict()
+ assert venue_dict["title"] == venue.title
+ assert venue_dict["address"] == venue.address
+ assert venue_dict["foursquare_id"] == venue.foursquare_id
+ assert venue_dict["foursquare_type"] == venue.foursquare_type
+ assert venue_dict["google_place_id"] == venue.google_place_id
+ assert venue_dict["google_place_type"] == venue.google_place_type
+
+ def test_equality(self):
+ a = Venue(Location(0, 0), self.title, self.address)
+ b = Venue(Location(0, 0), self.title, self.address)
+ c = Venue(Location(0, 0), self.title, "")
+ d = Venue(Location(0, 1), self.title, self.address)
+ d2 = Venue(Location(0, 0), "", self.address)
+
+ 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 != d2
+ assert hash(a) != hash(d2)
+
+ async def test_send_venue_without_required(self, bot, chat_id):
+ with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"):
+ await bot.send_venue(chat_id=chat_id)
+
+ async def test_send_venue_mutually_exclusive(self, bot, chat_id, venue):
+ with pytest.raises(ValueError, match="Not both"):
+ await bot.send_venue(
+ chat_id=chat_id,
+ latitude=1,
+ longitude=1,
+ address="address",
+ title="title",
+ venue=venue,
+ )
+
async def test_send_with_venue(self, monkeypatch, bot, chat_id, venue):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
data = request_data.json_parameters
@@ -89,7 +140,8 @@ class TestVenue:
message = await bot.send_venue(chat_id, venue=venue)
assert message
- @pytest.mark.flaky(3, 1)
+
+class TestVenueWithRequest(TestVenueBase):
@pytest.mark.parametrize(
"default_bot,custom",
[
@@ -123,57 +175,12 @@ class TestVenue:
chat_id, venue=venue, 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_venue_default_protect_content(self, default_bot, chat_id, venue):
- protected = await default_bot.send_venue(chat_id, venue=venue)
+ tasks = asyncio.gather(
+ default_bot.send_venue(chat_id, venue=venue),
+ default_bot.send_venue(chat_id, venue=venue, protect_content=False),
+ )
+ protected, unprotected = await tasks
assert protected.has_protected_content
- unprotected = await default_bot.send_venue(chat_id, venue=venue, protect_content=False)
assert not unprotected.has_protected_content
-
- async def test_send_venue_without_required(self, bot, chat_id):
- with pytest.raises(ValueError, match="Either venue or latitude, longitude, address and"):
- await bot.send_venue(chat_id=chat_id)
-
- async def test_send_venue_mutually_exclusive(self, bot, chat_id, venue):
- with pytest.raises(ValueError, match="Not both"):
- await bot.send_venue(
- chat_id=chat_id,
- latitude=1,
- longitude=1,
- address="address",
- title="title",
- venue=venue,
- )
-
- def test_to_dict(self, venue):
- venue_dict = venue.to_dict()
-
- assert isinstance(venue_dict, dict)
- assert venue_dict["location"] == venue.location.to_dict()
- assert venue_dict["title"] == venue.title
- assert venue_dict["address"] == venue.address
- assert venue_dict["foursquare_id"] == venue.foursquare_id
- assert venue_dict["foursquare_type"] == venue.foursquare_type
- assert venue_dict["google_place_id"] == venue.google_place_id
- assert venue_dict["google_place_type"] == venue.google_place_type
-
- def test_equality(self):
- a = Venue(Location(0, 0), self.title, self.address)
- b = Venue(Location(0, 0), self.title, self.address)
- c = Venue(Location(0, 0), self.title, "")
- d = Venue(Location(0, 1), self.title, self.address)
- d2 = Venue(Location(0, 0), "", self.address)
-
- 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 != d2
- assert hash(a) != hash(d2)
diff --git a/tests/test_video.py b/tests/test_video.py
index 3a3d524aa..e88cb831c 100644
--- a/tests/test_video.py
+++ b/tests/test_video.py
@@ -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 video_file():
- f = data_file("telegram.mp4").open("rb")
- yield f
- f.close()
+ with data_file("telegram.mp4").open("rb") as f:
+ yield f
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def video(bot, chat_id):
with data_file("telegram.mp4").open("rb") as f:
return (await bot.send_video(chat_id, video=f, read_timeout=50)).video
-class TestVideo:
+class TestVideoBase:
width = 360
height = 640
duration = 5
@@ -54,17 +54,16 @@ class TestVideo:
mime_type = "video/mp4"
supports_streaming = True
file_name = "telegram.mp4"
-
thumb_width = 180
thumb_height = 320
thumb_file_size = 1767
-
caption = "VideoTest - *Caption*"
video_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.mp4"
-
video_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
video_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
+
+class TestVideoWithoutRequest(TestVideoBase):
def test_slot_behaviour(self, video, mro_slots):
for attr in video.__slots__:
assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -91,221 +90,6 @@ class TestVideo:
assert video.file_size == self.file_size
assert video.mime_type == self.mime_type
- @pytest.mark.flaky(3, 1)
- async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
- message = await bot.send_video(
- chat_id,
- video_file,
- duration=self.duration,
- caption=self.caption,
- supports_streaming=self.supports_streaming,
- disable_notification=False,
- protect_content=True,
- width=video.width,
- height=video.height,
- parse_mode="Markdown",
- thumb=thumb_file,
- has_spoiler=True,
- )
-
- assert isinstance(message.video, Video)
- assert isinstance(message.video.file_id, str)
- assert isinstance(message.video.file_unique_id, str)
- assert message.video.file_id != ""
- assert message.video.file_unique_id != ""
- assert message.video.width == video.width
- assert message.video.height == video.height
- assert message.video.duration == video.duration
- assert message.video.file_size == video.file_size
-
- assert message.caption == self.caption.replace("*", "")
-
- assert message.video.thumb.file_size == self.thumb_file_size
- assert message.video.thumb.width == self.thumb_width
- assert message.video.thumb.height == self.thumb_height
-
- assert message.video.file_name == self.file_name
- assert message.has_protected_content
- assert message.has_media_spoiler
-
- @pytest.mark.flaky(3, 1)
- async def test_send_video_custom_filename(self, bot, chat_id, video_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_video(chat_id, video_file, filename="custom_filename")
-
- @pytest.mark.flaky(3, 1)
- async def test_get_and_download(self, bot, video):
- path = Path("telegram.mp4")
- if path.is_file():
- path.unlink()
-
- new_file = await bot.get_file(video.file_id)
-
- assert new_file.file_size == self.file_size
- assert new_file.file_id == video.file_id
- assert new_file.file_unique_id == video.file_unique_id
- assert new_file.file_path.startswith("https://")
-
- await new_file.download_to_drive("telegram.mp4")
-
- assert path.is_file()
-
- @pytest.mark.flaky(3, 1)
- async def test_send_mp4_file_url(self, bot, chat_id, video):
- message = await bot.send_video(chat_id, self.video_file_url, caption=self.caption)
-
- assert isinstance(message.video, Video)
- assert isinstance(message.video.file_id, str)
- assert isinstance(message.video.file_unique_id, str)
- assert message.video.file_id != ""
- assert message.video.file_unique_id != ""
- assert message.video.width == video.width
- assert message.video.height == video.height
- assert message.video.duration == video.duration
- assert message.video.file_size == video.file_size
-
- assert isinstance(message.video.thumb, PhotoSize)
- assert isinstance(message.video.thumb.file_id, str)
- assert isinstance(message.video.thumb.file_unique_id, str)
- assert message.video.thumb.file_id != ""
- assert message.video.thumb.file_unique_id != ""
- assert message.video.thumb.width == 51 # This seems odd that it's not self.thumb_width
- assert message.video.thumb.height == 90 # Ditto
- assert message.video.thumb.file_size == 645 # same
-
- assert message.caption == self.caption
-
- @pytest.mark.flaky(3, 1)
- async def test_send_video_caption_entities(self, bot, chat_id, video):
- 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_video(
- chat_id, video, caption=test_string, caption_entities=entities
- )
-
- assert message.caption == test_string
- assert message.caption_entities == tuple(entities)
-
- @pytest.mark.flaky(3, 1)
- async def test_resend(self, bot, chat_id, video):
- message = await bot.send_video(chat_id, video.file_id)
-
- assert message.video == video
-
- async def test_send_with_video(self, monkeypatch, bot, chat_id, video):
- async def make_assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters["video"] == video.file_id
-
- monkeypatch.setattr(bot.request, "post", make_assertion)
- message = await bot.send_video(chat_id, video=video)
- assert message
-
- @pytest.mark.flaky(3, 1)
- @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
- async def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video):
- test_string = "Italic Bold Code"
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_video(chat_id, video, 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_video_default_parse_mode_2(self, default_bot, chat_id, video):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_video(
- chat_id, video, 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_video_default_parse_mode_3(self, default_bot, chat_id, video):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_video(
- chat_id, video, 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_video_default_protect_content(self, chat_id, default_bot, video):
- protected = await default_bot.send_video(chat_id, video)
- assert protected.has_protected_content
- unprotected = await default_bot.send_video(chat_id, video, protect_content=False)
- assert not unprotected.has_protected_content
-
- @pytest.mark.parametrize("local_mode", [True, False])
- async def test_send_video_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("video") == expected and data.get("thumb") == expected
- else:
- test_flag = isinstance(data.get("video"), InputFile) and isinstance(
- data.get("thumb"), InputFile
- )
-
- monkeypatch.setattr(bot, "_post", make_assertion)
- await bot.send_video(chat_id, file, thumb=file)
- assert test_flag
- 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_video_default_allow_sending_without_reply(
- self, default_bot, chat_id, video, 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_video(
- chat_id,
- video,
- 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_video(
- chat_id, video, 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_video(
- chat_id, video, reply_to_message_id=reply_to_message.message_id
- )
-
def test_de_json(self, bot):
json_dict = {
"file_id": self.video_file_id,
@@ -342,31 +126,6 @@ class TestVideo:
assert video_dict["file_size"] == video.file_size
assert video_dict["file_name"] == video.file_name
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_video(chat_id, open(os.devnull, "rb"))
-
- @pytest.mark.flaky(3, 1)
- async def test_error_send_empty_file_id(self, bot, chat_id):
- with pytest.raises(TelegramError):
- await bot.send_video(chat_id, "")
-
- async def test_error_without_required_args(self, bot, chat_id):
- with pytest.raises(TypeError):
- await bot.send_video(chat_id=chat_id)
-
- async def test_get_file_instance_method(self, monkeypatch, video):
- async def make_assertion(*_, **kwargs):
- return kwargs["file_id"] == video.file_id
-
- assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], [])
- assert await check_shortcut_call(video.get_file, video.get_bot(), "get_file")
- assert await check_defaults_handling(video.get_file, video.get_bot())
-
- monkeypatch.setattr(video.get_bot(), "get_file", make_assertion)
- assert await video.get_file()
-
def test_equality(self, video):
a = Video(video.file_id, video.file_unique_id, self.width, self.height, self.duration)
b = Video("", video.file_unique_id, self.width, self.height, self.duration)
@@ -386,3 +145,232 @@ class TestVideo:
assert a != e
assert hash(a) != hash(e)
+
+ async def test_error_without_required_args(self, bot, chat_id):
+ with pytest.raises(TypeError):
+ await bot.send_video(chat_id=chat_id)
+
+ async def test_send_with_video(self, monkeypatch, bot, chat_id, video):
+ async def make_assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters["video"] == video.file_id
+
+ monkeypatch.setattr(bot.request, "post", make_assertion)
+ assert await bot.send_video(chat_id, video=video)
+
+ async def test_send_video_custom_filename(self, bot, chat_id, video_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_video(chat_id, video_file, filename="custom_filename")
+
+ @pytest.mark.parametrize("local_mode", [True, False])
+ async def test_send_video_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("video") == expected and data.get("thumb") == expected
+ else:
+ test_flag = isinstance(data.get("video"), InputFile) and isinstance(
+ data.get("thumb"), InputFile
+ )
+
+ monkeypatch.setattr(bot, "_post", make_assertion)
+ await bot.send_video(chat_id, file, thumb=file)
+ assert test_flag
+ finally:
+ bot._local_mode = False
+
+ async def test_get_file_instance_method(self, monkeypatch, video):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["file_id"] == video.file_id
+
+ assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], [])
+ assert await check_shortcut_call(video.get_file, video.get_bot(), "get_file")
+ assert await check_defaults_handling(video.get_file, video.get_bot())
+
+ monkeypatch.setattr(video.get_bot(), "get_file", make_assertion)
+ assert await video.get_file()
+
+
+class TestVideoWithRequest(TestVideoBase):
+ async def test_send_all_args(self, bot, chat_id, video_file, video, thumb_file):
+ message = await bot.send_video(
+ chat_id,
+ video_file,
+ duration=self.duration,
+ caption=self.caption,
+ supports_streaming=self.supports_streaming,
+ disable_notification=False,
+ protect_content=True,
+ width=video.width,
+ height=video.height,
+ parse_mode="Markdown",
+ thumb=thumb_file,
+ has_spoiler=True,
+ )
+
+ assert isinstance(message.video, Video)
+ assert isinstance(message.video.file_id, str)
+ assert isinstance(message.video.file_unique_id, str)
+ assert message.video.file_id != ""
+ assert message.video.file_unique_id != ""
+ assert message.video.width == video.width
+ assert message.video.height == video.height
+ assert message.video.duration == video.duration
+ assert message.video.file_size == video.file_size
+
+ assert message.caption == self.caption.replace("*", "")
+
+ assert message.video.thumb.file_size == self.thumb_file_size
+ assert message.video.thumb.width == self.thumb_width
+ assert message.video.thumb.height == self.thumb_height
+
+ assert message.video.file_name == self.file_name
+ assert message.has_protected_content
+ assert message.has_media_spoiler
+
+ async def test_get_and_download(self, bot, video, chat_id):
+ path = Path("telegram.mp4")
+ if path.is_file():
+ path.unlink()
+
+ new_file = await bot.get_file(video.file_id)
+
+ assert new_file.file_size == self.file_size
+ assert new_file.file_unique_id == video.file_unique_id
+ assert new_file.file_path.startswith("https://")
+
+ await new_file.download_to_drive("telegram.mp4")
+
+ assert path.is_file()
+
+ async def test_send_mp4_file_url(self, bot, chat_id, video):
+ message = await bot.send_video(chat_id, self.video_file_url, caption=self.caption)
+
+ assert isinstance(message.video, Video)
+ assert isinstance(message.video.file_id, str)
+ assert isinstance(message.video.file_unique_id, str)
+ assert message.video.file_id != ""
+ assert message.video.file_unique_id != ""
+ assert message.video.width == video.width
+ assert message.video.height == video.height
+ assert message.video.duration == video.duration
+ assert message.video.file_size == video.file_size
+
+ assert isinstance(message.video.thumb, PhotoSize)
+ assert isinstance(message.video.thumb.file_id, str)
+ assert isinstance(message.video.thumb.file_unique_id, str)
+ assert message.video.thumb.file_id != ""
+ assert message.video.thumb.file_unique_id != ""
+ assert message.video.thumb.width == 51 # This seems odd that it's not self.thumb_width
+ assert message.video.thumb.height == 90 # Ditto
+ assert message.video.thumb.file_size == 645 # same
+
+ assert message.caption == self.caption
+
+ async def test_send_video_caption_entities(self, bot, chat_id, video):
+ 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_video(
+ chat_id, video, caption=test_string, caption_entities=entities
+ )
+
+ assert message.caption == test_string
+ assert message.caption_entities == tuple(entities)
+
+ async def test_resend(self, bot, chat_id, video):
+ message = await bot.send_video(chat_id, video.file_id)
+ assert message.video == video
+
+ @pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
+ async def test_send_video_default_parse_mode_1(self, default_bot, chat_id, video):
+ test_string = "Italic Bold Code"
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_video(chat_id, video, 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_video_default_parse_mode_2(self, default_bot, chat_id, video):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_video(
+ chat_id, video, 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_video_default_parse_mode_3(self, default_bot, chat_id, video):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_video(
+ chat_id, video, 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_video_default_protect_content(self, chat_id, default_bot, video):
+ tasks = asyncio.gather(
+ default_bot.send_video(chat_id, video),
+ default_bot.send_video(chat_id, video, protect_content=False),
+ )
+ protected, unprotected = await tasks
+ assert protected.has_protected_content
+ assert not unprotected.has_protected_content
+
+ @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_video_default_allow_sending_without_reply(
+ self, default_bot, chat_id, video, 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_video(
+ chat_id,
+ video,
+ 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_video(
+ chat_id, video, 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_video(
+ chat_id, video, reply_to_message_id=reply_to_message.message_id
+ )
+
+ async def test_error_send_empty_file(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_video(chat_id, open(os.devnull, "rb"))
+
+ async def test_error_send_empty_file_id(self, bot, chat_id):
+ with pytest.raises(TelegramError):
+ await bot.send_video(chat_id, "")
diff --git a/tests/test_videochat.py b/tests/test_videochat.py
index e2609296e..8ebd2136e 100644
--- a/tests/test_videochat.py
+++ b/tests/test_videochat.py
@@ -30,17 +30,17 @@ from telegram import (
from telegram._utils.datetime import to_timestamp
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def user1():
return User(first_name="Misses Test", id=123, is_bot=False)
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def user2():
return User(first_name="Mister Test", id=124, is_bot=False)
-class TestVideoChatStarted:
+class TestVideoChatStartedWithoutRequest:
def test_slot_behaviour(self, mro_slots):
action = VideoChatStarted()
for attr in action.__slots__:
@@ -58,7 +58,7 @@ class TestVideoChatStarted:
assert video_chat_dict == {}
-class TestVideoChatEnded:
+class TestVideoChatEndedWithoutRequest:
duration = 100
def test_slot_behaviour(self, mro_slots):
@@ -97,7 +97,7 @@ class TestVideoChatEnded:
assert hash(a) != hash(d)
-class TestVideoChatParticipantsInvited:
+class TestVideoChatParticipantsInvitedWithoutRequest:
def test_slot_behaviour(self, mro_slots, user1):
action = VideoChatParticipantsInvited([user1])
for attr in action.__slots__:
@@ -148,7 +148,7 @@ class TestVideoChatParticipantsInvited:
assert hash(a) != hash(e)
-class TestVideoChatScheduled:
+class TestVideoChatScheduledWithoutRequest:
start_date = dtm.datetime.now(dtm.timezone.utc)
def test_slot_behaviour(self, mro_slots):
diff --git a/tests/test_videonote.py b/tests/test_videonote.py
index d5a0b69fe..9a857b0c3 100644
--- a/tests/test_videonote.py
+++ b/tests/test_videonote.py
@@ -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
@@ -34,30 +35,29 @@ from tests.conftest import data_file
@pytest.fixture(scope="function")
def video_note_file():
- f = data_file("telegram2.mp4").open("rb")
- yield f
- f.close()
+ with data_file("telegram2.mp4").open("rb") as f:
+ yield f
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def video_note(bot, chat_id):
with data_file("telegram2.mp4").open("rb") as f:
return (await bot.send_video_note(chat_id, video_note=f, read_timeout=50)).video_note
-class TestVideoNote:
+class TestVideoNoteBase:
length = 240
duration = 3
file_size = 132084
-
thumb_width = 240
thumb_height = 240
thumb_file_size = 11547
-
caption = "VideoNoteTest - Caption"
videonote_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
videonote_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
+
+class TestVideoNoteWithoutRequest(TestVideoNoteBase):
def test_slot_behaviour(self, video_note, mro_slots):
for attr in video_note.__slots__:
assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -82,74 +82,6 @@ class TestVideoNote:
assert video_note.duration == self.duration
assert video_note.file_size == self.file_size
- @pytest.mark.flaky(3, 1)
- async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
- message = await bot.send_video_note(
- chat_id,
- video_note_file,
- duration=self.duration,
- length=self.length,
- disable_notification=False,
- protect_content=True,
- thumb=thumb_file,
- )
-
- assert isinstance(message.video_note, VideoNote)
- assert isinstance(message.video_note.file_id, str)
- assert isinstance(message.video_note.file_unique_id, str)
- assert message.video_note.file_id != ""
- assert message.video_note.file_unique_id != ""
- assert message.video_note.length == video_note.length
- assert message.video_note.duration == video_note.duration
- assert message.video_note.file_size == video_note.file_size
-
- assert message.video_note.thumb.file_size == self.thumb_file_size
- assert message.video_note.thumb.width == self.thumb_width
- assert message.video_note.thumb.height == self.thumb_height
- assert message.has_protected_content
-
- @pytest.mark.flaky(3, 1)
- async def test_send_video_note_custom_filename(
- self, bot, chat_id, video_note_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_video_note(chat_id, video_note_file, filename="custom_filename")
-
- @pytest.mark.flaky(3, 1)
- async def test_get_and_download(self, bot, video_note):
- path = Path("telegram2.mp4")
- if path.is_file():
- path.unlink()
-
- new_file = await bot.get_file(video_note.file_id)
-
- assert new_file.file_size == self.file_size
- assert new_file.file_id == video_note.file_id
- assert new_file.file_unique_id == video_note.file_unique_id
- assert new_file.file_path.startswith("https://")
-
- await new_file.download_to_drive("telegram2.mp4")
-
- assert path.is_file()
-
- @pytest.mark.flaky(3, 1)
- async def test_resend(self, bot, chat_id, video_note):
- message = await bot.send_video_note(chat_id, video_note.file_id)
-
- assert message.video_note == video_note
-
- async def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note):
- async def make_assertion(url, request_data: RequestData, *args, **kwargs):
- return request_data.json_parameters["video_note"] == video_note.file_id
-
- monkeypatch.setattr(bot.request, "post", make_assertion)
- message = await bot.send_video_note(chat_id, video_note=video_note)
- assert message
-
def test_de_json(self, bot):
json_dict = {
"file_id": self.videonote_file_id,
@@ -177,6 +109,47 @@ class TestVideoNote:
assert video_note_dict["duration"] == video_note.duration
assert video_note_dict["file_size"] == video_note.file_size
+ def test_equality(self, video_note):
+ a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration)
+ b = VideoNote("", video_note.file_unique_id, self.length, self.duration)
+ c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0)
+ d = VideoNote("", "", self.length, self.duration)
+ e = Voice(video_note.file_id, video_note.file_unique_id, self.duration)
+
+ 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_error_without_required_args(self, bot, chat_id):
+ with pytest.raises(TypeError):
+ await bot.send_video_note(chat_id=chat_id)
+
+ async def test_send_with_video_note(self, monkeypatch, bot, chat_id, video_note):
+ async def make_assertion(url, request_data: RequestData, *args, **kwargs):
+ return request_data.json_parameters["video_note"] == video_note.file_id
+
+ monkeypatch.setattr(bot.request, "post", make_assertion)
+ assert await bot.send_video_note(chat_id, video_note=video_note)
+
+ async def test_send_video_note_custom_filename(
+ self, bot, chat_id, video_note_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_video_note(chat_id, video_note_file, filename="custom_filename")
+
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_video_note_local_files(self, monkeypatch, bot, chat_id, local_mode):
try:
@@ -203,7 +176,63 @@ class TestVideoNote:
finally:
bot._local_mode = False
- @pytest.mark.flaky(3, 1)
+ async def test_get_file_instance_method(self, monkeypatch, video_note):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["file_id"] == video_note.file_id
+
+ assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], [])
+ assert await check_shortcut_call(video_note.get_file, video_note.get_bot(), "get_file")
+ assert await check_defaults_handling(video_note.get_file, video_note.get_bot())
+
+ monkeypatch.setattr(video_note.get_bot(), "get_file", make_assertion)
+ assert await video_note.get_file()
+
+
+class TestVideoNoteWithRequest(TestVideoNoteBase):
+ async def test_send_all_args(self, bot, chat_id, video_note_file, video_note, thumb_file):
+ message = await bot.send_video_note(
+ chat_id,
+ video_note_file,
+ duration=self.duration,
+ length=self.length,
+ disable_notification=False,
+ protect_content=True,
+ thumb=thumb_file,
+ )
+
+ assert isinstance(message.video_note, VideoNote)
+ assert isinstance(message.video_note.file_id, str)
+ assert isinstance(message.video_note.file_unique_id, str)
+ assert message.video_note.file_id != ""
+ assert message.video_note.file_unique_id != ""
+ assert message.video_note.length == video_note.length
+ assert message.video_note.duration == video_note.duration
+ assert message.video_note.file_size == video_note.file_size
+
+ assert message.video_note.thumb.file_size == self.thumb_file_size
+ assert message.video_note.thumb.width == self.thumb_width
+ assert message.video_note.thumb.height == self.thumb_height
+ assert message.has_protected_content
+
+ async def test_get_and_download(self, bot, video_note, chat_id):
+ path = Path("telegram2.mp4")
+ if path.is_file():
+ path.unlink()
+
+ new_file = await bot.get_file(video_note.file_id)
+
+ assert new_file.file_size == self.file_size
+ assert new_file.file_unique_id == video_note.file_unique_id
+ assert new_file.file_path.startswith("https://")
+
+ await new_file.download_to_drive("telegram2.mp4")
+
+ assert path.is_file()
+
+ async def test_resend(self, bot, chat_id, video_note):
+ message = await bot.send_video_note(chat_id, video_note.file_id)
+ assert message.video_note == video_note
+
@pytest.mark.parametrize(
"default_bot,custom",
[
@@ -237,55 +266,20 @@ class TestVideoNote:
chat_id, video_note, 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_video_note_default_protect_content(self, chat_id, default_bot, video_note):
- protected = await default_bot.send_video_note(chat_id, video_note)
+ tasks = asyncio.gather(
+ default_bot.send_video_note(chat_id, video_note),
+ default_bot.send_video_note(chat_id, video_note, protect_content=False),
+ )
+ protected, unprotected = await tasks
assert protected.has_protected_content
- unprotected = await default_bot.send_video_note(chat_id, video_note, protect_content=False)
assert not unprotected.has_protected_content
- @pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_video_note(chat_id, open(os.devnull, "rb"))
- @pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.send_video_note(chat_id, "")
-
- async def test_error_without_required_args(self, bot, chat_id):
- with pytest.raises(TypeError):
- await bot.send_video_note(chat_id=chat_id)
-
- async def test_get_file_instance_method(self, monkeypatch, video_note):
- async def make_assertion(*_, **kwargs):
- return kwargs["file_id"] == video_note.file_id
-
- assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], [])
- assert await check_shortcut_call(video_note.get_file, video_note.get_bot(), "get_file")
- assert await check_defaults_handling(video_note.get_file, video_note.get_bot())
-
- monkeypatch.setattr(video_note.get_bot(), "get_file", make_assertion)
- assert await video_note.get_file()
-
- def test_equality(self, video_note):
- a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration)
- b = VideoNote("", video_note.file_unique_id, self.length, self.duration)
- c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0)
- d = VideoNote("", "", self.length, self.duration)
- e = Voice(video_note.file_id, video_note.file_unique_id, self.duration)
-
- 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)
diff --git a/tests/test_voice.py b/tests/test_voice.py
index 64152de21..67557661a 100644
--- a/tests/test_voice.py
+++ b/tests/test_voice.py
@@ -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,28 +36,27 @@ from tests.conftest import data_file
@pytest.fixture(scope="function")
def voice_file():
- f = data_file("telegram.ogg").open("rb")
- yield f
- f.close()
+ with data_file("telegram.ogg").open("rb") as f:
+ yield f
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
async def voice(bot, chat_id):
with data_file("telegram.ogg").open("rb") as f:
return (await bot.send_voice(chat_id, voice=f, read_timeout=50)).voice
-class TestVoice:
+class TestVoiceBase:
duration = 3
mime_type = "audio/ogg"
file_size = 9199
-
caption = "Test *voice*"
voice_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.ogg"
-
voice_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
voice_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
+
+class TestVoiceWithoutRequest(TestVoiceBase):
def test_slot_behaviour(self, voice, mro_slots):
for attr in voice.__slots__:
assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'"
@@ -75,30 +75,57 @@ class TestVoice:
assert voice.mime_type == self.mime_type
assert voice.file_size == self.file_size
- @pytest.mark.flaky(3, 1)
- async def test_send_all_args(self, bot, chat_id, voice_file, voice):
- message = await bot.send_voice(
- chat_id,
- voice_file,
- duration=self.duration,
- caption=self.caption,
- disable_notification=False,
- protect_content=True,
- parse_mode="Markdown",
- )
+ def test_de_json(self, bot):
+ json_dict = {
+ "file_id": self.voice_file_id,
+ "file_unique_id": self.voice_file_unique_id,
+ "duration": self.duration,
+ "mime_type": self.mime_type,
+ "file_size": self.file_size,
+ }
+ json_voice = Voice.de_json(json_dict, bot)
+ assert json_voice.api_kwargs == {}
- assert isinstance(message.voice, Voice)
- assert isinstance(message.voice.file_id, str)
- assert isinstance(message.voice.file_unique_id, str)
- assert message.voice.file_id != ""
- assert message.voice.file_unique_id != ""
- assert message.voice.duration == voice.duration
- assert message.voice.mime_type == voice.mime_type
- assert message.voice.file_size == voice.file_size
- assert message.caption == self.caption.replace("*", "")
- assert message.has_protected_content
+ assert json_voice.file_id == self.voice_file_id
+ assert json_voice.file_unique_id == self.voice_file_unique_id
+ assert json_voice.duration == self.duration
+ assert json_voice.mime_type == self.mime_type
+ assert json_voice.file_size == self.file_size
+
+ def test_to_dict(self, voice):
+ voice_dict = voice.to_dict()
+
+ assert isinstance(voice_dict, dict)
+ assert voice_dict["file_id"] == voice.file_id
+ assert voice_dict["file_unique_id"] == voice.file_unique_id
+ assert voice_dict["duration"] == voice.duration
+ assert voice_dict["mime_type"] == voice.mime_type
+ assert voice_dict["file_size"] == voice.file_size
+
+ def test_equality(self, voice):
+ a = Voice(voice.file_id, voice.file_unique_id, self.duration)
+ b = Voice("", voice.file_unique_id, self.duration)
+ c = Voice(voice.file_id, voice.file_unique_id, 0)
+ d = Voice("", "", self.duration)
+ e = Audio(voice.file_id, voice.file_unique_id, self.duration)
+
+ 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_error_without_required_args(self, bot, chat_id):
+ with pytest.raises(TypeError):
+ await bot.sendVoice(chat_id)
- @pytest.mark.flaky(3, 1)
async def test_send_voice_custom_filename(self, bot, chat_id, voice_file, monkeypatch):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return list(request_data.multipart_data.values())[0][0] == "custom_filename"
@@ -107,104 +134,12 @@ class TestVoice:
assert await bot.send_voice(chat_id, voice_file, filename="custom_filename")
- @pytest.mark.flaky(3, 1)
- async def test_get_and_download(self, bot, voice):
- path = Path("telegram.ogg")
- if path.is_file():
- path.unlink()
-
- new_file = await bot.get_file(voice.file_id)
-
- assert new_file.file_size == voice.file_size
- assert new_file.file_id == voice.file_id
- assert new_file.file_unique_id == voice.file_unique_id
- assert new_file.file_path.startswith("https://")
-
- await new_file.download_to_drive("telegram.ogg")
-
- assert path.is_file()
-
- @pytest.mark.flaky(3, 1)
- async def test_send_ogg_url_file(self, bot, chat_id, voice):
- message = await bot.sendVoice(chat_id, self.voice_file_url, duration=self.duration)
-
- assert isinstance(message.voice, Voice)
- assert isinstance(message.voice.file_id, str)
- assert isinstance(message.voice.file_unique_id, str)
- assert message.voice.file_id != ""
- assert message.voice.file_unique_id != ""
- assert message.voice.duration == voice.duration
- assert message.voice.mime_type == voice.mime_type
- assert message.voice.file_size == voice.file_size
-
- @pytest.mark.flaky(3, 1)
- async def test_resend(self, bot, chat_id, voice):
- message = await bot.sendVoice(chat_id, voice.file_id)
-
- assert message.voice == voice
-
async def test_send_with_voice(self, monkeypatch, bot, chat_id, voice):
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
return request_data.json_parameters["voice"] == voice.file_id
monkeypatch.setattr(bot.request, "post", make_assertion)
- message = await bot.send_voice(chat_id, voice=voice)
- assert message
-
- @pytest.mark.flaky(3, 1)
- async def test_send_voice_caption_entities(self, bot, chat_id, voice_file):
- 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_voice(
- chat_id, voice_file, 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_voice_default_parse_mode_1(self, default_bot, chat_id, voice):
- test_string = "Italic Bold Code"
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_voice(chat_id, voice, 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_voice_default_parse_mode_2(self, default_bot, chat_id, voice):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_voice(
- chat_id, voice, 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_voice_default_parse_mode_3(self, default_bot, chat_id, voice):
- test_markdown_string = "_Italic_ *Bold* `Code`"
-
- message = await default_bot.send_voice(
- chat_id, voice, 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_voice_default_protect_content(self, chat_id, default_bot, voice):
- protected = await default_bot.send_voice(chat_id, voice)
- assert protected.has_protected_content
- unprotected = await default_bot.send_voice(chat_id, voice, protect_content=False)
- assert not unprotected.has_protected_content
+ assert await bot.send_voice(chat_id, voice=voice)
@pytest.mark.parametrize("local_mode", [True, False])
async def test_send_voice_local_files(self, monkeypatch, bot, chat_id, local_mode):
@@ -228,7 +163,126 @@ class TestVoice:
finally:
bot._local_mode = False
- @pytest.mark.flaky(3, 1)
+ async def test_get_file_instance_method(self, monkeypatch, voice):
+ async def make_assertion(*_, **kwargs):
+ return kwargs["file_id"] == voice.file_id
+
+ assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], [])
+ assert await check_shortcut_call(voice.get_file, voice.get_bot(), "get_file")
+ assert await check_defaults_handling(voice.get_file, voice.get_bot())
+
+ monkeypatch.setattr(voice.get_bot(), "get_file", make_assertion)
+ assert await voice.get_file()
+
+
+class TestVoiceWithRequest(TestVoiceBase):
+ async def test_send_all_args(self, bot, chat_id, voice_file, voice):
+ message = await bot.send_voice(
+ chat_id,
+ voice_file,
+ duration=self.duration,
+ caption=self.caption,
+ disable_notification=False,
+ protect_content=True,
+ parse_mode="Markdown",
+ )
+
+ assert isinstance(message.voice, Voice)
+ assert isinstance(message.voice.file_id, str)
+ assert isinstance(message.voice.file_unique_id, str)
+ assert message.voice.file_id != ""
+ assert message.voice.file_unique_id != ""
+ assert message.voice.duration == voice.duration
+ assert message.voice.mime_type == voice.mime_type
+ assert message.voice.file_size == voice.file_size
+ assert message.caption == self.caption.replace("*", "")
+ assert message.has_protected_content
+
+ async def test_get_and_download(self, bot, voice, chat_id):
+ path = Path("telegram.ogg")
+ if path.is_file():
+ path.unlink()
+
+ new_file = await bot.get_file(voice.file_id)
+
+ assert new_file.file_size == voice.file_size
+ assert new_file.file_unique_id == voice.file_unique_id
+ assert new_file.file_path.startswith("https://")
+
+ await new_file.download_to_drive("telegram.ogg")
+
+ assert path.is_file()
+
+ async def test_send_ogg_url_file(self, bot, chat_id, voice):
+ message = await bot.sendVoice(chat_id, self.voice_file_url, duration=self.duration)
+
+ assert isinstance(message.voice, Voice)
+ assert isinstance(message.voice.file_id, str)
+ assert isinstance(message.voice.file_unique_id, str)
+ assert message.voice.file_id != ""
+ assert message.voice.file_unique_id != ""
+ assert message.voice.duration == voice.duration
+ assert message.voice.mime_type == voice.mime_type
+ assert message.voice.file_size == voice.file_size
+
+ async def test_resend(self, bot, chat_id, voice):
+ message = await bot.sendVoice(chat_id, voice.file_id)
+
+ assert message.voice == voice
+
+ async def test_send_voice_caption_entities(self, bot, chat_id, voice_file):
+ 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_voice(
+ chat_id, voice_file, 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_voice_default_parse_mode_1(self, default_bot, chat_id, voice):
+ test_string = "Italic Bold Code"
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_voice(chat_id, voice, 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_voice_default_parse_mode_2(self, default_bot, chat_id, voice):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_voice(
+ chat_id, voice, 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_voice_default_parse_mode_3(self, default_bot, chat_id, voice):
+ test_markdown_string = "_Italic_ *Bold* `Code`"
+
+ message = await default_bot.send_voice(
+ chat_id, voice, 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_voice_default_protect_content(self, chat_id, default_bot, voice):
+ tasks = asyncio.gather(
+ default_bot.send_voice(chat_id, voice),
+ default_bot.send_voice(chat_id, voice, protect_content=False),
+ )
+ protected, unprotected = await tasks
+ assert protected.has_protected_content
+ assert not unprotected.has_protected_content
+
@pytest.mark.parametrize(
"default_bot,custom",
[
@@ -262,74 +316,10 @@ class TestVoice:
chat_id, voice, reply_to_message_id=reply_to_message.message_id
)
- def test_de_json(self, bot):
- json_dict = {
- "file_id": self.voice_file_id,
- "file_unique_id": self.voice_file_unique_id,
- "duration": self.duration,
- "mime_type": self.mime_type,
- "file_size": self.file_size,
- }
- json_voice = Voice.de_json(json_dict, bot)
- assert json_voice.api_kwargs == {}
-
- assert json_voice.file_id == self.voice_file_id
- assert json_voice.file_unique_id == self.voice_file_unique_id
- assert json_voice.duration == self.duration
- assert json_voice.mime_type == self.mime_type
- assert json_voice.file_size == self.file_size
-
- def test_to_dict(self, voice):
- voice_dict = voice.to_dict()
-
- assert isinstance(voice_dict, dict)
- assert voice_dict["file_id"] == voice.file_id
- assert voice_dict["file_unique_id"] == voice.file_unique_id
- assert voice_dict["duration"] == voice.duration
- assert voice_dict["mime_type"] == voice.mime_type
- assert voice_dict["file_size"] == voice.file_size
-
- @pytest.mark.flaky(3, 1)
async def test_error_send_empty_file(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.sendVoice(chat_id, open(os.devnull, "rb"))
- @pytest.mark.flaky(3, 1)
async def test_error_send_empty_file_id(self, bot, chat_id):
with pytest.raises(TelegramError):
await bot.sendVoice(chat_id, "")
-
- async def test_error_without_required_args(self, bot, chat_id):
- with pytest.raises(TypeError):
- await bot.sendVoice(chat_id)
-
- async def test_get_file_instance_method(self, monkeypatch, voice):
- async def make_assertion(*_, **kwargs):
- return kwargs["file_id"] == voice.file_id
-
- assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], [])
- assert await check_shortcut_call(voice.get_file, voice.get_bot(), "get_file")
- assert await check_defaults_handling(voice.get_file, voice.get_bot())
-
- monkeypatch.setattr(voice.get_bot(), "get_file", make_assertion)
- assert await voice.get_file()
-
- def test_equality(self, voice):
- a = Voice(voice.file_id, voice.file_unique_id, self.duration)
- b = Voice("", voice.file_unique_id, self.duration)
- c = Voice(voice.file_id, voice.file_unique_id, 0)
- d = Voice("", "", self.duration)
- e = Audio(voice.file_id, voice.file_unique_id, self.duration)
-
- 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)
diff --git a/tests/test_webappdata.py b/tests/test_webappdata.py
index 4d2e0b556..c64f3894d 100644
--- a/tests/test_webappdata.py
+++ b/tests/test_webappdata.py
@@ -22,18 +22,17 @@ import pytest
from telegram import WebAppData
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def web_app_data():
- return WebAppData(
- data=TestWebAppData.data,
- button_text=TestWebAppData.button_text,
- )
+ return WebAppData(data=TestWebAppDataBase.data, button_text=TestWebAppDataBase.button_text)
-class TestWebAppData:
+class TestWebAppDataBase:
data = "data"
button_text = "button_text"
+
+class TestWebAppDataWithoutRequest(TestWebAppDataBase):
def test_slot_behaviour(self, web_app_data, mro_slots):
for attr in web_app_data.__slots__:
assert getattr(web_app_data, attr, "err") != "err", f"got extra slot '{attr}'"
diff --git a/tests/test_webappinfo.py b/tests/test_webappinfo.py
index 4c41ae497..cd1b07233 100644
--- a/tests/test_webappinfo.py
+++ b/tests/test_webappinfo.py
@@ -22,14 +22,16 @@ import pytest
from telegram import WebAppInfo
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def web_app_info():
- return WebAppInfo(url=TestWebAppInfo.url)
+ return WebAppInfo(url=TestWebAppInfoBase.url)
-class TestWebAppInfo:
+class TestWebAppInfoBase:
url = "https://www.example.com"
+
+class TestWebAppInfoWithoutRequest(TestWebAppInfoBase):
def test_slot_behaviour(self, web_app_info, mro_slots):
for attr in web_app_info.__slots__:
assert getattr(web_app_info, attr, "err") != "err", f"got extra slot '{attr}'"
diff --git a/tests/test_webhookinfo.py b/tests/test_webhookinfo.py
index 41907094a..554db3992 100644
--- a/tests/test_webhookinfo.py
+++ b/tests/test_webhookinfo.py
@@ -25,21 +25,21 @@ from telegram import LoginUrl, WebhookInfo
from telegram._utils.datetime import from_timestamp
-@pytest.fixture(scope="class")
+@pytest.fixture(scope="module")
def webhook_info():
return WebhookInfo(
- url=TestWebhookInfo.url,
- has_custom_certificate=TestWebhookInfo.has_custom_certificate,
- pending_update_count=TestWebhookInfo.pending_update_count,
- ip_address=TestWebhookInfo.ip_address,
- last_error_date=TestWebhookInfo.last_error_date,
- max_connections=TestWebhookInfo.max_connections,
- allowed_updates=TestWebhookInfo.allowed_updates,
- last_synchronization_error_date=TestWebhookInfo.last_synchronization_error_date,
+ url=TestWebhookInfoBase.url,
+ has_custom_certificate=TestWebhookInfoBase.has_custom_certificate,
+ pending_update_count=TestWebhookInfoBase.pending_update_count,
+ ip_address=TestWebhookInfoBase.ip_address,
+ last_error_date=TestWebhookInfoBase.last_error_date,
+ max_connections=TestWebhookInfoBase.max_connections,
+ allowed_updates=TestWebhookInfoBase.allowed_updates,
+ last_synchronization_error_date=TestWebhookInfoBase.last_synchronization_error_date,
)
-class TestWebhookInfo:
+class TestWebhookInfoBase:
url = "http://www.google.com"
has_custom_certificate = False
pending_update_count = 5
@@ -49,6 +49,8 @@ class TestWebhookInfo:
allowed_updates = ["type1", "type2"]
last_synchronization_error_date = time.time()
+
+class TestWebhookInfoWithoutRequest(TestWebhookInfoBase):
def test_slot_behaviour(self, webhook_info, mro_slots):
for attr in webhook_info.__slots__:
assert getattr(webhook_info, attr, "err") != "err", f"got extra slot '{attr}'"