Skip to content

Commit 71773a5

Browse files
committed
Add slack handlers test suite
1 parent b0efa00 commit 71773a5

File tree

1 file changed

+353
-0
lines changed

1 file changed

+353
-0
lines changed
Lines changed: 353 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
import pytest
2+
from unittest.mock import Mock, patch
3+
import os
4+
from botocore.exceptions import ClientError
5+
6+
7+
@pytest.fixture
8+
def mock_env():
9+
"""Mock environment variables"""
10+
env_vars = {
11+
"SLACK_BOT_TOKEN_PARAMETER": "/test/bot-token",
12+
"SLACK_SIGNING_SECRET_PARAMETER": "/test/signing-secret",
13+
"SLACK_BOT_STATE_TABLE": "test-bot-state-table",
14+
"KNOWLEDGEBASE_ID": "test-kb-id",
15+
"RAG_MODEL_ID": "test-model-id",
16+
"AWS_REGION": "eu-west-2",
17+
"GUARD_RAIL_ID": "test-guard-id",
18+
"GUARD_RAIL_VERSION": "1",
19+
"AWS_LAMBDA_FUNCTION_NAME": "test-function",
20+
}
21+
with patch.dict(os.environ, env_vars):
22+
yield env_vars
23+
24+
25+
def test_middleware_function(mock_env):
26+
"""Test middleware function execution"""
27+
from app.slack.slack_handlers import setup_handlers
28+
29+
# Create mock app
30+
mock_app = Mock()
31+
32+
# Mock the middleware decorator to capture the function
33+
middleware_func = None
34+
35+
def capture_middleware(func):
36+
nonlocal middleware_func
37+
middleware_func = func
38+
return func
39+
40+
mock_app.middleware = capture_middleware
41+
mock_app.event = Mock()
42+
mock_app.action = Mock()
43+
44+
with patch("app.config.config.bot_token", "test-token"):
45+
setup_handlers(mock_app)
46+
47+
# Test the middleware function
48+
if middleware_func:
49+
mock_logger = Mock()
50+
mock_body = {"test": "data"}
51+
mock_next = Mock(return_value="next_result")
52+
53+
result = middleware_func(mock_logger, mock_body, mock_next)
54+
55+
assert result == "next_result"
56+
mock_next.assert_called_once()
57+
58+
59+
def test_app_mention_handler(mock_env):
60+
"""Test app mention handler execution"""
61+
from app.slack.slack_handlers import setup_handlers
62+
63+
mock_app = Mock()
64+
65+
# Capture the app_mention handler
66+
mention_handler = None
67+
68+
def capture_event(event_type):
69+
def decorator(func):
70+
nonlocal mention_handler
71+
if event_type == "app_mention":
72+
mention_handler = func
73+
return func
74+
75+
return decorator
76+
77+
mock_app.middleware = Mock()
78+
mock_app.event = capture_event
79+
mock_app.action = Mock()
80+
81+
with patch("app.config.config.bot_token", "test-token"):
82+
setup_handlers(mock_app)
83+
84+
# Test the handler
85+
if mention_handler:
86+
mock_ack = Mock()
87+
mock_event = {"user": "U123", "text": "test"}
88+
mock_body = {"event_id": "evt123"}
89+
90+
with patch("app.slack.slack_handlers.is_duplicate_event", return_value=False):
91+
with patch("app.slack.slack_handlers.trigger_async_processing") as mock_trigger:
92+
mention_handler(mock_event, mock_ack, mock_body)
93+
94+
mock_ack.assert_called_once()
95+
mock_trigger.assert_called_once()
96+
97+
98+
def test_message_handler_feedback_path(mock_env):
99+
"""Test message handler feedback detection"""
100+
from app.slack.slack_handlers import setup_handlers
101+
102+
mock_app = Mock()
103+
104+
# Capture the message handler
105+
message_handler = None
106+
107+
def capture_event(event_type):
108+
def decorator(func):
109+
nonlocal message_handler
110+
if event_type == "message":
111+
message_handler = func
112+
return func
113+
114+
return decorator
115+
116+
mock_app.middleware = Mock()
117+
mock_app.event = capture_event
118+
mock_app.action = Mock()
119+
120+
with patch("app.config.config.bot_token", "test-token"):
121+
setup_handlers(mock_app)
122+
123+
# Test feedback message path
124+
if message_handler:
125+
mock_ack = Mock()
126+
mock_event = {"text": "feedback test message", "channel": "C123", "user": "U456"}
127+
mock_body = {"event_id": "evt123"}
128+
129+
with patch("app.slack.slack_handlers.handle_feedback_message") as mock_handle:
130+
message_handler(mock_event, mock_ack, mock_body)
131+
132+
mock_ack.assert_called_once()
133+
mock_handle.assert_called_once_with(mock_event, "test-token")
134+
135+
136+
def test_message_handler_dm_path(mock_env):
137+
"""Test message handler DM processing"""
138+
from app.slack.slack_handlers import setup_handlers
139+
140+
mock_app = Mock()
141+
142+
# Capture the message handler
143+
message_handler = None
144+
145+
def capture_event(event_type):
146+
def decorator(func):
147+
nonlocal message_handler
148+
if event_type == "message":
149+
message_handler = func
150+
return func
151+
152+
return decorator
153+
154+
mock_app.middleware = Mock()
155+
mock_app.event = capture_event
156+
mock_app.action = Mock()
157+
158+
with patch("app.config.config.bot_token", "test-token"):
159+
setup_handlers(mock_app)
160+
161+
# Test DM processing path
162+
if message_handler:
163+
mock_ack = Mock()
164+
mock_event = {"text": "regular message", "channel_type": "im", "user": "U456"}
165+
mock_body = {"event_id": "evt123"}
166+
167+
with patch("app.slack.slack_handlers.is_duplicate_event", return_value=False):
168+
with patch("app.slack.slack_handlers.trigger_async_processing") as mock_trigger:
169+
message_handler(mock_event, mock_ack, mock_body)
170+
171+
mock_ack.assert_called_once()
172+
mock_trigger.assert_called_once()
173+
174+
175+
def test_message_handler_non_dm_skip(mock_env):
176+
"""Test message handler skips non-DM messages"""
177+
from app.slack.slack_handlers import setup_handlers
178+
179+
mock_app = Mock()
180+
181+
# Capture the message handler
182+
message_handler = None
183+
184+
def capture_event(event_type):
185+
def decorator(func):
186+
nonlocal message_handler
187+
if event_type == "message":
188+
message_handler = func
189+
return func
190+
191+
return decorator
192+
193+
mock_app.middleware = Mock()
194+
mock_app.event = capture_event
195+
mock_app.action = Mock()
196+
197+
with patch("app.config.config.bot_token", "test-token"):
198+
setup_handlers(mock_app)
199+
200+
# Test non-DM skip
201+
if message_handler:
202+
mock_ack = Mock()
203+
mock_event = {"text": "regular message", "channel_type": "channel"} # Not "im"
204+
mock_body = {"event_id": "evt123"}
205+
206+
with patch("app.slack.slack_handlers.trigger_async_processing") as mock_trigger:
207+
message_handler(mock_event, mock_ack, mock_body)
208+
209+
mock_ack.assert_called_once()
210+
mock_trigger.assert_not_called() # Should not trigger for non-DM
211+
212+
213+
def test_feedback_yes_action_handler(mock_env):
214+
"""Test feedback_yes action handler"""
215+
from app.slack.slack_handlers import setup_handlers
216+
217+
mock_app = Mock()
218+
219+
# Capture the feedback_yes handler
220+
yes_handler = None
221+
222+
def capture_action(action_id):
223+
def decorator(func):
224+
nonlocal yes_handler
225+
if action_id == "feedback_yes":
226+
yes_handler = func
227+
return func
228+
229+
return decorator
230+
231+
mock_app.middleware = Mock()
232+
mock_app.event = Mock()
233+
mock_app.action = capture_action
234+
235+
with patch("app.config.config.bot_token", "test-token"):
236+
setup_handlers(mock_app)
237+
238+
# Test the handler
239+
if yes_handler:
240+
mock_ack = Mock()
241+
mock_body = {
242+
"user": {"id": "U123"},
243+
"actions": [{"value": "conv-key|test query"}],
244+
"channel": {"id": "C123"},
245+
"message": {"ts": "123"},
246+
}
247+
mock_client = Mock()
248+
249+
with patch("app.slack.slack_handlers.store_feedback") as mock_store:
250+
yes_handler(mock_ack, mock_body, mock_client)
251+
252+
mock_ack.assert_called_once()
253+
mock_store.assert_called_once_with("conv-key", "test query", "positive", "U123")
254+
mock_client.chat_postMessage.assert_called_once()
255+
256+
257+
def test_feedback_no_action_handler(mock_env):
258+
"""Test feedback_no action handler"""
259+
from app.slack.slack_handlers import setup_handlers
260+
261+
mock_app = Mock()
262+
263+
# Capture the feedback_no handler
264+
no_handler = None
265+
266+
def capture_action(action_id):
267+
def decorator(func):
268+
nonlocal no_handler
269+
if action_id == "feedback_no":
270+
no_handler = func
271+
return func
272+
273+
return decorator
274+
275+
mock_app.middleware = Mock()
276+
mock_app.event = Mock()
277+
mock_app.action = capture_action
278+
279+
with patch("app.config.config.bot_token", "test-token"):
280+
setup_handlers(mock_app)
281+
282+
# Test the handler
283+
if no_handler:
284+
mock_ack = Mock()
285+
mock_body = {
286+
"user": {"id": "U123"},
287+
"actions": [{"value": "conv-key|test query"}],
288+
"channel": {"id": "C123"},
289+
"message": {"ts": "123"},
290+
}
291+
mock_client = Mock()
292+
293+
with patch("app.slack.slack_handlers.store_feedback") as mock_store:
294+
no_handler(mock_ack, mock_body, mock_client)
295+
296+
mock_ack.assert_called_once()
297+
mock_store.assert_called_once_with("conv-key", "test query", "negative", "U123")
298+
mock_client.chat_postMessage.assert_called_once()
299+
300+
301+
def test_is_duplicate_event_error_handling(mock_env):
302+
"""Test is_duplicate_event error handling"""
303+
from app.slack.slack_handlers import is_duplicate_event
304+
305+
with patch("app.config.config.table") as mock_table:
306+
# Test non-ConditionalCheckFailedException error
307+
error = ClientError(error_response={"Error": {"Code": "ServiceUnavailable"}}, operation_name="PutItem")
308+
mock_table.put_item.side_effect = error
309+
310+
with patch("time.time", return_value=1000):
311+
result = is_duplicate_event("test-event")
312+
313+
# Should return False on error to allow processing
314+
assert result is False
315+
316+
317+
def test_duplicate_event_skip_processing(mock_env):
318+
"""Test that duplicate events skip processing"""
319+
from app.slack.slack_handlers import setup_handlers
320+
321+
mock_app = Mock()
322+
323+
# Capture the app_mention handler
324+
mention_handler = None
325+
326+
def capture_event(event_type):
327+
def decorator(func):
328+
nonlocal mention_handler
329+
if event_type == "app_mention":
330+
mention_handler = func
331+
return func
332+
333+
return decorator
334+
335+
mock_app.middleware = Mock()
336+
mock_app.event = capture_event
337+
mock_app.action = Mock()
338+
339+
with patch("app.config.config.bot_token", "test-token"):
340+
setup_handlers(mock_app)
341+
342+
# Test duplicate event handling
343+
if mention_handler:
344+
mock_ack = Mock()
345+
mock_event = {"user": "U123", "text": "test"}
346+
mock_body = {"event_id": "evt123"}
347+
348+
with patch("app.slack.slack_handlers.is_duplicate_event", return_value=True):
349+
with patch("app.slack.slack_handlers.trigger_async_processing") as mock_trigger:
350+
mention_handler(mock_event, mock_ack, mock_body)
351+
352+
mock_ack.assert_called_once()
353+
mock_trigger.assert_not_called() # Should not trigger for duplicate

0 commit comments

Comments
 (0)