diff --git a/changes/unreleased/5304.6rKY3k6kGsCdnZFP8jdsQ2.toml b/changes/unreleased/5304.6rKY3k6kGsCdnZFP8jdsQ2.toml new file mode 100644 index 00000000000..319572984c9 --- /dev/null +++ b/changes/unreleased/5304.6rKY3k6kGsCdnZFP8jdsQ2.toml @@ -0,0 +1,5 @@ +internal = "Make `*WithoutRequest` tests actually not use the network" +[[pull_requests]] +uid = "5304" +author_uids = ["harshil21"] +closes_threads = ["4829"] diff --git a/pyproject.toml b/pyproject.toml index 6a4299d896e..e6828285ca0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -104,6 +104,8 @@ tests = [ "pytest-asyncio==0.21.2", # xdist runs tests in parallel "pytest-xdist==3.8.0", + # no_req tests must be fully offline, including during higher-scoped fixture setup + "pytest-socket==0.8.0", # Used for flaky tests (flaky decorator) "flaky>=3.8.1", # used in test_official for parsing tg docs @@ -173,7 +175,7 @@ typing-extensions = false ignore = ["PLR2004", "PLR0911", "PLR0912", "PLR0913", "PLR0915", "PERF203", "ASYNC240"] select = ["E", "F", "I", "PL", "UP", "RUF", "PTH", "C4", "B", "PIE", "SIM", "RET", "RSE", "G", "ISC", "PT", "ASYNC", "TCH", "SLOT", "PERF", "PYI", "FLY", "AIR", "RUF022", - "RUF023", "Q", "INP", "W", "YTT", "DTZ", "ARG", "T20", "FURB", "DOC", "TRY", + "RUF023", "Q", "INP", "W", "YTT", "DTZ", "ARG", "T20", "FURB", "DOC", "TRY", "D100", "D101", "D102", "D103", "D300", "D418", "D419", "S"] # Add "A (flake8-builtins)" after we drop pylint @@ -212,7 +214,7 @@ exclude-protected = ["_unfrozen"] [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["src"] -addopts = "--no-success-flaky-report -rX" +addopts = "--no-flaky-report -rX --allow-unix-socket" filterwarnings = [ "error", "ignore::DeprecationWarning", diff --git a/tests/README.rst b/tests/README.rst index 54792ceb561..c32634eb245 100644 --- a/tests/README.rst +++ b/tests/README.rst @@ -70,6 +70,7 @@ are a few conventions that you should follow: tooling can help you as well. You can look at the existing tests for examples and inspiration. - New fixtures should go into ``conftest.py``. New auxiliary functions and classes, used either directly in the tests or in the fixtures, should go into the ``tests/auxil`` directory. + Offline fixtures must be prefixed with `offline_*`. 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: diff --git a/tests/_files/conftest.py b/tests/_files/conftest.py index 9e8e5212e11..91324320f46 100644 --- a/tests/_files/conftest.py +++ b/tests/_files/conftest.py @@ -20,9 +20,49 @@ import pytest +from telegram import Animation, Audio, Document, PhotoSize, Sticker, Video from tests.auxil.files import data_file from tests.auxil.networking import expect_bad_request +FILE_ID = "5a3128a4d2a04750b5b58397f3b5e812" +FILE_UNIQUE_ID = "adc3145fd2e84d95b64d68eaa22aa33e" + + +def _with_bot(file, bot): + file.set_bot(bot) + return file + + +def _thumbnail(bot, *, width, height, file_size): + return _with_bot( + PhotoSize( + file_id=f"thumbnail-{FILE_ID}", + file_unique_id=f"thumbnail-{FILE_UNIQUE_ID}", + width=width, + height=height, + file_size=file_size, + ), + bot, + ) + + +@pytest.fixture(scope="session") +def offline_animation(offline_bot): + return _with_bot( + Animation( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + width=320, + height=180, + duration=1, + file_name="game.gif.webm", + mime_type="video/mp4", + file_size=5859, + thumbnail=_thumbnail(offline_bot, width=320, height=180, file_size=5859), + ), + offline_bot, + ) + @pytest.fixture(scope="session") async def animation(bot, chat_id): @@ -50,6 +90,22 @@ def animated_sticker_file(): yield f +@pytest.fixture(scope="session") +def offline_audio(offline_bot): + return _with_bot( + Audio( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + duration=3, + file_name="telegram.mp3", + mime_type="audio/mpeg", + file_size=122920, + thumbnail=_thumbnail(offline_bot, width=50, height=50, file_size=1427), + ), + offline_bot, + ) + + @pytest.fixture(scope="session") async def audio(bot, chat_id): with data_file("telegram.mp3").open("rb") as f, data_file("thumb.jpg").open("rb") as thumb: @@ -62,6 +118,21 @@ def audio_file(): yield f +@pytest.fixture(scope="session") +def offline_document(offline_bot): + return _with_bot( + Document( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + file_name="telegram.png", + mime_type="image/png", + file_size=12948, + thumbnail=_thumbnail(offline_bot, width=300, height=300, file_size=8090), + ), + offline_bot, + ) + + @pytest.fixture(scope="session") async def document(bot, chat_id): with data_file("telegram.png").open("rb") as f: @@ -74,6 +145,11 @@ def document_file(): yield f +@pytest.fixture(scope="session") +def offline_photo(offline_photolist): + return offline_photolist[-1] + + @pytest.fixture(scope="session") def photo(photolist): return photolist[-1] @@ -85,6 +161,32 @@ def photo_file(): yield f +@pytest.fixture(scope="session") +def offline_photolist(offline_bot): + return ( + _with_bot( + PhotoSize( + file_id=f"small-{FILE_ID}", + file_unique_id=f"small-{FILE_UNIQUE_ID}", + width=90, + height=90, + file_size=1474, + ), + offline_bot, + ), + _with_bot( + PhotoSize( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + width=800, + height=800, + file_size=29176, + ), + offline_bot, + ), + ) + + @pytest.fixture(scope="session") async def photolist(bot, chat_id): async def func(): @@ -96,6 +198,25 @@ async def func(): ) +@pytest.fixture(scope="module") +def offline_sticker(offline_bot): + return _with_bot( + Sticker( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + width=510, + height=512, + is_animated=False, + is_video=False, + type=Sticker.REGULAR, + file_size=39518, + thumbnail=_thumbnail(offline_bot, width=319, height=320, file_size=21448), + needs_repainting=True, + ), + offline_bot, + ) + + @pytest.fixture(scope="module") async def sticker(bot, chat_id): with data_file("telegram.webp").open("rb") as f: @@ -118,15 +239,39 @@ def sticker_set_thumb_file(): yield file +@pytest.fixture(scope="session") +def offline_thumb(offline_photolist): + return offline_photolist[0] + + @pytest.fixture(scope="session") def thumb(photolist): return photolist[0] +@pytest.fixture(scope="session") +def offline_video(offline_bot): + return _with_bot( + Video( + file_id=FILE_ID, + file_unique_id=FILE_UNIQUE_ID, + width=360, + height=640, + duration=5, + file_name="telegram.mp4", + mime_type="video/mp4", + file_size=326534, + thumbnail=_thumbnail(offline_bot, width=180, height=320, file_size=1767), + start_timestamp=3, + ), + offline_bot, + ) + + @pytest.fixture(scope="session") async def video(bot, chat_id): with data_file("telegram.mp4").open("rb") as f: - return (await bot.send_video(chat_id, video=f, read_timeout=50)).video + return (await bot.send_video(chat_id, video=f, start_timestamp=3, read_timeout=50)).video @pytest.fixture diff --git a/tests/_files/test_animation.py b/tests/_files/test_animation.py index df4ae468949..19dee14e0ec 100644 --- a/tests/_files/test_animation.py +++ b/tests/_files/test_animation.py @@ -55,69 +55,73 @@ class AnimationTestBase: class TestAnimationWithoutRequest(AnimationTestBase): - def test_slot_behaviour(self, animation): - for attr in animation.__slots__: - assert getattr(animation, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(animation)) == len(set(mro_slots(animation))), "duplicate slot" - - def test_creation(self, animation): - assert isinstance(animation, Animation) - assert isinstance(animation.file_id, str) - assert isinstance(animation.file_unique_id, str) - assert animation.file_id - assert animation.file_unique_id - - def test_expected_values(self, animation): - assert animation.mime_type == self.mime_type - assert animation.file_name.startswith("game.gif") == self.file_name.startswith("game.gif") - assert isinstance(animation.thumbnail, PhotoSize) - - def test_de_json(self, offline_bot, animation): + def test_slot_behaviour(self, offline_animation): + for attr in offline_animation.__slots__: + assert getattr(offline_animation, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_animation)) == len(set(mro_slots(offline_animation))), ( + "duplicate slot" + ) + + def test_creation(self, offline_animation): + assert isinstance(offline_animation, Animation) + assert isinstance(offline_animation.file_id, str) + assert isinstance(offline_animation.file_unique_id, str) + assert offline_animation.file_id + assert offline_animation.file_unique_id + + def test_expected_values(self, offline_animation): + assert offline_animation.mime_type == self.mime_type + assert offline_animation.file_name.startswith("game.gif") == self.file_name.startswith( + "game.gif" + ) + assert isinstance(offline_animation.thumbnail, PhotoSize) + + def test_de_json(self, offline_bot, offline_animation): json_dict = { "file_id": self.animation_file_id, "file_unique_id": self.animation_file_unique_id, "width": self.width, "height": self.height, "duration": self.duration.total_seconds(), - "thumbnail": animation.thumbnail.to_dict(), + "thumbnail": offline_animation.thumbnail.to_dict(), "file_name": self.file_name, "mime_type": self.mime_type, "file_size": self.file_size, } - animation = Animation.de_json(json_dict, offline_bot) - assert animation.api_kwargs == {} - assert animation.file_id == self.animation_file_id - assert animation.file_unique_id == self.animation_file_unique_id - assert animation.file_name == self.file_name - assert animation.mime_type == self.mime_type - assert animation.file_size == self.file_size - assert animation._duration == self.duration - - def test_to_dict(self, animation): - animation_dict = animation.to_dict() + offline_animation = Animation.de_json(json_dict, offline_bot) + assert offline_animation.api_kwargs == {} + assert offline_animation.file_id == self.animation_file_id + assert offline_animation.file_unique_id == self.animation_file_unique_id + assert offline_animation.file_name == self.file_name + assert offline_animation.mime_type == self.mime_type + assert offline_animation.file_size == self.file_size + assert offline_animation._duration == self.duration + + def test_to_dict(self, offline_animation): + animation_dict = offline_animation.to_dict() assert isinstance(animation_dict, dict) - assert animation_dict["file_id"] == animation.file_id - assert animation_dict["file_unique_id"] == animation.file_unique_id - assert animation_dict["width"] == animation.width - assert animation_dict["height"] == animation.height + assert animation_dict["file_id"] == offline_animation.file_id + assert animation_dict["file_unique_id"] == offline_animation.file_unique_id + assert animation_dict["width"] == offline_animation.width + assert animation_dict["height"] == offline_animation.height assert animation_dict["duration"] == int(self.duration.total_seconds()) assert isinstance(animation_dict["duration"], int) - assert animation_dict["thumbnail"] == animation.thumbnail.to_dict() - assert animation_dict["file_name"] == animation.file_name - assert animation_dict["mime_type"] == animation.mime_type - assert animation_dict["file_size"] == animation.file_size + assert animation_dict["thumbnail"] == offline_animation.thumbnail.to_dict() + assert animation_dict["file_name"] == offline_animation.file_name + assert animation_dict["mime_type"] == offline_animation.mime_type + assert animation_dict["file_size"] == offline_animation.file_size - def test_time_period_properties(self, PTB_TIMEDELTA, animation): + def test_time_period_properties(self, PTB_TIMEDELTA, offline_animation): if PTB_TIMEDELTA: - assert animation.duration == self.duration - assert isinstance(animation.duration, dtm.timedelta) + assert offline_animation.duration == self.duration + assert isinstance(offline_animation.duration, dtm.timedelta) else: - assert animation.duration == int(self.duration.total_seconds()) - assert isinstance(animation.duration, int) + assert offline_animation.duration == int(self.duration.total_seconds()) + assert isinstance(offline_animation.duration, int) - def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, animation): - animation.duration + def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, offline_animation): + offline_animation.duration if PTB_TIMEDELTA: assert len(recwarn) == 0 @@ -188,23 +192,27 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_send_with_animation(self, monkeypatch, offline_bot, chat_id, animation): + async def test_send_with_animation(self, monkeypatch, offline_bot, chat_id, offline_animation): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["animation"] == animation.file_id + return request_data.json_parameters["animation"] == offline_animation.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_animation(animation=animation, chat_id=chat_id) + assert await offline_bot.send_animation(animation=offline_animation, chat_id=chat_id) - async def test_get_file_instance_method(self, monkeypatch, animation): + async def test_get_file_instance_method(self, monkeypatch, offline_animation): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == animation.file_id + return kwargs["file_id"] == offline_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()) + assert await check_shortcut_call( + offline_animation.get_file, offline_animation.get_bot(), "get_file" + ) + assert await check_defaults_handling( + offline_animation.get_file, offline_animation.get_bot() + ) - monkeypatch.setattr(animation.get_bot(), "get_file", make_assertion) - assert await animation.get_file() + monkeypatch.setattr(offline_animation.get_bot(), "get_file", make_assertion) + assert await offline_animation.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -216,7 +224,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_animation_default_quote_parse_mode( - self, default_bot, chat_id, animation, custom, monkeypatch + self, default_bot, chat_id, offline_animation, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -230,7 +238,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(default_bot.request, "post", make_assertion) await default_bot.send_animation( - chat_id, animation, reply_parameters=ReplyParameters(**kwargs) + chat_id, offline_animation, reply_parameters=ReplyParameters(**kwargs) ) diff --git a/tests/_files/test_audio.py b/tests/_files/test_audio.py index dd832966fde..5b3cb46d33e 100644 --- a/tests/_files/test_audio.py +++ b/tests/_files/test_audio.py @@ -58,30 +58,32 @@ class AudioTestBase: class TestAudioWithoutRequest(AudioTestBase): - def test_slot_behaviour(self, audio): - for attr in audio.__slots__: - assert getattr(audio, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(audio)) == len(set(mro_slots(audio))), "duplicate slot" + def test_slot_behaviour(self, offline_audio): + for attr in offline_audio.__slots__: + assert getattr(offline_audio, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_audio)) == len(set(mro_slots(offline_audio))), ( + "duplicate slot" + ) - def test_creation(self, audio): + def test_creation(self, offline_audio): # Make sure file has been uploaded. - assert isinstance(audio, Audio) - assert isinstance(audio.file_id, str) - assert isinstance(audio.file_unique_id, str) - assert audio.file_id - assert audio.file_unique_id - - def test_expected_values(self, audio): - assert audio._duration == self.duration - assert audio.performer is None - assert audio.title is None - assert audio.mime_type == self.mime_type - assert audio.file_size == self.file_size - assert audio.thumbnail.file_size in [self.thumb_file_size, 1395] - assert audio.thumbnail.width == self.thumb_width - assert audio.thumbnail.height == self.thumb_height - - def test_de_json(self, offline_bot, audio): + assert isinstance(offline_audio, Audio) + assert isinstance(offline_audio.file_id, str) + assert isinstance(offline_audio.file_unique_id, str) + assert offline_audio.file_id + assert offline_audio.file_unique_id + + def test_expected_values(self, offline_audio): + assert offline_audio._duration == self.duration + assert offline_audio.performer is None + assert offline_audio.title is None + assert offline_audio.mime_type == self.mime_type + assert offline_audio.file_size == self.file_size + assert offline_audio.thumbnail.file_size in [self.thumb_file_size, 1395] + assert offline_audio.thumbnail.width == self.thumb_width + assert offline_audio.thumbnail.height == self.thumb_height + + def test_de_json(self, offline_bot, offline_audio): json_dict = { "file_id": self.audio_file_id, "file_unique_id": self.audio_file_unique_id, @@ -91,7 +93,7 @@ def test_de_json(self, offline_bot, audio): "file_name": self.file_name, "mime_type": self.mime_type, "file_size": self.file_size, - "thumbnail": audio.thumbnail.to_dict(), + "thumbnail": offline_audio.thumbnail.to_dict(), } json_audio = Audio.de_json(json_dict, offline_bot) assert json_audio.api_kwargs == {} @@ -104,30 +106,30 @@ def test_de_json(self, offline_bot, audio): assert json_audio.file_name == self.file_name assert json_audio.mime_type == self.mime_type assert json_audio.file_size == self.file_size - assert json_audio.thumbnail == audio.thumbnail + assert json_audio.thumbnail == offline_audio.thumbnail - def test_to_dict(self, audio): - audio_dict = audio.to_dict() + def test_to_dict(self, offline_audio): + audio_dict = offline_audio.to_dict() assert isinstance(audio_dict, dict) - assert audio_dict["file_id"] == audio.file_id - assert audio_dict["file_unique_id"] == audio.file_unique_id + assert audio_dict["file_id"] == offline_audio.file_id + assert audio_dict["file_unique_id"] == offline_audio.file_unique_id assert audio_dict["duration"] == int(self.duration.total_seconds()) assert isinstance(audio_dict["duration"], int) - assert audio_dict["mime_type"] == audio.mime_type - assert audio_dict["file_size"] == audio.file_size - assert audio_dict["file_name"] == audio.file_name + assert audio_dict["mime_type"] == offline_audio.mime_type + assert audio_dict["file_size"] == offline_audio.file_size + assert audio_dict["file_name"] == offline_audio.file_name - def test_time_period_properties(self, PTB_TIMEDELTA, audio): + def test_time_period_properties(self, PTB_TIMEDELTA, offline_audio): if PTB_TIMEDELTA: - assert audio.duration == self.duration - assert isinstance(audio.duration, dtm.timedelta) + assert offline_audio.duration == self.duration + assert isinstance(offline_audio.duration, dtm.timedelta) else: - assert audio.duration == int(self.duration.total_seconds()) - assert isinstance(audio.duration, int) + assert offline_audio.duration == int(self.duration.total_seconds()) + assert isinstance(offline_audio.duration, int) - def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, audio): - audio.duration + def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, offline_audio): + offline_audio.duration if PTB_TIMEDELTA: assert len(recwarn) == 0 @@ -136,12 +138,12 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, audio): assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_equality(self, audio): - a = Audio(audio.file_id, audio.file_unique_id, audio.duration) - b = Audio("", audio.file_unique_id, audio.duration) - c = Audio(audio.file_id, audio.file_unique_id, 0) - d = Audio("", "", audio.duration) - e = Voice(audio.file_id, audio.file_unique_id, audio.duration) + def test_equality(self, offline_audio): + a = Audio(offline_audio.file_id, offline_audio.file_unique_id, offline_audio.duration) + b = Audio("", offline_audio.file_unique_id, offline_audio.duration) + c = Audio(offline_audio.file_id, offline_audio.file_unique_id, 0) + d = Audio("", "", offline_audio.duration) + e = Voice(offline_audio.file_id, offline_audio.file_unique_id, offline_audio.duration) assert a == b assert hash(a) == hash(b) @@ -156,12 +158,12 @@ def test_equality(self, audio): assert a != e assert hash(a) != hash(e) - async def test_send_with_audio(self, monkeypatch, offline_bot, chat_id, audio): + async def test_send_with_audio(self, monkeypatch, offline_bot, chat_id, offline_audio): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["audio"] == audio.file_id + return request_data.json_parameters["audio"] == offline_audio.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_audio(audio=audio, chat_id=chat_id) + assert await offline_bot.send_audio(audio=offline_audio, chat_id=chat_id) async def test_send_audio_custom_filename(self, offline_bot, chat_id, audio_file, monkeypatch): async def make_assertion(url, request_data: RequestData, *args, **kwargs): @@ -197,16 +199,18 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, audio): + async def test_get_file_instance_method(self, monkeypatch, offline_audio): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == audio.file_id + return kwargs["file_id"] == offline_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()) + assert await check_shortcut_call( + offline_audio.get_file, offline_audio.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_audio.get_file, offline_audio.get_bot()) - monkeypatch.setattr(audio._bot, "get_file", make_assertion) - assert await audio.get_file() + monkeypatch.setattr(offline_audio._bot, "get_file", make_assertion) + assert await offline_audio.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -218,7 +222,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_audio_default_quote_parse_mode( - self, default_bot, chat_id, audio, custom, monkeypatch + self, default_bot, chat_id, offline_audio, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -231,7 +235,9 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): kwargs["quote_parse_mode"] = custom monkeypatch.setattr(default_bot.request, "post", make_assertion) - await default_bot.send_audio(chat_id, audio, reply_parameters=ReplyParameters(**kwargs)) + await default_bot.send_audio( + chat_id, offline_audio, reply_parameters=ReplyParameters(**kwargs) + ) class TestAudioWithRequest(AudioTestBase): diff --git a/tests/_files/test_chatphoto.py b/tests/_files/test_chatphoto.py index 651d2ced060..85be941ff0f 100644 --- a/tests/_files/test_chatphoto.py +++ b/tests/_files/test_chatphoto.py @@ -42,6 +42,18 @@ def chatphoto_file(): yield f +@pytest.fixture(scope="module") +def offline_chat_photo(offline_bot): + value = ChatPhoto( + small_file_id=ChatPhotoTestBase.chatphoto_small_file_id, + small_file_unique_id=ChatPhotoTestBase.chatphoto_small_file_unique_id, + big_file_id=ChatPhotoTestBase.chatphoto_big_file_id, + big_file_unique_id=ChatPhotoTestBase.chatphoto_big_file_unique_id, + ) + value.set_bot(offline_bot) + return value + + @pytest.fixture(scope="module") async def chat_photo(bot, super_group_id): async def func(): @@ -61,33 +73,35 @@ class ChatPhotoTestBase: class TestChatPhotoWithoutRequest(ChatPhotoTestBase): - def test_slot_behaviour(self, chat_photo): - for attr in chat_photo.__slots__: - assert getattr(chat_photo, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(chat_photo)) == len(set(mro_slots(chat_photo))), "duplicate slot" + def test_slot_behaviour(self, offline_chat_photo): + for attr in offline_chat_photo.__slots__: + assert getattr(offline_chat_photo, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_chat_photo)) == len(set(mro_slots(offline_chat_photo))), ( + "duplicate slot" + ) - def test_de_json(self, offline_bot, chat_photo): + def test_de_json(self, offline_bot): json_dict = { "small_file_id": self.chatphoto_small_file_id, "big_file_id": self.chatphoto_big_file_id, "small_file_unique_id": self.chatphoto_small_file_unique_id, "big_file_unique_id": self.chatphoto_big_file_unique_id, } - chat_photo = ChatPhoto.de_json(json_dict, offline_bot) - assert chat_photo.api_kwargs == {} - assert chat_photo.small_file_id == self.chatphoto_small_file_id - assert chat_photo.big_file_id == self.chatphoto_big_file_id - assert chat_photo.small_file_unique_id == self.chatphoto_small_file_unique_id - assert chat_photo.big_file_unique_id == self.chatphoto_big_file_unique_id + offline_chat_photo = ChatPhoto.de_json(json_dict, offline_bot) + assert offline_chat_photo.api_kwargs == {} + assert offline_chat_photo.small_file_id == self.chatphoto_small_file_id + assert offline_chat_photo.big_file_id == self.chatphoto_big_file_id + assert offline_chat_photo.small_file_unique_id == self.chatphoto_small_file_unique_id + assert offline_chat_photo.big_file_unique_id == self.chatphoto_big_file_unique_id - async def test_to_dict(self, chat_photo): - chat_photo_dict = chat_photo.to_dict() + async def test_to_dict(self, offline_chat_photo): + chat_photo_dict = offline_chat_photo.to_dict() assert isinstance(chat_photo_dict, dict) - assert chat_photo_dict["small_file_id"] == chat_photo.small_file_id - assert chat_photo_dict["big_file_id"] == chat_photo.big_file_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["small_file_id"] == offline_chat_photo.small_file_id + assert chat_photo_dict["big_file_id"] == offline_chat_photo.big_file_id + assert chat_photo_dict["small_file_unique_id"] == offline_chat_photo.small_file_unique_id + assert chat_photo_dict["big_file_unique_id"] == offline_chat_photo.big_file_unique_id def test_equality(self): a = ChatPhoto( @@ -122,38 +136,46 @@ def test_equality(self): assert hash(a) != hash(e) async def test_send_with_chat_photo( - self, monkeypatch, offline_bot, super_group_id, chat_photo + self, monkeypatch, offline_bot, super_group_id, offline_chat_photo ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.parameters["photo"] == chat_photo.to_dict() + return request_data.parameters["photo"] == offline_chat_photo.to_dict() monkeypatch.setattr(offline_bot.request, "post", make_assertion) - message = await offline_bot.set_chat_photo(photo=chat_photo, chat_id=super_group_id) + message = await offline_bot.set_chat_photo( + photo=offline_chat_photo, chat_id=super_group_id + ) assert message - async def test_get_small_file_instance_method(self, monkeypatch, chat_photo): + async def test_get_small_file_instance_method(self, monkeypatch, offline_chat_photo): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == chat_photo.small_file_id + return kwargs["file_id"] == offline_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" + offline_chat_photo.get_small_file, offline_chat_photo.get_bot(), "get_file" + ) + assert await check_defaults_handling( + offline_chat_photo.get_small_file, offline_chat_photo.get_bot() ) - 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() + monkeypatch.setattr(offline_chat_photo.get_bot(), "get_file", make_assertion) + assert await offline_chat_photo.get_small_file() - async def test_get_big_file_instance_method(self, monkeypatch, chat_photo): + async def test_get_big_file_instance_method(self, monkeypatch, offline_chat_photo): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == chat_photo.big_file_id + return kwargs["file_id"] == offline_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()) + assert await check_shortcut_call( + offline_chat_photo.get_big_file, offline_chat_photo.get_bot(), "get_file" + ) + assert await check_defaults_handling( + offline_chat_photo.get_big_file, offline_chat_photo.get_bot() + ) - monkeypatch.setattr(chat_photo.get_bot(), "get_file", make_assertion) - assert await chat_photo.get_big_file() + monkeypatch.setattr(offline_chat_photo.get_bot(), "get_file", make_assertion) + assert await offline_chat_photo.get_big_file() class TestChatPhotoWithRequest: diff --git a/tests/_files/test_document.py b/tests/_files/test_document.py index 224e05aa2fa..4ee2029ae47 100644 --- a/tests/_files/test_document.py +++ b/tests/_files/test_document.py @@ -51,31 +51,33 @@ class DocumentTestBase: class TestDocumentWithoutRequest(DocumentTestBase): - def test_slot_behaviour(self, document): - for attr in document.__slots__: - assert getattr(document, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(document)) == len(set(mro_slots(document))), "duplicate slot" - - def test_creation(self, document): - assert isinstance(document, Document) - assert isinstance(document.file_id, str) - assert isinstance(document.file_unique_id, str) - assert document.file_id - assert document.file_unique_id - - def test_expected_values(self, document): - assert document.file_size == self.file_size - assert document.mime_type == self.mime_type - assert document.file_name == self.file_name - assert document.thumbnail.file_size in [self.thumb_file_size, 7980] - assert document.thumbnail.width == self.thumb_width - assert document.thumbnail.height == self.thumb_height + def test_slot_behaviour(self, offline_document): + for attr in offline_document.__slots__: + assert getattr(offline_document, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_document)) == len(set(mro_slots(offline_document))), ( + "duplicate slot" + ) - def test_de_json(self, offline_bot, document): + def test_creation(self, offline_document): + assert isinstance(offline_document, Document) + assert isinstance(offline_document.file_id, str) + assert isinstance(offline_document.file_unique_id, str) + assert offline_document.file_id + assert offline_document.file_unique_id + + def test_expected_values(self, offline_document): + assert offline_document.file_size == self.file_size + assert offline_document.mime_type == self.mime_type + assert offline_document.file_name == self.file_name + assert offline_document.thumbnail.file_size in [self.thumb_file_size, 7980] + assert offline_document.thumbnail.width == self.thumb_width + assert offline_document.thumbnail.height == self.thumb_height + + def test_de_json(self, offline_bot, offline_document): json_dict = { "file_id": self.document_file_id, "file_unique_id": self.document_file_unique_id, - "thumbnail": document.thumbnail.to_dict(), + "thumbnail": offline_document.thumbnail.to_dict(), "file_name": self.file_name, "mime_type": self.mime_type, "file_size": self.file_size, @@ -85,26 +87,26 @@ def test_de_json(self, offline_bot, document): assert test_document.file_id == self.document_file_id assert test_document.file_unique_id == self.document_file_unique_id - assert test_document.thumbnail == document.thumbnail + assert test_document.thumbnail == offline_document.thumbnail 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() + def test_to_dict(self, offline_document): + document_dict = offline_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) + assert document_dict["file_id"] == offline_document.file_id + assert document_dict["file_unique_id"] == offline_document.file_unique_id + assert document_dict["file_name"] == offline_document.file_name + assert document_dict["mime_type"] == offline_document.mime_type + assert document_dict["file_size"] == offline_document.file_size + + def test_equality(self, offline_document): + a = Document(offline_document.file_id, offline_document.file_unique_id) + b = Document("", offline_document.file_unique_id) d = Document("", "") - e = Voice(document.file_id, document.file_unique_id, 0) + e = Voice(offline_document.file_id, offline_document.file_unique_id, 0) assert a == b assert hash(a) == hash(b) @@ -122,19 +124,19 @@ async def test_error_send_without_required_args(self, offline_bot, chat_id): @pytest.mark.parametrize("disable_content_type_detection", [True, False, None]) async def test_send_with_document( - self, monkeypatch, offline_bot, chat_id, document, disable_content_type_detection + self, monkeypatch, offline_bot, chat_id, offline_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 + return data["document"] == offline_document.file_id and type_detection monkeypatch.setattr(offline_bot.request, "post", make_assertion) message = await offline_bot.send_document( - document=document, + document=offline_document, chat_id=chat_id, disable_content_type_detection=disable_content_type_detection, ) @@ -151,7 +153,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): indirect=["default_bot"], ) async def test_send_document_default_quote_parse_mode( - self, default_bot, chat_id, document, custom, monkeypatch + self, default_bot, chat_id, offline_document, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -165,7 +167,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(default_bot.request, "post", make_assertion) await default_bot.send_document( - chat_id, document, reply_parameters=ReplyParameters(**kwargs) + chat_id, offline_document, reply_parameters=ReplyParameters(**kwargs) ) @pytest.mark.parametrize("local_mode", [True, False]) @@ -197,16 +199,18 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, document): + async def test_get_file_instance_method(self, monkeypatch, offline_document): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == document.file_id + return kwargs["file_id"] == offline_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()) + assert await check_shortcut_call( + offline_document.get_file, offline_document.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_document.get_file, offline_document.get_bot()) - monkeypatch.setattr(document.get_bot(), "get_file", make_assertion) - assert await document.get_file() + monkeypatch.setattr(offline_document.get_bot(), "get_file", make_assertion) + assert await offline_document.get_file() class TestDocumentWithRequest(DocumentTestBase): diff --git a/tests/_files/test_inputmedia.py b/tests/_files/test_inputmedia.py index 2cd9c9bc75a..8f39d4dabbe 100644 --- a/tests/_files/test_inputmedia.py +++ b/tests/_files/test_inputmedia.py @@ -304,14 +304,14 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, input_media_vi assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_with_video(self, video, PTB_TIMEDELTA): + def test_with_video(self, offline_video): # fixture found in test_video - input_media_video = InputMediaVideo(video, caption="test 3") + input_media_video = InputMediaVideo(offline_video, caption="test 3") assert input_media_video.type == self.type_ - assert input_media_video.media == video.file_id - assert input_media_video.width == video.width - assert input_media_video.height == video.height - assert input_media_video.duration == video.duration + assert input_media_video.media == offline_video.file_id + assert input_media_video.width == offline_video.width + assert input_media_video.height == offline_video.height + assert input_media_video.duration == offline_video.duration assert input_media_video.caption == "test 3" def test_with_video_file(self, video_file): @@ -467,11 +467,11 @@ def test_to_dict(self, input_media_photo): == input_media_photo.show_caption_above_media ) - def test_with_photo(self, photo): + def test_with_photo(self, offline_photo): # fixture found in test_photo - input_media_photo = InputMediaPhoto(photo, caption="test 2") + input_media_photo = InputMediaPhoto(offline_photo, caption="test 2") assert input_media_photo.type == self.type_ - assert input_media_photo.media == photo.file_id + assert input_media_photo.media == offline_photo.file_id assert input_media_photo.caption == "test 2" def test_with_photo_file(self, photo_file): @@ -638,11 +638,11 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, input_media_an assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_with_animation(self, animation): + def test_with_animation(self, offline_animation): # fixture found in test_animation - input_media_animation = InputMediaAnimation(animation, caption="test 2") + input_media_animation = InputMediaAnimation(offline_animation, caption="test 2") assert input_media_animation.type == self.type_ - assert input_media_animation.media == animation.file_id + assert input_media_animation.media == offline_animation.file_id assert input_media_animation.caption == "test 2" def test_with_animation_file(self, animation_file): @@ -815,14 +815,14 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, input_media_au assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_with_audio(self, audio): + def test_with_audio(self, offline_audio): # fixture found in test_audio - input_media_audio = InputMediaAudio(audio, caption="test 3") + input_media_audio = InputMediaAudio(offline_audio, caption="test 3") assert input_media_audio.type == self.type_ - assert input_media_audio.media == audio.file_id - assert input_media_audio.duration == audio.duration - assert input_media_audio.performer == audio.performer - assert input_media_audio.title == audio.title + assert input_media_audio.media == offline_audio.file_id + assert input_media_audio.duration == offline_audio.duration + assert input_media_audio.performer == offline_audio.performer + assert input_media_audio.title == offline_audio.title assert input_media_audio.caption == "test 3" def test_with_audio_file(self, audio_file): @@ -1042,10 +1042,10 @@ def test_to_dict(self, input_media_sticker): assert input_media_sticker_dict["media"] == input_media_sticker.media assert input_media_sticker_dict["emoji"] == input_media_sticker.emoji - def test_with_sticker(self, sticker): - input_media_sticker = InputMediaSticker(sticker, emoji=self.emoji) + def test_with_sticker(self, offline_sticker): + input_media_sticker = InputMediaSticker(offline_sticker, emoji=self.emoji) assert input_media_sticker.type == self.type_ - assert input_media_sticker.media == sticker.file_id + assert input_media_sticker.media == offline_sticker.file_id assert input_media_sticker.emoji == self.emoji def test_with_sticker_file(self, sticker_file): @@ -1103,11 +1103,11 @@ def test_to_dict(self, input_media_document): == input_media_document.disable_content_type_detection ) - def test_with_document(self, document): + def test_with_document(self, offline_document): # fixture found in test_document - input_media_document = InputMediaDocument(document, caption="test 3") + input_media_document = InputMediaDocument(offline_document, caption="test 3") assert input_media_document.type == self.type_ - assert input_media_document.media == document.file_id + assert input_media_document.media == offline_document.file_id assert input_media_document.caption == "test 3" def test_with_document_file(self, document_file): @@ -1218,11 +1218,11 @@ def test_to_dict(self, input_paid_media_photo): assert input_paid_media_photo_dict["type"] == input_paid_media_photo.type assert input_paid_media_photo_dict["media"] == input_paid_media_photo.media - def test_with_photo(self, photo): + def test_with_photo(self, offline_photo): # fixture found in conftest.py - input_paid_media_photo = InputPaidMediaPhoto(photo) + input_paid_media_photo = InputPaidMediaPhoto(offline_photo) assert input_paid_media_photo.type == self.type_ - assert input_paid_media_photo.media == photo.file_id + assert input_paid_media_photo.media == offline_photo.file_id def test_with_photo_file(self, photo_file): # fixture found in conftest.py @@ -1283,12 +1283,12 @@ def test_to_dict(self, input_media_live_photo): ) assert input_media_live_photo_dict["has_spoiler"] == input_media_live_photo.has_spoiler - def test_with_photo_and_video(self, video, photo): + def test_with_photo_and_video(self, offline_video, offline_photo): # fixtures found in conftest.py - input_media_live_photo = InputMediaLivePhoto(video, photo) + input_media_live_photo = InputMediaLivePhoto(offline_video, offline_photo) assert input_media_live_photo.type == self.type_ - assert input_media_live_photo.media == video.file_id - assert input_media_live_photo.photo == photo.file_id + assert input_media_live_photo.media == offline_video.file_id + assert input_media_live_photo.photo == offline_photo.file_id def test_with_photo_and_video_files(self, video_file, photo_file): # fixture found in conftest.py @@ -1362,14 +1362,14 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, input_paid_med assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_with_video(self, video): + def test_with_video(self, offline_video): # fixture found in test_video - input_paid_media_video = InputPaidMediaVideo(video) + input_paid_media_video = InputPaidMediaVideo(offline_video) assert input_paid_media_video.type == self.type_ - assert input_paid_media_video.media == video.file_id - assert input_paid_media_video.width == video.width - assert input_paid_media_video.height == video.height - assert input_paid_media_video.duration == video.duration + assert input_paid_media_video.media == offline_video.file_id + assert input_paid_media_video.width == offline_video.width + assert input_paid_media_video.height == offline_video.height + assert input_paid_media_video.duration == offline_video.duration def test_with_video_file(self, video_file): # fixture found in test_video @@ -1406,12 +1406,12 @@ def test_to_dict(self, input_paid_media_live_photo): assert input_paid_media_live_photo_dict["media"] == input_paid_media_live_photo.media assert input_paid_media_live_photo_dict["photo"] == input_paid_media_live_photo.photo - def test_with_photo(self, video, photo): + def test_with_photo(self, offline_video, offline_photo): # fixtures found in conftest.py - input_paid_media_live_photo = InputPaidMediaLivePhoto(video, photo) + input_paid_media_live_photo = InputPaidMediaLivePhoto(offline_video, offline_photo) assert input_paid_media_live_photo.type == self.type_ - assert input_paid_media_live_photo.media == video.file_id - assert input_paid_media_live_photo.photo == photo.file_id + assert input_paid_media_live_photo.media == offline_video.file_id + assert input_paid_media_live_photo.photo == offline_photo.file_id def test_with_photo_file(self, photo_file): # fixture found in conftest.py @@ -1428,20 +1428,64 @@ def test_with_local_files(self): assert input_paid_media_live_photo.photo == data_file("telegram.jpg").as_uri() +@pytest.fixture(scope="module") +def offline_media_group(offline_photo, offline_thumb): + return [ + InputMediaPhoto(offline_photo, caption="*photo* 1", parse_mode="Markdown"), + InputMediaPhoto(offline_thumb, caption="photo 2", parse_mode="HTML"), + InputMediaPhoto( + offline_photo, + caption="photo 3", + caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)], + ), + ] + + +@pytest.fixture(scope="module") +def offline_media_group_no_caption_args(offline_photo, offline_thumb): + return [ + InputMediaPhoto(offline_photo), + InputMediaPhoto(offline_thumb), + InputMediaPhoto(offline_photo), + ] + + +@pytest.fixture(scope="module") +def offline_media_group_no_caption_only_caption_entities(offline_photo): + return [ + InputMediaPhoto(offline_photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]), + InputMediaPhoto(offline_photo, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)]), + ] + + +@pytest.fixture(scope="module") +def offline_media_group_no_caption_only_parse_mode(offline_photo, offline_thumb): + return [ + InputMediaPhoto(offline_photo, parse_mode="Markdown"), + InputMediaPhoto(offline_thumb, parse_mode="HTML"), + ] + + @pytest.fixture(scope="module") def media_group(photo, thumb): return [ InputMediaPhoto(photo, caption="*photo* 1", parse_mode="Markdown"), InputMediaPhoto(thumb, caption="photo 2", parse_mode="HTML"), InputMediaPhoto( - photo, caption="photo 3", caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)] + photo, + caption="photo 3", + caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 5)], ), ] @pytest.fixture(scope="module") def media_group_no_caption_args(photo, thumb): - return [InputMediaPhoto(photo), InputMediaPhoto(thumb), InputMediaPhoto(photo)] + return [ + InputMediaPhoto(photo), + InputMediaPhoto(thumb), + InputMediaPhoto(photo), + ] @pytest.fixture(scope="module") @@ -1465,14 +1509,14 @@ async def test_send_media_group_throws_error_with_group_caption_and_individual_c self, offline_bot, chat_id, - media_group, - media_group_no_caption_only_caption_entities, - media_group_no_caption_only_parse_mode, + offline_media_group, + offline_media_group_no_caption_only_caption_entities, + offline_media_group_no_caption_only_parse_mode, ): for group in ( - media_group, - media_group_no_caption_only_caption_entities, - media_group_no_caption_only_parse_mode, + offline_media_group, + offline_media_group_no_caption_only_caption_entities, + offline_media_group_no_caption_only_parse_mode, ): with pytest.raises( ValueError, @@ -1558,7 +1602,7 @@ async def make_assertion( indirect=["default_bot"], ) async def test_send_media_group_default_quote_parse_mode( - self, default_bot, chat_id, media_group, custom, monkeypatch + self, default_bot, chat_id, offline_media_group, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -1572,7 +1616,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(default_bot.request, "post", make_assertion) await default_bot.send_media_group( - chat_id, media_group, reply_parameters=ReplyParameters(**kwargs) + chat_id, offline_media_group, reply_parameters=ReplyParameters(**kwargs) ) diff --git a/tests/_files/test_photo.py b/tests/_files/test_photo.py index 93e0ac40ea1..6325e793f46 100644 --- a/tests/_files/test_photo.py +++ b/tests/_files/test_photo.py @@ -48,39 +48,41 @@ class PhotoTestBase: class TestPhotoWithoutRequest(PhotoTestBase): - def test_slot_behaviour(self, photo): - for attr in photo.__slots__: - assert getattr(photo, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(photo)) == len(set(mro_slots(photo))), "duplicate slot" + def test_slot_behaviour(self, offline_photo): + for attr in offline_photo.__slots__: + assert getattr(offline_photo, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_photo)) == len(set(mro_slots(offline_photo))), ( + "duplicate slot" + ) - def test_creation(self, thumb, photo): + def test_creation(self, offline_thumb, offline_photo): # Make sure file has been uploaded. - 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 - - assert isinstance(thumb, PhotoSize) - assert isinstance(thumb.file_id, str) - assert isinstance(thumb.file_unique_id, str) - assert thumb.file_id - assert thumb.file_unique_id - - def test_expected_values(self, photo, thumb): - assert photo.width == self.width - assert photo.height == self.height - assert photo.file_size in self.file_size - assert thumb.width == 90 - assert thumb.height == 90 + assert isinstance(offline_photo, PhotoSize) + assert isinstance(offline_photo.file_id, str) + assert isinstance(offline_photo.file_unique_id, str) + assert offline_photo.file_id + assert offline_photo.file_unique_id + + assert isinstance(offline_thumb, PhotoSize) + assert isinstance(offline_thumb.file_id, str) + assert isinstance(offline_thumb.file_unique_id, str) + assert offline_thumb.file_id + assert offline_thumb.file_unique_id + + def test_expected_values(self, offline_photo, offline_thumb): + assert offline_photo.width == self.width + assert offline_photo.height == self.height + assert offline_photo.file_size in self.file_size + assert offline_thumb.width == 90 + assert offline_thumb.height == 90 # File sizes don't seem to be consistent, so we use the values that we have observed # so far - assert thumb.file_size in [1474, 1475, 1477] + assert offline_thumb.file_size in [1474, 1475, 1477] - def test_de_json(self, offline_bot, photo): + def test_de_json(self, offline_bot, offline_photo): json_dict = { - "file_id": photo.file_id, - "file_unique_id": photo.file_unique_id, + "file_id": offline_photo.file_id, + "file_unique_id": offline_photo.file_unique_id, "width": self.width, "height": self.height, "file_size": self.file_size, @@ -88,30 +90,30 @@ def test_de_json(self, offline_bot, photo): json_photo = PhotoSize.de_json(json_dict, offline_bot) assert json_photo.api_kwargs == {} - assert json_photo.file_id == photo.file_id - assert json_photo.file_unique_id == photo.file_unique_id + assert json_photo.file_id == offline_photo.file_id + assert json_photo.file_unique_id == offline_photo.file_unique_id assert json_photo.width == self.width assert json_photo.height == self.height assert json_photo.file_size == self.file_size - def test_to_dict(self, photo): - photo_dict = photo.to_dict() + def test_to_dict(self, offline_photo): + photo_dict = offline_photo.to_dict() assert isinstance(photo_dict, dict) - assert photo_dict["file_id"] == photo.file_id - assert photo_dict["file_unique_id"] == photo.file_unique_id - assert photo_dict["width"] == photo.width - assert photo_dict["height"] == photo.height - assert photo_dict["file_size"] == photo.file_size - - def test_equality(self, photo): - a = PhotoSize(photo.file_id, photo.file_unique_id, self.width, self.height) - b = PhotoSize("", photo.file_unique_id, self.width, self.height) - c = PhotoSize(photo.file_id, photo.file_unique_id, 0, 0) + assert photo_dict["file_id"] == offline_photo.file_id + assert photo_dict["file_unique_id"] == offline_photo.file_unique_id + assert photo_dict["width"] == offline_photo.width + assert photo_dict["height"] == offline_photo.height + assert photo_dict["file_size"] == offline_photo.file_size + + def test_equality(self, offline_photo): + a = PhotoSize(offline_photo.file_id, offline_photo.file_unique_id, self.width, self.height) + b = PhotoSize("", offline_photo.file_unique_id, self.width, self.height) + c = PhotoSize(offline_photo.file_id, offline_photo.file_unique_id, 0, 0) d = PhotoSize("", "", self.width, self.height) e = Sticker( - photo.file_id, - photo.file_unique_id, + offline_photo.file_id, + offline_photo.file_unique_id, self.width, self.height, False, @@ -168,23 +170,25 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_send_with_photosize(self, monkeypatch, offline_bot, chat_id, photo): + async def test_send_with_photosize(self, monkeypatch, offline_bot, chat_id, offline_photo): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["photo"] == photo.file_id + return request_data.json_parameters["photo"] == offline_photo.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_photo(photo=photo, chat_id=chat_id) + assert await offline_bot.send_photo(photo=offline_photo, chat_id=chat_id) - async def test_get_file_instance_method(self, monkeypatch, photo): + async def test_get_file_instance_method(self, monkeypatch, offline_photo): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == photo.file_id + return kwargs["file_id"] == offline_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()) + assert await check_shortcut_call( + offline_photo.get_file, offline_photo.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_photo.get_file, offline_photo.get_bot()) - monkeypatch.setattr(photo.get_bot(), "get_file", make_assertion) - assert await photo.get_file() + monkeypatch.setattr(offline_photo.get_bot(), "get_file", make_assertion) + assert await offline_photo.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -196,7 +200,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_photo_default_quote_parse_mode( - self, default_bot, chat_id, photo, custom, monkeypatch + self, default_bot, chat_id, offline_photo, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -209,7 +213,9 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): kwargs["quote_parse_mode"] = custom monkeypatch.setattr(default_bot.request, "post", make_assertion) - await default_bot.send_photo(chat_id, photo, reply_parameters=ReplyParameters(**kwargs)) + await default_bot.send_photo( + chat_id, offline_photo, reply_parameters=ReplyParameters(**kwargs) + ) class TestPhotoWithRequest(PhotoTestBase): diff --git a/tests/_files/test_sticker.py b/tests/_files/test_sticker.py index d76ce821e0f..d68597c495f 100644 --- a/tests/_files/test_sticker.py +++ b/tests/_files/test_sticker.py @@ -101,56 +101,72 @@ class StickerTestBase: class TestStickerWithoutRequest(StickerTestBase): - def test_slot_behaviour(self, sticker): - for attr in sticker.__slots__: - assert getattr(sticker, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(sticker)) == len(set(mro_slots(sticker))), "duplicate slot" + def test_slot_behaviour(self, offline_sticker): + for attr in offline_sticker.__slots__: + assert getattr(offline_sticker, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_sticker)) == len(set(mro_slots(offline_sticker))), ( + "duplicate slot" + ) - def test_creation(self, sticker): + def test_creation(self, offline_sticker): # Make sure file has been uploaded. - assert isinstance(sticker, Sticker) - assert isinstance(sticker.file_id, str) - assert isinstance(sticker.file_unique_id, str) - assert sticker.file_id - assert sticker.file_unique_id - assert isinstance(sticker.thumbnail, PhotoSize) - assert isinstance(sticker.thumbnail.file_id, str) - assert isinstance(sticker.thumbnail.file_unique_id, str) - assert sticker.thumbnail.file_id - assert sticker.thumbnail.file_unique_id - assert isinstance(sticker.needs_repainting, bool) - - def test_expected_values(self, sticker): - assert sticker.width == self.width - assert sticker.height == self.height - assert sticker.is_animated == self.is_animated - assert sticker.is_video == self.is_video - assert sticker.file_size == self.file_size - assert sticker.thumbnail.width == self.thumb_width - assert sticker.thumbnail.height == self.thumb_height - assert sticker.thumbnail.file_size == self.thumb_file_size - assert sticker.type == self.type - assert sticker.needs_repainting == self.needs_repainting + assert isinstance(offline_sticker, Sticker) + assert isinstance(offline_sticker.file_id, str) + assert isinstance(offline_sticker.file_unique_id, str) + assert offline_sticker.file_id + assert offline_sticker.file_unique_id + assert isinstance(offline_sticker.thumbnail, PhotoSize) + assert isinstance(offline_sticker.thumbnail.file_id, str) + assert isinstance(offline_sticker.thumbnail.file_unique_id, str) + assert offline_sticker.thumbnail.file_id + assert offline_sticker.thumbnail.file_unique_id + assert isinstance(offline_sticker.needs_repainting, bool) + + def test_expected_values(self, offline_sticker): + assert offline_sticker.width == self.width + assert offline_sticker.height == self.height + assert offline_sticker.is_animated == self.is_animated + assert offline_sticker.is_video == self.is_video + assert offline_sticker.file_size == self.file_size + assert offline_sticker.thumbnail.width == self.thumb_width + assert offline_sticker.thumbnail.height == self.thumb_height + assert offline_sticker.thumbnail.file_size == self.thumb_file_size + assert offline_sticker.type == self.type + assert offline_sticker.needs_repainting == self.needs_repainting # we need to be a premium TG user to send a premium sticker, so the below is not tested # assert sticker.premium_animation == self.premium_animation - def test_to_dict(self, sticker): - sticker_dict = sticker.to_dict() + def test_to_dict(self, offline_sticker): + sticker_dict = offline_sticker.to_dict() assert isinstance(sticker_dict, dict) - assert sticker_dict["file_id"] == sticker.file_id - assert sticker_dict["file_unique_id"] == sticker.file_unique_id - assert sticker_dict["width"] == sticker.width - assert sticker_dict["height"] == sticker.height - assert sticker_dict["is_animated"] == sticker.is_animated - assert sticker_dict["is_video"] == sticker.is_video - assert sticker_dict["file_size"] == sticker.file_size - assert sticker_dict["thumbnail"] == sticker.thumbnail.to_dict() - assert sticker_dict["type"] == sticker.type - assert sticker_dict["needs_repainting"] == sticker.needs_repainting - - def test_de_json(self, offline_bot): - json_dict = self.sticker.to_dict() + assert sticker_dict["file_id"] == offline_sticker.file_id + assert sticker_dict["file_unique_id"] == offline_sticker.file_unique_id + assert sticker_dict["width"] == offline_sticker.width + assert sticker_dict["height"] == offline_sticker.height + assert sticker_dict["is_animated"] == offline_sticker.is_animated + assert sticker_dict["is_video"] == offline_sticker.is_video + assert sticker_dict["file_size"] == offline_sticker.file_size + assert sticker_dict["thumbnail"] == offline_sticker.thumbnail.to_dict() + assert sticker_dict["type"] == offline_sticker.type + assert sticker_dict["needs_repainting"] == offline_sticker.needs_repainting + + def test_de_json(self, offline_bot, offline_sticker): + json_dict = { + "file_id": self.sticker_file_id, + "file_unique_id": self.sticker_file_unique_id, + "width": self.width, + "height": self.height, + "is_animated": self.is_animated, + "is_video": self.is_video, + "thumbnail": offline_sticker.thumbnail.to_dict(), + "emoji": self.emoji, + "file_size": self.file_size, + "premium_animation": self.premium_animation.to_dict(), + "type": self.type, + "custom_emoji_id": self.custom_emoji_id, + "needs_repainting": self.needs_repainting, + } json_sticker = Sticker.de_json(json_dict, offline_bot) assert json_sticker.api_kwargs == {} @@ -162,7 +178,7 @@ def test_de_json(self, offline_bot): assert json_sticker.is_video == self.is_video assert json_sticker.emoji == self.emoji assert json_sticker.file_size == self.file_size - assert json_sticker.thumbnail == self.thumbnail + assert json_sticker.thumbnail == offline_sticker.thumbnail assert json_sticker.premium_animation == self.premium_animation assert json_sticker.type == self.type assert json_sticker.custom_emoji_id == self.custom_emoji_id @@ -196,10 +212,10 @@ def test_type_enum_conversion(self): == "unknown" ) - def test_equality(self, sticker): + def test_equality(self, offline_sticker): a = Sticker( - sticker.file_id, - sticker.file_unique_id, + offline_sticker.file_id, + offline_sticker.file_unique_id, self.width, self.height, self.is_animated, @@ -208,7 +224,7 @@ def test_equality(self, sticker): ) b = Sticker( "", - sticker.file_unique_id, + offline_sticker.file_unique_id, self.width, self.height, self.is_animated, @@ -216,8 +232,8 @@ def test_equality(self, sticker): self.type, ) c = Sticker( - sticker.file_id, - sticker.file_unique_id, + offline_sticker.file_id, + offline_sticker.file_unique_id, 0, 0, False, @@ -234,8 +250,8 @@ def test_equality(self, sticker): self.type, ) e = PhotoSize( - sticker.file_id, - sticker.file_unique_id, + offline_sticker.file_id, + offline_sticker.file_unique_id, self.width, self.height, self.is_animated, @@ -258,12 +274,12 @@ async def test_error_without_required_args(self, offline_bot, chat_id): with pytest.raises(TypeError): await offline_bot.send_sticker(chat_id) - async def test_send_with_sticker(self, monkeypatch, offline_bot, chat_id, sticker): + async def test_send_with_sticker(self, monkeypatch, offline_bot, chat_id, offline_sticker): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_sticker(sticker=sticker, chat_id=chat_id) + assert await offline_bot.send_sticker(sticker=offline_sticker, chat_id=chat_id) @pytest.mark.parametrize("local_mode", [True, False]) async def test_send_sticker_local_files( @@ -300,7 +316,7 @@ async def make_assertion(_, data, *args, **kwargs): indirect=["default_bot"], ) async def test_send_sticker_default_quote_parse_mode( - self, default_bot, chat_id, sticker, custom, monkeypatch + self, default_bot, chat_id, offline_sticker, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -314,7 +330,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(default_bot.request, "post", make_assertion) await default_bot.send_sticker( - chat_id, sticker, reply_parameters=ReplyParameters(**kwargs) + chat_id, offline_sticker, reply_parameters=ReplyParameters(**kwargs) ) @@ -520,15 +536,21 @@ def test_slot_behaviour(self): assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - def test_de_json(self, offline_bot): - json_dict = self.sticker_set.to_dict() - json_dict["contains_masks"] = self.contains_masks + def test_de_json(self, offline_bot, offline_sticker): + json_dict = { + "name": self.name, + "title": self.title, + "stickers": [x.to_dict() for x in self.stickers], + "thumbnail": offline_sticker.thumbnail.to_dict(), + "sticker_type": self.sticker_type, + "contains_masks": self.contains_masks, + } sticker_set = StickerSet.de_json(json_dict, offline_bot) assert sticker_set.name == self.name assert sticker_set.title == self.title assert sticker_set.stickers == tuple(self.stickers) - assert sticker_set.thumbnail == self.thumbnail + assert sticker_set.thumbnail == offline_sticker.thumbnail assert sticker_set.sticker_type == self.sticker_type assert sticker_set.api_kwargs == {"contains_masks": self.contains_masks} @@ -710,64 +732,76 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, sticker): + async def test_get_file_instance_method(self, monkeypatch, offline_sticker): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == sticker.file_id + return kwargs["file_id"] == offline_sticker.file_id assert check_shortcut_signature(Sticker.get_file, Bot.get_file, ["file_id"], []) - assert await check_shortcut_call(sticker.get_file, sticker.get_bot(), "get_file") - assert await check_defaults_handling(sticker.get_file, sticker.get_bot()) + assert await check_shortcut_call( + offline_sticker.get_file, offline_sticker.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_sticker.get_file, offline_sticker.get_bot()) - monkeypatch.setattr(sticker.get_bot(), "get_file", make_assertion) - assert await sticker.get_file() + monkeypatch.setattr(offline_sticker.get_bot(), "get_file", make_assertion) + assert await offline_sticker.get_file() - async def test_delete_sticker_from_set_sticker_input(self, offline_bot, sticker, monkeypatch): + async def test_delete_sticker_from_set_sticker_input( + self, offline_bot, offline_sticker, monkeypatch + ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.delete_sticker_from_set(sticker) + assert await offline_bot.delete_sticker_from_set(offline_sticker) - async def test_replace_sticker_in_set_sticker_input(self, offline_bot, sticker, monkeypatch): + async def test_replace_sticker_in_set_sticker_input( + self, offline_bot, offline_sticker, monkeypatch + ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["old_sticker"] == sticker.file_id + return request_data.json_parameters["old_sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) assert await offline_bot.replace_sticker_in_set( - user_id=1, name="name", sticker="sticker", old_sticker=sticker + user_id=1, name="name", sticker="sticker", old_sticker=offline_sticker ) - async def test_set_sticker_emoji_list_sticker_input(self, offline_bot, sticker, monkeypatch): + async def test_set_sticker_emoji_list_sticker_input( + self, offline_bot, offline_sticker, monkeypatch + ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.set_sticker_emoji_list(sticker, ["emoji"]) + assert await offline_bot.set_sticker_emoji_list(offline_sticker, ["emoji"]) async def test_set_sticker_mask_position_sticker_input( - self, offline_bot, sticker, monkeypatch + self, offline_bot, offline_sticker, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.set_sticker_mask_position(sticker, MaskPosition("eyes", 1, 2, 3)) + assert await offline_bot.set_sticker_mask_position( + offline_sticker, MaskPosition("eyes", 1, 2, 3) + ) async def test_set_sticker_position_in_set_sticker_input( - self, offline_bot, sticker, monkeypatch + self, offline_bot, offline_sticker, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.set_sticker_position_in_set(sticker, 1) + assert await offline_bot.set_sticker_position_in_set(offline_sticker, 1) - async def test_set_sticker_keywords_sticker_input(self, offline_bot, sticker, monkeypatch): + async def test_set_sticker_keywords_sticker_input( + self, offline_bot, offline_sticker, monkeypatch + ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["sticker"] == sticker.file_id + return request_data.json_parameters["sticker"] == offline_sticker.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.set_sticker_keywords(sticker, ["keyword"]) + assert await offline_bot.set_sticker_keywords(offline_sticker, ["keyword"]) class TestStickerSetWithRequest: diff --git a/tests/_files/test_video.py b/tests/_files/test_video.py index 4a34e126668..d4f1edb443e 100644 --- a/tests/_files/test_video.py +++ b/tests/_files/test_video.py @@ -48,20 +48,6 @@ from tests.auxil.slots import mro_slots -# Override `video` fixture to provide start_timestamp -@pytest.fixture(scope="module") -async def video(bot, chat_id): - # The `video` object returned here does not have the `qualities` attribute. - # Tests using actual VideoQuality objects from real Telegram responses - # are implemented separately in `test_videoquality`. - with data_file("telegram.mp4").open("rb") as f: - return ( - await bot.send_video( - chat_id, video=f, start_timestamp=VideoTestBase.start_timestamp, read_timeout=50 - ) - ).video - - class VideoTestBase: width = 360 height = 640 @@ -83,32 +69,34 @@ class VideoTestBase: class TestVideoWithoutRequest(VideoTestBase): - def test_slot_behaviour(self, video): - for attr in video.__slots__: - assert getattr(video, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(video)) == len(set(mro_slots(video))), "duplicate slot" + def test_slot_behaviour(self, offline_video): + for attr in offline_video.__slots__: + assert getattr(offline_video, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_video)) == len(set(mro_slots(offline_video))), ( + "duplicate slot" + ) - def test_creation(self, video): + def test_creation(self, offline_video): # Make sure file has been uploaded. - assert isinstance(video, Video) - assert isinstance(video.file_id, str) - assert isinstance(video.file_unique_id, str) - assert video.file_id - assert video.file_unique_id - - assert isinstance(video.thumbnail, PhotoSize) - assert isinstance(video.thumbnail.file_id, str) - assert isinstance(video.thumbnail.file_unique_id, str) - assert video.thumbnail.file_id - assert video.thumbnail.file_unique_id - - def test_expected_values(self, video): - assert video.width == self.width - assert video.height == self.height - assert video._duration == self.duration - assert video.file_size == self.file_size - assert video.mime_type == self.mime_type - assert video._start_timestamp == self.start_timestamp + assert isinstance(offline_video, Video) + assert isinstance(offline_video.file_id, str) + assert isinstance(offline_video.file_unique_id, str) + assert offline_video.file_id + assert offline_video.file_unique_id + + assert isinstance(offline_video.thumbnail, PhotoSize) + assert isinstance(offline_video.thumbnail.file_id, str) + assert isinstance(offline_video.thumbnail.file_unique_id, str) + assert offline_video.thumbnail.file_id + assert offline_video.thumbnail.file_unique_id + + def test_expected_values(self, offline_video): + assert offline_video.width == self.width + assert offline_video.height == self.height + assert offline_video._duration == self.duration + assert offline_video.file_size == self.file_size + assert offline_video.mime_type == self.mime_type + assert offline_video._start_timestamp == self.start_timestamp def test_de_json(self, offline_bot): json_dict = { @@ -139,39 +127,39 @@ def test_de_json(self, offline_bot): assert json_video.cover == self.cover assert json_video.qualities == self.qualities - def test_to_dict(self, video): - video_dict = video.to_dict() + def test_to_dict(self, offline_video): + video_dict = offline_video.to_dict() assert isinstance(video_dict, dict) - assert video_dict["file_id"] == video.file_id - assert video_dict["file_unique_id"] == video.file_unique_id - assert video_dict["width"] == video.width - assert video_dict["height"] == video.height + assert video_dict["file_id"] == offline_video.file_id + assert video_dict["file_unique_id"] == offline_video.file_unique_id + assert video_dict["width"] == offline_video.width + assert video_dict["height"] == offline_video.height assert video_dict["duration"] == int(self.duration.total_seconds()) assert isinstance(video_dict["duration"], int) - assert video_dict["mime_type"] == video.mime_type - assert video_dict["file_size"] == video.file_size - assert video_dict["file_name"] == video.file_name + assert video_dict["mime_type"] == offline_video.mime_type + assert video_dict["file_size"] == offline_video.file_size + assert video_dict["file_name"] == offline_video.file_name assert video_dict["start_timestamp"] == int(self.start_timestamp.total_seconds()) assert isinstance(video_dict["start_timestamp"], int) - def test_time_period_properties(self, PTB_TIMEDELTA, video): + def test_time_period_properties(self, PTB_TIMEDELTA, offline_video): if PTB_TIMEDELTA: - assert video.duration == self.duration - assert isinstance(video.duration, dtm.timedelta) + assert offline_video.duration == self.duration + assert isinstance(offline_video.duration, dtm.timedelta) - assert video.start_timestamp == self.start_timestamp - assert isinstance(video.start_timestamp, dtm.timedelta) + assert offline_video.start_timestamp == self.start_timestamp + assert isinstance(offline_video.start_timestamp, dtm.timedelta) else: - assert video.duration == int(self.duration.total_seconds()) - assert isinstance(video.duration, int) + assert offline_video.duration == int(self.duration.total_seconds()) + assert isinstance(offline_video.duration, int) - assert video.start_timestamp == int(self.start_timestamp.total_seconds()) - assert isinstance(video.start_timestamp, int) + assert offline_video.start_timestamp == int(self.start_timestamp.total_seconds()) + assert isinstance(offline_video.start_timestamp, int) - def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, video): - video.duration - video.start_timestamp + def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, offline_video): + offline_video.duration + offline_video.start_timestamp if PTB_TIMEDELTA: assert len(recwarn) == 0 @@ -181,12 +169,18 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, video): assert f"`{attr}` will be of type `datetime.timedelta`" in str(recwarn[i].message) assert recwarn[i].category is PTBDeprecationWarning - def test_equality(self, video): - a = Video(video.file_id, video.file_unique_id, self.width, self.height, self.duration) - b = Video("", video.file_unique_id, self.width, self.height, self.duration) - c = Video(video.file_id, video.file_unique_id, 0, 0, 0) + def test_equality(self, offline_video): + a = Video( + offline_video.file_id, + offline_video.file_unique_id, + self.width, + self.height, + self.duration, + ) + b = Video("", offline_video.file_unique_id, self.width, self.height, self.duration) + c = Video(offline_video.file_id, offline_video.file_unique_id, 0, 0, 0) d = Video("", "", self.width, self.height, self.duration) - e = Voice(video.file_id, video.file_unique_id, self.duration) + e = Voice(offline_video.file_id, offline_video.file_unique_id, self.duration) assert a == b assert hash(a) == hash(b) @@ -205,12 +199,12 @@ async def test_error_without_required_args(self, offline_bot, chat_id): with pytest.raises(TypeError): await offline_bot.send_video(chat_id=chat_id) - async def test_send_with_video(self, monkeypatch, offline_bot, chat_id, video): + async def test_send_with_video(self, monkeypatch, offline_bot, chat_id, offline_video): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["video"] == video.file_id + return request_data.json_parameters["video"] == offline_video.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_video(chat_id, video=video) + assert await offline_bot.send_video(chat_id, video=offline_video) async def test_send_video_custom_filename(self, offline_bot, chat_id, video_file, monkeypatch): async def make_assertion(url, request_data: RequestData, *args, **kwargs): @@ -247,16 +241,18 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, video): + async def test_get_file_instance_method(self, monkeypatch, offline_video): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == video.file_id + return kwargs["file_id"] == offline_video.file_id assert check_shortcut_signature(Video.get_file, Bot.get_file, ["file_id"], []) - assert await check_shortcut_call(video.get_file, video.get_bot(), "get_file") - assert await check_defaults_handling(video.get_file, video.get_bot()) + assert await check_shortcut_call( + offline_video.get_file, offline_video.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_video.get_file, offline_video.get_bot()) - monkeypatch.setattr(video.get_bot(), "get_file", make_assertion) - assert await video.get_file() + monkeypatch.setattr(offline_video.get_bot(), "get_file", make_assertion) + assert await offline_video.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -268,7 +264,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_video_default_quote_parse_mode( - self, default_bot, chat_id, video, custom, monkeypatch + self, default_bot, chat_id, offline_video, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -281,7 +277,9 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): kwargs["quote_parse_mode"] = custom monkeypatch.setattr(default_bot.request, "post", make_assertion) - await default_bot.send_video(chat_id, video, reply_parameters=ReplyParameters(**kwargs)) + await default_bot.send_video( + chat_id, offline_video, reply_parameters=ReplyParameters(**kwargs) + ) class TestVideoWithRequest(VideoTestBase): diff --git a/tests/_files/test_videonote.py b/tests/_files/test_videonote.py index c2993021883..21e80b973dc 100644 --- a/tests/_files/test_videonote.py +++ b/tests/_files/test_videonote.py @@ -44,6 +44,28 @@ def video_note_file(): yield f +@pytest.fixture(scope="module") +def offline_video_note(offline_bot): + thumbnail = PhotoSize( + "thumbnail-file-id", + "thumbnail-file-unique-id", + width=VideoNoteTestBase.thumb_width, + height=VideoNoteTestBase.thumb_height, + file_size=VideoNoteTestBase.thumb_file_size, + ) + thumbnail.set_bot(offline_bot) + value = VideoNote( + file_id=VideoNoteTestBase.videonote_file_id, + file_unique_id=VideoNoteTestBase.videonote_file_unique_id, + length=VideoNoteTestBase.length, + duration=VideoNoteTestBase.duration, + file_size=VideoNoteTestBase.file_size, + thumbnail=thumbnail, + ) + value.set_bot(offline_bot) + return value + + @pytest.fixture(scope="module") async def video_note(bot, chat_id): with data_file("telegram2.mp4").open("rb") as f: @@ -63,24 +85,26 @@ class VideoNoteTestBase: class TestVideoNoteWithoutRequest(VideoNoteTestBase): - def test_slot_behaviour(self, video_note): - for attr in video_note.__slots__: - assert getattr(video_note, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(video_note)) == len(set(mro_slots(video_note))), "duplicate slot" + def test_slot_behaviour(self, offline_video_note): + for attr in offline_video_note.__slots__: + assert getattr(offline_video_note, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_video_note)) == len(set(mro_slots(offline_video_note))), ( + "duplicate slot" + ) - def test_creation(self, video_note): + def test_creation(self, offline_video_note): # Make sure file has been uploaded. - assert isinstance(video_note, VideoNote) - assert isinstance(video_note.file_id, str) - assert isinstance(video_note.file_unique_id, str) - assert video_note.file_id - assert video_note.file_unique_id - - assert isinstance(video_note.thumbnail, PhotoSize) - assert isinstance(video_note.thumbnail.file_id, str) - assert isinstance(video_note.thumbnail.file_unique_id, str) - assert video_note.thumbnail.file_id - assert video_note.thumbnail.file_unique_id + assert isinstance(offline_video_note, VideoNote) + assert isinstance(offline_video_note.file_id, str) + assert isinstance(offline_video_note.file_unique_id, str) + assert offline_video_note.file_id + assert offline_video_note.file_unique_id + + assert isinstance(offline_video_note.thumbnail, PhotoSize) + assert isinstance(offline_video_note.thumbnail.file_id, str) + assert isinstance(offline_video_note.thumbnail.file_unique_id, str) + assert offline_video_note.thumbnail.file_id + assert offline_video_note.thumbnail.file_unique_id def test_de_json(self, offline_bot): json_dict = { @@ -99,27 +123,27 @@ def test_de_json(self, offline_bot): assert json_video_note._duration == self.duration assert json_video_note.file_size == self.file_size - def test_to_dict(self, video_note): - video_note_dict = video_note.to_dict() + def test_to_dict(self, offline_video_note): + video_note_dict = offline_video_note.to_dict() assert isinstance(video_note_dict, dict) - assert video_note_dict["file_id"] == video_note.file_id - assert video_note_dict["file_unique_id"] == video_note.file_unique_id - assert video_note_dict["length"] == video_note.length + assert video_note_dict["file_id"] == offline_video_note.file_id + assert video_note_dict["file_unique_id"] == offline_video_note.file_unique_id + assert video_note_dict["length"] == offline_video_note.length assert video_note_dict["duration"] == int(self.duration.total_seconds()) assert isinstance(video_note_dict["duration"], int) - assert video_note_dict["file_size"] == video_note.file_size + assert video_note_dict["file_size"] == offline_video_note.file_size - def test_time_period_properties(self, PTB_TIMEDELTA, video_note): + def test_time_period_properties(self, PTB_TIMEDELTA, offline_video_note): if PTB_TIMEDELTA: - assert video_note.duration == self.duration - assert isinstance(video_note.duration, dtm.timedelta) + assert offline_video_note.duration == self.duration + assert isinstance(offline_video_note.duration, dtm.timedelta) else: - assert video_note.duration == int(self.duration.total_seconds()) - assert isinstance(video_note.duration, int) + assert offline_video_note.duration == int(self.duration.total_seconds()) + assert isinstance(offline_video_note.duration, int) - def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, video_note): - video_note.duration + def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, offline_video_note): + offline_video_note.duration if PTB_TIMEDELTA: assert len(recwarn) == 0 @@ -128,12 +152,17 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, video_note): assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_equality(self, video_note): - a = VideoNote(video_note.file_id, video_note.file_unique_id, self.length, self.duration) - b = VideoNote("", video_note.file_unique_id, self.length, self.duration) - c = VideoNote(video_note.file_id, video_note.file_unique_id, 0, 0) + def test_equality(self, offline_video_note): + a = VideoNote( + offline_video_note.file_id, + offline_video_note.file_unique_id, + self.length, + self.duration, + ) + b = VideoNote("", offline_video_note.file_unique_id, self.length, self.duration) + c = VideoNote(offline_video_note.file_id, offline_video_note.file_unique_id, 0, 0) d = VideoNote("", "", self.length, self.duration) - e = Voice(video_note.file_id, video_note.file_unique_id, self.duration) + e = Voice(offline_video_note.file_id, offline_video_note.file_unique_id, self.duration) assert a == b assert hash(a) == hash(b) @@ -152,12 +181,14 @@ async def test_error_without_required_args(self, offline_bot, chat_id): with pytest.raises(TypeError): await offline_bot.send_video_note(chat_id=chat_id) - async def test_send_with_video_note(self, monkeypatch, offline_bot, chat_id, video_note): + async def test_send_with_video_note( + self, monkeypatch, offline_bot, chat_id, offline_video_note + ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["video_note"] == video_note.file_id + return request_data.json_parameters["video_note"] == offline_video_note.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_video_note(chat_id, video_note=video_note) + assert await offline_bot.send_video_note(chat_id, video_note=offline_video_note) async def test_send_video_note_custom_filename( self, offline_bot, chat_id, video_note_file, monkeypatch @@ -200,16 +231,20 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, video_note): + async def test_get_file_instance_method(self, monkeypatch, offline_video_note): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == video_note.file_id + return kwargs["file_id"] == offline_video_note.file_id assert check_shortcut_signature(VideoNote.get_file, Bot.get_file, ["file_id"], []) - assert await check_shortcut_call(video_note.get_file, video_note.get_bot(), "get_file") - assert await check_defaults_handling(video_note.get_file, video_note.get_bot()) + assert await check_shortcut_call( + offline_video_note.get_file, offline_video_note.get_bot(), "get_file" + ) + assert await check_defaults_handling( + offline_video_note.get_file, offline_video_note.get_bot() + ) - monkeypatch.setattr(video_note.get_bot(), "get_file", make_assertion) - assert await video_note.get_file() + monkeypatch.setattr(offline_video_note.get_bot(), "get_file", make_assertion) + assert await offline_video_note.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -221,7 +256,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_video_note_default_quote_parse_mode( - self, default_bot, chat_id, video_note, custom, monkeypatch + self, default_bot, chat_id, offline_video_note, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -235,7 +270,7 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): monkeypatch.setattr(default_bot.request, "post", make_assertion) await default_bot.send_video_note( - chat_id, video_note, reply_parameters=ReplyParameters(**kwargs) + chat_id, offline_video_note, reply_parameters=ReplyParameters(**kwargs) ) diff --git a/tests/_files/test_videoquality.py b/tests/_files/test_videoquality.py index 766e12f800f..35db3f70863 100644 --- a/tests/_files/test_videoquality.py +++ b/tests/_files/test_videoquality.py @@ -29,6 +29,20 @@ def video_quality_message_id(): return 375821 +@pytest.fixture(scope="module") +def offline_video_quality_list(offline_bot): + quality = VideoQuality( + file_id="video-quality-file-id", + file_unique_id="video-quality-file-unique-id", + width=VideoQualityTestBase.width, + height=VideoQualityTestBase.height, + codec=VideoQualityTestBase.codec, + file_size=VideoQualityTestBase.file_size, + ) + quality.set_bot(offline_bot) + return (quality,) + + @pytest.fixture(scope="module") async def video_quality_list(bot, chat_id, channel_id, video_quality_message_id): return ( @@ -36,6 +50,11 @@ async def video_quality_list(bot, chat_id, channel_id, video_quality_message_id) ).video.qualities +@pytest.fixture(scope="module") +def offline_video_quality(offline_video_quality_list): + return offline_video_quality_list[-1] + + @pytest.fixture(scope="module") def video_quality(video_quality_list): return video_quality_list[-1] @@ -50,36 +69,36 @@ class VideoQualityTestBase: class TestVideoQualityWithoutRequest(VideoQualityTestBase): - def test_qualities_available(self, video_quality_list): - assert isinstance(video_quality_list, tuple) + def test_qualities_available(self, offline_video_quality_list): + assert isinstance(offline_video_quality_list, tuple) # Subsequent tests relie on the forwarded video # having exactly one video quality. - assert len(video_quality_list) == 1 - - def test_slot_behaviour(self, video_quality): - for attr in video_quality.__slots__: - assert getattr(video_quality, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(video_quality)) == len(set(mro_slots(video_quality))), ( - "duplicate slot" - ) - - def test_creation(self, video_quality): - assert isinstance(video_quality, VideoQuality) - assert isinstance(video_quality.file_id, str) - assert isinstance(video_quality.file_unique_id, str) - assert video_quality.file_id - assert video_quality.file_unique_id - - def test_expected_values(self, video_quality): - assert video_quality.width == self.width - assert video_quality.height == self.height - assert video_quality.codec == self.codec - assert video_quality.file_size == self.file_size - - def test_de_json(self, offline_bot, video_quality): + assert len(offline_video_quality_list) == 1 + + def test_slot_behaviour(self, offline_video_quality): + for attr in offline_video_quality.__slots__: + assert getattr(offline_video_quality, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_video_quality)) == len( + set(mro_slots(offline_video_quality)) + ), "duplicate slot" + + def test_creation(self, offline_video_quality): + assert isinstance(offline_video_quality, VideoQuality) + assert isinstance(offline_video_quality.file_id, str) + assert isinstance(offline_video_quality.file_unique_id, str) + assert offline_video_quality.file_id + assert offline_video_quality.file_unique_id + + def test_expected_values(self, offline_video_quality): + assert offline_video_quality.width == self.width + assert offline_video_quality.height == self.height + assert offline_video_quality.codec == self.codec + assert offline_video_quality.file_size == self.file_size + + def test_de_json(self, offline_bot, offline_video_quality): json_dict = { - "file_id": video_quality.file_id, - "file_unique_id": video_quality.file_unique_id, + "file_id": offline_video_quality.file_id, + "file_unique_id": offline_video_quality.file_unique_id, "width": self.width, "height": self.height, "codec": self.codec, @@ -88,38 +107,42 @@ def test_de_json(self, offline_bot, video_quality): json_videoquality = VideoQuality.de_json(json_dict, offline_bot) assert json_videoquality.api_kwargs == {} - assert json_videoquality.file_id == video_quality.file_id - assert json_videoquality.file_unique_id == video_quality.file_unique_id + assert json_videoquality.file_id == offline_video_quality.file_id + assert json_videoquality.file_unique_id == offline_video_quality.file_unique_id assert json_videoquality.width == self.width assert json_videoquality.height == self.height assert json_videoquality.codec == self.codec assert json_videoquality.file_size == self.file_size - def test_to_dict(self, video_quality): - videoquality_dict = video_quality.to_dict() + def test_to_dict(self, offline_video_quality): + videoquality_dict = offline_video_quality.to_dict() assert isinstance(videoquality_dict, dict) - assert videoquality_dict["file_id"] == video_quality.file_id - assert videoquality_dict["file_unique_id"] == video_quality.file_unique_id - assert videoquality_dict["width"] == video_quality.width - assert videoquality_dict["height"] == video_quality.height - assert videoquality_dict["codec"] == video_quality.codec - assert videoquality_dict["file_size"] == video_quality.file_size - - def test_equality(self, video_quality): + assert videoquality_dict["file_id"] == offline_video_quality.file_id + assert videoquality_dict["file_unique_id"] == offline_video_quality.file_unique_id + assert videoquality_dict["width"] == offline_video_quality.width + assert videoquality_dict["height"] == offline_video_quality.height + assert videoquality_dict["codec"] == offline_video_quality.codec + assert videoquality_dict["file_size"] == offline_video_quality.file_size + + def test_equality(self, offline_video_quality): a = VideoQuality( - video_quality.file_id, - video_quality.file_unique_id, + offline_video_quality.file_id, + offline_video_quality.file_unique_id, self.width, self.height, self.codec, ) - b = VideoQuality("", video_quality.file_unique_id, self.width, self.height, self.codec) - c = VideoQuality(video_quality.file_id, video_quality.file_unique_id, 0, 0, self.codec) + b = VideoQuality( + "", offline_video_quality.file_unique_id, self.width, self.height, self.codec + ) + c = VideoQuality( + offline_video_quality.file_id, offline_video_quality.file_unique_id, 0, 0, self.codec + ) d = VideoQuality("", "", self.width, self.height, self.codec) e = PhotoSize( - video_quality.file_id, - video_quality.file_unique_id, + offline_video_quality.file_id, + offline_video_quality.file_unique_id, self.width, self.height, ) diff --git a/tests/_files/test_voice.py b/tests/_files/test_voice.py index e1e47759db9..a2662cdc491 100644 --- a/tests/_files/test_voice.py +++ b/tests/_files/test_voice.py @@ -45,6 +45,19 @@ def voice_file(): yield f +@pytest.fixture(scope="module") +def offline_voice(offline_bot): + value = Voice( + file_id=VoiceTestBase.voice_file_id, + file_unique_id=VoiceTestBase.voice_file_unique_id, + duration=VoiceTestBase.duration, + mime_type=VoiceTestBase.mime_type, + file_size=VoiceTestBase.file_size, + ) + value.set_bot(offline_bot) + return value + + @pytest.fixture(scope="module") async def voice(bot, chat_id): with data_file("telegram.ogg").open("rb") as f: @@ -62,23 +75,25 @@ class VoiceTestBase: class TestVoiceWithoutRequest(VoiceTestBase): - def test_slot_behaviour(self, voice): - for attr in voice.__slots__: - assert getattr(voice, attr, "err") != "err", f"got extra slot '{attr}'" - assert len(mro_slots(voice)) == len(set(mro_slots(voice))), "duplicate slot" + def test_slot_behaviour(self, offline_voice): + for attr in offline_voice.__slots__: + assert getattr(offline_voice, attr, "err") != "err", f"got extra slot '{attr}'" + assert len(mro_slots(offline_voice)) == len(set(mro_slots(offline_voice))), ( + "duplicate slot" + ) - async def test_creation(self, voice): + async def test_creation(self, offline_voice): # Make sure file has been uploaded. - assert isinstance(voice, Voice) - assert isinstance(voice.file_id, str) - assert isinstance(voice.file_unique_id, str) - assert voice.file_id - assert voice.file_unique_id + assert isinstance(offline_voice, Voice) + assert isinstance(offline_voice.file_id, str) + assert isinstance(offline_voice.file_unique_id, str) + assert offline_voice.file_id + assert offline_voice.file_unique_id - def test_expected_values(self, voice): - assert voice._duration == self.duration - assert voice.mime_type == self.mime_type - assert voice.file_size == self.file_size + def test_expected_values(self, offline_voice): + assert offline_voice._duration == self.duration + assert offline_voice.mime_type == self.mime_type + assert offline_voice.file_size == self.file_size def test_de_json(self, offline_bot): json_dict = { @@ -97,27 +112,27 @@ def test_de_json(self, offline_bot): assert json_voice.mime_type == self.mime_type assert json_voice.file_size == self.file_size - def test_to_dict(self, voice): - voice_dict = voice.to_dict() + def test_to_dict(self, offline_voice): + voice_dict = offline_voice.to_dict() assert isinstance(voice_dict, dict) - assert voice_dict["file_id"] == voice.file_id - assert voice_dict["file_unique_id"] == voice.file_unique_id + assert voice_dict["file_id"] == offline_voice.file_id + assert voice_dict["file_unique_id"] == offline_voice.file_unique_id assert voice_dict["duration"] == int(self.duration.total_seconds()) assert isinstance(voice_dict["duration"], int) - assert voice_dict["mime_type"] == voice.mime_type - assert voice_dict["file_size"] == voice.file_size + assert voice_dict["mime_type"] == offline_voice.mime_type + assert voice_dict["file_size"] == offline_voice.file_size - def test_time_period_properties(self, PTB_TIMEDELTA, voice): + def test_time_period_properties(self, PTB_TIMEDELTA, offline_voice): if PTB_TIMEDELTA: - assert voice.duration == self.duration - assert isinstance(voice.duration, dtm.timedelta) + assert offline_voice.duration == self.duration + assert isinstance(offline_voice.duration, dtm.timedelta) else: - assert voice.duration == int(self.duration.total_seconds()) - assert isinstance(voice.duration, int) + assert offline_voice.duration == int(self.duration.total_seconds()) + assert isinstance(offline_voice.duration, int) - def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, voice): - voice.duration + def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, offline_voice): + offline_voice.duration if PTB_TIMEDELTA: assert len(recwarn) == 0 @@ -126,12 +141,12 @@ def test_time_period_int_deprecated(self, recwarn, PTB_TIMEDELTA, voice): assert "`duration` will be of type `datetime.timedelta`" in str(recwarn[0].message) assert recwarn[0].category is PTBDeprecationWarning - def test_equality(self, voice): - a = Voice(voice.file_id, voice.file_unique_id, self.duration) - b = Voice("", voice.file_unique_id, self.duration) - c = Voice(voice.file_id, voice.file_unique_id, 0) + def test_equality(self, offline_voice): + a = Voice(offline_voice.file_id, offline_voice.file_unique_id, self.duration) + b = Voice("", offline_voice.file_unique_id, self.duration) + c = Voice(offline_voice.file_id, offline_voice.file_unique_id, 0) d = Voice("", "", self.duration) - e = Audio(voice.file_id, voice.file_unique_id, self.duration) + e = Audio(offline_voice.file_id, offline_voice.file_unique_id, self.duration) assert a == b assert hash(a) == hash(b) @@ -158,12 +173,12 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert await offline_bot.send_voice(chat_id, voice_file, filename="custom_filename") - async def test_send_with_voice(self, monkeypatch, offline_bot, chat_id, voice): + async def test_send_with_voice(self, monkeypatch, offline_bot, chat_id, offline_voice): async def make_assertion(url, request_data: RequestData, *args, **kwargs): - return request_data.json_parameters["voice"] == voice.file_id + return request_data.json_parameters["voice"] == offline_voice.file_id monkeypatch.setattr(offline_bot.request, "post", make_assertion) - assert await offline_bot.send_voice(chat_id, voice=voice) + assert await offline_bot.send_voice(chat_id, voice=offline_voice) @pytest.mark.parametrize("local_mode", [True, False]) async def test_send_voice_local_files( @@ -190,16 +205,18 @@ async def make_assertion(_, data, *args, **kwargs): finally: offline_bot._local_mode = False - async def test_get_file_instance_method(self, monkeypatch, voice): + async def test_get_file_instance_method(self, monkeypatch, offline_voice): async def make_assertion(*_, **kwargs): - return kwargs["file_id"] == voice.file_id + return kwargs["file_id"] == offline_voice.file_id assert check_shortcut_signature(Voice.get_file, Bot.get_file, ["file_id"], []) - assert await check_shortcut_call(voice.get_file, voice.get_bot(), "get_file") - assert await check_defaults_handling(voice.get_file, voice.get_bot()) + assert await check_shortcut_call( + offline_voice.get_file, offline_voice.get_bot(), "get_file" + ) + assert await check_defaults_handling(offline_voice.get_file, offline_voice.get_bot()) - monkeypatch.setattr(voice.get_bot(), "get_file", make_assertion) - assert await voice.get_file() + monkeypatch.setattr(offline_voice.get_bot(), "get_file", make_assertion) + assert await offline_voice.get_file() @pytest.mark.parametrize( ("default_bot", "custom"), @@ -211,7 +228,7 @@ async def make_assertion(*_, **kwargs): indirect=["default_bot"], ) async def test_send_voice_default_quote_parse_mode( - self, default_bot, chat_id, voice, custom, monkeypatch + self, default_bot, chat_id, offline_voice, custom, monkeypatch ): async def make_assertion(url, request_data: RequestData, *args, **kwargs): assert request_data.parameters["reply_parameters"].get("quote_parse_mode") == ( @@ -224,7 +241,9 @@ async def make_assertion(url, request_data: RequestData, *args, **kwargs): kwargs["quote_parse_mode"] = custom monkeypatch.setattr(default_bot.request, "post", make_assertion) - await default_bot.send_voice(chat_id, voice, reply_parameters=ReplyParameters(**kwargs)) + await default_bot.send_voice( + chat_id, offline_voice, reply_parameters=ReplyParameters(**kwargs) + ) class TestVoiceWithRequest(VoiceTestBase): diff --git a/tests/conftest.py b/tests/conftest.py index ed0c3f64231..583642db929 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -38,9 +38,9 @@ ) from telegram.ext import Defaults from tests.auxil.build_messages import DATE, make_message -from tests.auxil.ci_bots import BOT_INFO_PROVIDER, JOB_INDEX +from tests.auxil.ci_bots import BOT_INFO_PROVIDER from tests.auxil.constants import PRIVATE_KEY, TEST_TOPIC_ICON_COLOR, TEST_TOPIC_NAME -from tests.auxil.envvars import GITHUB_ACTIONS, TEST_WITH_OPT_DEPS, env_var_2_bool +from tests.auxil.envvars import TEST_WITH_OPT_DEPS, env_var_2_bool from tests.auxil.files import data_file from tests.auxil.networking import NonchalantHttpxRequest from tests.auxil.pytest_classes import PytestBot, make_bot @@ -86,38 +86,11 @@ def pytest_collection_modifyitems(items: list[pytest.Item]): ): parent.add_marker(pytest.mark.no_req) - -if GITHUB_ACTIONS and JOB_INDEX == 0: - # let's not slow down the tests too much with these additional checks - # that's why we run them only in GitHub actions and only on *one* of the several test - # matrix entries - @pytest.fixture(autouse=True) - def _disallow_requests_in_without_request_tests(request): - """This fixture prevents tests that don't require requests from using the online-bot. - This is a sane-effort approach on trying to prevent requests from being made in the - *WithoutRequest classes. Note that we can not prevent all requests, as one can still - manually build a `Bot` object or use `httpx` directly. See #4317 and #4465 for some - discussion. - """ - - if type(request).__name__ == "SubRequest": - # Some fixtures used in the *WithoutRequests test classes do use requests, e.g. - # `animation`. Separating that would be too much effort, hence we allow that. - # Unfortunately the `SubRequest` class is not public, so we check only the name for - # less dependency on pytest's internal structure. - return - - if not request.cls: - return - name = request.cls.__name__ - if not name.endswith("WithoutRequest") or not request.fixturenames: - return - - if "bot" in request.fixturenames: - pytest.fail( - f"Test function {request.function} in test class {name} should not have a `bot` " - f"fixture. Use `offline_bot` instead." - ) + if item.get_closest_marker(name="no_req"): + # Applying this marker here instead of through a function-scoped fixture ensures that + # external network access is already blocked while higher-scoped fixtures are being + # set up. Loopback must remain available because some tests use it + item.add_marker(pytest.mark.allow_hosts(["localhost", "127.0.0.1", "::1"])) @pytest.fixture(scope="module", params=["true", "1", "false", "gibberish", None]) @@ -265,6 +238,11 @@ def class_thumb_file(): yield f +@pytest.fixture(scope="session") +def offline_emoji_id(): + return "5368324170671202286" + + @pytest.fixture(scope="session") async def emoji_id(bot): emoji_sticker_list = await bot.get_forum_topic_icon_stickers() diff --git a/tests/ext/test_application.py b/tests/ext/test_application.py index c26e8b3f32a..933cc0b0a78 100644 --- a/tests/ext/test_application.py +++ b/tests/ext/test_application.py @@ -2329,29 +2329,36 @@ async def test_run_polling_webhook_bootstrap_retries( """ def thread_target(): - asyncio.set_event_loop(asyncio.new_event_loop()) - app = ( - ApplicationBuilder().bot(offline_bot).application_class(PytestApplication).build() - ) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: + app = ( + ApplicationBuilder() + .bot(offline_bot) + .application_class(PytestApplication) + .build() + ) - async def initialize(*args, **kwargs): - self.count += 1 - raise exception_class(str(self.count)) + async def initialize(*args, **kwargs): + self.count += 1 + raise exception_class(str(self.count)) - monkeypatch.setattr(app, "initialize", initialize) - method = functools.partial( - getattr(app, method_name), - bootstrap_retries=retries, - close_loop=False, - stop_signals=None, - ) + monkeypatch.setattr(app, "initialize", initialize) + method = functools.partial( + getattr(app, method_name), + bootstrap_retries=retries, + close_loop=False, + stop_signals=None, + ) - if exception_class == InvalidToken: - with pytest.raises(InvalidToken, match="1"): - method() - else: - with pytest.raises(TelegramError, match=str(retries + 1)): - method() + if exception_class == InvalidToken: + with pytest.raises(InvalidToken, match="1"): + method() + else: + with pytest.raises(TelegramError, match=str(retries + 1)): + method() + finally: + loop.close() thread = Thread(target=thread_target) thread.start() @@ -2368,42 +2375,46 @@ async def test_run_polling_webhook_infinite_bootstrap_retries( """ def thread_target(): - asyncio.set_event_loop(asyncio.new_event_loop()) + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + try: - async def post_init(application): - application.stop_running() + async def post_init(application): + application.stop_running() - app = ( - ApplicationBuilder() - .bot(offline_bot) - .application_class(PytestApplication) - .post_init(post_init) - .build() - ) + app = ( + ApplicationBuilder() + .bot(offline_bot) + .application_class(PytestApplication) + .post_init(post_init) + .build() + ) - async def do_pass(*args, **kwargs): - pass + async def do_pass(*args, **kwargs): + pass - monkeypatch.setattr(app.bot, "initialize", do_pass) - monkeypatch.setattr(app.bot, "delete_webhook", do_pass) + monkeypatch.setattr(app.bot, "initialize", do_pass) + monkeypatch.setattr(app.bot, "delete_webhook", do_pass) - original_initialize = app.initialize + original_initialize = app.initialize - async def initialize(*args, **kwargs): - if self.count >= 3: - pytest.fail("Should be called only once. Test failed.") + async def initialize(*args, **kwargs): + if self.count >= 3: + pytest.fail("Should be called only once. Test failed.") - self.count += 1 - if self.count == 1: - raise TelegramError("Test Exception") - await original_initialize(*args, **kwargs) + self.count += 1 + if self.count == 1: + raise TelegramError("Test Exception") + await original_initialize(*args, **kwargs) - monkeypatch.setattr(app, "initialize", initialize) - getattr(app, method_name)( - bootstrap_retries=-1, - close_loop=False, - stop_signals=None, - ) + monkeypatch.setattr(app, "initialize", initialize) + getattr(app, method_name)( + bootstrap_retries=-1, + close_loop=False, + stop_signals=None, + ) + finally: + loop.close() thread = Thread(target=thread_target) thread.start() diff --git a/tests/request/test_request.py b/tests/request/test_request.py index a0d71544aa3..d1b416166b4 100644 --- a/tests/request/test_request.py +++ b/tests/request/test_request.py @@ -246,21 +246,22 @@ async def test_chat_migrated(self, monkeypatch, httpx_request: HTTPXRequest): assert exc_info.value.new_chat_id == 123 - async def test_retry_after(self, monkeypatch, httpx_request: HTTPXRequest, PTB_TIMEDELTA): + async def test_retry_after(self, monkeypatch, PTB_TIMEDELTA): server_response = b'{"ok": "False", "parameters": {"retry_after": 42}}' - monkeypatch.setattr( - httpx_request, - "do_request", - mocker_factory(response=server_response, return_code=HTTPStatus.BAD_REQUEST), - ) + async with HTTPXRequest() as httpx_request: + monkeypatch.setattr( + HTTPXRequest, + "do_request", + mocker_factory(response=server_response, return_code=HTTPStatus.BAD_REQUEST), + ) - with pytest.raises( - RetryAfter, match="Retry in " + "0:00:42" if PTB_TIMEDELTA else "42" - ) as exc_info: - await httpx_request.post(None, None, None) + with pytest.raises( + RetryAfter, match="Retry in " + "0:00:42" if PTB_TIMEDELTA else "42" + ) as exc_info: + await httpx_request.post(None, None, None) - assert exc_info.value.retry_after == (dtm.timdelta(seconds=42) if PTB_TIMEDELTA else 42) + assert exc_info.value.retry_after == (dtm.timedelta(seconds=42) if PTB_TIMEDELTA else 42) async def test_unknown_request_params(self, monkeypatch, httpx_request: HTTPXRequest): server_response = b'{"ok": "False", "parameters": {"unknown": "42"}}' diff --git a/tests/test_bot.py b/tests/test_bot.py index 58b57d3d469..d093ffa6df9 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -135,6 +135,11 @@ async def static_message(bot, chat_id): ) +@pytest.fixture +def offline_media_message(offline_bot): + return make_message("", bot=offline_bot, caption="my caption") + + @pytest.fixture async def media_message(bot, chat_id): # mostly used in tests for edit_message and hence can't be reused @@ -494,7 +499,11 @@ async def test_equality(self): async with ( make_bot(token=FALLBACKS[0]["token"]) as a, make_bot(token=FALLBACKS[0]["token"]) as b, - Bot(token=FALLBACKS[0]["token"]) as c, + PytestBot( + token=FALLBACKS[0]["token"], + request=OfflineRequest(), + get_updates_request=OfflineRequest(), + ) as c, make_bot(token=FALLBACKS[1]["token"]) as d, ): e = Update(123456789) @@ -539,7 +548,7 @@ async def test_get_me_and_properties_not_initialized(self, attribute): await bot.shutdown() async def test_get_me_and_properties(self, offline_bot): - get_me_bot = await ExtBot(offline_bot.token).get_me() + get_me_bot = await offline_bot.get_me() assert isinstance(get_me_bot, User) assert get_me_bot.id == offline_bot.id @@ -563,8 +572,20 @@ def test_bot_deepcopy_error(self, offline_bot): @pytest.mark.parametrize( ("cls", "logger_name"), [(Bot, "telegram.Bot"), (ExtBot, "telegram.ext.ExtBot")] ) - async def test_bot_method_logging(self, offline_bot: PytestExtBot, cls, logger_name, caplog): - instance = cls(offline_bot.token) + async def test_bot_method_logging( + self, monkeypatch, offline_bot: PytestExtBot, cls, logger_name, caplog + ): + request = OfflineRequest() + instance = cls( + offline_bot.token, + request=request, + get_updates_request=OfflineRequest(), + ) + + async def post(*args, **kwargs): + return offline_bot.bot.to_dict() + + monkeypatch.setattr(request, "post", post) # Second argument makes sure that we ignore logs from e.g. httpx with caplog.at_level(logging.DEBUG, logger="telegram"): await instance.get_me() @@ -1877,7 +1898,7 @@ async def assertion(url, request_data: RequestData, *args, **kwargs): @pytest.mark.parametrize("json_keyboard", [True, False]) @pytest.mark.parametrize("caption", ["Test", "", None]) async def test_copy_message( - self, monkeypatch, offline_bot, chat_id, media_message, json_keyboard, caption + self, monkeypatch, offline_bot, chat_id, offline_media_message, json_keyboard, caption ): keyboard = InlineKeyboardMarkup( [[InlineKeyboardButton(text="test", callback_data="test2")]] @@ -1889,11 +1910,11 @@ async def post(url, request_data: RequestData, *args, **kwargs): [ data["chat_id"] == chat_id, data["from_chat_id"] == chat_id, - data["message_id"] == media_message.message_id, + data["message_id"] == offline_media_message.message_id, data.get("caption") == caption, data["parse_mode"] == ParseMode.HTML, data["reply_parameters"] - == ReplyParameters(message_id=media_message.message_id).to_dict(), + == ReplyParameters(message_id=offline_media_message.message_id).to_dict(), ( data["reply_markup"] == keyboard.to_json() if json_keyboard @@ -1914,12 +1935,12 @@ async def post(url, request_data: RequestData, *args, **kwargs): await offline_bot.copy_message( chat_id, from_chat_id=chat_id, - message_id=media_message.message_id, + message_id=offline_media_message.message_id, caption=caption, video_start_timestamp=999, caption_entities=[MessageEntity(MessageEntity.BOLD, 0, 4)], parse_mode=ParseMode.HTML, - reply_to_message_id=media_message.message_id, + reply_to_message_id=offline_media_message.message_id, reply_markup=keyboard.to_json() if json_keyboard else keyboard, disable_notification=True, protect_content=True, diff --git a/tests/test_forum.py b/tests/test_forum.py index 73d20c6d786..2a16a17646c 100644 --- a/tests/test_forum.py +++ b/tests/test_forum.py @@ -47,6 +47,17 @@ async def forum_topic_object(forum_group_id, emoji_id): ) +@pytest.fixture(scope="module") +async def offline_forum_topic_object(forum_group_id, offline_emoji_id): + return ForumTopic( + message_thread_id=forum_group_id, + name=ForumTopicTestBase.TEST_TOPIC_NAME, + icon_color=ForumTopicTestBase.TEST_TOPIC_ICON_COLOR, + icon_custom_emoji_id=offline_emoji_id, + is_name_implicit=ForumTopicTestBase.is_name_implicit, + ) + + class ForumTopicTestBase: TEST_TOPIC_NAME = TEST_TOPIC_NAME TEST_TOPIC_ICON_COLOR = TEST_TOPIC_ICON_COLOR @@ -54,25 +65,27 @@ class ForumTopicTestBase: class TestForumTopicWithoutRequest(ForumTopicTestBase): - def test_slot_behaviour(self, forum_topic_object): - inst = forum_topic_object + def test_slot_behaviour(self, offline_forum_topic_object): + inst = offline_forum_topic_object for attr in inst.__slots__: assert getattr(inst, attr, "err") != "err", f"got extra slot '{attr}'" assert len(mro_slots(inst)) == len(set(mro_slots(inst))), "duplicate slot" - async def test_expected_values(self, emoji_id, forum_group_id, forum_topic_object): - assert forum_topic_object.message_thread_id == forum_group_id - assert forum_topic_object.icon_color == self.TEST_TOPIC_ICON_COLOR - assert forum_topic_object.name == self.TEST_TOPIC_NAME - assert forum_topic_object.icon_custom_emoji_id == emoji_id - assert forum_topic_object.is_name_implicit == self.is_name_implicit + async def test_expected_values( + self, offline_emoji_id, forum_group_id, offline_forum_topic_object + ): + assert offline_forum_topic_object.message_thread_id == forum_group_id + assert offline_forum_topic_object.icon_color == self.TEST_TOPIC_ICON_COLOR + assert offline_forum_topic_object.name == self.TEST_TOPIC_NAME + assert offline_forum_topic_object.icon_custom_emoji_id == offline_emoji_id + assert offline_forum_topic_object.is_name_implicit == self.is_name_implicit - def test_de_json(self, offline_bot, emoji_id, forum_group_id): + def test_de_json(self, offline_bot, offline_emoji_id, forum_group_id): json_dict = { "message_thread_id": forum_group_id, "name": self.TEST_TOPIC_NAME, "icon_color": self.TEST_TOPIC_ICON_COLOR, - "icon_custom_emoji_id": emoji_id, + "icon_custom_emoji_id": offline_emoji_id, "is_name_implicit": self.is_name_implicit, } topic = ForumTopic.de_json(json_dict, offline_bot) @@ -81,20 +94,20 @@ def test_de_json(self, offline_bot, emoji_id, forum_group_id): assert topic.message_thread_id == forum_group_id assert topic.icon_color == self.TEST_TOPIC_ICON_COLOR assert topic.name == self.TEST_TOPIC_NAME - assert topic.icon_custom_emoji_id == emoji_id + assert topic.icon_custom_emoji_id == offline_emoji_id assert topic.is_name_implicit == self.is_name_implicit - def test_to_dict(self, emoji_id, forum_group_id, forum_topic_object): - topic_dict = forum_topic_object.to_dict() + def test_to_dict(self, offline_emoji_id, forum_group_id, offline_forum_topic_object): + topic_dict = offline_forum_topic_object.to_dict() assert isinstance(topic_dict, dict) assert topic_dict["message_thread_id"] == forum_group_id assert topic_dict["name"] == self.TEST_TOPIC_NAME assert topic_dict["icon_color"] == self.TEST_TOPIC_ICON_COLOR - assert topic_dict["icon_custom_emoji_id"] == emoji_id + assert topic_dict["icon_custom_emoji_id"] == offline_emoji_id assert topic_dict["is_name_implicit"] == self.is_name_implicit - def test_equality(self, emoji_id, forum_group_id): + def test_equality(self, offline_emoji_id, forum_group_id): a = ForumTopic( message_thread_id=forum_group_id, name=TEST_TOPIC_NAME, @@ -104,7 +117,7 @@ def test_equality(self, emoji_id, forum_group_id): message_thread_id=forum_group_id, name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR, - icon_custom_emoji_id=emoji_id, + icon_custom_emoji_id=offline_emoji_id, ) c = ForumTopic( message_thread_id=forum_group_id, @@ -347,12 +360,12 @@ def test_to_dict(self, topic_created): assert action_dict["icon_color"] == self.TEST_TOPIC_ICON_COLOR assert action_dict["is_name_implicit"] == self.is_name_implicit - def test_equality(self, emoji_id): + def test_equality(self, offline_emoji_id): a = ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR) b = ForumTopicCreated( name=TEST_TOPIC_NAME, icon_color=TEST_TOPIC_ICON_COLOR, - icon_custom_emoji_id=emoji_id, + icon_custom_emoji_id=offline_emoji_id, ) c = ForumTopicCreated(name=f"{TEST_TOPIC_NAME}!", icon_color=TEST_TOPIC_ICON_COLOR) d = ForumTopicCreated(name=TEST_TOPIC_NAME, icon_color=0xFFD67E) diff --git a/uv.lock b/uv.lock index a9fa6193c9e..65c2fd91aad 100644 --- a/uv.lock +++ b/uv.lock @@ -1485,6 +1485,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, ] +[[package]] +name = "pytest-socket" +version = "0.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/59/3c/f9b58e57830e58980dbe8867d0e348f45701d3f3ea065672d448f4366da5/pytest_socket-0.8.0.tar.gz", hash = "sha256:af9bb5f487da72be63573a6194cfac033b6c7a1c1561e150521105970f9e99f2", size = 13912, upload-time = "2026-05-21T16:50:22.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/e8/4a8568580bae3dcd678599ed8e86a82d505a44df71c1ced4246c1aa14b4b/pytest_socket-0.8.0-py3-none-any.whl", hash = "sha256:81821ba59f07d7600fe2b551d8714f40b068bd46e8b6704c48664e9d60cdacb8", size = 8414, upload-time = "2026-05-21T16:50:21.022Z" }, +] + [[package]] name = "pytest-xdist" version = "3.8.0" @@ -1568,6 +1580,7 @@ all = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-socket" }, { name = "pytest-xdist" }, { name = "pytz" }, { name = "ruff" }, @@ -1603,6 +1616,7 @@ tests = [ { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-cov" }, + { name = "pytest-socket" }, { name = "pytest-xdist" }, { name = "pytz" }, { name = "tzdata" }, @@ -1649,6 +1663,7 @@ all = [ { name = "pytest", specifier = "==9.1.1" }, { name = "pytest-asyncio", specifier = "==0.21.2" }, { name = "pytest-cov" }, + { name = "pytest-socket", specifier = "==0.8.0" }, { name = "pytest-xdist", specifier = "==3.8.0" }, { name = "pytz" }, { name = "ruff", specifier = "==0.15.22" }, @@ -1684,6 +1699,7 @@ tests = [ { name = "pytest", specifier = "==9.1.1" }, { name = "pytest-asyncio", specifier = "==0.21.2" }, { name = "pytest-cov" }, + { name = "pytest-socket", specifier = "==0.8.0" }, { name = "pytest-xdist", specifier = "==3.8.0" }, { name = "pytz" }, { name = "tzdata" },