mirror of
https://github.com/python-telegram-bot/python-telegram-bot.git
synced 2025-01-03 17:52:31 +01:00
Refactor and Overhaul the Test Suite (#3426)
This commit is contained in:
parent
43c3c8f568
commit
963edbf191
129 changed files with 6046 additions and 5962 deletions
30
.github/CONTRIBUTING.rst
vendored
30
.github/CONTRIBUTING.rst
vendored
|
@ -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.
|
- 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.
|
- 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
|
- If you want run style & type checks before committing run
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
@ -287,4 +265,4 @@ break the API classes. For example:
|
||||||
.. _`RTD build`: https://docs.python-telegram-bot.org/en/doc-fixes
|
.. _`RTD build`: https://docs.python-telegram-bot.org/en/doc-fixes
|
||||||
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
|
.. _`CSI`: https://standards.mousepawmedia.com/en/stable/csi.html
|
||||||
.. _`section`: #documenting
|
.. _`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
|
||||||
|
|
41
.github/workflows/test.yml
vendored
41
.github/workflows/test.yml
vendored
|
@ -44,42 +44,23 @@ jobs:
|
||||||
# The first & second one are achieved by mocking the corresponding import
|
# The first & second one are achieved by mocking the corresponding import
|
||||||
# See test_helpers.py & test_no_passport.py for details
|
# See test_helpers.py & test_no_passport.py for details
|
||||||
run: |
|
run: |
|
||||||
# Test without passport
|
# We test without optional dependencies first. This includes:
|
||||||
pytest -v --cov -k test_no_passport.py
|
# - 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=$?
|
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
|
# Test the rest
|
||||||
export TEST_WITH_OPT_DEPS='true'
|
export TEST_WITH_OPT_DEPS='true'
|
||||||
pip install -r requirements-opts.txt
|
pip install -r requirements-opts.txt
|
||||||
# `-n auto --dist loadfile` uses pytest-xdist to run each test file on a different CPU
|
# `-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
|
pytest -v --cov --cov-append -n auto --dist loadfile
|
||||||
status=$(( $? > status ? $? : status))
|
status=$(( $? > status ? $? : status))
|
||||||
exit ${status}
|
exit ${status}
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
|
GitHub Repository <https://github.com/python-telegram-bot/python-telegram-bot/>
|
||||||
Telegram Channel <https://t.me/pythontelegrambotchannel/>
|
Telegram Channel <https://t.me/pythontelegrambotchannel/>
|
||||||
Telegram User Group <https://t.me/pythontelegrambotgroup/>
|
Telegram User Group <https://t.me/pythontelegrambotgroup/>
|
||||||
contributing
|
|
||||||
coc
|
coc
|
||||||
|
contributing
|
||||||
|
testing
|
||||||
|
|
1
docs/source/testing.rst
Normal file
1
docs/source/testing.rst
Normal file
|
@ -0,0 +1 @@
|
||||||
|
.. include:: ../../tests/README.rst
|
|
@ -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==7.2.1
|
||||||
pytest-asyncio==0.20.3
|
pytest-asyncio==0.20.3 # needed because pytest doesn't come with native support for coroutines as tests
|
||||||
pytest-timeout==2.1.0 # used to timeout tests
|
|
||||||
pytest-xdist==3.1.0 # xdist runs tests in parallel
|
pytest-xdist==3.1.0 # xdist runs tests in parallel
|
||||||
|
|
||||||
flaky # Used for flaky tests (flaky decorator)
|
flaky # Used for flaky tests (flaky decorator)
|
||||||
beautifulsoup4 # used in test_official for parsing tg docs
|
beautifulsoup4 # used in test_official for parsing tg docs
|
||||||
|
|
||||||
wheel # required for building the wheels for releases
|
wheel # required for building the wheels for releases
|
||||||
|
|
|
@ -36,7 +36,10 @@ filterwarnings =
|
||||||
; Unfortunately due to https://github.com/pytest-dev/pytest/issues/8343 we can't have this here
|
; 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
|
; and instead do a trick directly in tests/conftest.py
|
||||||
; ignore::telegram.utils.deprecate.TelegramDeprecationWarning
|
; 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
|
asyncio_mode = auto
|
||||||
|
|
||||||
[coverage:run]
|
[coverage:run]
|
||||||
|
|
101
tests/README.rst
Normal file
101
tests/README.rst
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
==============
|
||||||
|
Testing in PTB
|
||||||
|
==============
|
||||||
|
|
||||||
|
PTB uses `pytest`_ for testing. To run the tests, you need to
|
||||||
|
have pytest installed along with a few other dependencies. You can find the list of dependencies
|
||||||
|
in the ``requirements-dev.txt`` file in the root of the repository.
|
||||||
|
|
||||||
|
Running tests
|
||||||
|
=============
|
||||||
|
|
||||||
|
To run the entire test suite, you can use the following command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest
|
||||||
|
|
||||||
|
This will run all the tests, including the ones which make a request to the Telegram servers, which
|
||||||
|
may take a long time (total > 13 mins). To run only the tests that don't require a connection, you
|
||||||
|
can run the following command:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest -m no_req
|
||||||
|
|
||||||
|
Or alternatively, you can run the following command to run only the tests that require a connection:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest -m req
|
||||||
|
|
||||||
|
To further speed up the tests, you can run them in parallel using the ``-n`` flag (requires `pytest-xdist`_). But beware that
|
||||||
|
this will use multiple CPU cores on your machine. The ``--dist`` flag is used to specify how the
|
||||||
|
tests will be distributed across the cores. The ``loadgroup`` option is used to distribute the tests
|
||||||
|
such that tests marked with ``@pytest.mark.xdist_group("name")`` are run on the same core — important if you want avoid race conditions in some tests:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest -n auto --dist=loadgroup
|
||||||
|
|
||||||
|
This will result in a significant speedup, but may cause some tests to fail. If you want to run
|
||||||
|
the failed tests in isolation, you can use the ``--lf`` flag:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest --lf
|
||||||
|
|
||||||
|
|
||||||
|
Writing tests
|
||||||
|
=============
|
||||||
|
|
||||||
|
PTB has a separate test file for every file in the ``telegram.*`` namespace. Further, the tests for
|
||||||
|
the ``telegram`` module are split into two classes, based on whether the test methods in them make a
|
||||||
|
request or not. When writing tests, make sure to split them into these two classes, and make sure
|
||||||
|
to name the test class as: ``TestXXXWithoutRequest`` for tests that don't make a request, and ``TestXXXWithRequest`` for tests that do.
|
||||||
|
|
||||||
|
Writing tests is a creative process; allowing you to design your test however you'd like, but there
|
||||||
|
are a few conventions that you should follow:
|
||||||
|
|
||||||
|
- Each new test class needs a ``test_slot_behaviour``, ``test_to_dict``, ``test_de_json`` and
|
||||||
|
``test_equality`` (in most cases).
|
||||||
|
|
||||||
|
- Make use of pytest's fixtures and parametrize wherever possible. Having knowledge of pytest's
|
||||||
|
tooling can help you as well. You can look at the existing tests for examples and inspiration.
|
||||||
|
|
||||||
|
If you have made some API changes, you may want to run ``test_official`` to validate that the changes are
|
||||||
|
complete and correct. To run it, export an environment variable first:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ export TEST_OFFICIAL=true
|
||||||
|
|
||||||
|
and then run ``pytest tests/test_official.py``.
|
||||||
|
|
||||||
|
We also have another marker, ``@pytest.mark.dev``, which you can add to tests that you want to run selectively.
|
||||||
|
Use as follows:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pytest -m dev
|
||||||
|
|
||||||
|
|
||||||
|
Bots used in tests
|
||||||
|
==================
|
||||||
|
|
||||||
|
If you run the tests locally, the test setup will use one of the two public bots available. Which
|
||||||
|
bot of the two gets chosen for the test session is random. Whereas when the tests on the
|
||||||
|
Github Actions CI are run, the test setup allocates a different, but same bot for every combination of Python version and
|
||||||
|
OS.
|
||||||
|
|
||||||
|
Thus, number of bots used for testing locally is 2 (called as fallback bots), and on the CI,
|
||||||
|
its [3.7, 3.8, 3.9, 3.10, 3.11] x [ubuntu-latest, macos-latest, windows-latest] = 15. Bringing the
|
||||||
|
total number of bots used for testing to 17.
|
||||||
|
|
||||||
|
|
||||||
|
That's it! If you have any questions, feel free to ask them in the `PTB dev
|
||||||
|
group`_.
|
||||||
|
|
||||||
|
.. _pytest: https://docs.pytest.org/en/stable/
|
||||||
|
.. _pytest-xdist: https://pypi.org/project/pytest-xdist/
|
||||||
|
.. _PTB dev group: https://t.me/pythontelegrambotgroup
|
|
@ -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"))
|
BOTS = json.loads(base64.b64decode(BOTS).decode("utf-8"))
|
||||||
JOB_INDEX = int(JOB_INDEX)
|
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):
|
class BotInfoProvider:
|
||||||
# If we have TOKEN, PAYMENT_PROVIDER_TOKEN, CHAT_ID, SUPER_GROUP_ID,
|
def __init__(self):
|
||||||
# CHANNEL_ID, BOT_NAME, or BOT_USERNAME in the environment, then use that
|
self._cached = {}
|
||||||
val = os.getenv(name.upper())
|
|
||||||
if val:
|
|
||||||
return val
|
|
||||||
|
|
||||||
|
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 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:
|
if GITHUB_ACTION is not None and BOTS is not None and JOB_INDEX is not None:
|
||||||
try:
|
try:
|
||||||
return BOTS[JOB_INDEX][name]
|
return BOTS[JOB_INDEX][key]
|
||||||
except (KeyError, IndexError):
|
except (IndexError, KeyError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Otherwise go with the fallback
|
# Otherwise go with the fallback
|
||||||
return fallback
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
def get_bot():
|
|
||||||
return {k: get(k, v) for k, v in random.choice(FALLBACKS).items()}
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Callable, Optional
|
from typing import Callable, Dict, List, Optional
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from httpx import AsyncClient, Response
|
from httpx import AsyncClient, Response
|
||||||
|
@ -48,16 +48,49 @@ from telegram.ext.filters import MessageFilter, UpdateFilter
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
from telegram.request._httpxrequest import HTTPXRequest
|
from telegram.request._httpxrequest import HTTPXRequest
|
||||||
from tests.auxil.object_conversions import env_var_2_bool
|
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
|
# 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(
|
session.add_marker(
|
||||||
pytest.mark.filterwarnings("ignore::telegram.warnings.PTBDeprecationWarning")
|
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)
|
GITHUB_ACTION = os.getenv("GITHUB_ACTION", False)
|
||||||
|
|
||||||
if GITHUB_ACTION:
|
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
|
# 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")
|
@pytest.fixture(scope="session")
|
||||||
def bot_info():
|
def bot_info() -> Dict[str, str]:
|
||||||
return get_bot()
|
return BOT_INFO.get_info()
|
||||||
|
|
||||||
|
|
||||||
# Below classes are used to monkeypatch attributes since parent classes don't have __dict__
|
# Below classes are used to monkeypatch attributes since parent classes don't have __dict__
|
||||||
|
|
||||||
|
|
||||||
class TestHttpxRequest(HTTPXRequest):
|
class TestHttpxRequest(HTTPXRequest):
|
||||||
async def _request_wrapper(
|
async def _request_wrapper(
|
||||||
self,
|
self,
|
||||||
|
@ -132,6 +158,10 @@ class DictExtBot(ExtBot):
|
||||||
# Makes it easier to work with the bot in tests
|
# Makes it easier to work with the bot in tests
|
||||||
self._unfreeze()
|
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):
|
class DictBot(Bot):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -139,11 +169,42 @@ class DictBot(Bot):
|
||||||
# Makes it easier to work with the bot in tests
|
# Makes it easier to work with the bot in tests
|
||||||
self._unfreeze()
|
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):
|
class DictApplication(Application):
|
||||||
pass
|
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")
|
@pytest.fixture(scope="session")
|
||||||
async def bot(bot_info):
|
async def bot(bot_info):
|
||||||
"""Makes an ExtBot instance with the given bot_info"""
|
"""Makes an ExtBot instance with the given bot_info"""
|
||||||
|
@ -151,6 +212,12 @@ async def bot(bot_info):
|
||||||
yield _bot
|
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")
|
@pytest.fixture(scope="session")
|
||||||
async def cdc_bot(bot_info):
|
async def cdc_bot(bot_info):
|
||||||
"""Makes an ExtBot instance with the given bot_info that uses arbitrary callback_data"""
|
"""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
|
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):
|
async def default_bot(request, bot_info):
|
||||||
param = request.param if hasattr(request, "param") else {}
|
param = request.param if hasattr(request, "param") else {}
|
||||||
|
defaults = Defaults(**param)
|
||||||
|
|
||||||
default_bot = make_bot(bot_info, defaults=Defaults(**param))
|
# If the bot is already created, return it. Else make a new one.
|
||||||
async with default_bot:
|
default_bot = default_bots.get(defaults, None)
|
||||||
yield default_bot
|
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):
|
async def tz_bot(timezone, bot_info):
|
||||||
default_bot = make_bot(bot_info, defaults=Defaults(tzinfo=timezone))
|
defaults = Defaults(tzinfo=timezone)
|
||||||
async with default_bot:
|
try: # If the bot is already created, return it. Saves time since get_me is not called again.
|
||||||
yield default_bot
|
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")
|
@pytest.fixture(scope="session")
|
||||||
|
@ -243,16 +326,14 @@ def data_file(filename: str) -> Path:
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def thumb_file():
|
def thumb_file():
|
||||||
f = data_file("thumb.jpg").open("rb")
|
with data_file("thumb.jpg").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def class_thumb_file():
|
def class_thumb_file():
|
||||||
f = data_file("thumb.jpg").open("rb")
|
with data_file("thumb.jpg").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
def make_bot(bot_info=None, **kwargs):
|
def make_bot(bot_info=None, **kwargs):
|
||||||
|
@ -285,7 +366,7 @@ def make_message(text, **kwargs):
|
||||||
"""
|
"""
|
||||||
bot = kwargs.pop("bot", None)
|
bot = kwargs.pop("bot", None)
|
||||||
if bot is None:
|
if bot is None:
|
||||||
bot = make_bot(get_bot())
|
bot = make_bot(BOT_INFO.get_info())
|
||||||
message = Message(
|
message = Message(
|
||||||
message_id=1,
|
message_id=1,
|
||||||
from_user=kwargs.pop("user", User(id=1, first_name="", is_bot=False)),
|
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)
|
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):
|
def tzinfo(request):
|
||||||
if TEST_WITH_OPT_DEPS:
|
if TEST_WITH_OPT_DEPS:
|
||||||
return pytz.timezone(request.param)
|
return pytz.timezone(request.param)
|
||||||
|
@ -410,7 +491,7 @@ def tzinfo(request):
|
||||||
return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param)
|
return BasicTimezone(offset=datetime.timedelta(hours=hours_offset), name=request.param)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture(scope="session")
|
||||||
def timezone(tzinfo):
|
def timezone(tzinfo):
|
||||||
return tzinfo
|
return tzinfo
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -35,21 +36,19 @@ from tests.conftest import data_file
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def animation_file():
|
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:
|
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 (
|
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
|
).animation
|
||||||
|
|
||||||
|
|
||||||
class TestAnimation:
|
class TestAnimationBase:
|
||||||
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
|
animation_file_id = "CgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||||
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
animation_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
width = 320
|
width = 320
|
||||||
|
@ -63,6 +62,8 @@ class TestAnimation:
|
||||||
file_size = 5859
|
file_size = 5859
|
||||||
caption = "Test *animation*"
|
caption = "Test *animation*"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAnimationWithoutRequest(TestAnimationBase):
|
||||||
def test_slot_behaviour(self, animation, mro_slots):
|
def test_slot_behaviour(self, animation, mro_slots):
|
||||||
for attr in animation.__slots__:
|
for attr in animation.__slots__:
|
||||||
assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'"
|
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 animation.file_name.startswith("game.gif") == self.file_name.startswith("game.gif")
|
||||||
assert isinstance(animation.thumb, PhotoSize)
|
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):
|
def test_de_json(self, bot, animation):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"file_id": self.animation_file_id,
|
"file_id": self.animation_file_id,
|
||||||
|
@ -323,33 +115,6 @@ class TestAnimation:
|
||||||
assert animation_dict["mime_type"] == animation.mime_type
|
assert animation_dict["mime_type"] == animation.mime_type
|
||||||
assert animation_dict["file_size"] == animation.file_size
|
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):
|
def test_equality(self):
|
||||||
a = Animation(
|
a = Animation(
|
||||||
self.animation_file_id,
|
self.animation_file_id,
|
||||||
|
@ -371,3 +136,222 @@ class TestAnimation:
|
||||||
|
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(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)
|
||||||
|
|
|
@ -53,7 +53,13 @@ from telegram.ext import (
|
||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
from telegram.warnings import PTBUserWarning
|
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):
|
class CustomContext(CallbackContext):
|
||||||
|
@ -113,8 +119,8 @@ class TestApplication:
|
||||||
):
|
):
|
||||||
self.received = context.error.message
|
self.received = context.error.message
|
||||||
|
|
||||||
async def test_slot_behaviour(self, bot, mro_slots):
|
async def test_slot_behaviour(self, one_time_bot, mro_slots):
|
||||||
async with ApplicationBuilder().token(bot.token).build() as app:
|
async with ApplicationBuilder().bot(one_time_bot).build() as app:
|
||||||
for at in app.__slots__:
|
for at in app.__slots__:
|
||||||
at = f"_Application{at}" if at.startswith("__") and not at.endswith("__") else at
|
at = f"_Application{at}" if at.startswith("__") and not at.endswith("__") else at
|
||||||
assert getattr(app, at, "err") != "err", f"got extra slot '{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)]
|
"concurrent_updates, expected", [(0, 0), (4, 4), (False, 0), (True, 256)]
|
||||||
)
|
)
|
||||||
@pytest.mark.filterwarnings("ignore: `Application` instances should")
|
@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()
|
update_queue = asyncio.Queue()
|
||||||
job_queue = JobQueue()
|
job_queue = JobQueue()
|
||||||
persistence = PicklePersistence("file_path")
|
persistence = PicklePersistence("file_path")
|
||||||
context_types = ContextTypes()
|
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:
|
async def post_init(application: Application) -> None:
|
||||||
pass
|
pass
|
||||||
|
@ -161,7 +167,7 @@ class TestApplication:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
app = Application(
|
app = Application(
|
||||||
bot=bot,
|
bot=one_time_bot,
|
||||||
update_queue=update_queue,
|
update_queue=update_queue,
|
||||||
job_queue=job_queue,
|
job_queue=job_queue,
|
||||||
persistence=persistence,
|
persistence=persistence,
|
||||||
|
@ -172,7 +178,7 @@ class TestApplication:
|
||||||
post_shutdown=post_shutdown,
|
post_shutdown=post_shutdown,
|
||||||
post_stop=post_stop,
|
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.update_queue is update_queue
|
||||||
assert app.job_queue is job_queue
|
assert app.job_queue is job_queue
|
||||||
assert app.persistence is persistence
|
assert app.persistence is persistence
|
||||||
|
@ -196,7 +202,7 @@ class TestApplication:
|
||||||
|
|
||||||
with pytest.raises(ValueError, match="must be a non-negative"):
|
with pytest.raises(ValueError, match="must be a non-negative"):
|
||||||
Application(
|
Application(
|
||||||
bot=bot,
|
bot=one_time_bot,
|
||||||
update_queue=update_queue,
|
update_queue=update_queue,
|
||||||
job_queue=job_queue,
|
job_queue=job_queue,
|
||||||
persistence=persistence,
|
persistence=persistence,
|
||||||
|
@ -208,19 +214,19 @@ class TestApplication:
|
||||||
post_stop=None,
|
post_stop=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_job_queue(self, bot, app, recwarn):
|
def test_job_queue(self, one_time_bot, app, recwarn):
|
||||||
expected_warning = (
|
expected_warning = (
|
||||||
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
|
"No `JobQueue` set up. To use `JobQueue`, you must install PTB via "
|
||||||
"`pip install python-telegram-bot[job-queue]`."
|
"`pip install python-telegram-bot[job-queue]`."
|
||||||
)
|
)
|
||||||
assert app.job_queue is app._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 application.job_queue is None
|
||||||
assert len(recwarn) == 1
|
assert len(recwarn) == 1
|
||||||
assert str(recwarn[0].message) == expected_warning
|
assert str(recwarn[0].message) == expected_warning
|
||||||
assert recwarn[0].filename == __file__, "wrong stacklevel"
|
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(
|
cc = ContextTypes(
|
||||||
context=CustomContext,
|
context=CustomContext,
|
||||||
user_data=int,
|
user_data=int,
|
||||||
|
@ -228,14 +234,14 @@ class TestApplication:
|
||||||
bot_data=complex,
|
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.user_data[1], int)
|
||||||
assert isinstance(application.chat_data[1], float)
|
assert isinstance(application.chat_data[1], float)
|
||||||
assert isinstance(application.bot_data, complex)
|
assert isinstance(application.bot_data, complex)
|
||||||
|
|
||||||
@pytest.mark.parametrize("updater", (True, False))
|
@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"""
|
"""Initialization of persistence is tested test_basepersistence"""
|
||||||
self.test_flag = set()
|
self.test_flag = set()
|
||||||
|
|
||||||
|
@ -251,18 +257,18 @@ class TestApplication:
|
||||||
)
|
)
|
||||||
|
|
||||||
if updater:
|
if updater:
|
||||||
app = ApplicationBuilder().token(bot.token).build()
|
app = ApplicationBuilder().bot(one_time_bot).build()
|
||||||
await app.initialize()
|
await app.initialize()
|
||||||
assert self.test_flag == {"bot", "updater"}
|
assert self.test_flag == {"bot", "updater"}
|
||||||
await app.shutdown()
|
await app.shutdown()
|
||||||
else:
|
else:
|
||||||
app = ApplicationBuilder().token(bot.token).updater(None).build()
|
app = ApplicationBuilder().bot(one_time_bot).updater(None).build()
|
||||||
await app.initialize()
|
await app.initialize()
|
||||||
assert self.test_flag == {"bot"}
|
assert self.test_flag == {"bot"}
|
||||||
await app.shutdown()
|
await app.shutdown()
|
||||||
|
|
||||||
@pytest.mark.parametrize("updater", (True, False))
|
@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"""
|
"""Shutdown of persistence is tested in test_basepersistence"""
|
||||||
self.test_flag = set()
|
self.test_flag = set()
|
||||||
|
|
||||||
|
@ -278,11 +284,11 @@ class TestApplication:
|
||||||
)
|
)
|
||||||
|
|
||||||
if updater:
|
if updater:
|
||||||
async with ApplicationBuilder().token(bot.token).build():
|
async with ApplicationBuilder().bot(one_time_bot).build():
|
||||||
pass
|
pass
|
||||||
assert self.test_flag == {"bot", "updater"}
|
assert self.test_flag == {"bot", "updater"}
|
||||||
else:
|
else:
|
||||||
async with ApplicationBuilder().token(bot.token).updater(None).build():
|
async with ApplicationBuilder().bot(one_time_bot).updater(None).build():
|
||||||
pass
|
pass
|
||||||
assert self.test_flag == {"bot"}
|
assert self.test_flag == {"bot"}
|
||||||
|
|
||||||
|
@ -329,12 +335,12 @@ class TestApplication:
|
||||||
await app.shutdown()
|
await app.shutdown()
|
||||||
await app.stop()
|
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(_):
|
def start(_):
|
||||||
raise Exception("Test Exception")
|
raise Exception("Test Exception")
|
||||||
|
|
||||||
monkeypatch.setattr(JobQueue, "start", start)
|
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:
|
async with app:
|
||||||
with pytest.raises(Exception, match="Test Exception"):
|
with pytest.raises(Exception, match="Test Exception"):
|
||||||
|
@ -405,12 +411,12 @@ class TestApplication:
|
||||||
|
|
||||||
@pytest.mark.parametrize("job_queue", (True, False))
|
@pytest.mark.parametrize("job_queue", (True, False))
|
||||||
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
|
@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
|
# TODO: repeat a similar test for create_task, persistence processing and job queue
|
||||||
if job_queue:
|
if job_queue:
|
||||||
app = ApplicationBuilder().token(bot.token).build()
|
app = ApplicationBuilder().bot(one_time_bot).build()
|
||||||
else:
|
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):
|
async def callback(u, c):
|
||||||
self.received = u
|
self.received = u
|
||||||
|
@ -440,8 +446,11 @@ class TestApplication:
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
assert app.update_queue.empty()
|
assert app.update_queue.empty()
|
||||||
assert self.received == 1
|
assert self.received == 1
|
||||||
|
try: # just in case start_polling times out
|
||||||
await app.updater.start_polling()
|
await app.updater.start_polling()
|
||||||
|
except TelegramError:
|
||||||
|
pytest.xfail("start_polling timed out")
|
||||||
|
else:
|
||||||
await app.stop()
|
await app.stop()
|
||||||
assert not app.running
|
assert not app.running
|
||||||
# app.stop() should not stop the updater!
|
# app.stop() should not stop the updater!
|
||||||
|
@ -476,7 +485,7 @@ class TestApplication:
|
||||||
async def one(update, context):
|
async def one(update, context):
|
||||||
self.received = context
|
self.received = context
|
||||||
|
|
||||||
def two(update, context):
|
async def two(update, context):
|
||||||
if update.message.text == "test":
|
if update.message.text == "test":
|
||||||
if context is not self.received:
|
if context is not self.received:
|
||||||
pytest.fail("Expected same context object, got different")
|
pytest.fail("Expected same context object, got different")
|
||||||
|
@ -643,7 +652,7 @@ class TestApplication:
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
await app.stop()
|
await app.stop()
|
||||||
|
|
||||||
async def test_flow_stop(self, app, bot):
|
async def test_flow_stop(self, app, one_time_bot):
|
||||||
passed = []
|
passed = []
|
||||||
|
|
||||||
async def start1(b, u):
|
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:
|
async with app:
|
||||||
# If ApplicationHandlerStop raised handlers in other groups should not be called.
|
# If ApplicationHandlerStop raised handlers in other groups should not be called.
|
||||||
|
@ -679,18 +689,18 @@ class TestApplication:
|
||||||
await app.process_update(update)
|
await app.process_update(update)
|
||||||
assert passed == ["start1"]
|
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 = []
|
passed = []
|
||||||
exception = Exception("General excepition")
|
exception = Exception("General exception")
|
||||||
|
|
||||||
async def start1(b, u):
|
async def start1(u, c):
|
||||||
passed.append("start1")
|
passed.append("start1")
|
||||||
raise exception
|
raise exception
|
||||||
|
|
||||||
async def start2(b, u):
|
async def start2(u, c):
|
||||||
passed.append("start2")
|
passed.append("start2")
|
||||||
|
|
||||||
async def start3(b, u):
|
async def start3(u, c):
|
||||||
passed.append("start3")
|
passed.append("start3")
|
||||||
|
|
||||||
async def error(u, c):
|
async def error(u, c):
|
||||||
|
@ -728,7 +738,7 @@ class TestApplication:
|
||||||
# Higher groups should still be called
|
# Higher groups should still be called
|
||||||
assert self.count == 42
|
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 = []
|
passed = []
|
||||||
err = Exception("General exception")
|
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:
|
async with app:
|
||||||
# If an unhandled exception was caught, no further handlers from the same group should
|
# If an unhandled exception was caught, no further handlers from the same group should
|
||||||
|
@ -837,7 +848,7 @@ class TestApplication:
|
||||||
|
|
||||||
await app.stop()
|
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):
|
async def error_handler(_, context):
|
||||||
self.received = (
|
self.received = (
|
||||||
type(context),
|
type(context),
|
||||||
|
@ -848,7 +859,7 @@ class TestApplication:
|
||||||
|
|
||||||
application = (
|
application = (
|
||||||
ApplicationBuilder()
|
ApplicationBuilder()
|
||||||
.token(bot.token)
|
.bot(one_time_bot)
|
||||||
.context_types(
|
.context_types(
|
||||||
ContextTypes(
|
ContextTypes(
|
||||||
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
|
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
|
||||||
|
@ -866,8 +877,8 @@ class TestApplication:
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
assert self.received == (CustomContext, float, complex, int)
|
assert self.received == (CustomContext, float, complex, int)
|
||||||
|
|
||||||
async def test_custom_context_handler_callback(self, bot):
|
async def test_custom_context_handler_callback(self, one_time_bot):
|
||||||
def callback(_, context):
|
async def callback(_, context):
|
||||||
self.received = (
|
self.received = (
|
||||||
type(context),
|
type(context),
|
||||||
type(context.user_data),
|
type(context.user_data),
|
||||||
|
@ -877,7 +888,7 @@ class TestApplication:
|
||||||
|
|
||||||
application = (
|
application = (
|
||||||
ApplicationBuilder()
|
ApplicationBuilder()
|
||||||
.token(bot.token)
|
.bot(one_time_bot)
|
||||||
.context_types(
|
.context_types(
|
||||||
ContextTypes(
|
ContextTypes(
|
||||||
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
|
context=CustomContext, bot_data=int, user_data=float, chat_data=complex
|
||||||
|
@ -971,7 +982,7 @@ class TestApplication:
|
||||||
), "incorrect stacklevel!"
|
), "incorrect stacklevel!"
|
||||||
|
|
||||||
async def test_non_blocking_no_error_handler(self, app, caplog):
|
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):
|
with caplog.at_level(logging.ERROR):
|
||||||
async with app:
|
async with app:
|
||||||
|
@ -997,7 +1008,7 @@ class TestApplication:
|
||||||
|
|
||||||
app.add_error_handler(async_error_handler, block=False)
|
app.add_error_handler(async_error_handler, block=False)
|
||||||
app.add_error_handler(normal_error_handler)
|
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:
|
async with app:
|
||||||
await app.start()
|
await app.start()
|
||||||
|
@ -1041,14 +1052,15 @@ class TestApplication:
|
||||||
), "incorrect stacklevel!"
|
), "incorrect stacklevel!"
|
||||||
|
|
||||||
@pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)])
|
@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):
|
async def error_handler(*args, **kwargs):
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1)
|
||||||
self.count = 5
|
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:
|
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)
|
app.add_error_handler(error_handler)
|
||||||
await app.process_update(1)
|
await app.process_update(1)
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05)
|
||||||
|
@ -1057,8 +1069,9 @@ class TestApplication:
|
||||||
assert self.count == 5
|
assert self.count == 5
|
||||||
|
|
||||||
@pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)])
|
@pytest.mark.parametrize(["block", "expected_output"], [(False, 0), (True, 5)])
|
||||||
async def test_default_block_handler(self, bot, block, expected_output):
|
async def test_default_block_handler(self, bot_info, block, expected_output):
|
||||||
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:
|
async with app:
|
||||||
app.add_handler(TypeHandler(object, self.callback_set_count(5, sleep=0.1)))
|
app.add_handler(TypeHandler(object, self.callback_set_count(5, sleep=0.1)))
|
||||||
await app.process_update(1)
|
await app.process_update(1)
|
||||||
|
@ -1072,7 +1085,7 @@ class TestApplication:
|
||||||
async def test_nonblocking_handler_raises_and_non_blocking_error_handler_raises(
|
async def test_nonblocking_handler_raises_and_non_blocking_error_handler_raises(
|
||||||
self, app, caplog, handler_block, error_handler_block
|
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_handler(handler)
|
||||||
app.add_error_handler(self.error_handler_raise_error, block=error_handler_block)
|
app.add_error_handler(self.error_handler_raise_error, block=error_handler_block)
|
||||||
|
|
||||||
|
@ -1303,10 +1316,12 @@ class TestApplication:
|
||||||
await app.stop()
|
await app.stop()
|
||||||
|
|
||||||
@pytest.mark.parametrize("concurrent_updates", (15, 50, 100))
|
@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
|
# We don't test with `True` since the large number of parallel coroutines quickly leads
|
||||||
# to test instabilities
|
# 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)}
|
events = {i: asyncio.Event() for i in range(app.concurrent_updates + 10)}
|
||||||
queue = asyncio.Queue()
|
queue = asyncio.Queue()
|
||||||
for event in events.values():
|
for event in events.values():
|
||||||
|
@ -1337,8 +1352,8 @@ class TestApplication:
|
||||||
|
|
||||||
await app.stop()
|
await app.stop()
|
||||||
|
|
||||||
async def test_concurrent_updates_done_on_shutdown(self, bot):
|
async def test_concurrent_updates_done_on_shutdown(self, one_time_bot):
|
||||||
app = Application.builder().token(bot.token).concurrent_updates(True).build()
|
app = Application.builder().bot(one_time_bot).concurrent_updates(True).build()
|
||||||
event = asyncio.Event()
|
event = asyncio.Event()
|
||||||
|
|
||||||
async def callback(update, context):
|
async def callback(update, context):
|
||||||
|
@ -1422,7 +1437,7 @@ class TestApplication:
|
||||||
platform.system() == "Windows",
|
platform.system() == "Windows",
|
||||||
reason="Can't send signals without stopping whole process on 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 = []
|
events = []
|
||||||
|
|
||||||
async def get_updates(*args, **kwargs):
|
async def get_updates(*args, **kwargs):
|
||||||
|
@ -1443,7 +1458,7 @@ class TestApplication:
|
||||||
async def post_init(app: Application) -> None:
|
async def post_init(app: Application) -> None:
|
||||||
events.append("post_init")
|
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()
|
app.bot._unfreeze()
|
||||||
monkeypatch.setattr(app.bot, "get_updates", get_updates)
|
monkeypatch.setattr(app.bot, "get_updates", get_updates)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
@ -1465,7 +1480,7 @@ class TestApplication:
|
||||||
platform.system() == "Windows",
|
platform.system() == "Windows",
|
||||||
reason="Can't send signals without stopping whole process on 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 = []
|
events = []
|
||||||
|
|
||||||
async def get_updates(*args, **kwargs):
|
async def get_updates(*args, **kwargs):
|
||||||
|
@ -1486,7 +1501,7 @@ class TestApplication:
|
||||||
async def post_shutdown(app: Application) -> None:
|
async def post_shutdown(app: Application) -> None:
|
||||||
events.append("post_shutdown")
|
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()
|
app.bot._unfreeze()
|
||||||
monkeypatch.setattr(app.bot, "get_updates", get_updates)
|
monkeypatch.setattr(app.bot, "get_updates", get_updates)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
@ -1691,7 +1706,7 @@ class TestApplication:
|
||||||
platform.system() == "Windows",
|
platform.system() == "Windows",
|
||||||
reason="Can't send signals without stopping whole process on 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 = []
|
events = []
|
||||||
|
|
||||||
async def delete_webhook(*args, **kwargs):
|
async def delete_webhook(*args, **kwargs):
|
||||||
|
@ -1718,7 +1733,7 @@ class TestApplication:
|
||||||
async def post_init(app: Application) -> None:
|
async def post_init(app: Application) -> None:
|
||||||
events.append("post_init")
|
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()
|
app.bot._unfreeze()
|
||||||
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
|
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
|
||||||
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
|
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
|
||||||
|
@ -1751,7 +1766,7 @@ class TestApplication:
|
||||||
platform.system() == "Windows",
|
platform.system() == "Windows",
|
||||||
reason="Can't send signals without stopping whole process on 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 = []
|
events = []
|
||||||
|
|
||||||
async def delete_webhook(*args, **kwargs):
|
async def delete_webhook(*args, **kwargs):
|
||||||
|
@ -1778,7 +1793,7 @@ class TestApplication:
|
||||||
async def post_shutdown(app: Application) -> None:
|
async def post_shutdown(app: Application) -> None:
|
||||||
events.append("post_shutdown")
|
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()
|
app.bot._unfreeze()
|
||||||
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
|
monkeypatch.setattr(app.bot, "set_webhook", set_webhook)
|
||||||
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
|
monkeypatch.setattr(app.bot, "delete_webhook", delete_webhook)
|
||||||
|
@ -1883,7 +1898,7 @@ class TestApplication:
|
||||||
platform.system() == "Windows",
|
platform.system() == "Windows",
|
||||||
reason="Can't send signals without stopping whole process on 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
|
# Check that we pass them correctly
|
||||||
|
|
||||||
async def start_webhook(_, **kwargs):
|
async def start_webhook(_, **kwargs):
|
||||||
|
@ -1898,7 +1913,7 @@ class TestApplication:
|
||||||
|
|
||||||
monkeypatch.setattr(Updater, "start_webhook", start_webhook)
|
monkeypatch.setattr(Updater, "start_webhook", start_webhook)
|
||||||
monkeypatch.setattr(Updater, "stop", stop)
|
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)
|
app_signature = inspect.signature(app.run_webhook)
|
||||||
|
|
||||||
for name, param in updater_signature.parameters.items():
|
for name, param in updater_signature.parameters.items():
|
||||||
|
@ -1939,8 +1954,8 @@ class TestApplication:
|
||||||
assert set(self.received.keys()) == set(expected.keys())
|
assert set(self.received.keys()) == set(expected.keys())
|
||||||
assert self.received == expected
|
assert self.received == expected
|
||||||
|
|
||||||
def test_run_without_updater(self, bot):
|
def test_run_without_updater(self, one_time_bot):
|
||||||
app = ApplicationBuilder().token(bot.token).updater(None).build()
|
app = ApplicationBuilder().bot(one_time_bot).updater(None).build()
|
||||||
|
|
||||||
with pytest.raises(RuntimeError, match="only available if the application has an Updater"):
|
with pytest.raises(RuntimeError, match="only available if the application has an Updater"):
|
||||||
app.run_webhook()
|
app.run_webhook()
|
||||||
|
@ -1950,7 +1965,7 @@ class TestApplication:
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", ["start", "initialize"])
|
@pytest.mark.parametrize("method", ["start", "initialize"])
|
||||||
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
|
@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 = []
|
shutdowns = []
|
||||||
|
|
||||||
async def raise_method(*args, **kwargs):
|
async def raise_method(*args, **kwargs):
|
||||||
|
@ -1971,7 +1986,7 @@ class TestApplication:
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
Updater, "shutdown", call_after(Updater.shutdown, after_shutdown("updater"))
|
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"):
|
with pytest.raises(RuntimeError, match="Test Exception"):
|
||||||
app.run_polling(close_loop=False)
|
app.run_polling(close_loop=False)
|
||||||
|
|
||||||
|
@ -1986,7 +2001,7 @@ class TestApplication:
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", ["start_polling", "start_webhook"])
|
@pytest.mark.parametrize("method", ["start_polling", "start_webhook"])
|
||||||
@pytest.mark.filterwarnings("ignore::telegram.warnings.PTBUserWarning")
|
@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 = []
|
shutdowns = []
|
||||||
|
|
||||||
async def raise_method(*args, **kwargs):
|
async def raise_method(*args, **kwargs):
|
||||||
|
@ -2007,7 +2022,7 @@ class TestApplication:
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
Updater, "shutdown", call_after(Updater.shutdown, after_shutdown("updater"))
|
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"):
|
with pytest.raises(RuntimeError, match="Test Exception"):
|
||||||
if "polling" in method:
|
if "polling" in method:
|
||||||
app.run_polling(close_loop=False)
|
app.run_polling(close_loop=False)
|
||||||
|
@ -2023,12 +2038,12 @@ class TestApplication:
|
||||||
reason="Only really relevant on windows",
|
reason="Only really relevant on windows",
|
||||||
)
|
)
|
||||||
@pytest.mark.parametrize("method", ["start_polling", "start_webhook"])
|
@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):
|
async def raise_method(*args, **kwargs):
|
||||||
raise RuntimeError("Prevent Actually Running")
|
raise RuntimeError("Prevent Actually Running")
|
||||||
|
|
||||||
monkeypatch.setattr(Application, "initialize", raise_method)
|
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"):
|
with pytest.raises(RuntimeError, match="Prevent Actually Running"):
|
||||||
if "polling" in method:
|
if "polling" in method:
|
||||||
|
@ -2054,7 +2069,7 @@ class TestApplication:
|
||||||
|
|
||||||
assert len(recwarn) == 0
|
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):
|
def test_signal_handlers(self, app, monkeypatch):
|
||||||
# this test should make sure that signal handlers are set by default on Linux + Mac,
|
# this test should make sure that signal handlers are set by default on Linux + Mac,
|
||||||
# and not on Windows.
|
# and not on Windows.
|
||||||
|
@ -2068,11 +2083,10 @@ class TestApplication:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
monkeypatch.setattr(loop, "add_signal_handler", signal_handler_test)
|
monkeypatch.setattr(loop, "add_signal_handler", signal_handler_test)
|
||||||
|
|
||||||
async def abort_app():
|
def abort_app():
|
||||||
await asyncio.sleep(2)
|
|
||||||
raise SystemExit
|
raise SystemExit
|
||||||
|
|
||||||
loop.create_task(abort_app())
|
loop.call_later(0.6, abort_app)
|
||||||
|
|
||||||
app.run_polling(close_loop=False)
|
app.run_polling(close_loop=False)
|
||||||
|
|
||||||
|
@ -2082,7 +2096,7 @@ class TestApplication:
|
||||||
assert received_signals == [signal.SIGINT, signal.SIGTERM, signal.SIGABRT]
|
assert received_signals == [signal.SIGINT, signal.SIGTERM, signal.SIGABRT]
|
||||||
|
|
||||||
received_signals.clear()
|
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)
|
app.run_webhook(port=49152, webhook_url="example.com", close_loop=False)
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
if platform.system() == "Windows":
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
import asyncio
|
import asyncio
|
||||||
import os
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
|
@ -38,10 +37,7 @@ from telegram.ext import (
|
||||||
from telegram.ext._applicationbuilder import _BOT_CHECKS
|
from telegram.ext._applicationbuilder import _BOT_CHECKS
|
||||||
from telegram.request import HTTPXRequest
|
from telegram.request import HTTPXRequest
|
||||||
|
|
||||||
from .auxil.object_conversions import env_var_2_bool
|
from .conftest import PRIVATE_KEY, TEST_WITH_OPT_DEPS, data_file
|
||||||
from .conftest import PRIVATE_KEY, data_file
|
|
||||||
|
|
||||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -35,20 +36,17 @@ from tests.conftest import data_file
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def audio_file():
|
def audio_file():
|
||||||
with open(data_file("telegram.mp3"), "rb") as f:
|
with data_file("telegram.mp3").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
async def audio(bot, chat_id):
|
async def audio(bot, chat_id):
|
||||||
with data_file("telegram.mp3").open("rb") as f:
|
with data_file("telegram.mp3").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb:
|
||||||
thumb = data_file("thumb.jpg")
|
return (await bot.send_audio(chat_id, audio=f, read_timeout=50, thumb=thumb)).audio
|
||||||
return (
|
|
||||||
await bot.send_audio(chat_id, audio=f, read_timeout=50, thumb=thumb.open("rb"))
|
|
||||||
).audio
|
|
||||||
|
|
||||||
|
|
||||||
class TestAudio:
|
class TestAudioBase:
|
||||||
caption = "Test *audio*"
|
caption = "Test *audio*"
|
||||||
performer = "Leandro Toledo"
|
performer = "Leandro Toledo"
|
||||||
title = "Teste"
|
title = "Teste"
|
||||||
|
@ -65,6 +63,8 @@ class TestAudio:
|
||||||
audio_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
audio_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||||
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
audio_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
|
|
||||||
|
|
||||||
|
class TestAudioWithoutRequest(TestAudioBase):
|
||||||
def test_slot_behaviour(self, audio, mro_slots):
|
def test_slot_behaviour(self, audio, mro_slots):
|
||||||
for attr in audio.__slots__:
|
for attr in audio.__slots__:
|
||||||
assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'"
|
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.width == self.thumb_width
|
||||||
assert audio.thumb.height == self.thumb_height
|
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):
|
def test_de_json(self, bot, audio):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"file_id": self.audio_file_id,
|
"file_id": self.audio_file_id,
|
||||||
|
@ -294,33 +124,6 @@ class TestAudio:
|
||||||
assert audio_dict["file_size"] == audio.file_size
|
assert audio_dict["file_size"] == audio.file_size
|
||||||
assert audio_dict["file_name"] == audio.file_name
|
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):
|
def test_equality(self, audio):
|
||||||
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
|
a = Audio(audio.file_id, audio.file_unique_id, audio.duration)
|
||||||
b = Audio("", audio.file_unique_id, audio.duration)
|
b = Audio("", audio.file_unique_id, audio.duration)
|
||||||
|
@ -340,3 +143,187 @@ class TestAudio:
|
||||||
|
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(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)
|
||||||
|
|
|
@ -43,7 +43,7 @@ from telegram.ext import (
|
||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
from telegram.warnings import PTBUserWarning
|
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):
|
class HandlerStates(int, enum.Enum):
|
||||||
|
@ -227,7 +227,11 @@ class PappInput(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
def build_papp(
|
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:
|
) -> Application:
|
||||||
store_data = PersistenceInput(**(store_data or {}))
|
store_data = PersistenceInput(**(store_data or {}))
|
||||||
if update_interval is not None:
|
if update_interval is not None:
|
||||||
|
@ -237,12 +241,15 @@ def build_papp(
|
||||||
else:
|
else:
|
||||||
persistence = TrackingPersistence(store_data=store_data, fill_data=fill_data)
|
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 (
|
return (
|
||||||
ApplicationBuilder()
|
ApplicationBuilder()
|
||||||
.token(token)
|
.bot(bot)
|
||||||
.persistence(persistence)
|
.persistence(persistence)
|
||||||
.application_class(DictApplication)
|
.application_class(DictApplication)
|
||||||
.arbitrary_callback_data(True)
|
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -252,7 +259,7 @@ def build_conversation_handler(name: str, persistent: bool = True) -> BaseHandle
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def papp(request, bot) -> Application:
|
def papp(request, bot_info) -> Application:
|
||||||
papp_input = request.param
|
papp_input = request.param
|
||||||
store_data = {}
|
store_data = {}
|
||||||
if papp_input.bot_data is not None:
|
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
|
store_data["callback_data"] = papp_input.callback_data
|
||||||
|
|
||||||
app = build_papp(
|
app = build_papp(
|
||||||
bot.token,
|
bot_info=bot_info,
|
||||||
store_data=store_data,
|
store_data=store_data,
|
||||||
update_interval=papp_input.update_interval,
|
update_interval=papp_input.update_interval,
|
||||||
fill_data=papp_input.fill_data,
|
fill_data=papp_input.fill_data,
|
||||||
|
@ -998,7 +1005,7 @@ class TestBasePersistence:
|
||||||
assert papp.persistence.dropped_chat_ids == {1: 1}
|
assert papp.persistence.dropped_chat_ids == {1: 1}
|
||||||
assert papp.persistence.updated_chat_ids == {2: 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):
|
class ErrorPersistence(TrackingPersistence):
|
||||||
def raise_error(self):
|
def raise_error(self):
|
||||||
raise Exception("PersistenceError")
|
raise Exception("PersistenceError")
|
||||||
|
@ -1032,8 +1039,7 @@ class TestBasePersistence:
|
||||||
|
|
||||||
app = (
|
app = (
|
||||||
ApplicationBuilder()
|
ApplicationBuilder()
|
||||||
.token(bot.token)
|
.bot(make_bot(bot_info, arbitrary_callback_data=True))
|
||||||
.arbitrary_callback_data(True)
|
|
||||||
.persistence(ErrorPersistence())
|
.persistence(ErrorPersistence())
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
|
|
3022
tests/test_bot.py
3022
tests/test_bot.py
File diff suppressed because it is too large
Load diff
|
@ -22,12 +22,12 @@ import pytest
|
||||||
from telegram import BotCommand, Dice
|
from telegram import BotCommand, Dice
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def bot_command():
|
def bot_command():
|
||||||
return BotCommand(command="start", description="A command")
|
return BotCommand(command="start", description="A command")
|
||||||
|
|
||||||
|
|
||||||
class TestBotCommand:
|
class TestBotCommandWithoutRequest:
|
||||||
command = "start"
|
command = "start"
|
||||||
description = "A command"
|
description = "A command"
|
||||||
|
|
||||||
|
|
|
@ -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):
|
def chat_id(request):
|
||||||
if request.param == "str":
|
if request.param == "str":
|
||||||
return "@supergroupusername"
|
return "@supergroupusername"
|
||||||
|
@ -57,7 +57,7 @@ def scope_type(request):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
scope="class",
|
scope="module",
|
||||||
params=[
|
params=[
|
||||||
BotCommandScopeDefault,
|
BotCommandScopeDefault,
|
||||||
BotCommandScopeAllPrivateChats,
|
BotCommandScopeAllPrivateChats,
|
||||||
|
@ -82,7 +82,7 @@ def scope_class(request):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
scope="class",
|
scope="module",
|
||||||
params=[
|
params=[
|
||||||
(BotCommandScopeDefault, BotCommandScope.DEFAULT),
|
(BotCommandScopeDefault, BotCommandScope.DEFAULT),
|
||||||
(BotCommandScopeAllPrivateChats, BotCommandScope.ALL_PRIVATE_CHATS),
|
(BotCommandScopeAllPrivateChats, BotCommandScope.ALL_PRIVATE_CHATS),
|
||||||
|
@ -106,7 +106,7 @@ def scope_class_and_type(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def bot_command_scope(scope_class_and_type, chat_id):
|
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
|
# 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(
|
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
|
# 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):
|
def test_slot_behaviour(self, bot_command_scope, mro_slots):
|
||||||
for attr in bot_command_scope.__slots__:
|
for attr in bot_command_scope.__slots__:
|
||||||
assert getattr(bot_command_scope, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(bot_command_scope, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
@ -28,7 +27,7 @@ from telegram import CallbackQuery, Chat, InlineKeyboardButton, InlineKeyboardMa
|
||||||
from telegram._utils.datetime import UTC
|
from telegram._utils.datetime import UTC
|
||||||
from telegram.ext import ExtBot
|
from telegram.ext import ExtBot
|
||||||
from telegram.ext._callbackdatacache import CallbackDataCache, InvalidCallbackData, _KeyboardData
|
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")
|
@pytest.fixture(scope="function")
|
||||||
|
@ -36,9 +35,6 @@ def callback_data_cache(bot):
|
||||||
return CallbackDataCache(bot)
|
return CallbackDataCache(bot)
|
||||||
|
|
||||||
|
|
||||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
TEST_WITH_OPT_DEPS,
|
TEST_WITH_OPT_DEPS,
|
||||||
reason="Only relevant if the optional dependency is not installed",
|
reason="Only relevant if the optional dependency is not installed",
|
||||||
|
|
|
@ -32,23 +32,23 @@ from tests.auxil.bot_method_checks import (
|
||||||
@pytest.fixture(scope="function", params=["message", "inline"])
|
@pytest.fixture(scope="function", params=["message", "inline"])
|
||||||
def callback_query(bot, request):
|
def callback_query(bot, request):
|
||||||
cbq = CallbackQuery(
|
cbq = CallbackQuery(
|
||||||
TestCallbackQuery.id_,
|
TestCallbackQueryBase.id_,
|
||||||
TestCallbackQuery.from_user,
|
TestCallbackQueryBase.from_user,
|
||||||
TestCallbackQuery.chat_instance,
|
TestCallbackQueryBase.chat_instance,
|
||||||
data=TestCallbackQuery.data,
|
data=TestCallbackQueryBase.data,
|
||||||
game_short_name=TestCallbackQuery.game_short_name,
|
game_short_name=TestCallbackQueryBase.game_short_name,
|
||||||
)
|
)
|
||||||
cbq.set_bot(bot)
|
cbq.set_bot(bot)
|
||||||
cbq._unfreeze()
|
cbq._unfreeze()
|
||||||
if request.param == "message":
|
if request.param == "message":
|
||||||
cbq.message = TestCallbackQuery.message
|
cbq.message = TestCallbackQueryBase.message
|
||||||
cbq.message.set_bot(bot)
|
cbq.message.set_bot(bot)
|
||||||
else:
|
else:
|
||||||
cbq.inline_message_id = TestCallbackQuery.inline_message_id
|
cbq.inline_message_id = TestCallbackQueryBase.inline_message_id
|
||||||
return cbq
|
return cbq
|
||||||
|
|
||||||
|
|
||||||
class TestCallbackQuery:
|
class TestCallbackQueryBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
from_user = User(1, "test_user", False)
|
from_user = User(1, "test_user", False)
|
||||||
chat_instance = "chat_instance"
|
chat_instance = "chat_instance"
|
||||||
|
@ -57,6 +57,8 @@ class TestCallbackQuery:
|
||||||
inline_message_id = "inline_message_id"
|
inline_message_id = "inline_message_id"
|
||||||
game_short_name = "the_game"
|
game_short_name = "the_game"
|
||||||
|
|
||||||
|
|
||||||
|
class TestCallbackQueryWithoutRequest(TestCallbackQueryBase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def skip_params(callback_query: CallbackQuery):
|
def skip_params(callback_query: CallbackQuery):
|
||||||
if callback_query.inline_message_id:
|
if callback_query.inline_message_id:
|
||||||
|
@ -121,6 +123,26 @@ class TestCallbackQuery:
|
||||||
assert callback_query_dict["data"] == callback_query.data
|
assert callback_query_dict["data"] == callback_query.data
|
||||||
assert callback_query_dict["game_short_name"] == callback_query.game_short_name
|
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 test_answer(self, monkeypatch, callback_query):
|
||||||
async def make_assertion(*_, **kwargs):
|
async def make_assertion(*_, **kwargs):
|
||||||
return kwargs["callback_query_id"] == callback_query.id
|
return kwargs["callback_query_id"] == callback_query.id
|
||||||
|
@ -447,23 +469,3 @@ class TestCallbackQuery:
|
||||||
|
|
||||||
monkeypatch.setattr(callback_query.get_bot(), "copy_message", make_assertion)
|
monkeypatch.setattr(callback_query.get_bot(), "copy_message", make_assertion)
|
||||||
assert await callback_query.copy_message(1)
|
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)
|
|
||||||
|
|
|
@ -29,37 +29,37 @@ from tests.auxil.bot_method_checks import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat(bot):
|
def chat(bot):
|
||||||
chat = Chat(
|
chat = Chat(
|
||||||
TestChat.id_,
|
TestChatBase.id_,
|
||||||
title=TestChat.title,
|
title=TestChatBase.title,
|
||||||
type=TestChat.type_,
|
type=TestChatBase.type_,
|
||||||
username=TestChat.username,
|
username=TestChatBase.username,
|
||||||
sticker_set_name=TestChat.sticker_set_name,
|
sticker_set_name=TestChatBase.sticker_set_name,
|
||||||
can_set_sticker_set=TestChat.can_set_sticker_set,
|
can_set_sticker_set=TestChatBase.can_set_sticker_set,
|
||||||
permissions=TestChat.permissions,
|
permissions=TestChatBase.permissions,
|
||||||
slow_mode_delay=TestChat.slow_mode_delay,
|
slow_mode_delay=TestChatBase.slow_mode_delay,
|
||||||
bio=TestChat.bio,
|
bio=TestChatBase.bio,
|
||||||
linked_chat_id=TestChat.linked_chat_id,
|
linked_chat_id=TestChatBase.linked_chat_id,
|
||||||
location=TestChat.location,
|
location=TestChatBase.location,
|
||||||
has_private_forwards=True,
|
has_private_forwards=True,
|
||||||
has_protected_content=True,
|
has_protected_content=True,
|
||||||
join_to_send_messages=True,
|
join_to_send_messages=True,
|
||||||
join_by_request=True,
|
join_by_request=True,
|
||||||
has_restricted_voice_and_video_messages=True,
|
has_restricted_voice_and_video_messages=True,
|
||||||
is_forum=True,
|
is_forum=True,
|
||||||
active_usernames=TestChat.active_usernames,
|
active_usernames=TestChatBase.active_usernames,
|
||||||
emoji_status_custom_emoji_id=TestChat.emoji_status_custom_emoji_id,
|
emoji_status_custom_emoji_id=TestChatBase.emoji_status_custom_emoji_id,
|
||||||
has_aggressive_anti_spam_enabled=TestChat.has_aggressive_anti_spam_enabled,
|
has_aggressive_anti_spam_enabled=TestChatBase.has_aggressive_anti_spam_enabled,
|
||||||
has_hidden_members=TestChat.has_hidden_members,
|
has_hidden_members=TestChatBase.has_hidden_members,
|
||||||
)
|
)
|
||||||
chat.set_bot(bot)
|
chat.set_bot(bot)
|
||||||
chat._unfreeze()
|
chat._unfreeze()
|
||||||
return chat
|
return chat
|
||||||
|
|
||||||
|
|
||||||
class TestChat:
|
class TestChatBase:
|
||||||
id_ = -28767330
|
id_ = -28767330
|
||||||
title = "ToledosPalaceBot - Group"
|
title = "ToledosPalaceBot - Group"
|
||||||
type_ = "group"
|
type_ = "group"
|
||||||
|
@ -87,6 +87,8 @@ class TestChat:
|
||||||
has_aggressive_anti_spam_enabled = True
|
has_aggressive_anti_spam_enabled = True
|
||||||
has_hidden_members = True
|
has_hidden_members = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatWithoutRequest(TestChatBase):
|
||||||
def test_slot_behaviour(self, chat, mro_slots):
|
def test_slot_behaviour(self, chat, mro_slots):
|
||||||
for attr in chat.__slots__:
|
for attr in chat.__slots__:
|
||||||
assert getattr(chat, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(chat, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -194,6 +196,26 @@ class TestChat:
|
||||||
chat = Chat(id=1, type="private")
|
chat = Chat(id=1, type="private")
|
||||||
assert chat.type is ChatType.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):
|
def test_link(self, chat):
|
||||||
assert chat.link == f"https://t.me/{chat.username}"
|
assert chat.link == f"https://t.me/{chat.username}"
|
||||||
chat.username = None
|
chat.username = None
|
||||||
|
@ -234,7 +256,6 @@ class TestChat:
|
||||||
|
|
||||||
monkeypatch.setattr(chat.get_bot(), "send_chat_action", make_assertion)
|
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)
|
||||||
assert await chat.send_action(action=ChatAction.TYPING)
|
|
||||||
|
|
||||||
async def test_leave(self, monkeypatch, chat):
|
async def test_leave(self, monkeypatch, chat):
|
||||||
async def make_assertion(*_, **kwargs):
|
async def make_assertion(*_, **kwargs):
|
||||||
|
@ -1251,23 +1272,3 @@ class TestChat:
|
||||||
):
|
):
|
||||||
chat = Chat(id=1, type="foo", username="user\u2022name")
|
chat = Chat(id=1, type="foo", username="user\u2022name")
|
||||||
chat.mention_markdown_v2()
|
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)
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ import pytest
|
||||||
from telegram import ChatAdministratorRights
|
from telegram import ChatAdministratorRights
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat_admin_rights():
|
def chat_admin_rights():
|
||||||
return ChatAdministratorRights(
|
return ChatAdministratorRights(
|
||||||
can_change_info=True,
|
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):
|
def test_slot_behaviour(self, chat_admin_rights, mro_slots):
|
||||||
inst = chat_admin_rights
|
inst = chat_admin_rights
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -24,27 +24,27 @@ from telegram import ChatInviteLink, User
|
||||||
from telegram._utils.datetime import to_timestamp
|
from telegram._utils.datetime import to_timestamp
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def creator():
|
def creator():
|
||||||
return User(1, "First name", False)
|
return User(1, "First name", False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def invite_link(creator):
|
def invite_link(creator):
|
||||||
return ChatInviteLink(
|
return ChatInviteLink(
|
||||||
TestChatInviteLink.link,
|
TestChatInviteLinkBase.link,
|
||||||
creator,
|
creator,
|
||||||
TestChatInviteLink.creates_join_request,
|
TestChatInviteLinkBase.creates_join_request,
|
||||||
TestChatInviteLink.primary,
|
TestChatInviteLinkBase.primary,
|
||||||
TestChatInviteLink.revoked,
|
TestChatInviteLinkBase.revoked,
|
||||||
expire_date=TestChatInviteLink.expire_date,
|
expire_date=TestChatInviteLinkBase.expire_date,
|
||||||
member_limit=TestChatInviteLink.member_limit,
|
member_limit=TestChatInviteLinkBase.member_limit,
|
||||||
name=TestChatInviteLink.name,
|
name=TestChatInviteLinkBase.name,
|
||||||
pending_join_request_count=TestChatInviteLink.pending_join_request_count,
|
pending_join_request_count=TestChatInviteLinkBase.pending_join_request_count,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestChatInviteLink:
|
class TestChatInviteLinkBase:
|
||||||
link = "thisialink"
|
link = "thisialink"
|
||||||
creates_join_request = False
|
creates_join_request = False
|
||||||
primary = True
|
primary = True
|
||||||
|
@ -54,6 +54,8 @@ class TestChatInviteLink:
|
||||||
name = "LinkName"
|
name = "LinkName"
|
||||||
pending_join_request_count = 42
|
pending_join_request_count = 42
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatInviteLinkWithoutRequest(TestChatInviteLinkBase):
|
||||||
def test_slot_behaviour(self, mro_slots, invite_link):
|
def test_slot_behaviour(self, mro_slots, invite_link):
|
||||||
for attr in invite_link.__slots__:
|
for attr in invite_link.__slots__:
|
||||||
assert getattr(invite_link, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(invite_link, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -29,26 +29,26 @@ from tests.auxil.bot_method_checks import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def time():
|
def time():
|
||||||
return datetime.datetime.now(tz=UTC)
|
return datetime.datetime.now(tz=UTC)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat_join_request(bot, time):
|
def chat_join_request(bot, time):
|
||||||
cjr = ChatJoinRequest(
|
cjr = ChatJoinRequest(
|
||||||
chat=TestChatJoinRequest.chat,
|
chat=TestChatJoinRequestBase.chat,
|
||||||
from_user=TestChatJoinRequest.from_user,
|
from_user=TestChatJoinRequestBase.from_user,
|
||||||
date=time,
|
date=time,
|
||||||
bio=TestChatJoinRequest.bio,
|
bio=TestChatJoinRequestBase.bio,
|
||||||
invite_link=TestChatJoinRequest.invite_link,
|
invite_link=TestChatJoinRequestBase.invite_link,
|
||||||
user_chat_id=TestChatJoinRequest.from_user.id,
|
user_chat_id=TestChatJoinRequestBase.from_user.id,
|
||||||
)
|
)
|
||||||
cjr.set_bot(bot)
|
cjr.set_bot(bot)
|
||||||
return cjr
|
return cjr
|
||||||
|
|
||||||
|
|
||||||
class TestChatJoinRequest:
|
class TestChatJoinRequestBase:
|
||||||
chat = Chat(1, Chat.SUPERGROUP)
|
chat = Chat(1, Chat.SUPERGROUP)
|
||||||
from_user = User(2, "first_name", False)
|
from_user = User(2, "first_name", False)
|
||||||
bio = "bio"
|
bio = "bio"
|
||||||
|
@ -61,6 +61,8 @@ class TestChatJoinRequest:
|
||||||
is_primary=False,
|
is_primary=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatJoinRequestWithoutRequest(TestChatJoinRequestBase):
|
||||||
def test_slot_behaviour(self, chat_join_request, mro_slots):
|
def test_slot_behaviour(self, chat_join_request, mro_slots):
|
||||||
inst = chat_join_request
|
inst = chat_join_request
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -22,15 +22,17 @@ import pytest
|
||||||
from telegram import ChatLocation, Location, User
|
from telegram import ChatLocation, Location, User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat_location(bot):
|
def chat_location():
|
||||||
return ChatLocation(TestChatLocation.location, TestChatLocation.address)
|
return ChatLocation(TestChatLocationBase.location, TestChatLocationBase.address)
|
||||||
|
|
||||||
|
|
||||||
class TestChatLocation:
|
class TestChatLocationBase:
|
||||||
location = Location(123, 456)
|
location = Location(123, 456)
|
||||||
address = "The Shire"
|
address = "The Shire"
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatLocationWithoutRequest(TestChatLocationBase):
|
||||||
def test_slot_behaviour(self, chat_location, mro_slots):
|
def test_slot_behaviour(self, chat_location, mro_slots):
|
||||||
inst = chat_location
|
inst = chat_location
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -187,7 +187,7 @@ def chat_member_type(request):
|
||||||
],
|
],
|
||||||
indirect=True,
|
indirect=True,
|
||||||
)
|
)
|
||||||
class TestChatMemberTypes:
|
class TestChatMemberTypesWithoutRequest:
|
||||||
def test_slot_behaviour(self, chat_member_type, mro_slots):
|
def test_slot_behaviour(self, chat_member_type, mro_slots):
|
||||||
inst = chat_member_type
|
inst = chat_member_type
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -34,26 +34,26 @@ from telegram import (
|
||||||
from telegram._utils.datetime import UTC, to_timestamp
|
from telegram._utils.datetime import UTC, to_timestamp
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def user():
|
def user():
|
||||||
return User(1, "First name", False)
|
return User(1, "First name", False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat():
|
def chat():
|
||||||
return Chat(1, Chat.SUPERGROUP, "Chat")
|
return Chat(1, Chat.SUPERGROUP, "Chat")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def old_chat_member(user):
|
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):
|
def new_chat_member(user):
|
||||||
return ChatMemberAdministrator(
|
return ChatMemberAdministrator(
|
||||||
user,
|
user,
|
||||||
TestChatMemberUpdated.new_status,
|
TestChatMemberUpdatedBase.new_status,
|
||||||
True,
|
True,
|
||||||
True,
|
True,
|
||||||
True,
|
True,
|
||||||
|
@ -66,25 +66,27 @@ def new_chat_member(user):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def time():
|
def time():
|
||||||
return datetime.datetime.now(tz=UTC)
|
return datetime.datetime.now(tz=UTC)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def invite_link(user):
|
def invite_link(user):
|
||||||
return ChatInviteLink("link", user, False, True, True)
|
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):
|
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)
|
return ChatMemberUpdated(chat, user, time, old_chat_member, new_chat_member, invite_link)
|
||||||
|
|
||||||
|
|
||||||
class TestChatMemberUpdated:
|
class TestChatMemberUpdatedBase:
|
||||||
old_status = ChatMember.MEMBER
|
old_status = ChatMember.MEMBER
|
||||||
new_status = ChatMember.ADMINISTRATOR
|
new_status = ChatMember.ADMINISTRATOR
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatMemberUpdatedWithoutRequest(TestChatMemberUpdatedBase):
|
||||||
def test_slot_behaviour(self, mro_slots, chat_member_updated):
|
def test_slot_behaviour(self, mro_slots, chat_member_updated):
|
||||||
action = chat_member_updated
|
action = chat_member_updated
|
||||||
for attr in action.__slots__:
|
for attr in action.__slots__:
|
||||||
|
|
|
@ -22,7 +22,7 @@ import pytest
|
||||||
from telegram import ChatPermissions, User
|
from telegram import ChatPermissions, User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chat_permissions():
|
def chat_permissions():
|
||||||
return ChatPermissions(
|
return ChatPermissions(
|
||||||
can_send_messages=True,
|
can_send_messages=True,
|
||||||
|
@ -43,7 +43,7 @@ def chat_permissions():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestChatPermissions:
|
class TestChatPermissionsBase:
|
||||||
can_send_messages = True
|
can_send_messages = True
|
||||||
can_send_media_messages = True
|
can_send_media_messages = True
|
||||||
can_send_polls = True
|
can_send_polls = True
|
||||||
|
@ -60,6 +60,8 @@ class TestChatPermissions:
|
||||||
can_send_video_notes = False
|
can_send_video_notes = False
|
||||||
can_send_voice_notes = None
|
can_send_voice_notes = None
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatPermissionsWithoutRequest(TestChatPermissionsBase):
|
||||||
def test_slot_behaviour(self, chat_permissions, mro_slots):
|
def test_slot_behaviour(self, chat_permissions, mro_slots):
|
||||||
inst = chat_permissions
|
inst = chat_permissions
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -35,12 +36,11 @@ from tests.conftest import data_file, expect_bad_request
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def chatphoto_file():
|
def chatphoto_file():
|
||||||
f = data_file("telegram.jpg").open("rb")
|
with data_file("telegram.jpg").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="module")
|
||||||
async def chat_photo(bot, super_group_id):
|
async def chat_photo(bot, super_group_id):
|
||||||
async def func():
|
async def func():
|
||||||
return (await bot.get_chat(super_group_id, read_timeout=50)).photo
|
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_small_file_id = "smallCgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||||
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
|
chatphoto_big_file_id = "bigCgADAQADngIAAuyVeEez0xRovKi9VAI"
|
||||||
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
|
chatphoto_small_file_unique_id = "smalladc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
chatphoto_big_file_unique_id = "bigadc3145fd2e84d95b64d68eaa22aa33e"
|
chatphoto_big_file_unique_id = "bigadc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
|
chatphoto_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.jpg"
|
||||||
|
|
||||||
|
|
||||||
|
class TestChatPhotoWithoutRequest(TestChatPhotoBase):
|
||||||
def test_slot_behaviour(self, chat_photo, mro_slots):
|
def test_slot_behaviour(self, chat_photo, mro_slots):
|
||||||
for attr in chat_photo.__slots__:
|
for attr in chat_photo.__slots__:
|
||||||
assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'"
|
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"
|
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):
|
def test_de_json(self, bot, chat_photo):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"small_file_id": self.chatphoto_small_file_id,
|
"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["small_file_unique_id"] == chat_photo.small_file_unique_id
|
||||||
assert chat_photo_dict["big_file_unique_id"] == chat_photo.big_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):
|
def test_equality(self):
|
||||||
a = ChatPhoto(
|
a = ChatPhoto(
|
||||||
self.chatphoto_small_file_id,
|
self.chatphoto_small_file_id,
|
||||||
|
@ -199,3 +118,80 @@ class TestChatPhoto:
|
||||||
|
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(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)
|
||||||
|
|
|
@ -22,22 +22,26 @@ import pytest
|
||||||
from telegram import ChosenInlineResult, Location, User, Voice
|
from telegram import ChosenInlineResult, Location, User, Voice
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def user():
|
def user():
|
||||||
user = User(1, "First name", False)
|
user = User(1, "First name", False)
|
||||||
user._unfreeze()
|
user._unfreeze()
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def chosen_inline_result(user):
|
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"
|
result_id = "result id"
|
||||||
query = "query text"
|
query = "query text"
|
||||||
|
|
||||||
|
|
||||||
|
class TestChosenInlineResultWithoutRequest(TestChosenInlineResultBase):
|
||||||
def test_slot_behaviour(self, chosen_inline_result, mro_slots):
|
def test_slot_behaviour(self, chosen_inline_result, mro_slots):
|
||||||
inst = chosen_inline_result
|
inst = chosen_inline_result
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -16,10 +16,9 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from telegram import constants
|
from telegram import constants
|
||||||
from telegram._utils.enum import IntEnum, StringEnum
|
from telegram._utils.enum import IntEnum, StringEnum
|
||||||
from telegram.error import BadRequest
|
from telegram.error import BadRequest
|
||||||
|
@ -36,7 +35,7 @@ class IntEnumTest(IntEnum):
|
||||||
BAR = 2
|
BAR = 2
|
||||||
|
|
||||||
|
|
||||||
class TestConstants:
|
class TestConstantsWithoutRequest:
|
||||||
"""Also test _utils.enum.StringEnum on the fly because tg.constants is currently the only
|
"""Also test _utils.enum.StringEnum on the fly because tg.constants is currently the only
|
||||||
place where that class is used."""
|
place where that class is used."""
|
||||||
|
|
||||||
|
@ -110,30 +109,6 @@ class TestConstants:
|
||||||
|
|
||||||
assert hash(IntEnumTest.FOO) == hash(1)
|
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):
|
def test_bot_api_version_and_info(self):
|
||||||
assert constants.BOT_API_VERSION == str(constants.BOT_API_VERSION_INFO)
|
assert constants.BOT_API_VERSION == str(constants.BOT_API_VERSION_INFO)
|
||||||
assert constants.BOT_API_VERSION_INFO == tuple(
|
assert constants.BOT_API_VERSION_INFO == tuple(
|
||||||
|
@ -151,3 +126,29 @@ class TestConstants:
|
||||||
assert vi < (vi[0] + 1, vi[1] + 1)
|
assert vi < (vi[0] + 1, vi[1] + 1)
|
||||||
assert vi[0] == vi.major
|
assert vi[0] == vi.major
|
||||||
assert vi[1] == vi.minor
|
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)
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import Contact, Voice
|
from telegram import Contact, Voice
|
||||||
|
@ -24,22 +26,24 @@ from telegram.error import BadRequest
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def contact():
|
def contact():
|
||||||
return Contact(
|
return Contact(
|
||||||
TestContact.phone_number,
|
TestContactBase.phone_number,
|
||||||
TestContact.first_name,
|
TestContactBase.first_name,
|
||||||
TestContact.last_name,
|
TestContactBase.last_name,
|
||||||
TestContact.user_id,
|
TestContactBase.user_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestContact:
|
class TestContactBase:
|
||||||
phone_number = "+11234567890"
|
phone_number = "+11234567890"
|
||||||
first_name = "Leandro"
|
first_name = "Leandro"
|
||||||
last_name = "Toledo"
|
last_name = "Toledo"
|
||||||
user_id = 23
|
user_id = 23
|
||||||
|
|
||||||
|
|
||||||
|
class TestContactWithoutRequest(TestContactBase):
|
||||||
def test_slot_behaviour(self, contact, mro_slots):
|
def test_slot_behaviour(self, contact, mro_slots):
|
||||||
for attr in contact.__slots__:
|
for attr in contact.__slots__:
|
||||||
assert getattr(contact, attr, "err") != "err", f"got extra slot '{attr}'"
|
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.last_name == self.last_name
|
||||||
assert contact.user_id == self.user_id
|
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 test_send_with_contact(self, monkeypatch, bot, chat_id, contact):
|
||||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
||||||
data = request_data.json_parameters
|
data = request_data.json_parameters
|
||||||
|
@ -77,10 +123,10 @@ class TestContact:
|
||||||
return phone and first and last
|
return phone and first and last
|
||||||
|
|
||||||
monkeypatch.setattr(bot.request, "post", make_assertion)
|
monkeypatch.setattr(bot.request, "post", make_assertion)
|
||||||
message = await bot.send_contact(contact=contact, chat_id=chat_id)
|
assert await bot.send_contact(contact=contact, chat_id=chat_id)
|
||||||
assert message
|
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
|
class TestContactWithRequest(TestContactBase):
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"default_bot,custom",
|
"default_bot,custom",
|
||||||
[
|
[
|
||||||
|
@ -114,54 +160,12 @@ class TestContact:
|
||||||
chat_id, contact=contact, reply_to_message_id=reply_to_message.message_id
|
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)
|
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||||
async def test_send_contact_default_protect_content(self, chat_id, default_bot, contact):
|
async def test_send_contact_default_protect_content(self, chat_id, default_bot, contact):
|
||||||
protected = await default_bot.send_contact(chat_id, contact=contact)
|
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 protected.has_protected_content
|
||||||
unprotected = await default_bot.send_contact(
|
|
||||||
chat_id, contact=contact, protect_content=False
|
|
||||||
)
|
|
||||||
assert not unprotected.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)
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ from warnings import filterwarnings
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import (
|
from telegram import (
|
||||||
Bot,
|
|
||||||
CallbackQuery,
|
CallbackQuery,
|
||||||
Chat,
|
Chat,
|
||||||
ChosenInlineResult,
|
ChosenInlineResult,
|
||||||
|
@ -45,7 +44,6 @@ from telegram.ext import (
|
||||||
CommandHandler,
|
CommandHandler,
|
||||||
ConversationHandler,
|
ConversationHandler,
|
||||||
Defaults,
|
Defaults,
|
||||||
ExtBot,
|
|
||||||
InlineQueryHandler,
|
InlineQueryHandler,
|
||||||
JobQueue,
|
JobQueue,
|
||||||
MessageHandler,
|
MessageHandler,
|
||||||
|
@ -59,7 +57,7 @@ from telegram.ext import (
|
||||||
filters,
|
filters,
|
||||||
)
|
)
|
||||||
from telegram.warnings import PTBUserWarning
|
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")
|
@pytest.fixture(scope="class")
|
||||||
|
@ -1255,7 +1253,7 @@ class TestConversationHandler:
|
||||||
await app.process_update(Update(update_id=2, message=brew_message))
|
await app.process_update(Update(update_id=2, message=brew_message))
|
||||||
assert handler.check_update(Update(0, message=pour_coffee_message))
|
assert handler.check_update(Update(0, message=pour_coffee_message))
|
||||||
# assert handler.conversations.get((self.group.id, user1.id)) == self.BREWING
|
# 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.check_update(Update(0, message=start_message))
|
||||||
# assert handler.conversations.get((self.group.id, user1.id)) is None
|
# 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("handler_block", [True, False, None])
|
||||||
@pytest.mark.parametrize("ext_bot", [True, False], ids=["ExtBot", "Bot"])
|
@pytest.mark.parametrize("ext_bot", [True, False], ids=["ExtBot", "Bot"])
|
||||||
async def test_blocking_resolution_order(
|
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()
|
event = asyncio.Event()
|
||||||
|
|
||||||
|
@ -2149,7 +2147,7 @@ class TestConversationHandler:
|
||||||
fallbacks=[fallback],
|
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 = ApplicationBuilder().bot(bot).build()
|
||||||
app.add_handler(conv_handler)
|
app.add_handler(conv_handler)
|
||||||
|
|
||||||
|
@ -2160,7 +2158,7 @@ class TestConversationHandler:
|
||||||
fallback_message.set_bot(bot)
|
fallback_message.set_bot(bot)
|
||||||
|
|
||||||
# This loop makes sure that we test all of entry points, states handler & fallbacks
|
# 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(
|
process_update_task = asyncio.create_task(
|
||||||
app.process_update(Update(0, message=message))
|
app.process_update(Update(0, message=message))
|
||||||
)
|
)
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
import datetime as dtm
|
import datetime as dtm
|
||||||
import os
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -26,18 +25,22 @@ from telegram._utils import datetime as tg_dtm
|
||||||
from telegram.ext import Defaults
|
from telegram.ext import Defaults
|
||||||
|
|
||||||
# sample time specification values categorised into absolute / delta / time-of-day
|
# 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 = [
|
# We do not parametrize tests with these variables, since there's a tiny chance that there is an
|
||||||
dtm.datetime.now(tz=dtm.timezone(dtm.timedelta(hours=-7))).replace(second=0, microsecond=0),
|
# error while collecting the tests (happens when time goes from HH:59:00 -> HH+1:00:00) when we
|
||||||
dtm.datetime.utcnow().replace(second=0, microsecond=0),
|
# run the test suite with multiple workers
|
||||||
]
|
|
||||||
DELTA_TIME_SPECS = [dtm.timedelta(hours=3, seconds=42, milliseconds=2), 30, 7.5]
|
DELTA_TIME_SPECS = [dtm.timedelta(hours=3, seconds=42, milliseconds=2), 30, 7.5]
|
||||||
TIME_OF_DAY_TIME_SPECS = [
|
TIME_OF_DAY_TIME_SPECS = [
|
||||||
dtm.time(12, 42, tzinfo=dtm.timezone(dtm.timedelta(hours=-7))),
|
dtm.time(12, 42, tzinfo=dtm.timezone(dtm.timedelta(hours=-7))),
|
||||||
dtm.time(12, 42),
|
dtm.time(12, 42),
|
||||||
]
|
]
|
||||||
RELATIVE_TIME_SPECS = DELTA_TIME_SPECS + TIME_OF_DAY_TIME_SPECS
|
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
|
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.
|
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:
|
class TestDatetime:
|
||||||
|
@ -97,12 +99,15 @@ class TestDatetime:
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
tg_dtm.to_float_timestamp(dtm.datetime(2019, 11, 11), reference_timestamp=123)
|
tg_dtm.to_float_timestamp(dtm.datetime(2019, 11, 11), reference_timestamp=123)
|
||||||
|
|
||||||
@pytest.mark.parametrize("time_spec", DELTA_TIME_SPECS, ids=str)
|
# see note on parametrization at the top of this file
|
||||||
def test_to_float_timestamp_delta(self, time_spec):
|
def test_to_float_timestamp_delta(self):
|
||||||
"""Conversion from a 'delta' time specification to timestamp"""
|
"""Conversion from a 'delta' time specification to timestamp"""
|
||||||
reference_t = 0
|
reference_t = 0
|
||||||
delta = time_spec.total_seconds() if hasattr(time_spec, "total_seconds") else time_spec
|
for i in DELTA_TIME_SPECS:
|
||||||
assert tg_dtm.to_float_timestamp(time_spec, reference_t) == reference_t + delta
|
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):
|
def test_to_float_timestamp_time_of_day(self):
|
||||||
"""Conversion from time-of-day specification to timestamp"""
|
"""Conversion from time-of-day specification to timestamp"""
|
||||||
|
@ -130,22 +135,24 @@ class TestDatetime:
|
||||||
ref_t + (-utc_offset.total_seconds() % (24 * 60 * 60))
|
ref_t + (-utc_offset.total_seconds() % (24 * 60 * 60))
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize("time_spec", RELATIVE_TIME_SPECS, ids=str)
|
# see note on parametrization at the top of this file
|
||||||
def test_to_float_timestamp_default_reference(self, time_spec):
|
def test_to_float_timestamp_default_reference(self):
|
||||||
"""The reference timestamp for relative time specifications should default to now"""
|
"""The reference timestamp for relative time specifications should default to now"""
|
||||||
|
for i in RELATIVE_TIME_SPECS:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
assert tg_dtm.to_float_timestamp(time_spec) == pytest.approx(
|
assert tg_dtm.to_float_timestamp(i) == pytest.approx(
|
||||||
tg_dtm.to_float_timestamp(time_spec, reference_timestamp=now)
|
tg_dtm.to_float_timestamp(i, reference_timestamp=now)
|
||||||
)
|
), f"Failed for {i}"
|
||||||
|
|
||||||
def test_to_float_timestamp_error(self):
|
def test_to_float_timestamp_error(self):
|
||||||
with pytest.raises(TypeError, match="Defaults"):
|
with pytest.raises(TypeError, match="Defaults"):
|
||||||
tg_dtm.to_float_timestamp(Defaults())
|
tg_dtm.to_float_timestamp(Defaults())
|
||||||
|
|
||||||
@pytest.mark.parametrize("time_spec", TIME_SPECS, ids=str)
|
# see note on parametrization at the top of this file
|
||||||
def test_to_timestamp(self, time_spec):
|
def test_to_timestamp(self):
|
||||||
# delegate tests to `to_float_timestamp`
|
# 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):
|
def test_to_timestamp_none(self):
|
||||||
# this 'convenience' behaviour has been left left for backwards compatibility
|
# this 'convenience' behaviour has been left left for backwards compatibility
|
||||||
|
|
|
@ -19,15 +19,12 @@
|
||||||
|
|
||||||
import datetime as dtm
|
import datetime as dtm
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import User
|
from telegram import User
|
||||||
from telegram.ext import Defaults
|
from telegram.ext import Defaults
|
||||||
from tests.auxil.object_conversions import env_var_2_bool
|
from tests.conftest import TEST_WITH_OPT_DEPS
|
||||||
|
|
||||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
|
|
||||||
|
|
||||||
|
|
||||||
class TestDefault:
|
class TestDefault:
|
||||||
|
|
|
@ -22,14 +22,16 @@ import pytest
|
||||||
from telegram import BotCommand, Dice
|
from telegram import BotCommand, Dice
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class", params=Dice.ALL_EMOJI)
|
@pytest.fixture(scope="module", params=Dice.ALL_EMOJI)
|
||||||
def dice(request):
|
def dice(request):
|
||||||
return Dice(value=5, emoji=request.param)
|
return Dice(value=5, emoji=request.param)
|
||||||
|
|
||||||
|
|
||||||
class TestDice:
|
class TestDiceBase:
|
||||||
value = 4
|
value = 4
|
||||||
|
|
||||||
|
|
||||||
|
class TestDiceWithoutRequest(TestDiceBase):
|
||||||
def test_slot_behaviour(self, dice, mro_slots):
|
def test_slot_behaviour(self, dice, mro_slots):
|
||||||
for attr in dice.__slots__:
|
for attr in dice.__slots__:
|
||||||
assert getattr(dice, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(dice, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
@ -35,18 +36,17 @@ from tests.conftest import data_file
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def document_file():
|
def document_file():
|
||||||
f = data_file("telegram.png").open("rb")
|
with data_file("telegram.png").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
async def document(bot, chat_id):
|
async def document(bot, chat_id):
|
||||||
with data_file("telegram.png").open("rb") as f:
|
with data_file("telegram.png").open("rb") as f:
|
||||||
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
|
return (await bot.send_document(chat_id, document=f, read_timeout=50)).document
|
||||||
|
|
||||||
|
|
||||||
class TestDocument:
|
class TestDocumentBase:
|
||||||
caption = "DocumentTest - *Caption*"
|
caption = "DocumentTest - *Caption*"
|
||||||
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
|
document_file_url = "https://python-telegram-bot.org/static/testfiles/telegram.gif"
|
||||||
file_size = 12948
|
file_size = 12948
|
||||||
|
@ -58,6 +58,8 @@ class TestDocument:
|
||||||
document_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
document_file_id = "5a3128a4d2a04750b5b58397f3b5e812"
|
||||||
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
document_file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
|
|
||||||
|
|
||||||
|
class TestDocumentWithoutRequest(TestDocumentBase):
|
||||||
def test_slot_behaviour(self, document, mro_slots):
|
def test_slot_behaviour(self, document, mro_slots):
|
||||||
for attr in document.__slots__:
|
for attr in document.__slots__:
|
||||||
assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'"
|
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.width == self.thumb_width
|
||||||
assert document.thumb.height == self.thumb_height
|
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):
|
async def test_send_all_args(self, bot, chat_id, document_file, document, thumb_file):
|
||||||
message = await bot.send_document(
|
message = await bot.send_document(
|
||||||
chat_id,
|
chat_id,
|
||||||
|
@ -105,24 +241,6 @@ class TestDocument:
|
||||||
assert message.document.thumb.height == self.thumb_height
|
assert message.document.thumb.height == self.thumb_height
|
||||||
assert message.has_protected_content
|
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):
|
async def test_send_url_gif_file(self, bot, chat_id):
|
||||||
message = await bot.send_document(chat_id, self.document_file_url)
|
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.mime_type == "image/gif"
|
||||||
assert document.file_size == 3878
|
assert document.file_size == 3878
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||||
async def test_send_resend(self, bot, chat_id, document):
|
async def test_send_document_default_protect_content(self, chat_id, default_bot, document):
|
||||||
message = await bot.send_document(chat_id=chat_id, document=document.file_id)
|
tasks = asyncio.gather(
|
||||||
|
default_bot.send_document(chat_id, document),
|
||||||
assert message.document == document
|
default_bot.send_document(chat_id, document, protect_content=False),
|
||||||
|
|
||||||
@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
|
protected, unprotected = await tasks
|
||||||
|
assert protected.has_protected_content
|
||||||
|
assert not unprotected.has_protected_content
|
||||||
|
|
||||||
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.flaky(3, 1)
|
|
||||||
async def test_send_document_caption_entities(self, bot, chat_id, document):
|
async def test_send_document_caption_entities(self, bot, chat_id, document):
|
||||||
test_string = "Italic Bold Code"
|
test_string = "Italic Bold Code"
|
||||||
entities = [
|
entities = [
|
||||||
|
@ -180,7 +280,6 @@ class TestDocument:
|
||||||
assert message.caption == test_string
|
assert message.caption == test_string
|
||||||
assert message.caption_entities == tuple(entities)
|
assert message.caption_entities == tuple(entities)
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
@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):
|
async def test_send_document_default_parse_mode_1(self, default_bot, chat_id, document):
|
||||||
test_string = "Italic Bold Code"
|
test_string = "Italic Bold Code"
|
||||||
|
@ -190,7 +289,6 @@ class TestDocument:
|
||||||
assert message.caption_markdown == test_markdown_string
|
assert message.caption_markdown == test_markdown_string
|
||||||
assert message.caption == test_string
|
assert message.caption == test_string
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
@pytest.mark.parametrize("default_bot", [{"parse_mode": "Markdown"}], indirect=True)
|
@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):
|
async def test_send_document_default_parse_mode_2(self, default_bot, chat_id, document):
|
||||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||||
|
@ -201,7 +299,6 @@ class TestDocument:
|
||||||
assert message.caption == test_markdown_string
|
assert message.caption == test_markdown_string
|
||||||
assert message.caption_markdown == escape_markdown(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)
|
@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):
|
async def test_send_document_default_parse_mode_3(self, default_bot, chat_id, document):
|
||||||
test_markdown_string = "_Italic_ *Bold* `Code`"
|
test_markdown_string = "_Italic_ *Bold* `Code`"
|
||||||
|
@ -212,7 +309,6 @@ class TestDocument:
|
||||||
assert message.caption == test_markdown_string
|
assert message.caption == test_markdown_string
|
||||||
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
assert message.caption_markdown == escape_markdown(test_markdown_string)
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"default_bot,custom",
|
"default_bot,custom",
|
||||||
[
|
[
|
||||||
|
@ -245,106 +341,3 @@ class TestDocument:
|
||||||
await default_bot.send_document(
|
await default_bot.send_document(
|
||||||
chat_id, document, reply_to_message_id=reply_to_message.message_id
|
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)
|
|
||||||
|
|
|
@ -22,20 +22,22 @@ import pytest
|
||||||
from telegram import EncryptedCredentials, PassportElementError
|
from telegram import EncryptedCredentials, PassportElementError
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def encrypted_credentials():
|
def encrypted_credentials():
|
||||||
return EncryptedCredentials(
|
return EncryptedCredentials(
|
||||||
TestEncryptedCredentials.data,
|
TestEncryptedCredentialsBase.data,
|
||||||
TestEncryptedCredentials.hash,
|
TestEncryptedCredentialsBase.hash,
|
||||||
TestEncryptedCredentials.secret,
|
TestEncryptedCredentialsBase.secret,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestEncryptedCredentials:
|
class TestEncryptedCredentialsBase:
|
||||||
data = "data"
|
data = "data"
|
||||||
hash = "hash"
|
hash = "hash"
|
||||||
secret = "secret"
|
secret = "secret"
|
||||||
|
|
||||||
|
|
||||||
|
class TestEncryptedCredentialsWithoutRequest(TestEncryptedCredentialsBase):
|
||||||
def test_slot_behaviour(self, encrypted_credentials, mro_slots):
|
def test_slot_behaviour(self, encrypted_credentials, mro_slots):
|
||||||
inst = encrypted_credentials
|
inst = encrypted_credentials
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -22,22 +22,22 @@ import pytest
|
||||||
from telegram import EncryptedPassportElement, PassportElementError, PassportFile
|
from telegram import EncryptedPassportElement, PassportElementError, PassportFile
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def encrypted_passport_element():
|
def encrypted_passport_element():
|
||||||
return EncryptedPassportElement(
|
return EncryptedPassportElement(
|
||||||
TestEncryptedPassportElement.type_,
|
TestEncryptedPassportElementBase.type_,
|
||||||
"this is a hash",
|
"this is a hash",
|
||||||
data=TestEncryptedPassportElement.data,
|
data=TestEncryptedPassportElementBase.data,
|
||||||
phone_number=TestEncryptedPassportElement.phone_number,
|
phone_number=TestEncryptedPassportElementBase.phone_number,
|
||||||
email=TestEncryptedPassportElement.email,
|
email=TestEncryptedPassportElementBase.email,
|
||||||
files=TestEncryptedPassportElement.files,
|
files=TestEncryptedPassportElementBase.files,
|
||||||
front_side=TestEncryptedPassportElement.front_side,
|
front_side=TestEncryptedPassportElementBase.front_side,
|
||||||
reverse_side=TestEncryptedPassportElement.reverse_side,
|
reverse_side=TestEncryptedPassportElementBase.reverse_side,
|
||||||
selfie=TestEncryptedPassportElement.selfie,
|
selfie=TestEncryptedPassportElementBase.selfie,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestEncryptedPassportElement:
|
class TestEncryptedPassportElementBase:
|
||||||
type_ = "type"
|
type_ = "type"
|
||||||
hash = "this is a hash"
|
hash = "this is a hash"
|
||||||
data = "data"
|
data = "data"
|
||||||
|
@ -48,6 +48,8 @@ class TestEncryptedPassportElement:
|
||||||
reverse_side = PassportFile("file_id", 50, 0, 25)
|
reverse_side = PassportFile("file_id", 50, 0, 25)
|
||||||
selfie = 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):
|
def test_slot_behaviour(self, encrypted_passport_element, mro_slots):
|
||||||
inst = encrypted_passport_element
|
inst = encrypted_passport_element
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -27,20 +27,20 @@ from telegram.error import TelegramError
|
||||||
from tests.conftest import data_file
|
from tests.conftest import data_file
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def file(bot):
|
def file(bot):
|
||||||
file = File(
|
file = File(
|
||||||
TestFile.file_id,
|
TestFileBase.file_id,
|
||||||
TestFile.file_unique_id,
|
TestFileBase.file_unique_id,
|
||||||
file_path=TestFile.file_path,
|
file_path=TestFileBase.file_path,
|
||||||
file_size=TestFile.file_size,
|
file_size=TestFileBase.file_size,
|
||||||
)
|
)
|
||||||
file.set_bot(bot)
|
file.set_bot(bot)
|
||||||
file._unfreeze()
|
file._unfreeze()
|
||||||
return file
|
return file
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def encrypted_file(bot):
|
def encrypted_file(bot):
|
||||||
# check https://github.com/python-telegram-bot/python-telegram-bot/wiki/\
|
# check https://github.com/python-telegram-bot/python-telegram-bot/wiki/\
|
||||||
# PTB-test-writing-knowledge-base#how-to-generate-encrypted-passport-files
|
# PTB-test-writing-knowledge-base#how-to-generate-encrypted-passport-files
|
||||||
|
@ -49,13 +49,18 @@ def encrypted_file(bot):
|
||||||
"Oq3G4sX+bKZthoyms1YlPqvWou9esb+z0Bi/KqQUG8s=",
|
"Oq3G4sX+bKZthoyms1YlPqvWou9esb+z0Bi/KqQUG8s=",
|
||||||
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
|
"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_bot(bot)
|
||||||
ef.set_credentials(fc)
|
ef.set_credentials(fc)
|
||||||
return ef
|
return ef
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def encrypted_local_file(bot):
|
def encrypted_local_file(bot):
|
||||||
# check encrypted_file() for the source of the fc values
|
# check encrypted_file() for the source of the fc values
|
||||||
fc = FileCredentials(
|
fc = FileCredentials(
|
||||||
|
@ -63,9 +68,9 @@ def encrypted_local_file(bot):
|
||||||
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
|
"Pt7fKPgYWKA/7a8E64Ea1X8C+Wf7Ky1tF4ANBl63vl4=",
|
||||||
)
|
)
|
||||||
ef = File(
|
ef = File(
|
||||||
TestFile.file_id,
|
TestFileBase.file_id,
|
||||||
TestFile.file_unique_id,
|
TestFileBase.file_unique_id,
|
||||||
TestFile.file_size,
|
TestFileBase.file_size,
|
||||||
file_path=str(data_file("image_encrypted.jpg")),
|
file_path=str(data_file("image_encrypted.jpg")),
|
||||||
)
|
)
|
||||||
ef.set_bot(bot)
|
ef.set_bot(bot)
|
||||||
|
@ -73,19 +78,19 @@ def encrypted_local_file(bot):
|
||||||
return ef
|
return ef
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def local_file(bot):
|
def local_file(bot):
|
||||||
file = File(
|
file = File(
|
||||||
TestFile.file_id,
|
TestFileBase.file_id,
|
||||||
TestFile.file_unique_id,
|
TestFileBase.file_unique_id,
|
||||||
file_path=str(data_file("local_file.txt")),
|
file_path=str(data_file("local_file.txt")),
|
||||||
file_size=TestFile.file_size,
|
file_size=TestFileBase.file_size,
|
||||||
)
|
)
|
||||||
file.set_bot(bot)
|
file.set_bot(bot)
|
||||||
return file
|
return file
|
||||||
|
|
||||||
|
|
||||||
class TestFile:
|
class TestFileBase:
|
||||||
file_id = "NOTVALIDDOESNOTMATTER"
|
file_id = "NOTVALIDDOESNOTMATTER"
|
||||||
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
file_path = (
|
file_path = (
|
||||||
|
@ -94,6 +99,8 @@ class TestFile:
|
||||||
file_size = 28232
|
file_size = 28232
|
||||||
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
|
file_content = "Saint-Saëns".encode() # Intentionally contains unicode chars.
|
||||||
|
|
||||||
|
|
||||||
|
class TestFileWithoutRequest(TestFileBase):
|
||||||
def test_slot_behaviour(self, file, mro_slots):
|
def test_slot_behaviour(self, file, mro_slots):
|
||||||
for attr in file.__slots__:
|
for attr in file.__slots__:
|
||||||
assert getattr(file, attr, "err") != "err", f"got extra slot '{attr}'"
|
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_path"] == file.file_path
|
||||||
assert file_dict["file_size"] == file.file_size
|
assert file_dict["file_size"] == file.file_size
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
def test_equality(self, bot):
|
||||||
async def test_error_get_empty_file_id(self, bot):
|
a = File(self.file_id, self.file_unique_id, bot)
|
||||||
with pytest.raises(TelegramError):
|
b = File("", self.file_unique_id, bot)
|
||||||
await bot.get_file(file_id="")
|
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_download(self, monkeypatch, file):
|
||||||
async def test(*args, **kwargs):
|
async def test(*args, **kwargs):
|
||||||
|
@ -140,9 +162,6 @@ class TestFile:
|
||||||
finally:
|
finally:
|
||||||
out_file.unlink()
|
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(
|
@pytest.mark.parametrize(
|
||||||
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
"custom_path_type", [str, Path], ids=["str custom_path", "pathlib.Path custom_path"]
|
||||||
)
|
)
|
||||||
|
@ -161,20 +180,6 @@ class TestFile:
|
||||||
os.close(file_handle)
|
os.close(file_handle)
|
||||||
custom_path.unlink()
|
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_download_no_filename(self, monkeypatch, file):
|
||||||
async def test(*args, **kwargs):
|
async def test(*args, **kwargs):
|
||||||
return self.file_content
|
return self.file_content
|
||||||
|
@ -200,12 +205,6 @@ class TestFile:
|
||||||
custom_fobj.seek(0)
|
custom_fobj.seek(0)
|
||||||
assert custom_fobj.read() == self.file_content
|
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_download_bytearray(self, monkeypatch, file):
|
||||||
async def test(*args, **kwargs):
|
async def test(*args, **kwargs):
|
||||||
return self.file_content
|
return self.file_content
|
||||||
|
@ -223,18 +222,6 @@ class TestFile:
|
||||||
assert buf2[len(buf) :] == buf
|
assert buf2[len(buf) :] == buf
|
||||||
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_download_encrypted(self, monkeypatch, bot, encrypted_file):
|
||||||
async def test(*args, **kwargs):
|
async def test(*args, **kwargs):
|
||||||
return data_file("image_encrypted.jpg").read_bytes()
|
return data_file("image_encrypted.jpg").read_bytes()
|
||||||
|
@ -257,29 +244,6 @@ class TestFile:
|
||||||
custom_fobj.seek(0)
|
custom_fobj.seek(0)
|
||||||
assert custom_fobj.read() == data_file("image_decrypted.jpg").read_bytes()
|
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_download_file_obj_local_file_encrypted(self, monkeypatch, encrypted_local_file):
|
||||||
async def test(*args, **kwargs):
|
async def test(*args, **kwargs):
|
||||||
return data_file("image_encrypted.jpg").read_bytes()
|
return data_file("image_encrypted.jpg").read_bytes()
|
||||||
|
@ -307,6 +271,70 @@ class TestFile:
|
||||||
assert buf2[len(buf) :] == buf
|
assert buf2[len(buf) :] == buf
|
||||||
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):
|
async def test_download_bytearray_local_file_encrypted(self, encrypted_local_file):
|
||||||
# Check that a download to a newly allocated bytearray works.
|
# Check that a download to a newly allocated bytearray works.
|
||||||
buf = await encrypted_local_file.download_as_bytearray()
|
buf = await encrypted_local_file.download_as_bytearray()
|
||||||
|
@ -318,23 +346,3 @@ class TestFile:
|
||||||
assert buf3 is buf2
|
assert buf3 is buf2
|
||||||
assert buf2[len(buf) :] == buf
|
assert buf2[len(buf) :] == buf
|
||||||
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)
|
|
||||||
|
|
|
@ -22,30 +22,23 @@ import pytest
|
||||||
from telegram import ForceReply, ReplyKeyboardRemove
|
from telegram import ForceReply, ReplyKeyboardRemove
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def force_reply():
|
def force_reply():
|
||||||
return ForceReply(
|
return ForceReply(TestForceReplyBase.selective, TestForceReplyBase.input_field_placeholder)
|
||||||
TestForceReply.selective,
|
|
||||||
TestForceReply.input_field_placeholder,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestForceReply:
|
class TestForceReplyBase:
|
||||||
force_reply = True
|
force_reply = True
|
||||||
selective = True
|
selective = True
|
||||||
input_field_placeholder = "force replies can be annoying if not used properly"
|
input_field_placeholder = "force replies can be annoying if not used properly"
|
||||||
|
|
||||||
|
|
||||||
|
class TestForceReplyWithoutRequest(TestForceReplyBase):
|
||||||
def test_slot_behaviour(self, force_reply, mro_slots):
|
def test_slot_behaviour(self, force_reply, mro_slots):
|
||||||
for attr in force_reply.__slots__:
|
for attr in force_reply.__slots__:
|
||||||
assert getattr(force_reply, attr, "err") != "err", f"got extra slot '{attr}'"
|
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"
|
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):
|
def test_expected(self, force_reply):
|
||||||
assert force_reply.force_reply == self.force_reply
|
assert force_reply.force_reply == self.force_reply
|
||||||
assert force_reply.selective == self.selective
|
assert force_reply.selective == self.selective
|
||||||
|
@ -73,3 +66,9 @@ class TestForceReply:
|
||||||
|
|
||||||
assert a != d
|
assert a != d
|
||||||
assert hash(a) != hash(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"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -44,7 +45,7 @@ async def emoji_id(bot):
|
||||||
return first_sticker.custom_emoji_id
|
return first_sticker.custom_emoji_id
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(scope="module")
|
||||||
async def forum_topic_object(forum_group_id, emoji_id):
|
async def forum_topic_object(forum_group_id, emoji_id):
|
||||||
return ForumTopic(
|
return ForumTopic(
|
||||||
message_thread_id=forum_group_id,
|
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):
|
async def real_topic(bot, emoji_id, forum_group_id):
|
||||||
result = await bot.create_forum_topic(
|
result = await bot.create_forum_topic(
|
||||||
chat_id=forum_group_id,
|
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"
|
assert result is True, "Topic was not deleted"
|
||||||
|
|
||||||
|
|
||||||
class TestForumTopic:
|
class TestForumTopicWithoutRequest:
|
||||||
def test_slot_behaviour(self, mro_slots, forum_topic_object):
|
def test_slot_behaviour(self, mro_slots, forum_topic_object):
|
||||||
for attr in forum_topic_object.__slots__:
|
inst = forum_topic_object
|
||||||
assert getattr(forum_topic_object, attr, "err") != "err", f"got extra slot '{attr}'"
|
for attr in inst.__slots__:
|
||||||
assert len(mro_slots(forum_topic_object)) == len(
|
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
set(mro_slots(forum_topic_object))
|
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
||||||
), "duplicate slot"
|
|
||||||
|
|
||||||
async def test_expected_values(self, emoji_id, forum_group_id, forum_topic_object):
|
async def test_expected_values(self, emoji_id, forum_group_id, forum_topic_object):
|
||||||
assert forum_topic_object.message_thread_id == forum_group_id
|
assert forum_topic_object.message_thread_id == forum_group_id
|
||||||
|
@ -152,8 +152,7 @@ class TestForumTopic:
|
||||||
assert hash(a) != hash(e)
|
assert hash(a) != hash(e)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
class TestForumMethodsWithRequest:
|
||||||
class TestForumMethods:
|
|
||||||
async def test_create_forum_topic(self, real_topic):
|
async def test_create_forum_topic(self, real_topic):
|
||||||
result = real_topic
|
result = real_topic
|
||||||
assert isinstance(result, ForumTopic)
|
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):
|
async def test_unpin_all_forum_topic_messages(self, bot, forum_group_id, real_topic):
|
||||||
message_thread_id = real_topic.message_thread_id
|
message_thread_id = real_topic.message_thread_id
|
||||||
|
pin_msg_tasks = set()
|
||||||
|
|
||||||
msgs = [
|
awaitables = {
|
||||||
await (
|
bot.send_message(forum_group_id, TEST_MSG_TEXT, message_thread_id=message_thread_id)
|
||||||
await bot.send_message(
|
|
||||||
chat_id=forum_group_id, text=TEST_MSG_TEXT, message_thread_id=message_thread_id
|
|
||||||
)
|
|
||||||
).pin()
|
|
||||||
for _ in range(2)
|
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
|
# 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(
|
result = await bot.unpin_all_forum_topic_messages(forum_group_id, message_thread_id)
|
||||||
chat_id=forum_group_id, message_thread_id=message_thread_id
|
|
||||||
)
|
|
||||||
assert result is True, "Failed to unpin all the messages in forum topic"
|
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):
|
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"
|
assert result is True, "Failed to reopen general forum topic"
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture(scope="module")
|
||||||
def topic_created():
|
def topic_created():
|
||||||
return ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR)
|
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):
|
def test_slot_behaviour(self, topic_created, mro_slots):
|
||||||
for attr in topic_created.__slots__:
|
for attr in topic_created.__slots__:
|
||||||
assert getattr(topic_created, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(topic_created, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -358,7 +355,7 @@ class TestForumTopicCreated:
|
||||||
assert hash(a) != hash(d)
|
assert hash(a) != hash(d)
|
||||||
|
|
||||||
|
|
||||||
class TestForumTopicClosed:
|
class TestForumTopicClosedWithoutRequest:
|
||||||
def test_slot_behaviour(self, mro_slots):
|
def test_slot_behaviour(self, mro_slots):
|
||||||
action = ForumTopicClosed()
|
action = ForumTopicClosed()
|
||||||
for attr in action.__slots__:
|
for attr in action.__slots__:
|
||||||
|
@ -376,7 +373,7 @@ class TestForumTopicClosed:
|
||||||
assert action_dict == {}
|
assert action_dict == {}
|
||||||
|
|
||||||
|
|
||||||
class TestForumTopicReopened:
|
class TestForumTopicReopenedWithoutRequest:
|
||||||
def test_slot_behaviour(self, mro_slots):
|
def test_slot_behaviour(self, mro_slots):
|
||||||
action = ForumTopicReopened()
|
action = ForumTopicReopened()
|
||||||
for attr in action.__slots__:
|
for attr in action.__slots__:
|
||||||
|
|
|
@ -22,21 +22,21 @@ import pytest
|
||||||
from telegram import Animation, Game, MessageEntity, PhotoSize
|
from telegram import Animation, Game, MessageEntity, PhotoSize
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="module")
|
||||||
def game():
|
def game():
|
||||||
game = Game(
|
game = Game(
|
||||||
TestGame.title,
|
TestGameBase.title,
|
||||||
TestGame.description,
|
TestGameBase.description,
|
||||||
TestGame.photo,
|
TestGameBase.photo,
|
||||||
text=TestGame.text,
|
text=TestGameBase.text,
|
||||||
text_entities=TestGame.text_entities,
|
text_entities=TestGameBase.text_entities,
|
||||||
animation=TestGame.animation,
|
animation=TestGameBase.animation,
|
||||||
)
|
)
|
||||||
game._unfreeze()
|
game._unfreeze()
|
||||||
return game
|
return game
|
||||||
|
|
||||||
|
|
||||||
class TestGame:
|
class TestGameBase:
|
||||||
title = "Python-telegram-bot Test Game"
|
title = "Python-telegram-bot Test Game"
|
||||||
description = "description"
|
description = "description"
|
||||||
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
|
photo = [PhotoSize("Blah", "ElseBlah", 640, 360, file_size=0)]
|
||||||
|
@ -47,6 +47,8 @@ class TestGame:
|
||||||
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
|
text_entities = [MessageEntity(13, 17, MessageEntity.URL)]
|
||||||
animation = Animation("blah", "unique_id", 320, 180, 1)
|
animation = Animation("blah", "unique_id", 320, 180, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGameWithoutRequest(TestGameBase):
|
||||||
def test_slot_behaviour(self, game, mro_slots):
|
def test_slot_behaviour(self, game, mro_slots):
|
||||||
for attr in game.__slots__:
|
for attr in game.__slots__:
|
||||||
assert getattr(game, attr, "err") != "err", f"got extra slot '{attr}'"
|
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["text_entities"] == [game.text_entities[0].to_dict()]
|
||||||
assert game_dict["animation"] == game.animation.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):
|
def test_equality(self):
|
||||||
a = Game("title", "description", [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)])
|
a = Game("title", "description", [PhotoSize("Blah", "unique_id", 640, 360, file_size=0)])
|
||||||
b = Game(
|
b = Game(
|
||||||
|
@ -133,3 +121,17 @@ class TestGame:
|
||||||
|
|
||||||
assert a != d
|
assert a != d
|
||||||
assert hash(a) != hash(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"}
|
||||||
|
|
|
@ -22,25 +22,31 @@ import pytest
|
||||||
from telegram import GameHighScore, User
|
from telegram import GameHighScore, User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def game_highscore():
|
def game_highscore():
|
||||||
return GameHighScore(
|
return GameHighScore(
|
||||||
TestGameHighScore.position, TestGameHighScore.user, TestGameHighScore.score
|
TestGameHighScoreBase.position, TestGameHighScoreBase.user, TestGameHighScoreBase.score
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestGameHighScore:
|
class TestGameHighScoreBase:
|
||||||
position = 12
|
position = 12
|
||||||
user = User(2, "test user", False)
|
user = User(2, "test user", False)
|
||||||
score = 42
|
score = 42
|
||||||
|
|
||||||
|
|
||||||
|
class TestGameHighScoreWithoutRequest(TestGameHighScoreBase):
|
||||||
def test_slot_behaviour(self, game_highscore, mro_slots):
|
def test_slot_behaviour(self, game_highscore, mro_slots):
|
||||||
for attr in game_highscore.__slots__:
|
for attr in game_highscore.__slots__:
|
||||||
assert getattr(game_highscore, attr, "err") != "err", f"got extra slot '{attr}'"
|
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"
|
assert len(mro_slots(game_highscore)) == len(set(mro_slots(game_highscore))), "same slot"
|
||||||
|
|
||||||
def test_de_json(self, bot):
|
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)
|
highscore = GameHighScore.de_json(json_dict, bot)
|
||||||
assert highscore.api_kwargs == {}
|
assert highscore.api_kwargs == {}
|
||||||
|
|
||||||
|
|
|
@ -22,22 +22,22 @@ import pytest
|
||||||
from telegram import CallbackGame, InlineKeyboardButton, LoginUrl, WebAppInfo
|
from telegram import CallbackGame, InlineKeyboardButton, LoginUrl, WebAppInfo
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_keyboard_button():
|
def inline_keyboard_button():
|
||||||
return InlineKeyboardButton(
|
return InlineKeyboardButton(
|
||||||
TestInlineKeyboardButton.text,
|
TestInlineKeyboardButtonBase.text,
|
||||||
url=TestInlineKeyboardButton.url,
|
url=TestInlineKeyboardButtonBase.url,
|
||||||
callback_data=TestInlineKeyboardButton.callback_data,
|
callback_data=TestInlineKeyboardButtonBase.callback_data,
|
||||||
switch_inline_query=TestInlineKeyboardButton.switch_inline_query,
|
switch_inline_query=TestInlineKeyboardButtonBase.switch_inline_query,
|
||||||
switch_inline_query_current_chat=TestInlineKeyboardButton.switch_inline_query_current_chat,
|
switch_inline_query_current_chat=TestInlineKeyboardButtonBase.switch_inline_query_current_chat, # noqa: E501
|
||||||
callback_game=TestInlineKeyboardButton.callback_game,
|
callback_game=TestInlineKeyboardButtonBase.callback_game,
|
||||||
pay=TestInlineKeyboardButton.pay,
|
pay=TestInlineKeyboardButtonBase.pay,
|
||||||
login_url=TestInlineKeyboardButton.login_url,
|
login_url=TestInlineKeyboardButtonBase.login_url,
|
||||||
web_app=TestInlineKeyboardButton.web_app,
|
web_app=TestInlineKeyboardButtonBase.web_app,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineKeyboardButton:
|
class TestInlineKeyboardButtonBase:
|
||||||
text = "text"
|
text = "text"
|
||||||
url = "url"
|
url = "url"
|
||||||
callback_data = "callback data"
|
callback_data = "callback data"
|
||||||
|
@ -48,6 +48,8 @@ class TestInlineKeyboardButton:
|
||||||
login_url = LoginUrl("http://google.com")
|
login_url = LoginUrl("http://google.com")
|
||||||
web_app = WebAppInfo(url="https://example.com")
|
web_app = WebAppInfo(url="https://example.com")
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineKeyboardButtonWithoutRequest(TestInlineKeyboardButtonBase):
|
||||||
def test_slot_behaviour(self, inline_keyboard_button, mro_slots):
|
def test_slot_behaviour(self, inline_keyboard_button, mro_slots):
|
||||||
inst = inline_keyboard_button
|
inst = inline_keyboard_button
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,12 +28,12 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_keyboard_markup():
|
def inline_keyboard_markup():
|
||||||
return InlineKeyboardMarkup(TestInlineKeyboardMarkup.inline_keyboard)
|
return InlineKeyboardMarkup(TestInlineKeyboardMarkupBase.inline_keyboard)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineKeyboardMarkup:
|
class TestInlineKeyboardMarkupBase:
|
||||||
inline_keyboard = [
|
inline_keyboard = [
|
||||||
[
|
[
|
||||||
InlineKeyboardButton(text="button1", callback_data="data1"),
|
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):
|
def test_slot_behaviour(self, inline_keyboard_markup, mro_slots):
|
||||||
inst = inline_keyboard_markup
|
inst = inline_keyboard_markup
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot"
|
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):
|
def test_to_dict(self, inline_keyboard_markup):
|
||||||
inline_keyboard_markup_dict = inline_keyboard_markup.to_dict()
|
inline_keyboard_markup_dict = inline_keyboard_markup.to_dict()
|
||||||
|
|
||||||
|
@ -233,3 +143,96 @@ class TestInlineKeyboardMarkup:
|
||||||
|
|
||||||
assert a != g
|
assert a != g
|
||||||
assert hash(a) != hash(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"
|
||||||
|
|
|
@ -27,26 +27,28 @@ from tests.auxil.bot_method_checks import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query(bot):
|
def inline_query(bot):
|
||||||
ilq = InlineQuery(
|
ilq = InlineQuery(
|
||||||
TestInlineQuery.id_,
|
TestInlineQueryBase.id_,
|
||||||
TestInlineQuery.from_user,
|
TestInlineQueryBase.from_user,
|
||||||
TestInlineQuery.query,
|
TestInlineQueryBase.query,
|
||||||
TestInlineQuery.offset,
|
TestInlineQueryBase.offset,
|
||||||
location=TestInlineQuery.location,
|
location=TestInlineQueryBase.location,
|
||||||
)
|
)
|
||||||
ilq.set_bot(bot)
|
ilq.set_bot(bot)
|
||||||
return ilq
|
return ilq
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQuery:
|
class TestInlineQueryBase:
|
||||||
id_ = 1234
|
id_ = 1234
|
||||||
from_user = User(1, "First name", False)
|
from_user = User(1, "First name", False)
|
||||||
query = "query text"
|
query = "query text"
|
||||||
offset = "offset"
|
offset = "offset"
|
||||||
location = Location(8.8, 53.1)
|
location = Location(8.8, 53.1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryWithoutRequest(TestInlineQueryBase):
|
||||||
def test_slot_behaviour(self, inline_query, mro_slots):
|
def test_slot_behaviour(self, inline_query, mro_slots):
|
||||||
for attr in inline_query.__slots__:
|
for attr in inline_query.__slots__:
|
||||||
assert getattr(inline_query, attr, "err") != "err", f"got extra slot '{attr}'"
|
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["query"] == inline_query.query
|
||||||
assert inline_query_dict["offset"] == inline_query.offset
|
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):
|
def test_equality(self):
|
||||||
a = InlineQuery(self.id_, User(1, "", False), "", "")
|
a = InlineQuery(self.id_, User(1, "", False), "", "")
|
||||||
b = InlineQuery(self.id_, User(1, "", False), "", "")
|
b = InlineQuery(self.id_, User(1, "", False), "", "")
|
||||||
|
@ -126,3 +100,31 @@ class TestInlineQuery:
|
||||||
|
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(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)
|
||||||
|
|
|
@ -28,23 +28,23 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_article():
|
def inline_query_result_article():
|
||||||
return InlineQueryResultArticle(
|
return InlineQueryResultArticle(
|
||||||
TestInlineQueryResultArticle.id_,
|
TestInlineQueryResultArticleBase.id_,
|
||||||
TestInlineQueryResultArticle.title,
|
TestInlineQueryResultArticleBase.title,
|
||||||
input_message_content=TestInlineQueryResultArticle.input_message_content,
|
input_message_content=TestInlineQueryResultArticleBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultArticle.reply_markup,
|
reply_markup=TestInlineQueryResultArticleBase.reply_markup,
|
||||||
url=TestInlineQueryResultArticle.url,
|
url=TestInlineQueryResultArticleBase.url,
|
||||||
hide_url=TestInlineQueryResultArticle.hide_url,
|
hide_url=TestInlineQueryResultArticleBase.hide_url,
|
||||||
description=TestInlineQueryResultArticle.description,
|
description=TestInlineQueryResultArticleBase.description,
|
||||||
thumb_url=TestInlineQueryResultArticle.thumb_url,
|
thumb_url=TestInlineQueryResultArticleBase.thumb_url,
|
||||||
thumb_height=TestInlineQueryResultArticle.thumb_height,
|
thumb_height=TestInlineQueryResultArticleBase.thumb_height,
|
||||||
thumb_width=TestInlineQueryResultArticle.thumb_width,
|
thumb_width=TestInlineQueryResultArticleBase.thumb_width,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultArticle:
|
class TestInlineQueryResultArticleBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "article"
|
type_ = "article"
|
||||||
title = "title"
|
title = "title"
|
||||||
|
@ -57,6 +57,8 @@ class TestInlineQueryResultArticle:
|
||||||
thumb_height = 10
|
thumb_height = 10
|
||||||
thumb_width = 15
|
thumb_width = 15
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultArticleWithoutRequest(TestInlineQueryResultArticleBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_article, mro_slots, recwarn):
|
def test_slot_behaviour(self, inline_query_result_article, mro_slots, recwarn):
|
||||||
inst = inline_query_result_article
|
inst = inline_query_result_article
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -29,23 +29,23 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_audio():
|
def inline_query_result_audio():
|
||||||
return InlineQueryResultAudio(
|
return InlineQueryResultAudio(
|
||||||
TestInlineQueryResultAudio.id_,
|
TestInlineQueryResultAudioBase.id_,
|
||||||
TestInlineQueryResultAudio.audio_url,
|
TestInlineQueryResultAudioBase.audio_url,
|
||||||
TestInlineQueryResultAudio.title,
|
TestInlineQueryResultAudioBase.title,
|
||||||
performer=TestInlineQueryResultAudio.performer,
|
performer=TestInlineQueryResultAudioBase.performer,
|
||||||
audio_duration=TestInlineQueryResultAudio.audio_duration,
|
audio_duration=TestInlineQueryResultAudioBase.audio_duration,
|
||||||
caption=TestInlineQueryResultAudio.caption,
|
caption=TestInlineQueryResultAudioBase.caption,
|
||||||
parse_mode=TestInlineQueryResultAudio.parse_mode,
|
parse_mode=TestInlineQueryResultAudioBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultAudio.caption_entities,
|
caption_entities=TestInlineQueryResultAudioBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultAudio.input_message_content,
|
input_message_content=TestInlineQueryResultAudioBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultAudio.reply_markup,
|
reply_markup=TestInlineQueryResultAudioBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultAudio:
|
class TestInlineQueryResultAudioBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "audio"
|
type_ = "audio"
|
||||||
audio_url = "audio url"
|
audio_url = "audio url"
|
||||||
|
@ -58,6 +58,8 @@ class TestInlineQueryResultAudio:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultAudioWithoutRequest(TestInlineQueryResultAudioBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_audio, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_audio, mro_slots):
|
||||||
inst = inline_query_result_audio
|
inst = inline_query_result_audio
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -29,20 +29,20 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_audio():
|
def inline_query_result_cached_audio():
|
||||||
return InlineQueryResultCachedAudio(
|
return InlineQueryResultCachedAudio(
|
||||||
TestInlineQueryResultCachedAudio.id_,
|
TestInlineQueryResultCachedAudioBase.id_,
|
||||||
TestInlineQueryResultCachedAudio.audio_file_id,
|
TestInlineQueryResultCachedAudioBase.audio_file_id,
|
||||||
caption=TestInlineQueryResultCachedAudio.caption,
|
caption=TestInlineQueryResultCachedAudioBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedAudio.parse_mode,
|
parse_mode=TestInlineQueryResultCachedAudioBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedAudio.caption_entities,
|
caption_entities=TestInlineQueryResultCachedAudioBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultCachedAudio.input_message_content,
|
input_message_content=TestInlineQueryResultCachedAudioBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedAudio.reply_markup,
|
reply_markup=TestInlineQueryResultCachedAudioBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedAudio:
|
class TestInlineQueryResultCachedAudioBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "audio"
|
type_ = "audio"
|
||||||
audio_file_id = "audio file id"
|
audio_file_id = "audio file id"
|
||||||
|
@ -52,6 +52,8 @@ class TestInlineQueryResultCachedAudio:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedAudioWithoutRequest(TestInlineQueryResultCachedAudioBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_audio, mro_slots):
|
||||||
inst = inline_query_result_cached_audio
|
inst = inline_query_result_cached_audio
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -29,22 +29,22 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_document():
|
def inline_query_result_cached_document():
|
||||||
return InlineQueryResultCachedDocument(
|
return InlineQueryResultCachedDocument(
|
||||||
TestInlineQueryResultCachedDocument.id_,
|
TestInlineQueryResultCachedDocumentBase.id_,
|
||||||
TestInlineQueryResultCachedDocument.title,
|
TestInlineQueryResultCachedDocumentBase.title,
|
||||||
TestInlineQueryResultCachedDocument.document_file_id,
|
TestInlineQueryResultCachedDocumentBase.document_file_id,
|
||||||
caption=TestInlineQueryResultCachedDocument.caption,
|
caption=TestInlineQueryResultCachedDocumentBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedDocument.parse_mode,
|
parse_mode=TestInlineQueryResultCachedDocumentBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedDocument.caption_entities,
|
caption_entities=TestInlineQueryResultCachedDocumentBase.caption_entities,
|
||||||
description=TestInlineQueryResultCachedDocument.description,
|
description=TestInlineQueryResultCachedDocumentBase.description,
|
||||||
input_message_content=TestInlineQueryResultCachedDocument.input_message_content,
|
input_message_content=TestInlineQueryResultCachedDocumentBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedDocument.reply_markup,
|
reply_markup=TestInlineQueryResultCachedDocumentBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedDocument:
|
class TestInlineQueryResultCachedDocumentBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "document"
|
type_ = "document"
|
||||||
document_file_id = "document file id"
|
document_file_id = "document file id"
|
||||||
|
@ -56,6 +56,8 @@ class TestInlineQueryResultCachedDocument:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedDocumentWithoutRequest(TestInlineQueryResultCachedDocumentBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_document, mro_slots):
|
||||||
inst = inline_query_result_cached_document
|
inst = inline_query_result_cached_document
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,21 +28,21 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_gif():
|
def inline_query_result_cached_gif():
|
||||||
return InlineQueryResultCachedGif(
|
return InlineQueryResultCachedGif(
|
||||||
TestInlineQueryResultCachedGif.id_,
|
TestInlineQueryResultCachedGifBase.id_,
|
||||||
TestInlineQueryResultCachedGif.gif_file_id,
|
TestInlineQueryResultCachedGifBase.gif_file_id,
|
||||||
title=TestInlineQueryResultCachedGif.title,
|
title=TestInlineQueryResultCachedGifBase.title,
|
||||||
caption=TestInlineQueryResultCachedGif.caption,
|
caption=TestInlineQueryResultCachedGifBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedGif.parse_mode,
|
parse_mode=TestInlineQueryResultCachedGifBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedGif.caption_entities,
|
caption_entities=TestInlineQueryResultCachedGifBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultCachedGif.input_message_content,
|
input_message_content=TestInlineQueryResultCachedGifBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedGif.reply_markup,
|
reply_markup=TestInlineQueryResultCachedGifBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedGif:
|
class TestInlineQueryResultCachedGifBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "gif"
|
type_ = "gif"
|
||||||
gif_file_id = "gif file id"
|
gif_file_id = "gif file id"
|
||||||
|
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedGif:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedGifWithoutRequest(TestInlineQueryResultCachedGifBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_gif, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_gif, mro_slots):
|
||||||
inst = inline_query_result_cached_gif
|
inst = inline_query_result_cached_gif
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,21 +28,21 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_mpeg4_gif():
|
def inline_query_result_cached_mpeg4_gif():
|
||||||
return InlineQueryResultCachedMpeg4Gif(
|
return InlineQueryResultCachedMpeg4Gif(
|
||||||
TestInlineQueryResultCachedMpeg4Gif.id_,
|
TestInlineQueryResultCachedMpeg4GifBase.id_,
|
||||||
TestInlineQueryResultCachedMpeg4Gif.mpeg4_file_id,
|
TestInlineQueryResultCachedMpeg4GifBase.mpeg4_file_id,
|
||||||
title=TestInlineQueryResultCachedMpeg4Gif.title,
|
title=TestInlineQueryResultCachedMpeg4GifBase.title,
|
||||||
caption=TestInlineQueryResultCachedMpeg4Gif.caption,
|
caption=TestInlineQueryResultCachedMpeg4GifBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedMpeg4Gif.parse_mode,
|
parse_mode=TestInlineQueryResultCachedMpeg4GifBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedMpeg4Gif.caption_entities,
|
caption_entities=TestInlineQueryResultCachedMpeg4GifBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultCachedMpeg4Gif.input_message_content,
|
input_message_content=TestInlineQueryResultCachedMpeg4GifBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedMpeg4Gif.reply_markup,
|
reply_markup=TestInlineQueryResultCachedMpeg4GifBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedMpeg4Gif:
|
class TestInlineQueryResultCachedMpeg4GifBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "mpeg4_gif"
|
type_ = "mpeg4_gif"
|
||||||
mpeg4_file_id = "mpeg4 file id"
|
mpeg4_file_id = "mpeg4 file id"
|
||||||
|
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedMpeg4Gif:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedMpeg4GifWithoutRequest(TestInlineQueryResultCachedMpeg4GifBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_mpeg4_gif, mro_slots):
|
||||||
inst = inline_query_result_cached_mpeg4_gif
|
inst = inline_query_result_cached_mpeg4_gif
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,22 +28,22 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_photo():
|
def inline_query_result_cached_photo():
|
||||||
return InlineQueryResultCachedPhoto(
|
return InlineQueryResultCachedPhoto(
|
||||||
TestInlineQueryResultCachedPhoto.id_,
|
TestInlineQueryResultCachedPhotoBase.id_,
|
||||||
TestInlineQueryResultCachedPhoto.photo_file_id,
|
TestInlineQueryResultCachedPhotoBase.photo_file_id,
|
||||||
title=TestInlineQueryResultCachedPhoto.title,
|
title=TestInlineQueryResultCachedPhotoBase.title,
|
||||||
description=TestInlineQueryResultCachedPhoto.description,
|
description=TestInlineQueryResultCachedPhotoBase.description,
|
||||||
caption=TestInlineQueryResultCachedPhoto.caption,
|
caption=TestInlineQueryResultCachedPhotoBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedPhoto.parse_mode,
|
parse_mode=TestInlineQueryResultCachedPhotoBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedPhoto.caption_entities,
|
caption_entities=TestInlineQueryResultCachedPhotoBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultCachedPhoto.input_message_content,
|
input_message_content=TestInlineQueryResultCachedPhotoBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedPhoto.reply_markup,
|
reply_markup=TestInlineQueryResultCachedPhotoBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedPhoto:
|
class TestInlineQueryResultCachedPhotoBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "photo"
|
type_ = "photo"
|
||||||
photo_file_id = "photo file id"
|
photo_file_id = "photo file id"
|
||||||
|
@ -55,6 +55,8 @@ class TestInlineQueryResultCachedPhoto:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedPhotoWithoutRequest(TestInlineQueryResultCachedPhotoBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_photo, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_photo, mro_slots):
|
||||||
inst = inline_query_result_cached_photo
|
inst = inline_query_result_cached_photo
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -27,23 +27,25 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_sticker():
|
def inline_query_result_cached_sticker():
|
||||||
return InlineQueryResultCachedSticker(
|
return InlineQueryResultCachedSticker(
|
||||||
TestInlineQueryResultCachedSticker.id_,
|
TestInlineQueryResultCachedStickerBase.id_,
|
||||||
TestInlineQueryResultCachedSticker.sticker_file_id,
|
TestInlineQueryResultCachedStickerBase.sticker_file_id,
|
||||||
input_message_content=TestInlineQueryResultCachedSticker.input_message_content,
|
input_message_content=TestInlineQueryResultCachedStickerBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedSticker.reply_markup,
|
reply_markup=TestInlineQueryResultCachedStickerBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedSticker:
|
class TestInlineQueryResultCachedStickerBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "sticker"
|
type_ = "sticker"
|
||||||
sticker_file_id = "sticker file id"
|
sticker_file_id = "sticker file id"
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedStickerWithoutRequest(TestInlineQueryResultCachedStickerBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_sticker, mro_slots):
|
||||||
inst = inline_query_result_cached_sticker
|
inst = inline_query_result_cached_sticker
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,22 +28,22 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_video():
|
def inline_query_result_cached_video():
|
||||||
return InlineQueryResultCachedVideo(
|
return InlineQueryResultCachedVideo(
|
||||||
TestInlineQueryResultCachedVideo.id_,
|
TestInlineQueryResultCachedVideoBase.id_,
|
||||||
TestInlineQueryResultCachedVideo.video_file_id,
|
TestInlineQueryResultCachedVideoBase.video_file_id,
|
||||||
TestInlineQueryResultCachedVideo.title,
|
TestInlineQueryResultCachedVideoBase.title,
|
||||||
caption=TestInlineQueryResultCachedVideo.caption,
|
caption=TestInlineQueryResultCachedVideoBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedVideo.parse_mode,
|
parse_mode=TestInlineQueryResultCachedVideoBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedVideo.caption_entities,
|
caption_entities=TestInlineQueryResultCachedVideoBase.caption_entities,
|
||||||
description=TestInlineQueryResultCachedVideo.description,
|
description=TestInlineQueryResultCachedVideoBase.description,
|
||||||
input_message_content=TestInlineQueryResultCachedVideo.input_message_content,
|
input_message_content=TestInlineQueryResultCachedVideoBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedVideo.reply_markup,
|
reply_markup=TestInlineQueryResultCachedVideoBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedVideo:
|
class TestInlineQueryResultCachedVideoBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "video"
|
type_ = "video"
|
||||||
video_file_id = "video file id"
|
video_file_id = "video file id"
|
||||||
|
@ -55,6 +55,8 @@ class TestInlineQueryResultCachedVideo:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedVideoWithoutRequest(TestInlineQueryResultCachedVideoBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_video, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_video, mro_slots):
|
||||||
inst = inline_query_result_cached_video
|
inst = inline_query_result_cached_video
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,21 +28,21 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_cached_voice():
|
def inline_query_result_cached_voice():
|
||||||
return InlineQueryResultCachedVoice(
|
return InlineQueryResultCachedVoice(
|
||||||
TestInlineQueryResultCachedVoice.id_,
|
TestInlineQueryResultCachedVoiceBase.id_,
|
||||||
TestInlineQueryResultCachedVoice.voice_file_id,
|
TestInlineQueryResultCachedVoiceBase.voice_file_id,
|
||||||
TestInlineQueryResultCachedVoice.title,
|
TestInlineQueryResultCachedVoiceBase.title,
|
||||||
caption=TestInlineQueryResultCachedVoice.caption,
|
caption=TestInlineQueryResultCachedVoiceBase.caption,
|
||||||
parse_mode=TestInlineQueryResultCachedVoice.parse_mode,
|
parse_mode=TestInlineQueryResultCachedVoiceBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultCachedVoice.caption_entities,
|
caption_entities=TestInlineQueryResultCachedVoiceBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultCachedVoice.input_message_content,
|
input_message_content=TestInlineQueryResultCachedVoiceBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultCachedVoice.reply_markup,
|
reply_markup=TestInlineQueryResultCachedVoiceBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultCachedVoice:
|
class TestInlineQueryResultCachedVoiceBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "voice"
|
type_ = "voice"
|
||||||
voice_file_id = "voice file id"
|
voice_file_id = "voice file id"
|
||||||
|
@ -53,6 +53,8 @@ class TestInlineQueryResultCachedVoice:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultCachedVoiceWithoutRequest(TestInlineQueryResultCachedVoiceBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_cached_voice, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_cached_voice, mro_slots):
|
||||||
inst = inline_query_result_cached_voice
|
inst = inline_query_result_cached_voice
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -27,22 +27,22 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_contact():
|
def inline_query_result_contact():
|
||||||
return InlineQueryResultContact(
|
return InlineQueryResultContact(
|
||||||
TestInlineQueryResultContact.id_,
|
TestInlineQueryResultContactBase.id_,
|
||||||
TestInlineQueryResultContact.phone_number,
|
TestInlineQueryResultContactBase.phone_number,
|
||||||
TestInlineQueryResultContact.first_name,
|
TestInlineQueryResultContactBase.first_name,
|
||||||
last_name=TestInlineQueryResultContact.last_name,
|
last_name=TestInlineQueryResultContactBase.last_name,
|
||||||
thumb_url=TestInlineQueryResultContact.thumb_url,
|
thumb_url=TestInlineQueryResultContactBase.thumb_url,
|
||||||
thumb_width=TestInlineQueryResultContact.thumb_width,
|
thumb_width=TestInlineQueryResultContactBase.thumb_width,
|
||||||
thumb_height=TestInlineQueryResultContact.thumb_height,
|
thumb_height=TestInlineQueryResultContactBase.thumb_height,
|
||||||
input_message_content=TestInlineQueryResultContact.input_message_content,
|
input_message_content=TestInlineQueryResultContactBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultContact.reply_markup,
|
reply_markup=TestInlineQueryResultContactBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultContact:
|
class TestInlineQueryResultContactBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "contact"
|
type_ = "contact"
|
||||||
phone_number = "phone_number"
|
phone_number = "phone_number"
|
||||||
|
@ -54,6 +54,8 @@ class TestInlineQueryResultContact:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultContactWithoutRequest(TestInlineQueryResultContactBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_contact, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_contact, mro_slots):
|
||||||
inst = inline_query_result_contact
|
inst = inline_query_result_contact
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,26 +28,26 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_document():
|
def inline_query_result_document():
|
||||||
return InlineQueryResultDocument(
|
return InlineQueryResultDocument(
|
||||||
TestInlineQueryResultDocument.id_,
|
TestInlineQueryResultDocumentBase.id_,
|
||||||
TestInlineQueryResultDocument.document_url,
|
TestInlineQueryResultDocumentBase.document_url,
|
||||||
TestInlineQueryResultDocument.title,
|
TestInlineQueryResultDocumentBase.title,
|
||||||
TestInlineQueryResultDocument.mime_type,
|
TestInlineQueryResultDocumentBase.mime_type,
|
||||||
caption=TestInlineQueryResultDocument.caption,
|
caption=TestInlineQueryResultDocumentBase.caption,
|
||||||
parse_mode=TestInlineQueryResultDocument.parse_mode,
|
parse_mode=TestInlineQueryResultDocumentBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultDocument.caption_entities,
|
caption_entities=TestInlineQueryResultDocumentBase.caption_entities,
|
||||||
description=TestInlineQueryResultDocument.description,
|
description=TestInlineQueryResultDocumentBase.description,
|
||||||
thumb_url=TestInlineQueryResultDocument.thumb_url,
|
thumb_url=TestInlineQueryResultDocumentBase.thumb_url,
|
||||||
thumb_width=TestInlineQueryResultDocument.thumb_width,
|
thumb_width=TestInlineQueryResultDocumentBase.thumb_width,
|
||||||
thumb_height=TestInlineQueryResultDocument.thumb_height,
|
thumb_height=TestInlineQueryResultDocumentBase.thumb_height,
|
||||||
input_message_content=TestInlineQueryResultDocument.input_message_content,
|
input_message_content=TestInlineQueryResultDocumentBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultDocument.reply_markup,
|
reply_markup=TestInlineQueryResultDocumentBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultDocument:
|
class TestInlineQueryResultDocumentBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "document"
|
type_ = "document"
|
||||||
document_url = "document url"
|
document_url = "document url"
|
||||||
|
@ -63,6 +63,8 @@ class TestInlineQueryResultDocument:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultDocumentWithoutRequest(TestInlineQueryResultDocumentBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_document, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_document, mro_slots):
|
||||||
inst = inline_query_result_document
|
inst = inline_query_result_document
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -26,21 +26,23 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_game():
|
def inline_query_result_game():
|
||||||
return InlineQueryResultGame(
|
return InlineQueryResultGame(
|
||||||
TestInlineQueryResultGame.id_,
|
TestInlineQueryResultGameBase.id_,
|
||||||
TestInlineQueryResultGame.game_short_name,
|
TestInlineQueryResultGameBase.game_short_name,
|
||||||
reply_markup=TestInlineQueryResultGame.reply_markup,
|
reply_markup=TestInlineQueryResultGameBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultGame:
|
class TestInlineQueryResultGameBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "game"
|
type_ = "game"
|
||||||
game_short_name = "game short name"
|
game_short_name = "game short name"
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultGameWithoutRequest(TestInlineQueryResultGameBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_game, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_game, mro_slots):
|
||||||
inst = inline_query_result_game
|
inst = inline_query_result_game
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,26 +28,26 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_gif():
|
def inline_query_result_gif():
|
||||||
return InlineQueryResultGif(
|
return InlineQueryResultGif(
|
||||||
TestInlineQueryResultGif.id_,
|
TestInlineQueryResultGifBase.id_,
|
||||||
TestInlineQueryResultGif.gif_url,
|
TestInlineQueryResultGifBase.gif_url,
|
||||||
TestInlineQueryResultGif.thumb_url,
|
TestInlineQueryResultGifBase.thumb_url,
|
||||||
gif_width=TestInlineQueryResultGif.gif_width,
|
gif_width=TestInlineQueryResultGifBase.gif_width,
|
||||||
gif_height=TestInlineQueryResultGif.gif_height,
|
gif_height=TestInlineQueryResultGifBase.gif_height,
|
||||||
gif_duration=TestInlineQueryResultGif.gif_duration,
|
gif_duration=TestInlineQueryResultGifBase.gif_duration,
|
||||||
title=TestInlineQueryResultGif.title,
|
title=TestInlineQueryResultGifBase.title,
|
||||||
caption=TestInlineQueryResultGif.caption,
|
caption=TestInlineQueryResultGifBase.caption,
|
||||||
parse_mode=TestInlineQueryResultGif.parse_mode,
|
parse_mode=TestInlineQueryResultGifBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultGif.caption_entities,
|
caption_entities=TestInlineQueryResultGifBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultGif.input_message_content,
|
input_message_content=TestInlineQueryResultGifBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultGif.reply_markup,
|
reply_markup=TestInlineQueryResultGifBase.reply_markup,
|
||||||
thumb_mime_type=TestInlineQueryResultGif.thumb_mime_type,
|
thumb_mime_type=TestInlineQueryResultGifBase.thumb_mime_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultGif:
|
class TestInlineQueryResultGifBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "gif"
|
type_ = "gif"
|
||||||
gif_url = "gif url"
|
gif_url = "gif url"
|
||||||
|
@ -63,6 +63,8 @@ class TestInlineQueryResultGif:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultGifWithoutRequest(TestInlineQueryResultGifBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_gif, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_gif, mro_slots):
|
||||||
inst = inline_query_result_gif
|
inst = inline_query_result_gif
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -27,26 +27,26 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_location():
|
def inline_query_result_location():
|
||||||
return InlineQueryResultLocation(
|
return InlineQueryResultLocation(
|
||||||
TestInlineQueryResultLocation.id_,
|
TestInlineQueryResultLocationBase.id_,
|
||||||
TestInlineQueryResultLocation.latitude,
|
TestInlineQueryResultLocationBase.latitude,
|
||||||
TestInlineQueryResultLocation.longitude,
|
TestInlineQueryResultLocationBase.longitude,
|
||||||
TestInlineQueryResultLocation.title,
|
TestInlineQueryResultLocationBase.title,
|
||||||
live_period=TestInlineQueryResultLocation.live_period,
|
live_period=TestInlineQueryResultLocationBase.live_period,
|
||||||
thumb_url=TestInlineQueryResultLocation.thumb_url,
|
thumb_url=TestInlineQueryResultLocationBase.thumb_url,
|
||||||
thumb_width=TestInlineQueryResultLocation.thumb_width,
|
thumb_width=TestInlineQueryResultLocationBase.thumb_width,
|
||||||
thumb_height=TestInlineQueryResultLocation.thumb_height,
|
thumb_height=TestInlineQueryResultLocationBase.thumb_height,
|
||||||
input_message_content=TestInlineQueryResultLocation.input_message_content,
|
input_message_content=TestInlineQueryResultLocationBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultLocation.reply_markup,
|
reply_markup=TestInlineQueryResultLocationBase.reply_markup,
|
||||||
horizontal_accuracy=TestInlineQueryResultLocation.horizontal_accuracy,
|
horizontal_accuracy=TestInlineQueryResultLocationBase.horizontal_accuracy,
|
||||||
heading=TestInlineQueryResultLocation.heading,
|
heading=TestInlineQueryResultLocationBase.heading,
|
||||||
proximity_alert_radius=TestInlineQueryResultLocation.proximity_alert_radius,
|
proximity_alert_radius=TestInlineQueryResultLocationBase.proximity_alert_radius,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultLocation:
|
class TestInlineQueryResultLocationBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "location"
|
type_ = "location"
|
||||||
latitude = 0.0
|
latitude = 0.0
|
||||||
|
@ -62,6 +62,8 @@ class TestInlineQueryResultLocation:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultLocationWithoutRequest(TestInlineQueryResultLocationBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_location, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_location, mro_slots):
|
||||||
inst = inline_query_result_location
|
inst = inline_query_result_location
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,26 +28,26 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_mpeg4_gif():
|
def inline_query_result_mpeg4_gif():
|
||||||
return InlineQueryResultMpeg4Gif(
|
return InlineQueryResultMpeg4Gif(
|
||||||
TestInlineQueryResultMpeg4Gif.id_,
|
TestInlineQueryResultMpeg4GifBase.id_,
|
||||||
TestInlineQueryResultMpeg4Gif.mpeg4_url,
|
TestInlineQueryResultMpeg4GifBase.mpeg4_url,
|
||||||
TestInlineQueryResultMpeg4Gif.thumb_url,
|
TestInlineQueryResultMpeg4GifBase.thumb_url,
|
||||||
mpeg4_width=TestInlineQueryResultMpeg4Gif.mpeg4_width,
|
mpeg4_width=TestInlineQueryResultMpeg4GifBase.mpeg4_width,
|
||||||
mpeg4_height=TestInlineQueryResultMpeg4Gif.mpeg4_height,
|
mpeg4_height=TestInlineQueryResultMpeg4GifBase.mpeg4_height,
|
||||||
mpeg4_duration=TestInlineQueryResultMpeg4Gif.mpeg4_duration,
|
mpeg4_duration=TestInlineQueryResultMpeg4GifBase.mpeg4_duration,
|
||||||
title=TestInlineQueryResultMpeg4Gif.title,
|
title=TestInlineQueryResultMpeg4GifBase.title,
|
||||||
caption=TestInlineQueryResultMpeg4Gif.caption,
|
caption=TestInlineQueryResultMpeg4GifBase.caption,
|
||||||
parse_mode=TestInlineQueryResultMpeg4Gif.parse_mode,
|
parse_mode=TestInlineQueryResultMpeg4GifBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultMpeg4Gif.caption_entities,
|
caption_entities=TestInlineQueryResultMpeg4GifBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultMpeg4Gif.input_message_content,
|
input_message_content=TestInlineQueryResultMpeg4GifBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultMpeg4Gif.reply_markup,
|
reply_markup=TestInlineQueryResultMpeg4GifBase.reply_markup,
|
||||||
thumb_mime_type=TestInlineQueryResultMpeg4Gif.thumb_mime_type,
|
thumb_mime_type=TestInlineQueryResultMpeg4GifBase.thumb_mime_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultMpeg4Gif:
|
class TestInlineQueryResultMpeg4GifBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "mpeg4_gif"
|
type_ = "mpeg4_gif"
|
||||||
mpeg4_url = "mpeg4 url"
|
mpeg4_url = "mpeg4 url"
|
||||||
|
@ -63,6 +63,8 @@ class TestInlineQueryResultMpeg4Gif:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultMpeg4GifWithoutRequest(TestInlineQueryResultMpeg4GifBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_mpeg4_gif, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_mpeg4_gif, mro_slots):
|
||||||
inst = inline_query_result_mpeg4_gif
|
inst = inline_query_result_mpeg4_gif
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,25 +28,25 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_photo():
|
def inline_query_result_photo():
|
||||||
return InlineQueryResultPhoto(
|
return InlineQueryResultPhoto(
|
||||||
TestInlineQueryResultPhoto.id_,
|
TestInlineQueryResultPhotoBase.id_,
|
||||||
TestInlineQueryResultPhoto.photo_url,
|
TestInlineQueryResultPhotoBase.photo_url,
|
||||||
TestInlineQueryResultPhoto.thumb_url,
|
TestInlineQueryResultPhotoBase.thumb_url,
|
||||||
photo_width=TestInlineQueryResultPhoto.photo_width,
|
photo_width=TestInlineQueryResultPhotoBase.photo_width,
|
||||||
photo_height=TestInlineQueryResultPhoto.photo_height,
|
photo_height=TestInlineQueryResultPhotoBase.photo_height,
|
||||||
title=TestInlineQueryResultPhoto.title,
|
title=TestInlineQueryResultPhotoBase.title,
|
||||||
description=TestInlineQueryResultPhoto.description,
|
description=TestInlineQueryResultPhotoBase.description,
|
||||||
caption=TestInlineQueryResultPhoto.caption,
|
caption=TestInlineQueryResultPhotoBase.caption,
|
||||||
parse_mode=TestInlineQueryResultPhoto.parse_mode,
|
parse_mode=TestInlineQueryResultPhotoBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultPhoto.caption_entities,
|
caption_entities=TestInlineQueryResultPhotoBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultPhoto.input_message_content,
|
input_message_content=TestInlineQueryResultPhotoBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultPhoto.reply_markup,
|
reply_markup=TestInlineQueryResultPhotoBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultPhoto:
|
class TestInlineQueryResultPhotoBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "photo"
|
type_ = "photo"
|
||||||
photo_url = "photo url"
|
photo_url = "photo url"
|
||||||
|
@ -62,6 +62,8 @@ class TestInlineQueryResultPhoto:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultPhotoWithoutRequest(TestInlineQueryResultPhotoBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_photo, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_photo, mro_slots):
|
||||||
inst = inline_query_result_photo
|
inst = inline_query_result_photo
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -27,27 +27,27 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_venue():
|
def inline_query_result_venue():
|
||||||
return InlineQueryResultVenue(
|
return InlineQueryResultVenue(
|
||||||
TestInlineQueryResultVenue.id_,
|
TestInlineQueryResultVenueBase.id_,
|
||||||
TestInlineQueryResultVenue.latitude,
|
TestInlineQueryResultVenueBase.latitude,
|
||||||
TestInlineQueryResultVenue.longitude,
|
TestInlineQueryResultVenueBase.longitude,
|
||||||
TestInlineQueryResultVenue.title,
|
TestInlineQueryResultVenueBase.title,
|
||||||
TestInlineQueryResultVenue.address,
|
TestInlineQueryResultVenueBase.address,
|
||||||
foursquare_id=TestInlineQueryResultVenue.foursquare_id,
|
foursquare_id=TestInlineQueryResultVenueBase.foursquare_id,
|
||||||
foursquare_type=TestInlineQueryResultVenue.foursquare_type,
|
foursquare_type=TestInlineQueryResultVenueBase.foursquare_type,
|
||||||
thumb_url=TestInlineQueryResultVenue.thumb_url,
|
thumb_url=TestInlineQueryResultVenueBase.thumb_url,
|
||||||
thumb_width=TestInlineQueryResultVenue.thumb_width,
|
thumb_width=TestInlineQueryResultVenueBase.thumb_width,
|
||||||
thumb_height=TestInlineQueryResultVenue.thumb_height,
|
thumb_height=TestInlineQueryResultVenueBase.thumb_height,
|
||||||
input_message_content=TestInlineQueryResultVenue.input_message_content,
|
input_message_content=TestInlineQueryResultVenueBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultVenue.reply_markup,
|
reply_markup=TestInlineQueryResultVenueBase.reply_markup,
|
||||||
google_place_id=TestInlineQueryResultVenue.google_place_id,
|
google_place_id=TestInlineQueryResultVenueBase.google_place_id,
|
||||||
google_place_type=TestInlineQueryResultVenue.google_place_type,
|
google_place_type=TestInlineQueryResultVenueBase.google_place_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultVenue:
|
class TestInlineQueryResultVenueBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "venue"
|
type_ = "venue"
|
||||||
latitude = "latitude"
|
latitude = "latitude"
|
||||||
|
@ -64,6 +64,8 @@ class TestInlineQueryResultVenue:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultVenueWithoutRequest(TestInlineQueryResultVenueBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_venue, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_venue, mro_slots):
|
||||||
inst = inline_query_result_venue
|
inst = inline_query_result_venue
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,27 +28,27 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_video():
|
def inline_query_result_video():
|
||||||
return InlineQueryResultVideo(
|
return InlineQueryResultVideo(
|
||||||
TestInlineQueryResultVideo.id_,
|
TestInlineQueryResultVideoBase.id_,
|
||||||
TestInlineQueryResultVideo.video_url,
|
TestInlineQueryResultVideoBase.video_url,
|
||||||
TestInlineQueryResultVideo.mime_type,
|
TestInlineQueryResultVideoBase.mime_type,
|
||||||
TestInlineQueryResultVideo.thumb_url,
|
TestInlineQueryResultVideoBase.thumb_url,
|
||||||
TestInlineQueryResultVideo.title,
|
TestInlineQueryResultVideoBase.title,
|
||||||
video_width=TestInlineQueryResultVideo.video_width,
|
video_width=TestInlineQueryResultVideoBase.video_width,
|
||||||
video_height=TestInlineQueryResultVideo.video_height,
|
video_height=TestInlineQueryResultVideoBase.video_height,
|
||||||
video_duration=TestInlineQueryResultVideo.video_duration,
|
video_duration=TestInlineQueryResultVideoBase.video_duration,
|
||||||
caption=TestInlineQueryResultVideo.caption,
|
caption=TestInlineQueryResultVideoBase.caption,
|
||||||
parse_mode=TestInlineQueryResultVideo.parse_mode,
|
parse_mode=TestInlineQueryResultVideoBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultVideo.caption_entities,
|
caption_entities=TestInlineQueryResultVideoBase.caption_entities,
|
||||||
description=TestInlineQueryResultVideo.description,
|
description=TestInlineQueryResultVideoBase.description,
|
||||||
input_message_content=TestInlineQueryResultVideo.input_message_content,
|
input_message_content=TestInlineQueryResultVideoBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultVideo.reply_markup,
|
reply_markup=TestInlineQueryResultVideoBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultVideo:
|
class TestInlineQueryResultVideoBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "video"
|
type_ = "video"
|
||||||
video_url = "video url"
|
video_url = "video url"
|
||||||
|
@ -65,6 +65,8 @@ class TestInlineQueryResultVideo:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultVideoWithoutRequest(TestInlineQueryResultVideoBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_video, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_video, mro_slots):
|
||||||
inst = inline_query_result_video
|
inst = inline_query_result_video
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -28,22 +28,22 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def inline_query_result_voice():
|
def inline_query_result_voice():
|
||||||
return InlineQueryResultVoice(
|
return InlineQueryResultVoice(
|
||||||
id=TestInlineQueryResultVoice.id_,
|
id=TestInlineQueryResultVoiceBase.id_,
|
||||||
voice_url=TestInlineQueryResultVoice.voice_url,
|
voice_url=TestInlineQueryResultVoiceBase.voice_url,
|
||||||
title=TestInlineQueryResultVoice.title,
|
title=TestInlineQueryResultVoiceBase.title,
|
||||||
voice_duration=TestInlineQueryResultVoice.voice_duration,
|
voice_duration=TestInlineQueryResultVoiceBase.voice_duration,
|
||||||
caption=TestInlineQueryResultVoice.caption,
|
caption=TestInlineQueryResultVoiceBase.caption,
|
||||||
parse_mode=TestInlineQueryResultVoice.parse_mode,
|
parse_mode=TestInlineQueryResultVoiceBase.parse_mode,
|
||||||
caption_entities=TestInlineQueryResultVoice.caption_entities,
|
caption_entities=TestInlineQueryResultVoiceBase.caption_entities,
|
||||||
input_message_content=TestInlineQueryResultVoice.input_message_content,
|
input_message_content=TestInlineQueryResultVoiceBase.input_message_content,
|
||||||
reply_markup=TestInlineQueryResultVoice.reply_markup,
|
reply_markup=TestInlineQueryResultVoiceBase.reply_markup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInlineQueryResultVoice:
|
class TestInlineQueryResultVoiceBase:
|
||||||
id_ = "id"
|
id_ = "id"
|
||||||
type_ = "voice"
|
type_ = "voice"
|
||||||
voice_url = "voice url"
|
voice_url = "voice url"
|
||||||
|
@ -55,6 +55,8 @@ class TestInlineQueryResultVoice:
|
||||||
input_message_content = InputTextMessageContent("input_message_content")
|
input_message_content = InputTextMessageContent("input_message_content")
|
||||||
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
reply_markup = InlineKeyboardMarkup([[InlineKeyboardButton("reply_markup")]])
|
||||||
|
|
||||||
|
|
||||||
|
class TestInlineQueryResultVoiceWithoutRequest(TestInlineQueryResultVoiceBase):
|
||||||
def test_slot_behaviour(self, inline_query_result_voice, mro_slots):
|
def test_slot_behaviour(self, inline_query_result_voice, mro_slots):
|
||||||
inst = inline_query_result_voice
|
inst = inline_query_result_voice
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,20 +21,22 @@ import pytest
|
||||||
from telegram import InputContactMessageContent, User
|
from telegram import InputContactMessageContent, User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_contact_message_content():
|
def input_contact_message_content():
|
||||||
return InputContactMessageContent(
|
return InputContactMessageContent(
|
||||||
TestInputContactMessageContent.phone_number,
|
TestInputContactMessageContentBase.phone_number,
|
||||||
TestInputContactMessageContent.first_name,
|
TestInputContactMessageContentBase.first_name,
|
||||||
last_name=TestInputContactMessageContent.last_name,
|
TestInputContactMessageContentBase.last_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInputContactMessageContent:
|
class TestInputContactMessageContentBase:
|
||||||
phone_number = "phone number"
|
phone_number = "phone number"
|
||||||
first_name = "first name"
|
first_name = "first name"
|
||||||
last_name = "last name"
|
last_name = "last name"
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputContactMessageContentWithoutRequest(TestInputContactMessageContentBase):
|
||||||
def test_slot_behaviour(self, input_contact_message_content, mro_slots):
|
def test_slot_behaviour(self, input_contact_message_content, mro_slots):
|
||||||
inst = input_contact_message_content
|
inst = input_contact_message_content
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -26,12 +26,12 @@ from telegram import InputFile
|
||||||
from tests.conftest import data_file
|
from tests.conftest import data_file
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def png_file():
|
def png_file():
|
||||||
return data_file("game.png")
|
return data_file("game.png")
|
||||||
|
|
||||||
|
|
||||||
class TestInputFile:
|
class TestInputFileWithoutRequest:
|
||||||
def test_slot_behaviour(self, mro_slots):
|
def test_slot_behaviour(self, mro_slots):
|
||||||
inst = InputFile(BytesIO(b"blah"), filename="tg.jpg")
|
inst = InputFile(BytesIO(b"blah"), filename="tg.jpg")
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -65,7 +65,7 @@ class TestInputFile:
|
||||||
assert input_file.attach_name is None
|
assert input_file.attach_name is None
|
||||||
assert input_file.attach_uri 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
|
# Only test a few to make sure logic works okay
|
||||||
assert InputFile(data_file("telegram.jpg").open("rb")).mimetype == "image/jpeg"
|
assert InputFile(data_file("telegram.jpg").open("rb")).mimetype == "image/jpeg"
|
||||||
# For some reason python can guess the type on macOS
|
# For some reason python can guess the type on macOS
|
||||||
|
@ -139,6 +139,8 @@ class TestInputFile:
|
||||||
== "blah.jpg"
|
== "blah.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputFileWithRequest:
|
||||||
async def test_send_bytes(self, bot, chat_id):
|
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
|
# We test this here and not at the respective test modules because it's not worth
|
||||||
# duplicating the test for the different methods
|
# duplicating the test for the different methods
|
||||||
|
|
|
@ -22,33 +22,33 @@ import pytest
|
||||||
from telegram import InputInvoiceMessageContent, InputTextMessageContent, LabeledPrice
|
from telegram import InputInvoiceMessageContent, InputTextMessageContent, LabeledPrice
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_invoice_message_content():
|
def input_invoice_message_content():
|
||||||
return InputInvoiceMessageContent(
|
return InputInvoiceMessageContent(
|
||||||
title=TestInputInvoiceMessageContent.title,
|
title=TestInputInvoiceMessageContentBase.title,
|
||||||
description=TestInputInvoiceMessageContent.description,
|
description=TestInputInvoiceMessageContentBase.description,
|
||||||
payload=TestInputInvoiceMessageContent.payload,
|
payload=TestInputInvoiceMessageContentBase.payload,
|
||||||
provider_token=TestInputInvoiceMessageContent.provider_token,
|
provider_token=TestInputInvoiceMessageContentBase.provider_token,
|
||||||
currency=TestInputInvoiceMessageContent.currency,
|
currency=TestInputInvoiceMessageContentBase.currency,
|
||||||
prices=TestInputInvoiceMessageContent.prices,
|
prices=TestInputInvoiceMessageContentBase.prices,
|
||||||
max_tip_amount=TestInputInvoiceMessageContent.max_tip_amount,
|
max_tip_amount=TestInputInvoiceMessageContentBase.max_tip_amount,
|
||||||
suggested_tip_amounts=TestInputInvoiceMessageContent.suggested_tip_amounts,
|
suggested_tip_amounts=TestInputInvoiceMessageContentBase.suggested_tip_amounts,
|
||||||
provider_data=TestInputInvoiceMessageContent.provider_data,
|
provider_data=TestInputInvoiceMessageContentBase.provider_data,
|
||||||
photo_url=TestInputInvoiceMessageContent.photo_url,
|
photo_url=TestInputInvoiceMessageContentBase.photo_url,
|
||||||
photo_size=TestInputInvoiceMessageContent.photo_size,
|
photo_size=TestInputInvoiceMessageContentBase.photo_size,
|
||||||
photo_width=TestInputInvoiceMessageContent.photo_width,
|
photo_width=TestInputInvoiceMessageContentBase.photo_width,
|
||||||
photo_height=TestInputInvoiceMessageContent.photo_height,
|
photo_height=TestInputInvoiceMessageContentBase.photo_height,
|
||||||
need_name=TestInputInvoiceMessageContent.need_name,
|
need_name=TestInputInvoiceMessageContentBase.need_name,
|
||||||
need_phone_number=TestInputInvoiceMessageContent.need_phone_number,
|
need_phone_number=TestInputInvoiceMessageContentBase.need_phone_number,
|
||||||
need_email=TestInputInvoiceMessageContent.need_email,
|
need_email=TestInputInvoiceMessageContentBase.need_email,
|
||||||
need_shipping_address=TestInputInvoiceMessageContent.need_shipping_address,
|
need_shipping_address=TestInputInvoiceMessageContentBase.need_shipping_address,
|
||||||
send_phone_number_to_provider=TestInputInvoiceMessageContent.send_phone_number_to_provider,
|
send_phone_number_to_provider=TestInputInvoiceMessageContentBase.send_phone_number_to_provider, # noqa: E501
|
||||||
send_email_to_provider=TestInputInvoiceMessageContent.send_email_to_provider,
|
send_email_to_provider=TestInputInvoiceMessageContentBase.send_email_to_provider,
|
||||||
is_flexible=TestInputInvoiceMessageContent.is_flexible,
|
is_flexible=TestInputInvoiceMessageContentBase.is_flexible,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInputInvoiceMessageContent:
|
class TestInputInvoiceMessageContentBase:
|
||||||
title = "invoice title"
|
title = "invoice title"
|
||||||
description = "invoice description"
|
description = "invoice description"
|
||||||
payload = "invoice payload"
|
payload = "invoice payload"
|
||||||
|
@ -70,6 +70,8 @@ class TestInputInvoiceMessageContent:
|
||||||
send_email_to_provider = True
|
send_email_to_provider = True
|
||||||
is_flexible = True
|
is_flexible = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputInvoiceMessageContentWithoutRequest(TestInputInvoiceMessageContentBase):
|
||||||
def test_slot_behaviour(self, input_invoice_message_content, mro_slots):
|
def test_slot_behaviour(self, input_invoice_message_content, mro_slots):
|
||||||
inst = input_invoice_message_content
|
inst = input_invoice_message_content
|
||||||
for attr in inst.__slots__:
|
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.send_email_to_provider == self.send_email_to_provider
|
||||||
assert input_invoice_message_content.is_flexible == self.is_flexible
|
assert input_invoice_message_content.is_flexible == self.is_flexible
|
||||||
|
|
||||||
def test_suggested_tip_amonuts_always_tuple(self):
|
def test_suggested_tip_amonuts_always_tuple(self, input_invoice_message_content):
|
||||||
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,
|
|
||||||
)
|
|
||||||
assert isinstance(input_invoice_message_content.suggested_tip_amounts, tuple)
|
assert isinstance(input_invoice_message_content.suggested_tip_amounts, tuple)
|
||||||
assert 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
|
int(amount) for amount in self.suggested_tip_amounts
|
||||||
|
|
|
@ -21,19 +21,19 @@ import pytest
|
||||||
from telegram import InputLocationMessageContent, Location
|
from telegram import InputLocationMessageContent, Location
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_location_message_content():
|
def input_location_message_content():
|
||||||
return InputLocationMessageContent(
|
return InputLocationMessageContent(
|
||||||
TestInputLocationMessageContent.latitude,
|
TestInputLocationMessageContentBase.latitude,
|
||||||
TestInputLocationMessageContent.longitude,
|
TestInputLocationMessageContentBase.longitude,
|
||||||
live_period=TestInputLocationMessageContent.live_period,
|
live_period=TestInputLocationMessageContentBase.live_period,
|
||||||
horizontal_accuracy=TestInputLocationMessageContent.horizontal_accuracy,
|
horizontal_accuracy=TestInputLocationMessageContentBase.horizontal_accuracy,
|
||||||
heading=TestInputLocationMessageContent.heading,
|
heading=TestInputLocationMessageContentBase.heading,
|
||||||
proximity_alert_radius=TestInputLocationMessageContent.proximity_alert_radius,
|
proximity_alert_radius=TestInputLocationMessageContentBase.proximity_alert_radius,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInputLocationMessageContent:
|
class TestInputLocationMessageContentBase:
|
||||||
latitude = -23.691288
|
latitude = -23.691288
|
||||||
longitude = -46.788279
|
longitude = -46.788279
|
||||||
live_period = 80
|
live_period = 80
|
||||||
|
@ -41,6 +41,8 @@ class TestInputLocationMessageContent:
|
||||||
heading = 90
|
heading = 90
|
||||||
proximity_alert_radius = 999
|
proximity_alert_radius = 999
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputLocationMessageContentWithoutRequest(TestInputLocationMessageContentBase):
|
||||||
def test_slot_behaviour(self, input_location_message_content, mro_slots):
|
def test_slot_behaviour(self, input_location_message_content, mro_slots):
|
||||||
inst = input_location_message_content
|
inst = input_location_message_content
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import copy
|
import copy
|
||||||
from collections.abc import Sequence
|
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
|
from .test_video import video, video_file # noqa: F401
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_media_video(class_thumb_file):
|
def input_media_video(class_thumb_file):
|
||||||
return InputMediaVideo(
|
return InputMediaVideo(
|
||||||
media=TestInputMediaVideo.media,
|
media=TestInputMediaVideoBase.media,
|
||||||
caption=TestInputMediaVideo.caption,
|
caption=TestInputMediaVideoBase.caption,
|
||||||
width=TestInputMediaVideo.width,
|
width=TestInputMediaVideoBase.width,
|
||||||
height=TestInputMediaVideo.height,
|
height=TestInputMediaVideoBase.height,
|
||||||
duration=TestInputMediaVideo.duration,
|
duration=TestInputMediaVideoBase.duration,
|
||||||
parse_mode=TestInputMediaVideo.parse_mode,
|
parse_mode=TestInputMediaVideoBase.parse_mode,
|
||||||
caption_entities=TestInputMediaVideo.caption_entities,
|
caption_entities=TestInputMediaVideoBase.caption_entities,
|
||||||
thumb=class_thumb_file,
|
thumb=class_thumb_file,
|
||||||
supports_streaming=TestInputMediaVideo.supports_streaming,
|
supports_streaming=TestInputMediaVideoBase.supports_streaming,
|
||||||
has_spoiler=TestInputMediaVideo.has_spoiler,
|
has_spoiler=TestInputMediaVideoBase.has_spoiler,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_media_photo(class_thumb_file):
|
def input_media_photo():
|
||||||
return InputMediaPhoto(
|
return InputMediaPhoto(
|
||||||
media=TestInputMediaPhoto.media,
|
media=TestInputMediaPhotoBase.media,
|
||||||
caption=TestInputMediaPhoto.caption,
|
caption=TestInputMediaPhotoBase.caption,
|
||||||
parse_mode=TestInputMediaPhoto.parse_mode,
|
parse_mode=TestInputMediaPhotoBase.parse_mode,
|
||||||
caption_entities=TestInputMediaPhoto.caption_entities,
|
caption_entities=TestInputMediaPhotoBase.caption_entities,
|
||||||
has_spoiler=TestInputMediaPhoto.has_spoiler,
|
has_spoiler=TestInputMediaPhotoBase.has_spoiler,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_media_animation(class_thumb_file):
|
def input_media_animation(class_thumb_file):
|
||||||
return InputMediaAnimation(
|
return InputMediaAnimation(
|
||||||
media=TestInputMediaAnimation.media,
|
media=TestInputMediaAnimationBase.media,
|
||||||
caption=TestInputMediaAnimation.caption,
|
caption=TestInputMediaAnimationBase.caption,
|
||||||
parse_mode=TestInputMediaAnimation.parse_mode,
|
parse_mode=TestInputMediaAnimationBase.parse_mode,
|
||||||
caption_entities=TestInputMediaAnimation.caption_entities,
|
caption_entities=TestInputMediaAnimationBase.caption_entities,
|
||||||
width=TestInputMediaAnimation.width,
|
width=TestInputMediaAnimationBase.width,
|
||||||
height=TestInputMediaAnimation.height,
|
height=TestInputMediaAnimationBase.height,
|
||||||
thumb=class_thumb_file,
|
thumb=class_thumb_file,
|
||||||
duration=TestInputMediaAnimation.duration,
|
duration=TestInputMediaAnimationBase.duration,
|
||||||
has_spoiler=TestInputMediaAnimation.has_spoiler,
|
has_spoiler=TestInputMediaAnimationBase.has_spoiler,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_media_audio(class_thumb_file):
|
def input_media_audio(class_thumb_file):
|
||||||
return InputMediaAudio(
|
return InputMediaAudio(
|
||||||
media=TestInputMediaAudio.media,
|
media=TestInputMediaAudioBase.media,
|
||||||
caption=TestInputMediaAudio.caption,
|
caption=TestInputMediaAudioBase.caption,
|
||||||
duration=TestInputMediaAudio.duration,
|
duration=TestInputMediaAudioBase.duration,
|
||||||
performer=TestInputMediaAudio.performer,
|
performer=TestInputMediaAudioBase.performer,
|
||||||
title=TestInputMediaAudio.title,
|
title=TestInputMediaAudioBase.title,
|
||||||
thumb=class_thumb_file,
|
thumb=class_thumb_file,
|
||||||
parse_mode=TestInputMediaAudio.parse_mode,
|
parse_mode=TestInputMediaAudioBase.parse_mode,
|
||||||
caption_entities=TestInputMediaAudio.caption_entities,
|
caption_entities=TestInputMediaAudioBase.caption_entities,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_media_document(class_thumb_file):
|
def input_media_document(class_thumb_file):
|
||||||
return InputMediaDocument(
|
return InputMediaDocument(
|
||||||
media=TestInputMediaDocument.media,
|
media=TestInputMediaDocumentBase.media,
|
||||||
caption=TestInputMediaDocument.caption,
|
caption=TestInputMediaDocumentBase.caption,
|
||||||
thumb=class_thumb_file,
|
thumb=class_thumb_file,
|
||||||
parse_mode=TestInputMediaDocument.parse_mode,
|
parse_mode=TestInputMediaDocumentBase.parse_mode,
|
||||||
caption_entities=TestInputMediaDocument.caption_entities,
|
caption_entities=TestInputMediaDocumentBase.caption_entities,
|
||||||
disable_content_type_detection=TestInputMediaDocument.disable_content_type_detection,
|
disable_content_type_detection=TestInputMediaDocumentBase.disable_content_type_detection,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class CustomSequence(Sequence):
|
class TestInputMediaVideoBase:
|
||||||
def __init__(self, items):
|
|
||||||
self.items = items
|
|
||||||
|
|
||||||
def __getitem__(self, index):
|
|
||||||
return self.items[index]
|
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self.items)
|
|
||||||
|
|
||||||
|
|
||||||
class TestInputMediaVideo:
|
|
||||||
type_ = "video"
|
type_ = "video"
|
||||||
media = "NOTAREALFILEID"
|
media = "NOTAREALFILEID"
|
||||||
caption = "My Caption"
|
caption = "My Caption"
|
||||||
|
@ -147,6 +137,8 @@ class TestInputMediaVideo:
|
||||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||||
has_spoiler = True
|
has_spoiler = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputMediaVideoWithoutRequest(TestInputMediaVideoBase):
|
||||||
def test_slot_behaviour(self, input_media_video, mro_slots):
|
def test_slot_behaviour(self, input_media_video, mro_slots):
|
||||||
inst = input_media_video
|
inst = input_media_video
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -210,7 +202,7 @@ class TestInputMediaVideo:
|
||||||
assert input_media_video.thumb == data_file("telegram.jpg").as_uri()
|
assert input_media_video.thumb == data_file("telegram.jpg").as_uri()
|
||||||
|
|
||||||
|
|
||||||
class TestInputMediaPhoto:
|
class TestInputMediaPhotoBase:
|
||||||
type_ = "photo"
|
type_ = "photo"
|
||||||
media = "NOTAREALFILEID"
|
media = "NOTAREALFILEID"
|
||||||
caption = "My Caption"
|
caption = "My Caption"
|
||||||
|
@ -218,6 +210,8 @@ class TestInputMediaPhoto:
|
||||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||||
has_spoiler = True
|
has_spoiler = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputMediaPhotoWithoutRequest(TestInputMediaPhotoBase):
|
||||||
def test_slot_behaviour(self, input_media_photo, mro_slots):
|
def test_slot_behaviour(self, input_media_photo, mro_slots):
|
||||||
inst = input_media_photo
|
inst = input_media_photo
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -266,7 +260,7 @@ class TestInputMediaPhoto:
|
||||||
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
|
assert input_media_photo.media == data_file("telegram.mp4").as_uri()
|
||||||
|
|
||||||
|
|
||||||
class TestInputMediaAnimation:
|
class TestInputMediaAnimationBase:
|
||||||
type_ = "animation"
|
type_ = "animation"
|
||||||
media = "NOTAREALFILEID"
|
media = "NOTAREALFILEID"
|
||||||
caption = "My Caption"
|
caption = "My Caption"
|
||||||
|
@ -277,6 +271,8 @@ class TestInputMediaAnimation:
|
||||||
duration = 1
|
duration = 1
|
||||||
has_spoiler = True
|
has_spoiler = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputMediaAnimationWithoutRequest(TestInputMediaAnimationBase):
|
||||||
def test_slot_behaviour(self, input_media_animation, mro_slots):
|
def test_slot_behaviour(self, input_media_animation, mro_slots):
|
||||||
inst = input_media_animation
|
inst = input_media_animation
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -332,7 +328,7 @@ class TestInputMediaAnimation:
|
||||||
assert input_media_animation.thumb == data_file("telegram.jpg").as_uri()
|
assert input_media_animation.thumb == data_file("telegram.jpg").as_uri()
|
||||||
|
|
||||||
|
|
||||||
class TestInputMediaAudio:
|
class TestInputMediaAudioBase:
|
||||||
type_ = "audio"
|
type_ = "audio"
|
||||||
media = "NOTAREALFILEID"
|
media = "NOTAREALFILEID"
|
||||||
caption = "My Caption"
|
caption = "My Caption"
|
||||||
|
@ -342,6 +338,8 @@ class TestInputMediaAudio:
|
||||||
parse_mode = "HTML"
|
parse_mode = "HTML"
|
||||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputMediaAudioWithoutRequest(TestInputMediaAudioBase):
|
||||||
def test_slot_behaviour(self, input_media_audio, mro_slots):
|
def test_slot_behaviour(self, input_media_audio, mro_slots):
|
||||||
inst = input_media_audio
|
inst = input_media_audio
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -401,7 +399,7 @@ class TestInputMediaAudio:
|
||||||
assert input_media_audio.thumb == data_file("telegram.jpg").as_uri()
|
assert input_media_audio.thumb == data_file("telegram.jpg").as_uri()
|
||||||
|
|
||||||
|
|
||||||
class TestInputMediaDocument:
|
class TestInputMediaDocumentBase:
|
||||||
type_ = "document"
|
type_ = "document"
|
||||||
media = "NOTAREALFILEID"
|
media = "NOTAREALFILEID"
|
||||||
caption = "My Caption"
|
caption = "My Caption"
|
||||||
|
@ -409,6 +407,8 @@ class TestInputMediaDocument:
|
||||||
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
caption_entities = [MessageEntity(MessageEntity.BOLD, 0, 2)]
|
||||||
disable_content_type_detection = True
|
disable_content_type_detection = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputMediaDocumentWithoutRequest(TestInputMediaDocumentBase):
|
||||||
def test_slot_behaviour(self, input_media_document, mro_slots):
|
def test_slot_behaviour(self, input_media_document, mro_slots):
|
||||||
inst = input_media_document
|
inst = input_media_document
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -467,7 +467,7 @@ class TestInputMediaDocument:
|
||||||
assert input_media_document.thumb == data_file("telegram.jpg").as_uri()
|
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
|
def media_group(photo, thumb): # noqa: F811
|
||||||
return [
|
return [
|
||||||
InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"),
|
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
|
def media_group_no_caption_args(photo, thumb): # noqa: F811
|
||||||
return [InputMediaPhoto(photo), InputMediaPhoto(thumb), InputMediaPhoto(photo)]
|
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
|
def media_group_no_caption_only_caption_entities(photo, thumb): # noqa: F811
|
||||||
return [
|
return [
|
||||||
InputMediaPhoto(photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]),
|
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
|
def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
|
||||||
return [
|
return [
|
||||||
InputMediaPhoto(photo, parse_mode="Markdown"),
|
InputMediaPhoto(photo, parse_mode="Markdown"),
|
||||||
|
@ -499,32 +499,7 @@ def media_group_no_caption_only_parse_mode(photo, thumb): # noqa: F811
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class TestSendMediaGroup:
|
class TestSendMediaGroupWithoutRequest:
|
||||||
@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)
|
|
||||||
|
|
||||||
async def test_send_media_group_throws_error_with_group_caption_and_individual_captions(
|
async def test_send_media_group_throws_error_with_group_caption_and_individual_captions(
|
||||||
self,
|
self,
|
||||||
bot,
|
bot,
|
||||||
|
@ -544,6 +519,143 @@ class TestSendMediaGroup:
|
||||||
):
|
):
|
||||||
await bot.send_media_group(chat_id, group, caption="foo")
|
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(
|
@pytest.mark.parametrize(
|
||||||
"caption, parse_mode, caption_entities",
|
"caption, parse_mode, caption_entities",
|
||||||
[
|
[
|
||||||
|
@ -553,7 +665,6 @@ class TestSendMediaGroup:
|
||||||
("photo 1", None, [MessageEntity(MessageEntity.BOLD, 0, 5)]),
|
("photo 1", None, [MessageEntity(MessageEntity.BOLD, 0, 5)]),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
async def test_send_media_group_with_group_caption(
|
async def test_send_media_group_with_group_caption(
|
||||||
self,
|
self,
|
||||||
bot,
|
bot,
|
||||||
|
@ -598,16 +709,15 @@ class TestSendMediaGroup:
|
||||||
assert all(mes.caption is None for mes in other_messages)
|
assert all(mes.caption is None for mes in other_messages)
|
||||||
assert not any(mes.caption_entities 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):
|
async def test_send_media_group_all_args(self, bot, raw_bot, chat_id, media_group):
|
||||||
ext_bot = bot
|
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
|
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||||
# pytest.parametrize appears to be difficult ...
|
# pytest.parametrize appears to be difficult ...
|
||||||
|
aws = {b.send_message(chat_id, text="test") for b in (ext_bot, raw_bot)}
|
||||||
m1 = await bot.send_message(chat_id, text="test")
|
for msg_task in asyncio.as_completed(aws):
|
||||||
|
m1 = await msg_task
|
||||||
copied_media_group = copy.copy(media_group)
|
copied_media_group = copy.copy(media_group)
|
||||||
messages = await bot.send_media_group(
|
messages = await m1.get_bot().send_media_group(
|
||||||
chat_id,
|
chat_id,
|
||||||
media_group,
|
media_group,
|
||||||
disable_notification=True,
|
disable_notification=True,
|
||||||
|
@ -633,7 +743,6 @@ class TestSendMediaGroup:
|
||||||
)
|
)
|
||||||
assert all(mes.has_protected_content for mes in messages)
|
assert all(mes.has_protected_content for mes in messages)
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
|
||||||
async def test_send_media_group_with_spoiler(
|
async def test_send_media_group_with_spoiler(
|
||||||
self, bot, chat_id, photo_file, video_file # noqa: F811
|
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.media_group_id == messages[0].media_group_id for mes in messages)
|
||||||
assert all(mes.has_media_spoiler for mes in messages)
|
assert all(mes.has_media_spoiler for mes in messages)
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
async def test_edit_message_media(self, bot, raw_bot, chat_id, media_group):
|
||||||
async def test_send_media_group_custom_filename(
|
ext_bot = bot
|
||||||
self,
|
# We need to test 1) below both the bot and raw_bot and setting this up with
|
||||||
bot,
|
# pytest.parametrize appears to be difficult ...
|
||||||
chat_id,
|
aws = {b.send_media_group(chat_id, media_group) for b in (ext_bot, raw_bot)}
|
||||||
photo_file, # noqa: F811
|
for msg_task in asyncio.as_completed(aws):
|
||||||
animation_file, # noqa: F811
|
messages = await msg_task
|
||||||
audio_file, # noqa: F811
|
cid = messages[-1].chat.id
|
||||||
video_file, # noqa: F811
|
mid = messages[-1].message_id
|
||||||
monkeypatch,
|
copied_media = copy.copy(media_group[0])
|
||||||
):
|
new_message = (
|
||||||
async def make_assertion(url, request_data: RequestData, *args, **kwargs):
|
await messages[-1]
|
||||||
result = all(
|
.get_bot()
|
||||||
field_tuple[0] == "custom_filename"
|
.edit_message_media(chat_id=cid, message_id=mid, media=media_group[0])
|
||||||
for field_tuple in request_data.multipart_data.values()
|
|
||||||
)
|
)
|
||||||
if result is True:
|
assert isinstance(new_message, Message)
|
||||||
raise Exception("Test was successful")
|
|
||||||
|
|
||||||
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 = [
|
async def test_edit_message_media_new_file(self, bot, chat_id, media_group, thumb_file):
|
||||||
InputMediaAnimation(animation_file, filename="custom_filename"),
|
messages = await bot.send_media_group(chat_id, media_group)
|
||||||
InputMediaAudio(audio_file, filename="custom_filename"),
|
cid = messages[-1].chat.id
|
||||||
InputMediaPhoto(photo_file, filename="custom_filename"),
|
mid = messages[-1].message_id
|
||||||
InputMediaVideo(video_file, filename="custom_filename"),
|
new_message = await bot.edit_message_media(
|
||||||
]
|
chat_id=cid, message_id=mid, media=InputMediaPhoto(thumb_file)
|
||||||
|
|
||||||
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()),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
assert isinstance(new_message, Message)
|
||||||
|
|
||||||
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.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(
|
@pytest.mark.parametrize(
|
||||||
"default_bot,custom",
|
"default_bot,custom",
|
||||||
[
|
[
|
||||||
|
@ -773,19 +821,18 @@ class TestSendMediaGroup:
|
||||||
chat_id, media_group, reply_to_message_id=reply_to_message.message_id
|
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)
|
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
||||||
async def test_send_media_group_default_protect_content(
|
async def test_send_media_group_default_protect_content(
|
||||||
self, chat_id, media_group, default_bot
|
self, chat_id, media_group, default_bot
|
||||||
):
|
):
|
||||||
protected = await default_bot.send_media_group(chat_id, media_group)
|
tasks = asyncio.gather(
|
||||||
assert all(msg.has_protected_content for msg in protected)
|
default_bot.send_media_group(chat_id, media_group),
|
||||||
unprotected = await default_bot.send_media_group(
|
default_bot.send_media_group(chat_id, media_group, protect_content=False),
|
||||||
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)
|
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)
|
@pytest.mark.parametrize("default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True)
|
||||||
async def test_send_media_group_default_parse_mode(
|
async def test_send_media_group_default_parse_mode(
|
||||||
self, chat_id, media_group_no_caption_args, default_bot
|
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
|
# 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)
|
assert not any(item.parse_mode for item in media_group_no_caption_args)
|
||||||
|
|
||||||
overridden_markdown_v2 = await default_bot.send_media_group(
|
tasks = asyncio.gather(
|
||||||
|
default_bot.send_media_group(
|
||||||
chat_id,
|
chat_id,
|
||||||
media_group_no_caption_args.copy(),
|
media_group_no_caption_args.copy(),
|
||||||
caption="*photo* 1",
|
caption="*photo* 1",
|
||||||
parse_mode=ParseMode.MARKDOWN_V2,
|
parse_mode=ParseMode.MARKDOWN_V2,
|
||||||
)
|
),
|
||||||
|
default_bot.send_media_group(
|
||||||
overridden_none = await default_bot.send_media_group(
|
|
||||||
chat_id,
|
chat_id,
|
||||||
media_group_no_caption_args.copy(),
|
media_group_no_caption_args.copy(),
|
||||||
caption="<b>photo</b> 1",
|
caption="<b>photo</b> 1",
|
||||||
parse_mode=None,
|
parse_mode=None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
overridden_markdown_v2, overridden_none = await tasks
|
||||||
|
|
||||||
# Make sure first message got the caption, which will lead to Telegram
|
# Make sure first message got the caption, which will lead to Telegram
|
||||||
# displaying its caption as group caption
|
# displaying its caption as group caption
|
||||||
|
@ -830,53 +879,6 @@ class TestSendMediaGroup:
|
||||||
assert all(mes.caption is None for mes in other_messages)
|
assert all(mes.caption is None for mes in other_messages)
|
||||||
assert not any(mes.caption_entities 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(
|
@pytest.mark.parametrize(
|
||||||
"default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True, ids=["HTML-Bot"]
|
"default_bot", [{"parse_mode": ParseMode.HTML}], indirect=True, ids=["HTML-Bot"]
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,22 +22,24 @@ from telegram import InputTextMessageContent, MessageEntity
|
||||||
from telegram.constants import ParseMode
|
from telegram.constants import ParseMode
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_text_message_content():
|
def input_text_message_content():
|
||||||
return InputTextMessageContent(
|
return InputTextMessageContent(
|
||||||
TestInputTextMessageContent.message_text,
|
TestInputTextMessageContentBase.message_text,
|
||||||
parse_mode=TestInputTextMessageContent.parse_mode,
|
parse_mode=TestInputTextMessageContentBase.parse_mode,
|
||||||
entities=TestInputTextMessageContent.entities,
|
entities=TestInputTextMessageContentBase.entities,
|
||||||
disable_web_page_preview=TestInputTextMessageContent.disable_web_page_preview,
|
disable_web_page_preview=TestInputTextMessageContentBase.disable_web_page_preview,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInputTextMessageContent:
|
class TestInputTextMessageContentBase:
|
||||||
message_text = "*message text*"
|
message_text = "*message text*"
|
||||||
parse_mode = ParseMode.MARKDOWN
|
parse_mode = ParseMode.MARKDOWN
|
||||||
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
|
entities = [MessageEntity(MessageEntity.ITALIC, 0, 7)]
|
||||||
disable_web_page_preview = True
|
disable_web_page_preview = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputTextMessageContentWithoutRequest(TestInputTextMessageContentBase):
|
||||||
def test_slot_behaviour(self, input_text_message_content, mro_slots):
|
def test_slot_behaviour(self, input_text_message_content, mro_slots):
|
||||||
inst = input_text_message_content
|
inst = input_text_message_content
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,21 @@ import pytest
|
||||||
from telegram import InputVenueMessageContent, Location
|
from telegram import InputVenueMessageContent, Location
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def input_venue_message_content():
|
def input_venue_message_content():
|
||||||
return InputVenueMessageContent(
|
return InputVenueMessageContent(
|
||||||
TestInputVenueMessageContent.latitude,
|
TestInputVenueMessageContentBase.latitude,
|
||||||
TestInputVenueMessageContent.longitude,
|
TestInputVenueMessageContentBase.longitude,
|
||||||
TestInputVenueMessageContent.title,
|
TestInputVenueMessageContentBase.title,
|
||||||
TestInputVenueMessageContent.address,
|
TestInputVenueMessageContentBase.address,
|
||||||
foursquare_id=TestInputVenueMessageContent.foursquare_id,
|
foursquare_id=TestInputVenueMessageContentBase.foursquare_id,
|
||||||
foursquare_type=TestInputVenueMessageContent.foursquare_type,
|
foursquare_type=TestInputVenueMessageContentBase.foursquare_type,
|
||||||
google_place_id=TestInputVenueMessageContent.google_place_id,
|
google_place_id=TestInputVenueMessageContentBase.google_place_id,
|
||||||
google_place_type=TestInputVenueMessageContent.google_place_type,
|
google_place_type=TestInputVenueMessageContentBase.google_place_type,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInputVenueMessageContent:
|
class TestInputVenueMessageContentBase:
|
||||||
latitude = 1.0
|
latitude = 1.0
|
||||||
longitude = 2.0
|
longitude = 2.0
|
||||||
title = "title"
|
title = "title"
|
||||||
|
@ -45,6 +45,8 @@ class TestInputVenueMessageContent:
|
||||||
google_place_id = "google place id"
|
google_place_id = "google place id"
|
||||||
google_place_type = "google place type"
|
google_place_type = "google place type"
|
||||||
|
|
||||||
|
|
||||||
|
class TestInputVenueMessageContentWithoutRequest(TestInputVenueMessageContentBase):
|
||||||
def test_slot_behaviour(self, input_venue_message_content, mro_slots):
|
def test_slot_behaviour(self, input_venue_message_content, mro_slots):
|
||||||
inst = input_venue_message_content
|
inst = input_venue_message_content
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import Invoice, LabeledPrice
|
from telegram import Invoice, LabeledPrice
|
||||||
|
@ -23,18 +25,18 @@ from telegram.error import BadRequest
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def invoice():
|
def invoice():
|
||||||
return Invoice(
|
return Invoice(
|
||||||
TestInvoice.title,
|
TestInvoiceBase.title,
|
||||||
TestInvoice.description,
|
TestInvoiceBase.description,
|
||||||
TestInvoice.start_parameter,
|
TestInvoiceBase.start_parameter,
|
||||||
TestInvoice.currency,
|
TestInvoiceBase.currency,
|
||||||
TestInvoice.total_amount,
|
TestInvoiceBase.total_amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestInvoice:
|
class TestInvoiceBase:
|
||||||
payload = "payload"
|
payload = "payload"
|
||||||
prices = [LabeledPrice("Fish", 100), LabeledPrice("Fish Tax", 1000)]
|
prices = [LabeledPrice("Fish", 100), LabeledPrice("Fish Tax", 1000)]
|
||||||
provider_data = """{"test":"test"}"""
|
provider_data = """{"test":"test"}"""
|
||||||
|
@ -46,6 +48,8 @@ class TestInvoice:
|
||||||
max_tip_amount = 42
|
max_tip_amount = 42
|
||||||
suggested_tip_amounts = [13, 42]
|
suggested_tip_amounts = [13, 42]
|
||||||
|
|
||||||
|
|
||||||
|
class TestInvoiceWithoutRequest(TestInvoiceBase):
|
||||||
def test_slot_behaviour(self, invoice, mro_slots):
|
def test_slot_behaviour(self, invoice, mro_slots):
|
||||||
for attr in invoice.__slots__:
|
for attr in invoice.__slots__:
|
||||||
assert getattr(invoice, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(invoice, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -54,11 +58,11 @@ class TestInvoice:
|
||||||
def test_de_json(self, bot):
|
def test_de_json(self, bot):
|
||||||
invoice_json = Invoice.de_json(
|
invoice_json = Invoice.de_json(
|
||||||
{
|
{
|
||||||
"title": TestInvoice.title,
|
"title": self.title,
|
||||||
"description": TestInvoice.description,
|
"description": self.description,
|
||||||
"start_parameter": TestInvoice.start_parameter,
|
"start_parameter": self.start_parameter,
|
||||||
"currency": TestInvoice.currency,
|
"currency": self.currency,
|
||||||
"total_amount": TestInvoice.total_amount,
|
"total_amount": self.total_amount,
|
||||||
},
|
},
|
||||||
bot,
|
bot,
|
||||||
)
|
)
|
||||||
|
@ -80,100 +84,12 @@ class TestInvoice:
|
||||||
assert invoice_dict["currency"] == invoice.currency
|
assert invoice_dict["currency"] == invoice.currency
|
||||||
assert invoice_dict["total_amount"] == invoice.total_amount
|
assert invoice_dict["total_amount"] == invoice.total_amount
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
async def test_send_invoice_all_args_mock(self, bot, monkeypatch):
|
||||||
async def test_send_required_args_only(self, bot, chat_id, provider_token):
|
# We do this one as safety guard to make sure that we pass all of the optional
|
||||||
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
|
|
||||||
# parameters correctly because #2526 went unnoticed for 3 years …
|
# parameters correctly because #2526 went unnoticed for 3 years …
|
||||||
async def make_assertion(*args, **_):
|
async def make_assertion(*args, **_):
|
||||||
kwargs = args[1]
|
kwargs = args[1]
|
||||||
return (
|
return all([kwargs[key] == key for key in kwargs])
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_send_message", make_assertion)
|
monkeypatch.setattr(bot, "_send_message", make_assertion)
|
||||||
assert await bot.send_invoice(
|
assert await bot.send_invoice(
|
||||||
|
@ -183,7 +99,7 @@ class TestInvoice:
|
||||||
payload="payload",
|
payload="payload",
|
||||||
provider_token="provider_token",
|
provider_token="provider_token",
|
||||||
currency="currency",
|
currency="currency",
|
||||||
prices=self.prices,
|
prices="prices",
|
||||||
max_tip_amount="max_tip_amount",
|
max_tip_amount="max_tip_amount",
|
||||||
suggested_tip_amounts="suggested_tip_amounts",
|
suggested_tip_amounts="suggested_tip_amounts",
|
||||||
start_parameter="start_parameter",
|
start_parameter="start_parameter",
|
||||||
|
@ -203,33 +119,10 @@ class TestInvoice:
|
||||||
protect_content=True,
|
protect_content=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
async def test_send_all_args_create_invoice_link(
|
async def test_send_all_args_create_invoice_link(self, bot, monkeypatch):
|
||||||
self, bot, chat_id, provider_token, monkeypatch
|
|
||||||
):
|
|
||||||
async def make_assertion(*args, **_):
|
async def make_assertion(*args, **_):
|
||||||
kwargs = args[1]
|
kwargs = args[1]
|
||||||
return (
|
return all([kwargs[i] == i for i in kwargs])
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
monkeypatch.setattr(bot, "_post", make_assertion)
|
monkeypatch.setattr(bot, "_post", make_assertion)
|
||||||
assert await bot.create_invoice_link(
|
assert await bot.create_invoice_link(
|
||||||
|
@ -238,7 +131,7 @@ class TestInvoice:
|
||||||
payload="payload",
|
payload="payload",
|
||||||
provider_token="provider_token",
|
provider_token="provider_token",
|
||||||
currency="currency",
|
currency="currency",
|
||||||
prices=self.prices,
|
prices="prices",
|
||||||
max_tip_amount="max_tip_amount",
|
max_tip_amount="max_tip_amount",
|
||||||
suggested_tip_amounts="suggested_tip_amounts",
|
suggested_tip_amounts="suggested_tip_amounts",
|
||||||
provider_data="provider_data",
|
provider_data="provider_data",
|
||||||
|
@ -273,7 +166,75 @@ class TestInvoice:
|
||||||
start_parameter=self.start_parameter,
|
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(
|
@pytest.mark.parametrize(
|
||||||
"default_bot,custom",
|
"default_bot,custom",
|
||||||
[
|
[
|
||||||
|
@ -326,12 +287,8 @@ class TestInvoice:
|
||||||
reply_to_message_id=reply_to_message.message_id,
|
reply_to_message_id=reply_to_message.message_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.flaky(3, 1)
|
async def test_send_all_args_send_invoice(self, bot, chat_id, provider_token):
|
||||||
@pytest.mark.parametrize("default_bot", [{"protect_content": True}], indirect=True)
|
message = await bot.send_invoice(
|
||||||
async def test_send_invoice_default_protect_content(
|
|
||||||
self, chat_id, default_bot, provider_token
|
|
||||||
):
|
|
||||||
protected = await default_bot.send_invoice(
|
|
||||||
chat_id,
|
chat_id,
|
||||||
self.title,
|
self.title,
|
||||||
self.description,
|
self.description,
|
||||||
|
@ -339,31 +296,26 @@ class TestInvoice:
|
||||||
provider_token,
|
provider_token,
|
||||||
self.currency,
|
self.currency,
|
||||||
self.prices,
|
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):
|
for attr in message.invoice.__slots__:
|
||||||
a = Invoice("invoice", "desc", "start", "EUR", 7)
|
assert getattr(message.invoice, attr) == getattr(self, attr)
|
||||||
b = Invoice("invoice", "desc", "start", "EUR", 7)
|
assert message.has_protected_content
|
||||||
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)
|
|
||||||
|
|
|
@ -27,9 +27,7 @@ import time
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue
|
from telegram.ext import ApplicationBuilder, CallbackContext, ContextTypes, Job, JobQueue
|
||||||
from tests.auxil.object_conversions import env_var_2_bool
|
from tests.conftest import TEST_WITH_OPT_DEPS, make_bot
|
||||||
|
|
||||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
|
|
||||||
|
|
||||||
if TEST_WITH_OPT_DEPS:
|
if TEST_WITH_OPT_DEPS:
|
||||||
import pytz
|
import pytz
|
||||||
|
@ -46,7 +44,7 @@ class CustomContext(CallbackContext):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
async def job_queue(bot, app):
|
async def job_queue(app):
|
||||||
jq = JobQueue()
|
jq = JobQueue()
|
||||||
jq.set_application(app)
|
jq.set_application(app)
|
||||||
await jq.start()
|
await jq.start()
|
||||||
|
@ -179,9 +177,9 @@ class TestJobQueue:
|
||||||
job_queue.run_repeating(
|
job_queue.run_repeating(
|
||||||
self.job_run_once, 0.5, first=dtm.datetime.now(timezone) + dtm.timedelta(seconds=0.2)
|
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
|
assert self.result == 0
|
||||||
await asyncio.sleep(0.2)
|
await asyncio.sleep(0.25)
|
||||||
assert self.result == 1
|
assert self.result == 1
|
||||||
|
|
||||||
async def test_run_repeating_last(self, job_queue):
|
async def test_run_repeating_last(self, job_queue):
|
||||||
|
@ -192,7 +190,7 @@ class TestJobQueue:
|
||||||
assert self.result == 1
|
assert self.result == 1
|
||||||
|
|
||||||
async def test_run_repeating_last_timezone(self, job_queue, timezone):
|
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(
|
job_queue.run_repeating(
|
||||||
self.job_run_once, 0.25, last=dtm.datetime.now(timezone) + dtm.timedelta(seconds=0.4)
|
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)
|
j2 = job_queue.run_repeating(self.job_run_once, 0.2)
|
||||||
|
|
||||||
await asyncio.sleep(0.25)
|
await asyncio.sleep(0.25)
|
||||||
|
assert self.result == 1
|
||||||
|
|
||||||
j1.schedule_removal()
|
j1.schedule_removal()
|
||||||
j2.schedule_removal()
|
j2.schedule_removal()
|
||||||
|
@ -273,8 +272,8 @@ class TestJobQueue:
|
||||||
await asyncio.sleep(0.3)
|
await asyncio.sleep(0.3)
|
||||||
assert self.result == 1
|
assert self.result == 1
|
||||||
|
|
||||||
async def test_in_application(self, bot):
|
async def test_in_application(self, bot_info):
|
||||||
app = ApplicationBuilder().token(bot.token).build()
|
app = ApplicationBuilder().bot(make_bot(bot_info)).build()
|
||||||
async with app:
|
async with app:
|
||||||
assert not app.job_queue.scheduler.running
|
assert not app.job_queue.scheduler.running
|
||||||
await app.start()
|
await app.start()
|
||||||
|
@ -311,7 +310,7 @@ class TestJobQueue:
|
||||||
# Testing running at a specific datetime
|
# Testing running at a specific datetime
|
||||||
delta, now = dtm.timedelta(seconds=0.5), dtm.datetime.now(UTC)
|
delta, now = dtm.timedelta(seconds=0.5), dtm.datetime.now(UTC)
|
||||||
when = now + delta
|
when = now + delta
|
||||||
expected_time = (now + delta).timestamp()
|
expected_time = when.timestamp()
|
||||||
|
|
||||||
job_queue.run_once(self.job_datetime_tests, when)
|
job_queue.run_once(self.job_datetime_tests, when)
|
||||||
await asyncio.sleep(0.6)
|
await asyncio.sleep(0.6)
|
||||||
|
@ -444,8 +443,11 @@ class TestJobQueue:
|
||||||
callback = self.job_run_once
|
callback = self.job_run_once
|
||||||
|
|
||||||
job1 = job_queue.run_once(callback, 10, name="name1")
|
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")
|
job2 = job_queue.run_once(callback, 10, name="name1")
|
||||||
|
await asyncio.sleep(0.03)
|
||||||
job3 = job_queue.run_once(callback, 10, name="name2")
|
job3 = job_queue.run_once(callback, 10, name="name2")
|
||||||
|
await asyncio.sleep(0.03)
|
||||||
|
|
||||||
assert job_queue.jobs() == (job1, job2, job3)
|
assert job_queue.jobs() == (job1, job2, job3)
|
||||||
assert job_queue.get_jobs_by_name("name1") == (job1, job2)
|
assert job_queue.get_jobs_by_name("name1") == (job1, job2)
|
||||||
|
@ -453,9 +455,9 @@ class TestJobQueue:
|
||||||
|
|
||||||
async def test_job_run(self, app):
|
async def test_job_run(self, app):
|
||||||
job = app.job_queue.run_repeating(self.job_run_once, 0.02)
|
job = app.job_queue.run_repeating(self.job_run_once, 0.02)
|
||||||
await asyncio.sleep(0.05)
|
await asyncio.sleep(0.05) # the job queue has not started yet
|
||||||
assert self.result == 0
|
assert self.result == 0 # so the job will not run
|
||||||
await job.run(app)
|
await job.run(app) # but this will force it to run
|
||||||
assert self.result == 1
|
assert self.result == 1
|
||||||
|
|
||||||
async def test_enable_disable_job(self, job_queue):
|
async def test_enable_disable_job(self, job_queue):
|
||||||
|
@ -569,7 +571,7 @@ class TestJobQueue:
|
||||||
)
|
)
|
||||||
job_queue.set_application(application)
|
job_queue.set_application(application)
|
||||||
|
|
||||||
def callback(context):
|
async def callback(context):
|
||||||
self.result = (
|
self.result = (
|
||||||
type(context),
|
type(context),
|
||||||
context.user_data,
|
context.user_data,
|
||||||
|
@ -603,8 +605,8 @@ class TestJobQueue:
|
||||||
if wait:
|
if wait:
|
||||||
assert not task.done()
|
assert not task.done()
|
||||||
ready_event.set()
|
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()
|
assert task.done()
|
||||||
else:
|
else:
|
||||||
await asyncio.sleep(0.1)
|
await asyncio.sleep(0.1) # unfortunately we will get a CancelledError here
|
||||||
assert task.done()
|
assert task.done()
|
||||||
|
|
|
@ -28,20 +28,20 @@ from telegram import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def keyboard_button():
|
def keyboard_button():
|
||||||
return KeyboardButton(
|
return KeyboardButton(
|
||||||
TestKeyboardButton.text,
|
TestKeyboardButtonBase.text,
|
||||||
request_location=TestKeyboardButton.request_location,
|
request_location=TestKeyboardButtonBase.request_location,
|
||||||
request_contact=TestKeyboardButton.request_contact,
|
request_contact=TestKeyboardButtonBase.request_contact,
|
||||||
request_poll=TestKeyboardButton.request_poll,
|
request_poll=TestKeyboardButtonBase.request_poll,
|
||||||
web_app=TestKeyboardButton.web_app,
|
web_app=TestKeyboardButtonBase.web_app,
|
||||||
request_chat=TestKeyboardButton.request_chat,
|
request_chat=TestKeyboardButtonBase.request_chat,
|
||||||
request_user=TestKeyboardButton.request_user,
|
request_user=TestKeyboardButtonBase.request_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboardButton:
|
class TestKeyboardButtonBase:
|
||||||
text = "text"
|
text = "text"
|
||||||
request_location = True
|
request_location = True
|
||||||
request_contact = True
|
request_contact = True
|
||||||
|
@ -50,6 +50,8 @@ class TestKeyboardButton:
|
||||||
request_chat = KeyboardButtonRequestChat(1, True)
|
request_chat = KeyboardButtonRequestChat(1, True)
|
||||||
request_user = KeyboardButtonRequestUser(2)
|
request_user = KeyboardButtonRequestUser(2)
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeyboardButtonWithoutRequest(TestKeyboardButtonBase):
|
||||||
def test_slot_behaviour(self, keyboard_button, mro_slots):
|
def test_slot_behaviour(self, keyboard_button, mro_slots):
|
||||||
inst = keyboard_button
|
inst = keyboard_button
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,14 +21,16 @@ import pytest
|
||||||
from telegram import KeyboardButtonPollType, Poll
|
from telegram import KeyboardButtonPollType, Poll
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def keyboard_button_poll_type():
|
def keyboard_button_poll_type():
|
||||||
return KeyboardButtonPollType(TestKeyboardButtonPollType.type)
|
return KeyboardButtonPollType(TestKeyboardButtonPollTypeBase.type)
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboardButtonPollType:
|
class TestKeyboardButtonPollTypeBase:
|
||||||
type = Poll.QUIZ
|
type = Poll.QUIZ
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeyboardButtonPollTypeWithoutRequest(TestKeyboardButtonPollTypeBase):
|
||||||
def test_slot_behaviour(self, keyboard_button_poll_type, mro_slots):
|
def test_slot_behaviour(self, keyboard_button_poll_type, mro_slots):
|
||||||
inst = keyboard_button_poll_type
|
inst = keyboard_button_poll_type
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -25,17 +25,19 @@ from telegram import ChatAdministratorRights, KeyboardButtonRequestChat, Keyboar
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def request_user():
|
def request_user():
|
||||||
return KeyboardButtonRequestUser(
|
return KeyboardButtonRequestUser(
|
||||||
TestKeyboardButtonRequestUser.request_id,
|
TestKeyboardButtonRequestUserBase.request_id,
|
||||||
TestKeyboardButtonRequestUser.user_is_bot,
|
TestKeyboardButtonRequestUserBase.user_is_bot,
|
||||||
TestKeyboardButtonRequestUser.user_is_premium,
|
TestKeyboardButtonRequestUserBase.user_is_premium,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboardButtonRequestUser:
|
class TestKeyboardButtonRequestUserBase:
|
||||||
request_id = 123
|
request_id = 123
|
||||||
user_is_bot = True
|
user_is_bot = True
|
||||||
user_is_premium = False
|
user_is_premium = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeyboardButtonRequestUserWithoutRequest(TestKeyboardButtonRequestUserBase):
|
||||||
def test_slot_behaviour(self, request_user, mro_slots):
|
def test_slot_behaviour(self, request_user, mro_slots):
|
||||||
for attr in request_user.__slots__:
|
for attr in request_user.__slots__:
|
||||||
assert getattr(request_user, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(request_user, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -78,18 +80,18 @@ class TestKeyboardButtonRequestUser:
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def request_chat():
|
def request_chat():
|
||||||
return KeyboardButtonRequestChat(
|
return KeyboardButtonRequestChat(
|
||||||
TestKeyboardButtonRequestChat.request_id,
|
TestKeyboardButtonRequestChatBase.request_id,
|
||||||
TestKeyboardButtonRequestChat.chat_is_channel,
|
TestKeyboardButtonRequestChatBase.chat_is_channel,
|
||||||
TestKeyboardButtonRequestChat.chat_is_forum,
|
TestKeyboardButtonRequestChatBase.chat_is_forum,
|
||||||
TestKeyboardButtonRequestChat.chat_has_username,
|
TestKeyboardButtonRequestChatBase.chat_has_username,
|
||||||
TestKeyboardButtonRequestChat.chat_is_created,
|
TestKeyboardButtonRequestChatBase.chat_is_created,
|
||||||
TestKeyboardButtonRequestChat.user_administrator_rights,
|
TestKeyboardButtonRequestChatBase.user_administrator_rights,
|
||||||
TestKeyboardButtonRequestChat.bot_administrator_rights,
|
TestKeyboardButtonRequestChatBase.bot_administrator_rights,
|
||||||
TestKeyboardButtonRequestChat.bot_is_member,
|
TestKeyboardButtonRequestChatBase.bot_is_member,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestKeyboardButtonRequestChat:
|
class TestKeyboardButtonRequestChatBase:
|
||||||
request_id = 456
|
request_id = 456
|
||||||
chat_is_channel = True
|
chat_is_channel = True
|
||||||
chat_is_forum = False
|
chat_is_forum = False
|
||||||
|
@ -103,6 +105,8 @@ class TestKeyboardButtonRequestChat:
|
||||||
)
|
)
|
||||||
bot_is_member = True
|
bot_is_member = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeyboardButtonRequestChatWithoutRequest(TestKeyboardButtonRequestChatBase):
|
||||||
def test_slot_behaviour(self, request_chat, mro_slots):
|
def test_slot_behaviour(self, request_chat, mro_slots):
|
||||||
for attr in request_chat.__slots__:
|
for attr in request_chat.__slots__:
|
||||||
assert getattr(request_chat, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(request_chat, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -21,15 +21,17 @@ import pytest
|
||||||
from telegram import LabeledPrice, Location
|
from telegram import LabeledPrice, Location
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def labeled_price():
|
def labeled_price():
|
||||||
return LabeledPrice(TestLabeledPrice.label, TestLabeledPrice.amount)
|
return LabeledPrice(TestLabeledPriceBase.label, TestLabeledPriceBase.amount)
|
||||||
|
|
||||||
|
|
||||||
class TestLabeledPrice:
|
class TestLabeledPriceBase:
|
||||||
label = "label"
|
label = "label"
|
||||||
amount = 100
|
amount = 100
|
||||||
|
|
||||||
|
|
||||||
|
class TestLabeledPriceWithoutRequest(TestLabeledPriceBase):
|
||||||
def test_slot_behaviour(self, labeled_price, mro_slots):
|
def test_slot_behaviour(self, labeled_price, mro_slots):
|
||||||
inst = labeled_price
|
inst = labeled_price
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import Location
|
from telegram import Location
|
||||||
|
@ -23,19 +25,19 @@ from telegram.error import BadRequest
|
||||||
from telegram.request import RequestData
|
from telegram.request import RequestData
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def location():
|
def location():
|
||||||
return Location(
|
return Location(
|
||||||
latitude=TestLocation.latitude,
|
latitude=TestLocationBase.latitude,
|
||||||
longitude=TestLocation.longitude,
|
longitude=TestLocationBase.longitude,
|
||||||
horizontal_accuracy=TestLocation.horizontal_accuracy,
|
horizontal_accuracy=TestLocationBase.horizontal_accuracy,
|
||||||
live_period=TestLocation.live_period,
|
live_period=TestLocationBase.live_period,
|
||||||
heading=TestLocation.live_period,
|
heading=TestLocationBase.live_period,
|
||||||
proximity_alert_radius=TestLocation.proximity_alert_radius,
|
proximity_alert_radius=TestLocationBase.proximity_alert_radius,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestLocation:
|
class TestLocationBase:
|
||||||
latitude = -23.691288
|
latitude = -23.691288
|
||||||
longitude = -46.788279
|
longitude = -46.788279
|
||||||
horizontal_accuracy = 999
|
horizontal_accuracy = 999
|
||||||
|
@ -43,6 +45,8 @@ class TestLocation:
|
||||||
heading = 90
|
heading = 90
|
||||||
proximity_alert_radius = 50
|
proximity_alert_radius = 50
|
||||||
|
|
||||||
|
|
||||||
|
class TestLocationWithoutRequest(TestLocationBase):
|
||||||
def test_slot_behaviour(self, location, mro_slots):
|
def test_slot_behaviour(self, location, mro_slots):
|
||||||
for attr in location.__slots__:
|
for attr in location.__slots__:
|
||||||
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(location, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -50,12 +54,12 @@ class TestLocation:
|
||||||
|
|
||||||
def test_de_json(self, bot):
|
def test_de_json(self, bot):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"latitude": TestLocation.latitude,
|
"latitude": self.latitude,
|
||||||
"longitude": TestLocation.longitude,
|
"longitude": self.longitude,
|
||||||
"horizontal_accuracy": TestLocation.horizontal_accuracy,
|
"horizontal_accuracy": self.horizontal_accuracy,
|
||||||
"live_period": TestLocation.live_period,
|
"live_period": self.live_period,
|
||||||
"heading": TestLocation.heading,
|
"heading": self.heading,
|
||||||
"proximity_alert_radius": TestLocation.proximity_alert_radius,
|
"proximity_alert_radius": self.proximity_alert_radius,
|
||||||
}
|
}
|
||||||
location = Location.de_json(json_dict, bot)
|
location = Location.de_json(json_dict, bot)
|
||||||
assert location.api_kwargs == {}
|
assert location.api_kwargs == {}
|
||||||
|
@ -67,7 +71,139 @@ class TestLocation:
|
||||||
assert location.heading == self.heading
|
assert location.heading == self.heading
|
||||||
assert location.proximity_alert_radius == self.proximity_alert_radius
|
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
|
@pytest.mark.xfail
|
||||||
async def test_send_live_location(self, bot, chat_id):
|
async def test_send_live_location(self, bot, chat_id):
|
||||||
message = await bot.send_location(
|
message = await bot.send_location(
|
||||||
|
@ -110,135 +246,3 @@ class TestLocation:
|
||||||
await bot.edit_message_live_location(
|
await bot.edit_message_live_location(
|
||||||
message.chat_id, message.message_id, latitude=52.223880, longitude=5.164306
|
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)
|
|
||||||
|
|
|
@ -21,22 +21,24 @@ import pytest
|
||||||
from telegram import LoginUrl
|
from telegram import LoginUrl
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def login_url():
|
def login_url():
|
||||||
return LoginUrl(
|
return LoginUrl(
|
||||||
url=TestLoginUrl.url,
|
url=TestLoginUrlBase.url,
|
||||||
forward_text=TestLoginUrl.forward_text,
|
forward_text=TestLoginUrlBase.forward_text,
|
||||||
bot_username=TestLoginUrl.bot_username,
|
bot_username=TestLoginUrlBase.bot_username,
|
||||||
request_write_access=TestLoginUrl.request_write_access,
|
request_write_access=TestLoginUrlBase.request_write_access,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestLoginUrl:
|
class TestLoginUrlBase:
|
||||||
url = "http://www.google.com"
|
url = "http://www.google.com"
|
||||||
forward_text = "Send me forward!"
|
forward_text = "Send me forward!"
|
||||||
bot_username = "botname"
|
bot_username = "botname"
|
||||||
request_write_access = True
|
request_write_access = True
|
||||||
|
|
||||||
|
|
||||||
|
class TestLoginUrlWithoutRequest(TestLoginUrlBase):
|
||||||
def test_slot_behaviour(self, login_url, mro_slots):
|
def test_slot_behaviour(self, login_url, mro_slots):
|
||||||
for attr in login_url.__slots__:
|
for attr in login_url.__slots__:
|
||||||
assert getattr(login_url, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(login_url, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -31,7 +31,7 @@ from telegram import (
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
scope="class",
|
scope="module",
|
||||||
params=[
|
params=[
|
||||||
MenuButton.DEFAULT,
|
MenuButton.DEFAULT,
|
||||||
MenuButton.WEB_APP,
|
MenuButton.WEB_APP,
|
||||||
|
@ -43,7 +43,7 @@ def scope_type(request):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
scope="class",
|
scope="module",
|
||||||
params=[
|
params=[
|
||||||
MenuButtonDefault,
|
MenuButtonDefault,
|
||||||
MenuButtonCommands,
|
MenuButtonCommands,
|
||||||
|
@ -60,7 +60,7 @@ def scope_class(request):
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
scope="class",
|
scope="module",
|
||||||
params=[
|
params=[
|
||||||
(MenuButtonDefault, MenuButton.DEFAULT),
|
(MenuButtonDefault, MenuButton.DEFAULT),
|
||||||
(MenuButtonCommands, MenuButton.COMMANDS),
|
(MenuButtonCommands, MenuButton.COMMANDS),
|
||||||
|
@ -76,24 +76,26 @@ def scope_class_and_type(request):
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def menu_button(scope_class_and_type):
|
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
|
# 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(
|
return scope_class_and_type[0].de_json(
|
||||||
dict(
|
dict(
|
||||||
type=scope_class_and_type[1],
|
type=scope_class_and_type[1],
|
||||||
text=TestMenuButton.text,
|
text=TestMenuButtonselfBase.text,
|
||||||
web_app=TestMenuButton.web_app.to_dict(),
|
web_app=TestMenuButtonselfBase.web_app.to_dict(),
|
||||||
),
|
),
|
||||||
bot=None,
|
bot=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# All the scope types are very similar, so we test everything via parametrization
|
class TestMenuButtonselfBase:
|
||||||
class TestMenuButton:
|
|
||||||
text = "button_text"
|
text = "button_text"
|
||||||
web_app = WebAppInfo(url="https://python-telegram-bot.org/web_app")
|
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):
|
def test_slot_behaviour(self, menu_button, mro_slots):
|
||||||
for attr in menu_button.__slots__:
|
for attr in menu_button.__slots__:
|
||||||
assert getattr(menu_button, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(menu_button, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
|
|
@ -66,13 +66,13 @@ from tests.auxil.bot_method_checks import (
|
||||||
from tests.test_passport import RAW_PASSPORT_DATA
|
from tests.test_passport import RAW_PASSPORT_DATA
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def message(bot):
|
def message(bot):
|
||||||
message = Message(
|
message = Message(
|
||||||
message_id=TestMessage.id_,
|
message_id=TestMessageBase.id_,
|
||||||
date=TestMessage.date,
|
date=TestMessageBase.date,
|
||||||
chat=copy(TestMessage.chat),
|
chat=copy(TestMessageBase.chat),
|
||||||
from_user=copy(TestMessage.from_user),
|
from_user=copy(TestMessageBase.from_user),
|
||||||
)
|
)
|
||||||
message.set_bot(bot)
|
message.set_bot(bot)
|
||||||
message._unfreeze()
|
message._unfreeze()
|
||||||
|
@ -271,17 +271,17 @@ def message(bot):
|
||||||
)
|
)
|
||||||
def message_params(bot, request):
|
def message_params(bot, request):
|
||||||
message = Message(
|
message = Message(
|
||||||
message_id=TestMessage.id_,
|
message_id=TestMessageBase.id_,
|
||||||
from_user=TestMessage.from_user,
|
from_user=TestMessageBase.from_user,
|
||||||
date=TestMessage.date,
|
date=TestMessageBase.date,
|
||||||
chat=TestMessage.chat,
|
chat=TestMessageBase.chat,
|
||||||
**request.param,
|
**request.param,
|
||||||
)
|
)
|
||||||
message.set_bot(bot)
|
message.set_bot(bot)
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
class TestMessage:
|
class TestMessageBase:
|
||||||
id_ = 1
|
id_ = 1
|
||||||
from_user = User(2, "testuser", False)
|
from_user = User(2, "testuser", False)
|
||||||
date = datetime.utcnow()
|
date = datetime.utcnow()
|
||||||
|
@ -347,6 +347,13 @@ class TestMessage:
|
||||||
caption_entities=[MessageEntity(**e) for e in test_entities_v2],
|
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):
|
def test_all_possibilities_de_json_and_to_dict(self, bot, message_params):
|
||||||
new = Message.de_json(message_params.to_dict(), bot)
|
new = Message.de_json(message_params.to_dict(), bot)
|
||||||
assert new.api_kwargs == {}
|
assert new.api_kwargs == {}
|
||||||
|
@ -358,10 +365,26 @@ class TestMessage:
|
||||||
for slot in new.__slots__:
|
for slot in new.__slots__:
|
||||||
assert not isinstance(new[slot], dict)
|
assert not isinstance(new[slot], dict)
|
||||||
|
|
||||||
def test_slot_behaviour(self, message, mro_slots):
|
def test_equality(self):
|
||||||
for attr in message.__slots__:
|
id_ = 1
|
||||||
assert getattr(message, attr, "err") != "err", f"got extra slot '{attr}'"
|
a = Message(id_, self.date, self.chat, from_user=self.from_user)
|
||||||
assert len(mro_slots(message)) == len(set(mro_slots(message))), "duplicate slot"
|
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):
|
async def test_parse_entity(self):
|
||||||
text = (
|
text = (
|
||||||
|
@ -563,7 +586,7 @@ class TestMessage:
|
||||||
expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape")
|
expected = b"\\U0001f469\\u200d\\U0001f469\\u200d *ABC*".decode("unicode-escape")
|
||||||
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
|
bold_entity = MessageEntity(type=MessageEntity.BOLD, offset=7, length=3)
|
||||||
message = Message(
|
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
|
assert expected == message.text_markdown
|
||||||
|
|
||||||
|
@ -1826,34 +1849,3 @@ class TestMessage:
|
||||||
|
|
||||||
monkeypatch.setattr(message.get_bot(), "unpin_all_forum_topic_messages", make_assertion)
|
monkeypatch.setattr(message.get_bot(), "unpin_all_forum_topic_messages", make_assertion)
|
||||||
assert await message.unpin_all_forum_topic_messages()
|
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)
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
from telegram import MessageAutoDeleteTimerChanged, VideoChatEnded
|
from telegram import MessageAutoDeleteTimerChanged, VideoChatEnded
|
||||||
|
|
||||||
|
|
||||||
class TestMessageAutoDeleteTimerChanged:
|
class TestMessageAutoDeleteTimerChangedWithoutRequest:
|
||||||
message_auto_delete_time = 100
|
message_auto_delete_time = 100
|
||||||
|
|
||||||
def test_slot_behaviour(self, mro_slots):
|
def test_slot_behaviour(self, mro_slots):
|
||||||
|
|
|
@ -22,7 +22,7 @@ from telegram import MessageEntity, User
|
||||||
from telegram.constants import MessageEntityType
|
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):
|
def message_entity(request):
|
||||||
type_ = request.param
|
type_ = request.param
|
||||||
url = None
|
url = None
|
||||||
|
@ -37,12 +37,14 @@ def message_entity(request):
|
||||||
return MessageEntity(type_, 1, 3, url=url, user=user, language=language)
|
return MessageEntity(type_, 1, 3, url=url, user=user, language=language)
|
||||||
|
|
||||||
|
|
||||||
class TestMessageEntity:
|
class TestMessageEntityBase:
|
||||||
type_ = "url"
|
type_ = "url"
|
||||||
offset = 1
|
offset = 1
|
||||||
length = 2
|
length = 2
|
||||||
url = "url"
|
url = "url"
|
||||||
|
|
||||||
|
|
||||||
|
class TestMessageEntityWithoutRequest(TestMessageEntityBase):
|
||||||
def test_slot_behaviour(self, message_entity, mro_slots):
|
def test_slot_behaviour(self, message_entity, mro_slots):
|
||||||
inst = message_entity
|
inst = message_entity
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -20,12 +20,12 @@ import pytest
|
||||||
from telegram import MessageId, User
|
from telegram import MessageId, User
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def message_id():
|
def message_id():
|
||||||
return MessageId(message_id=TestMessageId.m_id)
|
return MessageId(message_id=TestMessageIdWithoutRequest.m_id)
|
||||||
|
|
||||||
|
|
||||||
class TestMessageId:
|
class TestMessageIdWithoutRequest:
|
||||||
m_id = 1234
|
m_id = 1234
|
||||||
|
|
||||||
def test_slot_behaviour(self, message_id, mro_slots):
|
def test_slot_behaviour(self, message_id, mro_slots):
|
||||||
|
|
|
@ -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
|
with the TEST_WITH_OPT_DEPS environment variable set to False in addition to the regular test suite
|
||||||
"""
|
"""
|
||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from telegram import _bot as bot
|
from telegram import _bot as bot
|
||||||
from telegram._passport import credentials as credentials
|
from telegram._passport import credentials as credentials
|
||||||
from tests.auxil.object_conversions import env_var_2_bool
|
from tests.conftest import TEST_WITH_OPT_DEPS
|
||||||
|
|
||||||
TEST_WITH_OPT_DEPS = env_var_2_bool(os.getenv("TEST_WITH_OPT_DEPS", True))
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
TEST_WITH_OPT_DEPS, reason="Only relevant if the optional dependency is not installed"
|
TEST_WITH_OPT_DEPS, reason="Only relevant if the optional dependency is not installed"
|
||||||
)
|
)
|
||||||
class TestNoPassport:
|
class TestNoPassportWithoutRequest:
|
||||||
def test_bot_init(self, bot_info, monkeypatch):
|
def test_bot_init(self, bot_info):
|
||||||
with pytest.raises(RuntimeError, match="passport"):
|
with pytest.raises(RuntimeError, match="passport"):
|
||||||
bot.Bot(bot_info["token"], private_key=1, private_key_password=2)
|
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"):
|
with pytest.raises(RuntimeError, match="passport"):
|
||||||
credentials.decrypt(1, 1, 1)
|
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")
|
ec = credentials.EncryptedCredentials("data", "hash", "secret")
|
||||||
with pytest.raises(RuntimeError, match="passport"):
|
with pytest.raises(RuntimeError, match="passport"):
|
||||||
ec.decrypted_secret
|
ec.decrypted_secret
|
||||||
|
|
|
@ -21,22 +21,24 @@ import pytest
|
||||||
from telegram import OrderInfo, ShippingAddress
|
from telegram import OrderInfo, ShippingAddress
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def order_info():
|
def order_info():
|
||||||
return OrderInfo(
|
return OrderInfo(
|
||||||
TestOrderInfo.name,
|
TestOrderInfoBase.name,
|
||||||
TestOrderInfo.phone_number,
|
TestOrderInfoBase.phone_number,
|
||||||
TestOrderInfo.email,
|
TestOrderInfoBase.email,
|
||||||
TestOrderInfo.shipping_address,
|
TestOrderInfoBase.shipping_address,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestOrderInfo:
|
class TestOrderInfoBase:
|
||||||
name = "name"
|
name = "name"
|
||||||
phone_number = "phone_number"
|
phone_number = "phone_number"
|
||||||
email = "email"
|
email = "email"
|
||||||
shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1")
|
shipping_address = ShippingAddress("GB", "", "London", "12 Grimmauld Place", "", "WC1")
|
||||||
|
|
||||||
|
|
||||||
|
class TestOrderInfoWithoutRequest(TestOrderInfoBase):
|
||||||
def test_slot_behaviour(self, order_info, mro_slots):
|
def test_slot_behaviour(self, order_info, mro_slots):
|
||||||
for attr in order_info.__slots__:
|
for attr in order_info.__slots__:
|
||||||
assert getattr(order_info, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(order_info, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -44,10 +46,10 @@ class TestOrderInfo:
|
||||||
|
|
||||||
def test_de_json(self, bot):
|
def test_de_json(self, bot):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"name": TestOrderInfo.name,
|
"name": self.name,
|
||||||
"phone_number": TestOrderInfo.phone_number,
|
"phone_number": self.phone_number,
|
||||||
"email": TestOrderInfo.email,
|
"email": self.email,
|
||||||
"shipping_address": TestOrderInfo.shipping_address.to_dict(),
|
"shipping_address": self.shipping_address.to_dict(),
|
||||||
}
|
}
|
||||||
order_info = OrderInfo.de_json(json_dict, bot)
|
order_info = OrderInfo.de_json(json_dict, bot)
|
||||||
assert order_info.api_kwargs == {}
|
assert order_info.api_kwargs == {}
|
||||||
|
|
|
@ -128,7 +128,7 @@ RAW_PASSPORT_DATA = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="module")
|
||||||
def all_passport_data():
|
def all_passport_data():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -214,12 +214,12 @@ def all_passport_data():
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="module")
|
||||||
def passport_data(bot):
|
def passport_data(bot):
|
||||||
return PassportData.de_json(RAW_PASSPORT_DATA, bot=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_id = "DgADBAADEQQAAkopgFNr6oi-wISRtAI"
|
||||||
driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12"
|
driver_license_selfie_file_unique_id = "d4e390cca57b4da5a65322b304762a12"
|
||||||
driver_license_front_side_file_id = "DgADBAADxwMAApnQgVPK2-ckL2eXVAI"
|
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_file_hash = "Cila/qLXSBH7DpZFbb5bRZIRxeFW2uv/ulL0u0JNsYI="
|
||||||
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
|
driver_license_selfie_credentials_secret = "tivdId6RNYNsvXYPppdzrbxOBuBOr9wXRPDcCvnXU7E="
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportWithoutRequest(TestPassportBase):
|
||||||
def test_slot_behaviour(self, passport_data, mro_slots):
|
def test_slot_behaviour(self, passport_data, mro_slots):
|
||||||
inst = passport_data
|
inst = passport_data
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
@ -387,6 +389,37 @@ class TestPassport:
|
||||||
assert email.type == "email"
|
assert email.type == "email"
|
||||||
assert email.email == "fb3e3i47zt@dispostable.com"
|
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):
|
def test_all_types(self, passport_data, bot, all_passport_data):
|
||||||
credentials = passport_data.decrypted_credentials.to_dict()
|
credentials = passport_data.decrypted_credentials.to_dict()
|
||||||
|
|
||||||
|
@ -422,13 +455,6 @@ class TestPassport:
|
||||||
assert isinstance(new, PassportData)
|
assert isinstance(new, PassportData)
|
||||||
assert new.decrypted_data
|
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 def test_passport_data_okay_with_non_crypto_bot(self, bot):
|
||||||
async with make_bot(token=bot.token) as b:
|
async with make_bot(token=bot.token) as b:
|
||||||
assert PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
|
assert PassportData.de_json(RAW_PASSPORT_DATA, bot=b)
|
||||||
|
@ -506,26 +532,3 @@ class TestPassport:
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
assert message
|
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)
|
|
||||||
|
|
|
@ -21,23 +21,25 @@ import pytest
|
||||||
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
|
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_data_field():
|
def passport_element_error_data_field():
|
||||||
return PassportElementErrorDataField(
|
return PassportElementErrorDataField(
|
||||||
TestPassportElementErrorDataField.type_,
|
TestPassportElementErrorDataFieldBase.type_,
|
||||||
TestPassportElementErrorDataField.field_name,
|
TestPassportElementErrorDataFieldBase.field_name,
|
||||||
TestPassportElementErrorDataField.data_hash,
|
TestPassportElementErrorDataFieldBase.data_hash,
|
||||||
TestPassportElementErrorDataField.message,
|
TestPassportElementErrorDataFieldBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorDataField:
|
class TestPassportElementErrorDataFieldBase:
|
||||||
source = "data"
|
source = "data"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
field_name = "test_field"
|
field_name = "test_field"
|
||||||
data_hash = "data_hash"
|
data_hash = "data_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorDataFieldWithoutRequest(TestPassportElementErrorDataFieldBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_data_field, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_data_field, mro_slots):
|
||||||
inst = passport_element_error_data_field
|
inst = passport_element_error_data_field
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorFile, PassportElementErrorSelfie
|
from telegram import PassportElementErrorFile, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_file():
|
def passport_element_error_file():
|
||||||
return PassportElementErrorFile(
|
return PassportElementErrorFile(
|
||||||
TestPassportElementErrorFile.type_,
|
TestPassportElementErrorFileBase.type_,
|
||||||
TestPassportElementErrorFile.file_hash,
|
TestPassportElementErrorFileBase.file_hash,
|
||||||
TestPassportElementErrorFile.message,
|
TestPassportElementErrorFileBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorFile:
|
class TestPassportElementErrorFileBase:
|
||||||
source = "file"
|
source = "file"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hash = "file_hash"
|
file_hash = "file_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorFileWithoutRequest(TestPassportElementErrorFileBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_file, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_file, mro_slots):
|
||||||
inst = passport_element_error_file
|
inst = passport_element_error_file
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorFiles, PassportElementErrorSelfie
|
from telegram import PassportElementErrorFiles, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_files():
|
def passport_element_error_files():
|
||||||
return PassportElementErrorFiles(
|
return PassportElementErrorFiles(
|
||||||
TestPassportElementErrorFiles.type_,
|
TestPassportElementErrorFilesBase.type_,
|
||||||
TestPassportElementErrorFiles.file_hashes,
|
TestPassportElementErrorFilesBase.file_hashes,
|
||||||
TestPassportElementErrorFiles.message,
|
TestPassportElementErrorFilesBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorFiles:
|
class TestPassportElementErrorFilesBase:
|
||||||
source = "files"
|
source = "files"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hashes = ["hash1", "hash2"]
|
file_hashes = ["hash1", "hash2"]
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorFilesWithoutRequest(TestPassportElementErrorFilesBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_files, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_files, mro_slots):
|
||||||
inst = passport_element_error_files
|
inst = passport_element_error_files
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorFrontSide, PassportElementErrorSelfie
|
from telegram import PassportElementErrorFrontSide, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_front_side():
|
def passport_element_error_front_side():
|
||||||
return PassportElementErrorFrontSide(
|
return PassportElementErrorFrontSide(
|
||||||
TestPassportElementErrorFrontSide.type_,
|
TestPassportElementErrorFrontSideBase.type_,
|
||||||
TestPassportElementErrorFrontSide.file_hash,
|
TestPassportElementErrorFrontSideBase.file_hash,
|
||||||
TestPassportElementErrorFrontSide.message,
|
TestPassportElementErrorFrontSideBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorFrontSide:
|
class TestPassportElementErrorFrontSideBase:
|
||||||
source = "front_side"
|
source = "front_side"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hash = "file_hash"
|
file_hash = "file_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorFrontSideWithoutRequest(TestPassportElementErrorFrontSideBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_front_side, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_front_side, mro_slots):
|
||||||
inst = passport_element_error_front_side
|
inst = passport_element_error_front_side
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorReverseSide, PassportElementErrorSelfie
|
from telegram import PassportElementErrorReverseSide, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_reverse_side():
|
def passport_element_error_reverse_side():
|
||||||
return PassportElementErrorReverseSide(
|
return PassportElementErrorReverseSide(
|
||||||
TestPassportElementErrorReverseSide.type_,
|
TestPassportElementErrorReverseSideBase.type_,
|
||||||
TestPassportElementErrorReverseSide.file_hash,
|
TestPassportElementErrorReverseSideBase.file_hash,
|
||||||
TestPassportElementErrorReverseSide.message,
|
TestPassportElementErrorReverseSideBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorReverseSide:
|
class TestPassportElementErrorReverseSideBase:
|
||||||
source = "reverse_side"
|
source = "reverse_side"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hash = "file_hash"
|
file_hash = "file_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorReverseSideWithoutRequest(TestPassportElementErrorReverseSideBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_reverse_side, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_reverse_side, mro_slots):
|
||||||
inst = passport_element_error_reverse_side
|
inst = passport_element_error_reverse_side
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
|
from telegram import PassportElementErrorDataField, PassportElementErrorSelfie
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_selfie():
|
def passport_element_error_selfie():
|
||||||
return PassportElementErrorSelfie(
|
return PassportElementErrorSelfie(
|
||||||
TestPassportElementErrorSelfie.type_,
|
TestPassportElementErrorSelfieBase.type_,
|
||||||
TestPassportElementErrorSelfie.file_hash,
|
TestPassportElementErrorSelfieBase.file_hash,
|
||||||
TestPassportElementErrorSelfie.message,
|
TestPassportElementErrorSelfieBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorSelfie:
|
class TestPassportElementErrorSelfieBase:
|
||||||
source = "selfie"
|
source = "selfie"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hash = "file_hash"
|
file_hash = "file_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorSelfieWithoutRequest(TestPassportElementErrorSelfieBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_selfie, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_selfie, mro_slots):
|
||||||
inst = passport_element_error_selfie
|
inst = passport_element_error_selfie
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,25 @@ import pytest
|
||||||
from telegram import PassportElementErrorDataField, PassportElementErrorTranslationFile
|
from telegram import PassportElementErrorDataField, PassportElementErrorTranslationFile
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_translation_file():
|
def passport_element_error_translation_file():
|
||||||
return PassportElementErrorTranslationFile(
|
return PassportElementErrorTranslationFile(
|
||||||
TestPassportElementErrorTranslationFile.type_,
|
TestPassportElementErrorTranslationFileBase.type_,
|
||||||
TestPassportElementErrorTranslationFile.file_hash,
|
TestPassportElementErrorTranslationFileBase.file_hash,
|
||||||
TestPassportElementErrorTranslationFile.message,
|
TestPassportElementErrorTranslationFileBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorTranslationFile:
|
class TestPassportElementErrorTranslationFileBase:
|
||||||
source = "translation_file"
|
source = "translation_file"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hash = "file_hash"
|
file_hash = "file_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorTranslationFileWithoutRequest(
|
||||||
|
TestPassportElementErrorTranslationFileBase
|
||||||
|
):
|
||||||
def test_slot_behaviour(self, passport_element_error_translation_file, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_translation_file, mro_slots):
|
||||||
inst = passport_element_error_translation_file
|
inst = passport_element_error_translation_file
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,25 @@ import pytest
|
||||||
from telegram import PassportElementErrorSelfie, PassportElementErrorTranslationFiles
|
from telegram import PassportElementErrorSelfie, PassportElementErrorTranslationFiles
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_translation_files():
|
def passport_element_error_translation_files():
|
||||||
return PassportElementErrorTranslationFiles(
|
return PassportElementErrorTranslationFiles(
|
||||||
TestPassportElementErrorTranslationFiles.type_,
|
TestPassportElementErrorTranslationFilesBase.type_,
|
||||||
TestPassportElementErrorTranslationFiles.file_hashes,
|
TestPassportElementErrorTranslationFilesBase.file_hashes,
|
||||||
TestPassportElementErrorTranslationFiles.message,
|
TestPassportElementErrorTranslationFilesBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorTranslationFiles:
|
class TestPassportElementErrorTranslationFilesBase:
|
||||||
source = "translation_files"
|
source = "translation_files"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
file_hashes = ["hash1", "hash2"]
|
file_hashes = ["hash1", "hash2"]
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorTranslationFilesWithoutRequest(
|
||||||
|
TestPassportElementErrorTranslationFilesBase
|
||||||
|
):
|
||||||
def test_slot_behaviour(self, passport_element_error_translation_files, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_translation_files, mro_slots):
|
||||||
inst = passport_element_error_translation_files
|
inst = passport_element_error_translation_files
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -21,21 +21,23 @@ import pytest
|
||||||
from telegram import PassportElementErrorDataField, PassportElementErrorUnspecified
|
from telegram import PassportElementErrorDataField, PassportElementErrorUnspecified
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def passport_element_error_unspecified():
|
def passport_element_error_unspecified():
|
||||||
return PassportElementErrorUnspecified(
|
return PassportElementErrorUnspecified(
|
||||||
TestPassportElementErrorUnspecified.type_,
|
TestPassportElementErrorUnspecifiedBase.type_,
|
||||||
TestPassportElementErrorUnspecified.element_hash,
|
TestPassportElementErrorUnspecifiedBase.element_hash,
|
||||||
TestPassportElementErrorUnspecified.message,
|
TestPassportElementErrorUnspecifiedBase.message,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestPassportElementErrorUnspecified:
|
class TestPassportElementErrorUnspecifiedBase:
|
||||||
source = "unspecified"
|
source = "unspecified"
|
||||||
type_ = "test_type"
|
type_ = "test_type"
|
||||||
element_hash = "element_hash"
|
element_hash = "element_hash"
|
||||||
message = "Error message"
|
message = "Error message"
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportElementErrorUnspecifiedWithoutRequest(TestPassportElementErrorUnspecifiedBase):
|
||||||
def test_slot_behaviour(self, passport_element_error_unspecified, mro_slots):
|
def test_slot_behaviour(self, passport_element_error_unspecified, mro_slots):
|
||||||
inst = passport_element_error_unspecified
|
inst = passport_element_error_unspecified
|
||||||
for attr in inst.__slots__:
|
for attr in inst.__slots__:
|
||||||
|
|
|
@ -29,21 +29,23 @@ from tests.auxil.bot_method_checks import (
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="class")
|
||||||
def passport_file(bot):
|
def passport_file(bot):
|
||||||
pf = PassportFile(
|
pf = PassportFile(
|
||||||
file_id=TestPassportFile.file_id,
|
file_id=TestPassportFileBase.file_id,
|
||||||
file_unique_id=TestPassportFile.file_unique_id,
|
file_unique_id=TestPassportFileBase.file_unique_id,
|
||||||
file_size=TestPassportFile.file_size,
|
file_size=TestPassportFileBase.file_size,
|
||||||
file_date=TestPassportFile.file_date,
|
file_date=TestPassportFileBase.file_date,
|
||||||
)
|
)
|
||||||
pf.set_bot(bot)
|
pf.set_bot(bot)
|
||||||
return pf
|
return pf
|
||||||
|
|
||||||
|
|
||||||
class TestPassportFile:
|
class TestPassportFileBase:
|
||||||
file_id = "data"
|
file_id = "data"
|
||||||
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
file_unique_id = "adc3145fd2e84d95b64d68eaa22aa33e"
|
||||||
file_size = 50
|
file_size = 50
|
||||||
file_date = 1532879128
|
file_date = 1532879128
|
||||||
|
|
||||||
|
|
||||||
|
class TestPassportFileWithoutRequest(TestPassportFileBase):
|
||||||
def test_slot_behaviour(self, passport_file, mro_slots):
|
def test_slot_behaviour(self, passport_file, mro_slots):
|
||||||
inst = passport_file
|
inst = passport_file
|
||||||
for attr in inst.__slots__:
|
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_size"] == passport_file.file_size
|
||||||
assert passport_file_dict["file_date"] == passport_file.file_date
|
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):
|
def test_equality(self):
|
||||||
a = PassportFile(self.file_id, self.file_unique_id, self.file_size, self.file_date)
|
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)
|
b = PassportFile("", self.file_unique_id, self.file_size, self.file_date)
|
||||||
|
@ -99,3 +86,18 @@ class TestPassportFile:
|
||||||
|
|
||||||
assert a != e
|
assert a != e
|
||||||
assert hash(a) != hash(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"
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser Public License
|
# You should have received a copy of the GNU Lesser Public License
|
||||||
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
# along with this program. If not, see [http://www.gnu.org/licenses/].
|
||||||
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -35,34 +36,32 @@ from tests.conftest import data_file, expect_bad_request
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def photo_file():
|
def photo_file():
|
||||||
f = data_file("telegram.jpg").open("rb")
|
with data_file("telegram.jpg").open("rb") as f:
|
||||||
yield f
|
yield f
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
async def _photo(bot, chat_id):
|
async def _photo(bot, chat_id):
|
||||||
async def func():
|
async def func():
|
||||||
with data_file("telegram.jpg").open("rb") as f:
|
with data_file("telegram.jpg").open("rb") as f:
|
||||||
photo = (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
|
return (await bot.send_photo(chat_id, photo=f, read_timeout=50)).photo
|
||||||
return photo
|
|
||||||
|
|
||||||
return await expect_bad_request(
|
return await expect_bad_request(
|
||||||
func, "Type of file mismatch", "Telegram did not accept the file."
|
func, "Type of file mismatch", "Telegram did not accept the file."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def thumb(_photo):
|
def thumb(_photo):
|
||||||
return _photo[0]
|
return _photo[0]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="class")
|
@pytest.fixture(scope="module")
|
||||||
def photo(_photo):
|
def photo(_photo):
|
||||||
return _photo[-1]
|
return _photo[-1]
|
||||||
|
|
||||||
|
|
||||||
class TestPhoto:
|
class TestPhotoBase:
|
||||||
width = 800
|
width = 800
|
||||||
height = 800
|
height = 800
|
||||||
caption = "<b>PhotoTest</b> - *Caption*"
|
caption = "<b>PhotoTest</b> - *Caption*"
|
||||||
|
@ -71,6 +70,8 @@ class TestPhoto:
|
||||||
# so we accept three different sizes here. Shouldn't be too much
|
# so we accept three different sizes here. Shouldn't be too much
|
||||||
file_size = [29176, 27662]
|
file_size = [29176, 27662]
|
||||||
|
|
||||||
|
|
||||||
|
class TestPhotoWithoutRequest(TestPhotoBase):
|
||||||
def test_slot_behaviour(self, photo, mro_slots):
|
def test_slot_behaviour(self, photo, mro_slots):
|
||||||
for attr in photo.__slots__:
|
for attr in photo.__slots__:
|
||||||
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
|
assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'"
|
||||||
|
@ -98,328 +99,6 @@ class TestPhoto:
|
||||||
assert thumb.height == 90
|
assert thumb.height == 90
|
||||||
assert thumb.file_size == 1477
|
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("<b>", "").replace("</b>", "")
|
|
||||||
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):
|
def test_de_json(self, bot, photo):
|
||||||
json_dict = {
|
json_dict = {
|
||||||
"file_id": photo.file_id,
|
"file_id": photo.file_id,
|
||||||
|
@ -447,31 +126,6 @@ class TestPhoto:
|
||||||
assert photo_dict["height"] == photo.height
|
assert photo_dict["height"] == photo.height
|
||||||
assert photo_dict["file_size"] == photo.file_size
|
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):
|
def test_equality(self, photo):
|
||||||
a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height)
|
a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height)
|
||||||
b = PhotoSize("", 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 a != e
|
||||||
assert hash(a) != hash(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("<b>", "").replace("</b>", "")
|
||||||
|
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="")
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue