-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathconftest.py
More file actions
275 lines (229 loc) · 11 KB
/
conftest.py
File metadata and controls
275 lines (229 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#!/usr/bin/env python
#
# A library containing community-based extension for the python-telegram-bot library
# Copyright (C) 2020-2026
# The ptbcontrib developers
#
# 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 os
import sys
from collections import defaultdict
from queue import Queue
from threading import Event, Thread
from time import sleep
import pytest
from telegram import Bot, User, __version__
from telegram.ext import Defaults, JobQueue, Updater
v13 = __version__.startswith("1")
if v13:
from telegram.ext import Dispatcher
else:
from telegram.ext import Application, ApplicationBuilder, ExtBot
GITHUB_ACTION = os.getenv("GITHUB_ACTION", False)
# THIS IS OBVIOUSLY COMPROMISED
# DO NOT USE IN PRODUCTION!
PRIVATE_KEY = b"-----BEGIN RSA PRIVATE KEY-----\r\nMIIEowIBAAKCAQEA0AvEbNaOnfIL3GjB8VI4M5IaWe+GcK8eSPHkLkXREIsaddum\r\nwPBm/+w8lFYdnY+O06OEJrsaDtwGdU//8cbGJ/H/9cJH3dh0tNbfszP7nTrQD+88\r\nydlcYHzClaG8G+oTe9uEZSVdDXj5IUqR0y6rDXXb9tC9l+oSz+ShYg6+C4grAb3E\r\nSTv5khZ9Zsi/JEPWStqNdpoNuRh7qEYc3t4B/a5BH7bsQENyJSc8AWrfv+drPAEe\r\njQ8xm1ygzWvJp8yZPwOIYuL+obtANcoVT2G2150Wy6qLC0bD88Bm40GqLbSazueC\r\nRHZRug0B9rMUKvKc4FhG4AlNzBCaKgIcCWEqKwIDAQABAoIBACcIjin9d3Sa3S7V\r\nWM32JyVF3DvTfN3XfU8iUzV7U+ZOswA53eeFM04A/Ly4C4ZsUNfUbg72O8Vd8rg/\r\n8j1ilfsYpHVvphwxaHQlfIMa1bKCPlc/A6C7b2GLBtccKTbzjARJA2YWxIaqk9Nz\r\nMjj1IJK98i80qt29xRnMQ5sqOO3gn2SxTErvNchtBiwOH8NirqERXig8VCY6fr3n\r\nz7ZImPU3G/4qpD0+9ULrt9x/VkjqVvNdK1l7CyAuve3D7ha3jPMfVHFtVH5gqbyp\r\nKotyIHAyD+Ex3FQ1JV+H7DkP0cPctQiss7OiO9Zd9C1G2OrfQz9el7ewAPqOmZtC\r\nKjB3hUECgYEA/4MfKa1cvaCqzd3yUprp1JhvssVkhM1HyucIxB5xmBcVLX2/Kdhn\r\nhiDApZXARK0O9IRpFF6QVeMEX7TzFwB6dfkyIePsGxputA5SPbtBlHOvjZa8omMl\r\nEYfNa8x/mJkvSEpzvkWPascuHJWv1cEypqphu/70DxubWB5UKo/8o6cCgYEA0HFy\r\ncgwPMB//nltHGrmaQZPFT7/Qgl9ErZT3G9S8teWY4o4CXnkdU75tBoKAaJnpSfX3\r\nq8VuRerF45AFhqCKhlG4l51oW7TUH50qE3GM+4ivaH5YZB3biwQ9Wqw+QyNLAh/Q\r\nnS4/Wwb8qC9QuyEgcCju5lsCaPEXZiZqtPVxZd0CgYEAshBG31yZjO0zG1TZUwfy\r\nfN3euc8mRgZpSdXIHiS5NSyg7Zr8ZcUSID8jAkJiQ3n3OiAsuq1MGQ6kNa582kLT\r\nFPQdI9Ea8ahyDbkNR0gAY9xbM2kg/Gnro1PorH9PTKE0ekSodKk1UUyNrg4DBAwn\r\nqE6E3ebHXt/2WmqIbUD653ECgYBQCC8EAQNX3AFegPd1GGxU33Lz4tchJ4kMCNU0\r\nN2NZh9VCr3nTYjdTbxsXU8YP44CCKFG2/zAO4kymyiaFAWEOn5P7irGF/JExrjt4\r\nibGy5lFLEq/HiPtBjhgsl1O0nXlwUFzd7OLghXc+8CPUJaz5w42unqT3PBJa40c3\r\nQcIPdQKBgBnSb7BcDAAQ/Qx9juo/RKpvhyeqlnp0GzPSQjvtWi9dQRIu9Pe7luHc\r\nm1Img1EO1OyE3dis/rLaDsAa2AKu1Yx6h85EmNjavBqP9wqmFa0NIQQH8fvzKY3/\r\nP8IHY6009aoamLqYaexvrkHVq7fFKiI6k8myMJ6qblVNFv14+KXU\r\n-----END RSA PRIVATE KEY-----" # noqa: E501
TOKEN = "1281106207:AAHXR4nqP-ZYsPLnrHooton3zUGGnsoNjZ8"
def pytest_configure(config):
config.addinivalue_line("filterwarnings", "ignore::ResourceWarning")
# TODO: Write so good code that we don't need to ignore ResourceWarnings anymore
def env_var_2_bool(env_var: object) -> bool:
if isinstance(env_var, bool):
return env_var
if not isinstance(env_var, str):
return False
return env_var.lower().strip() == "true"
# This is here instead of in setup.cfg due to https://github.com/pytest-dev/pytest/issues/8343
def pytest_runtestloop(session):
# v13.x
try:
from telegram.utils.deprecate import TelegramDeprecationWarning # noqa: F401
session.add_marker(
pytest.mark.filterwarnings(
"ignore::telegram.utils.deprecate.TelegramDeprecationWarning"
)
)
except ImportError:
pass
# v20.x
try:
from telegram.warnings import PTBDeprecationWarning # noqa: F401
session.add_marker(
pytest.mark.filterwarnings("ignore::telegram.warnings.PTBDeprecationWarning")
)
except ImportError:
pass
if v13:
def make_bot(**kwargs):
return Bot(TOKEN, private_key=PRIVATE_KEY, **kwargs)
@pytest.fixture(scope="session")
def bot():
return make_bot()
DEFAULT_BOTS = {}
@pytest.fixture(scope="function")
def default_bot(request):
param = request.param if hasattr(request, "param") else {}
defaults = Defaults(**param)
default_bot = DEFAULT_BOTS.get(defaults)
if default_bot:
return default_bot
else:
default_bot = make_bot(**{"defaults": defaults})
DEFAULT_BOTS[defaults] = default_bot
return default_bot
def create_dp(bot):
# Dispatcher is heavy to init (due to many threads and such) so we have a single session
# scoped one here, but before each test, reset it (dp fixture below)
dispatcher = Dispatcher(bot, Queue(), job_queue=JobQueue(), workers=2, use_context=True)
dispatcher.job_queue.set_dispatcher(dispatcher)
# we mock bot.{get_me, get_my_commands} b/c those are used in the @info decorator
def get_me(*args, **kwargs):
user = User(1, "TestBot", True)
dispatcher.bot._bot = user
return user
def get_my_commands(*args, **kwargs):
return []
orig_get_me = dispatcher.bot.get_me
orig_get_my_commands = dispatcher.bot.get_my_commands
dispatcher.bot.get_me = get_me
dispatcher.bot.get_my_commands = get_my_commands
thr = Thread(target=dispatcher.start)
thr.start()
sleep(2)
yield dispatcher
dispatcher.bot.get_me = orig_get_me
dispatcher.bot.get_my_commands = orig_get_my_commands
sleep(1)
if dispatcher.running:
dispatcher.stop()
thr.join()
@pytest.fixture(scope="session")
def _dp(bot):
yield from create_dp(bot)
@pytest.fixture(scope="function")
def dp(_dp):
# Reset the dispatcher first
while not _dp.update_queue.empty():
_dp.update_queue.get(False)
_dp.chat_data = defaultdict(dict)
_dp.user_data = defaultdict(dict)
_dp.bot_data = {}
_dp.persistence = None
_dp.handlers = {}
_dp.groups = []
_dp.error_handlers = {}
# For some reason if we setattr with the name mangled, then some tests(like async)
# run forever,
# due to threads not acquiring, (blocking). This adds these attributes to the __dict__.
object.__setattr__(_dp, "__stop_event", Event())
object.__setattr__(_dp, "__exception_event", Event())
object.__setattr__(_dp, "__async_queue", Queue())
object.__setattr__(_dp, "__async_threads", set())
_dp.persistence = None
_dp.use_context = False
if _dp._Dispatcher__singleton_semaphore.acquire(blocking=0):
Dispatcher._set_singleton(_dp)
yield _dp
Dispatcher._Dispatcher__singleton_semaphore.release()
@pytest.fixture(scope="function")
def cdp(dp):
dp.use_context = True
yield dp
dp.use_context = False
@pytest.fixture(scope="function")
def updater(bot):
up = Updater(bot=bot, workers=2, use_context=False)
yield up
if up.running:
up.stop()
else:
# Redefine the event_loop fixture to have a session scope. Otherwise `bot` fixture can't be
# session. See https://github.com/pytest-dev/pytest-asyncio/issues/68 for more details.
@pytest.fixture(scope="session")
def event_loop(request):
# ever since ProactorEventLoop became the default in Win 3.8+, the app crashes after the
# loop is closed. Hence, we use SelectorEventLoop on Windows to avoid this. See
# https://github.com/python/cpython/issues/83413,
# https://github.com/encode/httpx/issues/914
if (
sys.version_info[0] == 3
and sys.version_info[1] >= 8
and sys.platform.startswith("win")
):
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
# loop.close() # instead of closing here, do that at the every end of the test session
# Related to the above, see https://stackoverflow.com/a/67307042/10606962
def pytest_sessionfinish(session, exitstatus):
asyncio.get_event_loop().close()
class DictBot(Bot):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Makes it easier to work with the bot in tests
self._unfreeze()
async def get_me(self, *args, **kwargs):
"""Will be called by Bot.initialize and we only have a face token ..."""
return User(id=1, first_name="Botty", last_name="McBotface", is_bot=True)
class DictExtBot(ExtBot):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Makes it easier to work with the bot in tests
self._unfreeze()
async def get_me(self, *args, **kwargs):
"""Will be called by Bot.initialize and we only have a face token ..."""
return User(id=1, first_name="Botty", last_name="McBotface", is_bot=True)
class DictApplication(Application):
pass
def make_bot(**kwargs):
return DictExtBot(TOKEN, private_key=PRIVATE_KEY, **kwargs)
@pytest.fixture(scope="session")
async def bot():
"""Makes an ExtBot instance with the given bot_info"""
async with make_bot() as _bot:
yield _bot
@pytest.fixture(scope="session")
async def raw_bot():
"""Makes an regular Bot instance with the given bot_info"""
async with DictBot(
token=TOKEN,
private_key=PRIVATE_KEY,
) as _bot:
yield _bot
@pytest.fixture(scope="function")
async def default_bot(request):
param = request.param if hasattr(request, "param") else {}
default_bot = make_bot(defaults=Defaults(**param))
async with default_bot:
yield default_bot
@pytest.fixture(scope="function")
async def app():
# We build a new bot each time so that we use `app` in a context manager without problems
application = (
ApplicationBuilder().bot(make_bot()).application_class(DictApplication).build()
)
yield application
if application.running:
await application.stop()
await application.shutdown()
@pytest.fixture(scope="function")
async def updater():
# We build a new bot each time so that we use `updater` in a context manager without
# problems
up = Updater(bot=make_bot(), update_queue=asyncio.Queue())
yield up
if up.running:
await up.stop()
await up.shutdown()