python-telegram-bot/tests/test_updater.py

712 lines
26 KiB
Python
Raw Normal View History

#!/usr/bin/env python
#
# A library that provides a Python interface to the Telegram Bot API
2022-01-03 08:15:18 +01:00
# Copyright (C) 2015-2022
# Leandro Toledo de Souza <devs@python-telegram-bot.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser Public License for more details.
#
# You should have received a copy of the GNU Lesser Public License
# along with this program. If not, see [http://www.gnu.org/licenses/].
import asyncio
import logging
import os
2015-11-24 14:57:54 +01:00
import signal
2016-04-24 04:11:25 +02:00
import sys
import threading
from contextlib import contextmanager
from flaky import flaky
from functools import partial
from queue import Queue
2016-04-27 00:28:21 +02:00
from random import randrange
from threading import Thread, Event
from time import sleep
from urllib.request import Request, urlopen
from urllib.error import HTTPError
import pytest
from .conftest import DictBot
2021-06-06 11:48:48 +02:00
from telegram import (
TelegramError,
Message,
User,
Chat,
Update,
Bot,
InlineKeyboardMarkup,
InlineKeyboardButton,
)
from telegram.error import Unauthorized, InvalidToken, TimedOut, RetryAfter
2021-06-06 11:48:48 +02:00
from telegram.ext import (
Updater,
Dispatcher,
DictPersistence,
Defaults,
InvalidCallbackData,
ExtBot,
)
from telegram.utils.deprecate import TelegramDeprecationWarning
from telegram.ext.utils.webhookhandler import WebhookServer
signalskip = pytest.mark.skipif(
sys.platform == 'win32',
reason="Can't send signals without stopping whole process on windows",
)
ASYNCIO_LOCK = threading.Lock()
@contextmanager
def set_asyncio_event_loop(loop):
with ASYNCIO_LOCK:
try:
orig_lop = asyncio.get_event_loop()
except RuntimeError:
orig_lop = None
asyncio.set_event_loop(loop)
try:
yield
finally:
asyncio.set_event_loop(orig_lop)
class TestUpdater:
message_count = 0
received = None
attempts = 0
err_handler_called = Event()
cb_handler_called = Event()
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
offset = 0
test_flag = False
2015-11-21 15:45:45 +01:00
def test_slot_behaviour(self, updater, mro_slots):
for at in updater.__slots__:
at = f"_Updater{at}" if at.startswith('__') and not at.endswith('__') else at
assert getattr(updater, at, 'err') != 'err', f"got extra slot '{at}'"
assert len(mro_slots(updater)) == len(set(mro_slots(updater))), "duplicate slot"
@pytest.fixture(autouse=True)
2015-11-24 20:34:38 +01:00
def reset(self):
self.message_count = 0
self.received = None
self.attempts = 0
self.err_handler_called.clear()
self.cb_handler_called.clear()
self.test_flag = False
2016-04-21 13:07:44 +02:00
def error_handler(self, bot, update, error):
self.received = error.message
self.err_handler_called.set()
2015-11-21 19:35:24 +01:00
def callback(self, bot, update):
self.received = update.message.text
self.cb_handler_called.set()
2021-06-06 11:48:48 +02:00
def test_warn_arbitrary_callback_data(self, bot, recwarn):
Updater(bot=bot, arbitrary_callback_data=True)
assert len(recwarn) == 1
assert 'Passing arbitrary_callback_data to an Updater' in str(recwarn[0].message)
@pytest.mark.parametrize(
('error',),
argvalues=[(TelegramError('Test Error 2'),), (Unauthorized('Test Unauthorized'),)],
ids=('TelegramError', 'Unauthorized'),
)
def test_get_updates_normal_err(self, monkeypatch, updater, error):
def test(*args, **kwargs):
raise error
monkeypatch.setattr(updater.bot, 'get_updates', test)
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
updater.dispatcher.add_error_handler(self.error_handler)
updater.start_polling(0.01)
# Make sure that the error handler was called
self.err_handler_called.wait()
assert self.received == error.message
# Make sure that Updater polling thread keeps running
self.err_handler_called.clear()
self.err_handler_called.wait()
@pytest.mark.filterwarnings('ignore:.*:pytest.PytestUnhandledThreadExceptionWarning')
def test_get_updates_bailout_err(self, monkeypatch, updater, caplog):
error = InvalidToken()
def test(*args, **kwargs):
raise error
with caplog.at_level(logging.DEBUG):
monkeypatch.setattr(updater.bot, 'get_updates', test)
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
updater.dispatcher.add_error_handler(self.error_handler)
updater.start_polling(0.01)
assert self.err_handler_called.wait(1) is not True
sleep(1)
# NOTE: This test might hit a race condition and fail (though the 1 seconds delay above
# should work around it).
# NOTE: Checking Updater.running is problematic because it is not set to False when there's
# an unhandled exception.
# TODO: We should have a way to poll Updater status and decide if it's running or not.
import pprint
pprint.pprint([rec.getMessage() for rec in caplog.get_records('call')])
assert any(
2020-11-23 22:09:29 +01:00
f'unhandled exception in Bot:{updater.bot.id}:updater' in rec.getMessage()
for rec in caplog.get_records('call')
)
@pytest.mark.parametrize(
('error',), argvalues=[(RetryAfter(0.01),), (TimedOut(),)], ids=('RetryAfter', 'TimedOut')
)
def test_get_updates_retries(self, monkeypatch, updater, error):
event = Event()
def test(*args, **kwargs):
event.set()
raise error
2016-08-06 14:47:45 +02:00
monkeypatch.setattr(updater.bot, 'get_updates', test)
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
updater.dispatcher.add_error_handler(self.error_handler)
updater.start_polling(0.01)
# Make sure that get_updates was called, but not the error handler
event.wait()
assert self.err_handler_called.wait(0.5) is not True
assert self.received != error.message
# Make sure that Updater polling thread keeps running
event.clear()
event.wait()
assert self.err_handler_called.wait(0.5) is not True
2016-08-06 14:47:45 +02:00
2021-06-06 11:48:48 +02:00
@pytest.mark.parametrize('ext_bot', [True, False])
def test_webhook(self, monkeypatch, updater, ext_bot):
# Testing with both ExtBot and Bot to make sure any logic in WebhookHandler
# that depends on this distinction works
if ext_bot and not isinstance(updater.bot, ExtBot):
updater.bot = ExtBot(updater.bot.token)
if not ext_bot and not type(updater.bot) is Bot:
updater.bot = DictBot(updater.bot.token)
2021-06-06 11:48:48 +02:00
q = Queue()
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
updater.start_webhook(ip, port, url_path='TOKEN')
sleep(0.2)
try:
# Now, we send an update to the server via urlopen
update = Update(
1,
message=Message(
1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook'
),
)
self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN')
sleep(0.2)
assert q.get(False) == update
# Returns 404 if path is incorrect
with pytest.raises(HTTPError) as excinfo:
self._send_webhook_msg(ip, port, None, 'webookhandler.py')
assert excinfo.value.code == 404
with pytest.raises(HTTPError) as excinfo:
self._send_webhook_msg(
ip, port, None, 'webookhandler.py', get_method=lambda: 'HEAD'
)
assert excinfo.value.code == 404
# Test multiple shutdown() calls
updater.httpd.shutdown()
finally:
updater.httpd.shutdown()
sleep(0.2)
assert not updater.httpd.is_running
updater.stop()
2021-06-06 11:48:48 +02:00
@pytest.mark.parametrize('invalid_data', [True, False])
def test_webhook_arbitrary_callback_data(self, monkeypatch, updater, invalid_data):
"""Here we only test one simple setup. telegram.ext.ExtBot.insert_callback_data is tested
extensively in test_bot.py in conjunction with get_updates."""
updater.bot.arbitrary_callback_data = True
try:
q = Queue()
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
updater.start_webhook(ip, port, url_path='TOKEN')
sleep(0.2)
try:
# Now, we send an update to the server via urlopen
reply_markup = InlineKeyboardMarkup.from_button(
InlineKeyboardButton(text='text', callback_data='callback_data')
)
if not invalid_data:
reply_markup = updater.bot.callback_data_cache.process_keyboard(reply_markup)
message = Message(
1,
None,
None,
reply_markup=reply_markup,
)
update = Update(1, message=message)
self._send_webhook_msg(ip, port, update.to_json(), 'TOKEN')
sleep(0.2)
received_update = q.get(False)
assert received_update == update
button = received_update.message.reply_markup.inline_keyboard[0][0]
if invalid_data:
assert isinstance(button.callback_data, InvalidCallbackData)
else:
assert button.callback_data == 'callback_data'
# Test multiple shutdown() calls
updater.httpd.shutdown()
finally:
updater.httpd.shutdown()
sleep(0.2)
assert not updater.httpd.is_running
updater.stop()
finally:
updater.bot.arbitrary_callback_data = False
updater.bot.callback_data_cache.clear_callback_data()
updater.bot.callback_data_cache.clear_callback_queries()
def test_start_webhook_no_warning_or_error_logs(self, caplog, updater, monkeypatch):
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
# prevent api calls from @info decorator when updater.bot.id is used in thread names
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
monkeypatch.setattr(updater.bot, '_commands', [])
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
with caplog.at_level(logging.WARNING):
updater.start_webhook(ip, port)
updater.stop()
assert not caplog.records
def test_webhook_ssl(self, monkeypatch, updater):
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
tg_err = False
try:
updater._start_webhook(
ip,
port,
url_path='TOKEN',
cert='./tests/test_updater.py',
key='./tests/test_updater.py',
bootstrap_retries=0,
drop_pending_updates=False,
webhook_url=None,
allowed_updates=None,
)
except TelegramError:
tg_err = True
assert tg_err
def test_webhook_no_ssl(self, monkeypatch, updater):
q = Queue()
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
updater.start_webhook(ip, port, webhook_url=None)
sleep(0.2)
# Now, we send an update to the server via urlopen
update = Update(
1,
message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'),
)
self._send_webhook_msg(ip, port, update.to_json())
sleep(0.2)
assert q.get(False) == update
updater.stop()
def test_webhook_ssl_just_for_telegram(self, monkeypatch, updater):
q = Queue()
def set_webhook(**kwargs):
self.test_flag.append(bool(kwargs.get('certificate')))
return True
orig_wh_server_init = WebhookServer.__init__
def webhook_server_init(*args):
self.test_flag = [args[-1] is None]
orig_wh_server_init(*args)
monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
monkeypatch.setattr(
'telegram.ext.utils.webhookhandler.WebhookServer.__init__', webhook_server_init
)
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
updater.start_webhook(ip, port, webhook_url=None, cert='./tests/test_updater.py')
sleep(0.2)
# Now, we send an update to the server via urlopen
update = Update(
1,
message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'),
)
self._send_webhook_msg(ip, port, update.to_json())
sleep(0.2)
assert q.get(False) == update
updater.stop()
assert self.test_flag == [True, True]
@pytest.mark.parametrize('pass_max_connections', [True, False])
def test_webhook_max_connections(self, monkeypatch, updater, pass_max_connections):
q = Queue()
max_connections = 42
def set_webhook(**kwargs):
print(kwargs)
self.test_flag = kwargs.get('max_connections') == (
max_connections if pass_max_connections else 40
)
return True
monkeypatch.setattr(updater.bot, 'set_webhook', set_webhook)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr('telegram.ext.Dispatcher.process_update', lambda _, u: q.put(u))
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
if pass_max_connections:
updater.start_webhook(ip, port, webhook_url=None, max_connections=max_connections)
else:
updater.start_webhook(ip, port, webhook_url=None)
sleep(0.2)
# Now, we send an update to the server via urlopen
update = Update(
1,
message=Message(1, None, Chat(1, ''), from_user=User(1, '', False), text='Webhook 2'),
)
self._send_webhook_msg(ip, port, update.to_json())
sleep(0.2)
assert q.get(False) == update
updater.stop()
assert self.test_flag is True
@pytest.mark.parametrize(('error',), argvalues=[(TelegramError(''),)], ids=('TelegramError',))
def test_bootstrap_retries_success(self, monkeypatch, updater, error):
retries = 2
def attempt(*args, **kwargs):
if self.attempts < retries:
self.attempts += 1
raise error
monkeypatch.setattr(updater.bot, 'set_webhook', attempt)
updater.running = True
updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0)
assert self.attempts == retries
@pytest.mark.parametrize(
('error', 'attempts'),
argvalues=[(TelegramError(''), 2), (Unauthorized(''), 1), (InvalidToken(), 1)],
ids=('TelegramError', 'Unauthorized', 'InvalidToken'),
)
def test_bootstrap_retries_error(self, monkeypatch, updater, error, attempts):
2016-03-14 00:36:01 +01:00
retries = 1
def attempt(*args, **kwargs):
self.attempts += 1
raise error
monkeypatch.setattr(updater.bot, 'set_webhook', attempt)
2016-03-14 00:36:01 +01:00
updater.running = True
with pytest.raises(type(error)):
updater._bootstrap(retries, False, 'path', None, bootstrap_interval=0)
assert self.attempts == attempts
@pytest.mark.parametrize('drop_pending_updates', (True, False))
def test_bootstrap_clean_updates(self, monkeypatch, updater, drop_pending_updates):
# As dropping pending updates is done by passing `drop_pending_updates` to
# set_webhook, we just check that we pass the correct value
self.test_flag = False
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
def delete_webhook(**kwargs):
self.test_flag = kwargs.get('drop_pending_updates') == drop_pending_updates
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
monkeypatch.setattr(updater.bot, 'delete_webhook', delete_webhook)
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
updater.running = True
updater._bootstrap(
1,
drop_pending_updates=drop_pending_updates,
webhook_url=None,
allowed_updates=None,
bootstrap_interval=0,
)
assert self.test_flag is True
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
def test_deprecation_warnings_start_webhook(self, recwarn, updater, monkeypatch):
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
# prevent api calls from @info decorator when updater.bot.id is used in thread names
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
monkeypatch.setattr(updater.bot, '_commands', [])
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
ip = '127.0.0.1'
port = randrange(1024, 49152) # Select random port
updater.start_webhook(ip, port, clean=True, force_event_loop=False)
updater.stop()
for warning in recwarn:
print(warning)
try: # This is for flaky tests (there's an unclosed socket sometimes)
recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it
except AssertionError:
pass
assert len(recwarn) == 3
assert str(recwarn[0].message).startswith('Old Handler API')
assert str(recwarn[1].message).startswith('The argument `clean` of')
assert str(recwarn[2].message).startswith('The argument `force_event_loop` of')
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
def test_clean_deprecation_warning_polling(self, recwarn, updater, monkeypatch):
monkeypatch.setattr(updater.bot, 'set_webhook', lambda *args, **kwargs: True)
monkeypatch.setattr(updater.bot, 'delete_webhook', lambda *args, **kwargs: True)
# prevent api calls from @info decorator when updater.bot.id is used in thread names
monkeypatch.setattr(updater.bot, '_bot', User(id=123, first_name='bot', is_bot=True))
monkeypatch.setattr(updater.bot, '_commands', [])
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
updater.start_polling(clean=True)
updater.stop()
for msg in recwarn:
print(msg)
try: # This is for flaky tests (there's an unclosed socket sometimes)
recwarn.pop(ResourceWarning) # internally iterates through recwarn.list and deletes it
except AssertionError:
pass
assert len(recwarn) == 2
assert str(recwarn[0].message).startswith('Old Handler API')
assert str(recwarn[1].message).startswith('The argument `clean` of')
def test_clean_drop_pending_mutually_exclusive(self, updater):
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
updater.start_polling(clean=True, drop_pending_updates=False)
with pytest.raises(TypeError, match='`clean` and `drop_pending_updates` are mutually'):
updater.start_webhook(clean=True, drop_pending_updates=False)
Add test for clean argument of Updater.start_polling/webhook (#2002) * added test for 'clean' argument passed to 'start_polling()' * remove TODO * prettify * remove bool from func name * improve name-ing of fake update func * cleanup class and nameing * replace while for for * swap valueerror for runtimeerror * remove all other code to reduce testing * add comments * don't raise error, complete cycle and assert * remove inf loop protection * Revert "remove all other code to reduce testing" This reverts commit 4566a1debd659ce831a52085867c5a74d1575262. * remove error parametrization * remove comment * remove pass from class * rename update_id to offset as the original get_updates() takes argument offset (which is the update_id) * rename test func to match original func * fix comment * shorten for loop * mock get_updates() behavior when 'offset' is passed. Assert with get_updates() * remove other functions to reduce testing * replicate original get_updates() * move fakeupdate class and list creation outside get_updates and store in var * loop from 0 to make update_id consistant w array key, just easier to debug * update comments * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * fix typo * Revert "fix typo" This reverts commit ade9fec609d7262051b24d17fa6b5b1cf579d760. * Revert "Revert "remove other functions to reduce testing"" This reverts commit 734de1371cfddbd003ceb3c4498e837bab55bd05. * Revert "update comments" This reverts commit f3a032e75eee8d2416a236208b71c35ccc237345. * Revert "loop from 0 to make update_id consistant w array key, just easier to debug" This reverts commit 0c6881d8a1066d5762131d28fb350eaeb92b341c. * Revert "move fakeupdate class and list creation outside get_updates and store in var" This reverts commit 71de999300f052afd6b0ffe2914bfdfc201b6b11. * Revert "replicate original get_updates()" This reverts commit 5d0710ac3a8e6f6ddf628b1ad1d53bfa219decdc. * Revert "remove other functions to reduce testing" This reverts commit 1fb498a6ccb619a745b745d61b8345bf1b29b6ba. * Revert "mock get_updates() behavior when 'offset' is passed. Assert with get_updates()" This reverts commit 8c727ba1e814871a59d7ae66d3dcf411847330d2. * loop from 0 to make update_id consistant w array key, for consitency Co-authored-by: ikkemaniac <ikkemaniac@localhost>
2020-06-24 00:25:58 +02:00
@flaky(3, 1)
def test_webhook_invalid_posts(self, updater):
ip = '127.0.0.1'
port = randrange(1024, 49152) # select random port for travis
2016-08-26 11:17:05 +02:00
thr = Thread(
target=updater._start_webhook, args=(ip, port, '', None, None, 0, False, None, None)
)
thr.start()
sleep(0.2)
try:
with pytest.raises(HTTPError) as excinfo:
self._send_webhook_msg(
ip, port, '<root><bla>data</bla></root>', content_type='application/xml'
)
assert excinfo.value.code == 403
with pytest.raises(HTTPError) as excinfo:
2016-05-15 02:52:35 +02:00
self._send_webhook_msg(ip, port, 'dummy-payload', content_len=-2)
assert excinfo.value.code == 500
# TODO: prevent urllib or the underlying from adding content-length
# with pytest.raises(HTTPError) as excinfo:
# self._send_webhook_msg(ip, port, 'dummy-payload', content_len=None)
# assert excinfo.value.code == 411
with pytest.raises(HTTPError):
2016-05-15 02:52:35 +02:00
self._send_webhook_msg(ip, port, 'dummy-payload', content_len='not-a-number')
assert excinfo.value.code == 500
finally:
updater.httpd.shutdown()
thr.join()
def _send_webhook_msg(
self,
ip,
port,
payload_str,
url_path='',
content_len=-1,
content_type='application/json',
get_method=None,
):
headers = {
'content-type': content_type,
}
if not payload_str:
content_len = None
payload = None
else:
payload = bytes(payload_str, encoding='utf-8')
if content_len == -1:
content_len = len(payload)
if content_len is not None:
headers['content-length'] = str(content_len)
2020-11-23 22:09:29 +01:00
url = f'http://{ip}:{port}/{url_path}'
req = Request(url, data=payload, headers=headers)
if get_method is not None:
req.get_method = get_method
return urlopen(req)
def signal_sender(self, updater):
sleep(0.2)
while not updater.running:
sleep(0.2)
2015-11-24 14:57:54 +01:00
os.kill(os.getpid(), signal.SIGTERM)
@signalskip
def test_idle(self, updater, caplog):
updater.start_polling(0.01)
Thread(target=partial(self.signal_sender, updater=updater)).start()
with caplog.at_level(logging.INFO):
updater.idle()
# There is a chance of a conflict when getting updates since there can be many tests
# (bots) running simultaneously while testing in github actions.
records = caplog.records.copy() # To avoid iterating and removing at same time
for idx, log in enumerate(records):
print(log)
msg = log.getMessage()
if msg.startswith('Error while getting Updates: Conflict'):
caplog.records.pop(idx) # For stability
if msg.startswith('No error handlers are registered'):
caplog.records.pop(idx)
assert len(caplog.records) == 2, caplog.records
rec = caplog.records[-2]
2020-11-23 22:09:29 +01:00
assert rec.getMessage().startswith(f'Received signal {signal.SIGTERM}')
assert rec.levelname == 'INFO'
rec = caplog.records[-1]
assert rec.getMessage().startswith('Scheduler has been shut down')
assert rec.levelname == 'INFO'
2015-11-24 14:57:54 +01:00
# If we get this far, idle() ran through
sleep(0.5)
assert updater.running is False
2015-11-24 14:57:54 +01:00
@signalskip
def test_user_signal(self, updater):
temp_var = {'a': 0}
def user_signal_inc(signum, frame):
temp_var['a'] = 1
updater.user_sig_handler = user_signal_inc
updater.start_polling(0.01)
Thread(target=partial(self.signal_sender, updater=updater)).start()
updater.idle()
# If we get this far, idle() ran through
sleep(0.5)
assert updater.running is False
assert temp_var['a'] != 0
def test_create_bot(self):
updater = Updater('123:abcd')
assert updater.bot is not None
2015-11-24 14:57:54 +01:00
def test_mutual_exclude_token_bot(self):
bot = Bot('123:zyxw')
with pytest.raises(ValueError):
Updater(token='123:abcd', bot=bot)
2016-04-24 04:11:25 +02:00
def test_no_token_or_bot_or_dispatcher(self):
with pytest.raises(ValueError):
Updater()
Bot API 4.0 (#1168) Telegram Passport (#1174): - Add full support for telegram passport. - New types: PassportData, PassportFile, EncryptedPassportElement, EncryptedCredentials, PassportElementError, PassportElementErrorDataField, PassportElementErrorFrontSide, PassportElementErrorReverseSide, PassportElementErrorSelfie, PassportElementErrorFile and PassportElementErrorFiles. - New bot method: set_passport_data_errors - New filter: Filters.passport_data - Field passport_data field on Message - PassportData is automagically decrypted when you specify your private key when creating Updater or Bot. - PassportFiles is also automagically decrypted as you download/retrieve them. - See new passportbot.py example for details on how to use, or go to our telegram passport wiki page for more info - NOTE: Passport decryption requires new dependency `cryptography`. Inputfile rework (#1184): - Change how Inputfile is handled internally - This allows support for specifying the thumbnails of photos and videos using the thumb= argument in the different send_ methods. - Also allows Bot.send_media_group to actually finally send more than one media. - Add thumb to Audio, Video and Videonote - Add Bot.edit_message_media together with InputMediaAnimation, InputMediaAudio, and inputMediaDocument. Other Bot API 4.0 changes: - Add forusquare_type to Venue, InlineQueryResultVenue, InputVenueMessageContent, and Bot.send_venue. (#1170) - Add vCard support by adding vcard field to Contact, InlineQueryResultContact, InputContactMessageContent, and Bot.send_contact. (#1166) - Support new message entities: CASHTAG and PHONE_NUMBER. (#1179) - Cashtag seems to be things like $USD and $GBP, but it seems telegram doesn't currently send them to bots. - Phone number also seems to have limited support for now - Add Bot.send_animation, add width, height, and duration to Animation, and add Filters.animation. (#1172) Co-authored-by: Jasmin Bom <jsmnbom@gmail.com> Co-authored-by: code1mountain <32801117+code1mountain@users.noreply.github.com> Co-authored-by: Eldinnie <pieter.schutz+github@gmail.com> Co-authored-by: mathefreak1 <mathefreak@hi2.in>
2018-08-29 14:18:58 +02:00
def test_mutual_exclude_bot_private_key(self):
bot = Bot('123:zyxw')
with pytest.raises(ValueError):
Updater(bot=bot, private_key=b'key')
2021-06-06 11:48:48 +02:00
def test_mutual_exclude_bot_dispatcher(self, bot):
dispatcher = Dispatcher(bot, None)
bot = Bot('123:zyxw')
with pytest.raises(ValueError):
Updater(bot=bot, dispatcher=dispatcher)
2021-06-06 11:48:48 +02:00
def test_mutual_exclude_persistence_dispatcher(self, bot):
dispatcher = Dispatcher(bot, None)
persistence = DictPersistence()
with pytest.raises(ValueError):
Updater(dispatcher=dispatcher, persistence=persistence)
2021-06-06 11:48:48 +02:00
def test_mutual_exclude_workers_dispatcher(self, bot):
dispatcher = Dispatcher(bot, None)
with pytest.raises(ValueError):
Updater(dispatcher=dispatcher, workers=8)
2021-06-06 11:48:48 +02:00
def test_mutual_exclude_use_context_dispatcher(self, bot):
dispatcher = Dispatcher(bot, None)
use_context = not dispatcher.use_context
with pytest.raises(ValueError):
Updater(dispatcher=dispatcher, use_context=use_context)
def test_mutual_exclude_custom_context_dispatcher(self):
dispatcher = Dispatcher(None, None)
with pytest.raises(ValueError):
Updater(dispatcher=dispatcher, context_types=True)
def test_defaults_warning(self, bot):
with pytest.warns(TelegramDeprecationWarning, match='no effect when a Bot is passed'):
Updater(bot=bot, defaults=Defaults())