Skip to content

Commit d446a82

Browse files
author
Irving Popovetsky
committed
fixed tests
1 parent 18fe03b commit d446a82

11 files changed

Lines changed: 1316 additions & 7 deletions

File tree

docs/UPGRADE_PLAN.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,28 @@ def slack_bot(bot: SirBot) -> SirBot:
11231123

11241124
> **Status**: Completed January 4, 2026
11251125
> **Result**: Updated to Python 3.12+, removed vendored deps, modernized all dependencies
1126+
> **Test Results**: 57/57 tests passing in Python 3.13! ✅
1127+
1128+
### Additional Fixes Applied
1129+
1130+
**1. Package Mode Configuration**
1131+
- Added `packages = [{include = "pybot"}]` to fix poetry installation
1132+
1133+
**2. Updated to Latest Versions**
1134+
- pytest: 7.4 → 9.0.2
1135+
- pytest-asyncio: 0.23 → 1.3.0
1136+
- black: 24.10 → 25.12.0
1137+
- ruff: 0.1 → 0.14.10
1138+
- sentry-sdk: 1.45 → 2.48.0
1139+
1140+
**3. Replaced Deprecated `asynctest` Library**
1141+
- Replaced `asynctest.CoroutineMock` with `unittest.mock.AsyncMock`
1142+
- Replaced `asynctest.asyncio` with standard `asyncio`
1143+
- Fixed test fixtures to remove deprecated `loop` parameter
1144+
1145+
**4. Fixed Test Fixtures**
1146+
- Updated `bot` fixture to manually create/cleanup aiohttp session
1147+
- Removed deprecated `loop` fixture dependency
11261148

11271149
### 6.1 New pyproject.toml
11281150

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
import copy
2+
import json
3+
import time
4+
from unittest.mock import Mock, AsyncMock
5+
6+
import pytest
7+
from pybot._vendor.slack.events import EventRouter, MessageRouter
8+
from pybot._vendor.slack.io.abc import SlackAPI
9+
from pybot._vendor.slack.actions import Router as ActionRouter
10+
from pybot._vendor.slack.commands import Router as CommandRouter
11+
12+
from . import data
13+
14+
try:
15+
from slack.io.requests import SlackAPI as SlackAPIRequest
16+
except ImportError:
17+
SlackAPIRequest = int # type: ignore
18+
19+
TOKEN = "abcdefg"
20+
21+
22+
class FakeIO(SlackAPI):
23+
async def _request(self, method, url, headers, body):
24+
pass
25+
26+
async def sleep(self, seconds):
27+
time.sleep(seconds)
28+
29+
async def _rtm(self, url):
30+
pass
31+
32+
33+
@pytest.fixture(params=(data.RTMEvents.__members__,))
34+
def rtm_iterator(request):
35+
async def events(url):
36+
for key in request.param:
37+
yield data.RTMEvents[key].value
38+
39+
return events
40+
41+
42+
@pytest.fixture(params=(data.RTMEvents.__members__,))
43+
def rtm_iterator_non_async(request):
44+
def events(url):
45+
for key in request.param:
46+
yield data.RTMEvents[key].value
47+
48+
return events
49+
50+
51+
def _slack_client_default_parameters(parameters):
52+
if "status" not in parameters:
53+
parameters["status"] = 200
54+
55+
if "body" not in parameters:
56+
parameters["body"] = {"ok": True}
57+
58+
if "headers" not in parameters:
59+
parameters["headers"] = {"content-type": "application/json; charset=utf-8"}
60+
61+
client_param = parameters.get("client_parameters", {})
62+
if "token" not in client_param:
63+
client_param["token"] = TOKEN
64+
65+
parameters["client_parameters"] = client_param
66+
return parameters
67+
68+
69+
def _slack_client(parameters):
70+
if "client" in parameters:
71+
client = parameters["client"]
72+
else:
73+
client = FakeIO
74+
75+
slackclient = client(**parameters["client_parameters"])
76+
77+
return slackclient
78+
79+
80+
def _slack_client_single_mock_request(slackclient, parameters):
81+
return_value = (
82+
parameters["status"],
83+
json.dumps(_fill_body(parameters["body"])).encode(),
84+
parameters["headers"],
85+
)
86+
87+
if isinstance(slackclient, SlackAPIRequest):
88+
slackclient._request = Mock(return_value=return_value)
89+
else:
90+
slackclient._request = AsyncMock(return_value=return_value)
91+
92+
93+
def _slack_client_multiple_mock_request(slackclient, parameters):
94+
responses = []
95+
for status, body, headers in zip(
96+
parameters["status"], parameters["body"], parameters["headers"]
97+
):
98+
responses.append((status, json.dumps(_fill_body(body)).encode(), headers))
99+
100+
if isinstance(slackclient, SlackAPIRequest):
101+
slackclient._request = Mock(side_effect=responses)
102+
else:
103+
slackclient._request = AsyncMock(side_effect=responses)
104+
105+
106+
def _expand_request_default(parameters):
107+
if not isinstance(parameters["status"], list):
108+
parameters["status"] = [parameters["status"]]
109+
if not isinstance(parameters["body"], list):
110+
parameters["body"] = [parameters["body"]]
111+
if not isinstance(parameters["headers"], list):
112+
parameters["headers"] = [parameters["headers"]]
113+
114+
return parameters
115+
116+
117+
def _slack_client_multiple_mock_request_with_default(slackclient, parameters):
118+
parameters = _expand_request_default(parameters)
119+
120+
responses = list()
121+
for index, _ in enumerate(
122+
max(
123+
parameters["status"],
124+
parameters["body"],
125+
parameters["headers"],
126+
key=lambda x: len(x),
127+
)
128+
):
129+
try:
130+
status = parameters["status"][index]
131+
except IndexError:
132+
status = parameters["status"][0]
133+
134+
try:
135+
body = parameters["body"][index]
136+
except IndexError:
137+
body = parameters["body"][0]
138+
139+
try:
140+
headers = parameters["headers"][index]
141+
except IndexError:
142+
headers = parameters["headers"][0]
143+
144+
responses.append((status, json.dumps(_fill_body(body)).encode(), headers))
145+
146+
if isinstance(slackclient, SlackAPIRequest):
147+
slackclient._request = Mock(side_effect=responses)
148+
else:
149+
slackclient._request = AsyncMock(side_effect=responses)
150+
151+
152+
@pytest.fixture(params=({},))
153+
def slack_client(request):
154+
155+
parameters = _slack_client_default_parameters(request.param)
156+
slackclient = _slack_client(parameters)
157+
158+
if all(
159+
isinstance(value, list)
160+
for value in (parameters["status"], parameters["body"], parameters["headers"],)
161+
):
162+
_slack_client_multiple_mock_request(slackclient, parameters)
163+
elif any(
164+
isinstance(value, list)
165+
for value in (parameters["status"], parameters["body"], parameters["headers"],)
166+
):
167+
_slack_client_multiple_mock_request_with_default(slackclient, parameters)
168+
else:
169+
_slack_client_single_mock_request(slackclient, parameters)
170+
171+
return slackclient
172+
173+
174+
def _fill_body(body):
175+
if isinstance(body, str):
176+
body = copy.deepcopy(data.Methods[body].value)
177+
return body
178+
179+
180+
@pytest.fixture(
181+
params={**data.Events.__members__, **data.Messages.__members__} # type: ignore
182+
)
183+
def slack_event(request):
184+
if isinstance(request.param, str):
185+
try:
186+
payload = copy.deepcopy(data.Events[request.param].value)
187+
except KeyError:
188+
payload = copy.deepcopy(data.Messages[request.param].value)
189+
else:
190+
payload = copy.deepcopy(request.param)
191+
192+
return payload
193+
194+
195+
@pytest.fixture(params={**data.Messages.__members__}) # type: ignore
196+
def slack_message(request):
197+
if isinstance(request.param, str):
198+
payload = copy.deepcopy(data.Messages[request.param].value)
199+
else:
200+
payload = copy.deepcopy(request.param)
201+
202+
return payload
203+
204+
205+
@pytest.fixture()
206+
def token():
207+
return copy.copy(TOKEN)
208+
209+
210+
@pytest.fixture()
211+
def itercursor():
212+
return "wxyz"
213+
214+
215+
@pytest.fixture()
216+
def event_router():
217+
return EventRouter()
218+
219+
220+
@pytest.fixture()
221+
def message_router():
222+
return MessageRouter()
223+
224+
225+
@pytest.fixture(
226+
params={
227+
**data.InteractiveMessage.__members__, # type: ignore
228+
**data.DialogSubmission.__members__, # type: ignore
229+
**data.MessageAction.__members__, # type: ignore
230+
}
231+
)
232+
def slack_action(request):
233+
if isinstance(request.param, str):
234+
try:
235+
payload = copy.deepcopy(data.InteractiveMessage[request.param].value)
236+
except KeyError:
237+
try:
238+
payload = copy.deepcopy(data.DialogSubmission[request.param].value)
239+
except KeyError:
240+
payload = copy.deepcopy(data.MessageAction[request.param].value)
241+
else:
242+
payload = copy.deepcopy(request.param)
243+
244+
return payload
245+
246+
247+
@pytest.fixture(params={**data.BlockAction.__members__}) # type: ignore
248+
def block_action(request):
249+
return copy.deepcopy(data.BlockAction[request.param].value)
250+
251+
252+
# @pytest.fixture(params={**data.InteractiveMessage.__members__})
253+
# def interactive_message(request):
254+
# return Action.from_http(raw_action(request))
255+
256+
257+
# @pytest.fixture(params={**data.DialogSubmission.__members__})
258+
# def dialog_submission(request):
259+
# return Action.from_http(raw_action(request))
260+
261+
262+
# @pytest.fixture(params={**data.MessageAction.__members__})
263+
# def message_action(request):
264+
# return Action.from_http(raw_action(request))
265+
266+
267+
@pytest.fixture()
268+
def action_router():
269+
return ActionRouter()
270+
271+
272+
@pytest.fixture(params={**data.Commands.__members__})
273+
def slack_command(request):
274+
if isinstance(request.param, str):
275+
payload = copy.deepcopy(data.Commands[request.param].value)
276+
else:
277+
payload = copy.deepcopy(request.param)
278+
279+
return payload
280+
281+
282+
@pytest.fixture()
283+
def command_router():
284+
return CommandRouter()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from .events import Events, Messages, RTMEvents # noQa F401
2+
from .actions import BlockAction # noQa F401
3+
from .actions import MessageAction # noQa F401
4+
from .actions import DialogSubmission # noQa F401
5+
from .actions import InteractiveMessage # noQa F401
6+
from .methods import Methods # noQa F401
7+
from .commands import Commands # noQa F401

0 commit comments

Comments
 (0)