Skip to content

Commit 6bc4443

Browse files
committed
tests
1 parent 7792ab5 commit 6bc4443

File tree

6 files changed

+1079
-0
lines changed

6 files changed

+1079
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
"""Tests for AI command functionality."""
2+
3+
from unittest.mock import patch
4+
5+
import pytest
6+
7+
from apps.slack.commands.ai import Ai
8+
9+
10+
class TestAiCommand:
11+
"""Test cases for AI command functionality."""
12+
13+
@pytest.fixture(autouse=True)
14+
def setup_method(self):
15+
"""Set up test data before each test method."""
16+
self.ai_command = Ai()
17+
18+
@patch("apps.slack.common.handlers.ai.get_blocks")
19+
def test_render_blocks_success(self, mock_get_blocks):
20+
"""Test successful rendering of AI response blocks."""
21+
command = {
22+
"text": "What is OWASP?",
23+
"user_id": "U123456",
24+
"channel_id": "C123456",
25+
}
26+
expected_blocks = [
27+
{
28+
"type": "section",
29+
"text": {
30+
"type": "mrkdwn",
31+
"text": "OWASP is a security organization...",
32+
},
33+
}
34+
]
35+
mock_get_blocks.return_value = expected_blocks
36+
37+
ai_command = Ai()
38+
result = ai_command.render_blocks(command)
39+
40+
mock_get_blocks.assert_called_once_with(query="What is OWASP?")
41+
assert result == expected_blocks
42+
43+
@patch("apps.slack.common.handlers.ai.get_blocks")
44+
def test_render_blocks_with_whitespace(self, mock_get_blocks):
45+
"""Test rendering blocks with text that has whitespace."""
46+
command = {
47+
"text": " What is OWASP security? ",
48+
"user_id": "U123456",
49+
"channel_id": "C123456",
50+
}
51+
expected_blocks = [
52+
{
53+
"type": "section",
54+
"text": {
55+
"type": "mrkdwn",
56+
"text": "OWASP is a security organization...",
57+
},
58+
}
59+
]
60+
mock_get_blocks.return_value = expected_blocks
61+
62+
ai_command = Ai()
63+
result = ai_command.render_blocks(command)
64+
65+
mock_get_blocks.assert_called_once_with(query="What is OWASP security?")
66+
assert result == expected_blocks
67+
68+
@patch("apps.slack.common.handlers.ai.get_blocks")
69+
def test_render_blocks_empty_text(self, mock_get_blocks):
70+
"""Test rendering blocks with empty text."""
71+
command = {"text": "", "user_id": "U123456", "channel_id": "C123456"}
72+
expected_blocks = [
73+
{"type": "section", "text": {"type": "mrkdwn", "text": "Error message"}}
74+
]
75+
mock_get_blocks.return_value = expected_blocks
76+
77+
ai_command = Ai()
78+
result = ai_command.render_blocks(command)
79+
80+
mock_get_blocks.assert_called_once_with(query="")
81+
assert result == expected_blocks
82+
83+
@patch("apps.slack.common.handlers.ai.get_blocks")
84+
def test_render_blocks_only_whitespace(self, mock_get_blocks):
85+
"""Test rendering blocks with only whitespace in text."""
86+
command = {"text": " ", "user_id": "U123456", "channel_id": "C123456"}
87+
expected_blocks = [
88+
{"type": "section", "text": {"type": "mrkdwn", "text": "Error message"}}
89+
]
90+
mock_get_blocks.return_value = expected_blocks
91+
92+
ai_command = Ai()
93+
result = ai_command.render_blocks(command)
94+
95+
mock_get_blocks.assert_called_once_with(query="")
96+
assert result == expected_blocks
97+
98+
@patch("apps.slack.common.handlers.ai.get_blocks")
99+
def test_render_blocks_complex_query(self, mock_get_blocks):
100+
"""Test rendering blocks with complex query."""
101+
command = {
102+
"text": "What are the OWASP Top 10 vulnerabilities and how can I prevent them?",
103+
"user_id": "U123456",
104+
"channel_id": "C123456",
105+
}
106+
expected_blocks = [
107+
{
108+
"type": "section",
109+
"text": {"type": "mrkdwn", "text": "The OWASP Top 10 is a list..."},
110+
},
111+
{"type": "divider"},
112+
{
113+
"type": "section",
114+
"text": {"type": "mrkdwn", "text": "Prevention techniques..."},
115+
},
116+
]
117+
mock_get_blocks.return_value = expected_blocks
118+
119+
ai_command = Ai()
120+
result = ai_command.render_blocks(command)
121+
122+
mock_get_blocks.assert_called_once_with(
123+
query="What are the OWASP Top 10 vulnerabilities and how can I prevent them?"
124+
)
125+
assert result == expected_blocks
126+
127+
@patch("apps.slack.common.handlers.ai.get_blocks")
128+
def test_render_blocks_handles_exception(self, mock_get_blocks):
129+
"""Test that render_blocks handles exceptions gracefully."""
130+
command = {
131+
"text": "What is OWASP?",
132+
"user_id": "U123456",
133+
"channel_id": "C123456",
134+
}
135+
mock_get_blocks.side_effect = Exception("AI service error")
136+
137+
ai_command = Ai()
138+
with pytest.raises(Exception, match="AI service error"):
139+
ai_command.render_blocks(command)
140+
141+
@patch("apps.slack.common.handlers.ai.get_blocks")
142+
def test_render_blocks_returns_none(self, mock_get_blocks):
143+
"""Test handling when get_blocks returns None."""
144+
command = {
145+
"text": "What is OWASP?",
146+
"user_id": "U123456",
147+
"channel_id": "C123456",
148+
}
149+
mock_get_blocks.return_value = None
150+
151+
ai_command = Ai()
152+
result = ai_command.render_blocks(command)
153+
154+
mock_get_blocks.assert_called_once_with(query="What is OWASP?")
155+
assert result is None
156+
157+
def test_ai_command_inheritance(self):
158+
"""Test that Ai command inherits from CommandBase."""
159+
from apps.slack.commands.command import CommandBase
160+
161+
ai_command = Ai()
162+
assert isinstance(ai_command, CommandBase)
163+
164+
@patch("apps.slack.common.handlers.ai.get_blocks")
165+
def test_render_blocks_special_characters(self, mock_get_blocks):
166+
"""Test rendering blocks with special characters in query."""
167+
command = {
168+
"text": "What is XSS & SQL injection? How to prevent <script> attacks?",
169+
"user_id": "U123456",
170+
"channel_id": "C123456",
171+
}
172+
expected_blocks = [
173+
{
174+
"type": "section",
175+
"text": {"type": "mrkdwn", "text": "XSS and SQL injection..."},
176+
}
177+
]
178+
mock_get_blocks.return_value = expected_blocks
179+
180+
ai_command = Ai()
181+
result = ai_command.render_blocks(command)
182+
183+
mock_get_blocks.assert_called_once_with(
184+
query="What is XSS & SQL injection? How to prevent <script> attacks?"
185+
)
186+
assert result == expected_blocks
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
"""Tests for AI handler functionality."""
2+
3+
from unittest.mock import Mock, patch
4+
5+
import pytest
6+
7+
from apps.slack.common.handlers.ai import get_blocks, get_error_blocks, process_ai_query
8+
9+
10+
class TestAiHandler:
11+
"""Test cases for AI handler functionality."""
12+
13+
@patch("apps.slack.common.handlers.ai.process_ai_query")
14+
@patch("apps.slack.common.handlers.ai.markdown")
15+
def test_get_blocks_with_successful_response(self, mock_markdown, mock_process_ai_query):
16+
"""Test get_blocks with successful AI response."""
17+
query = "What is OWASP?"
18+
ai_response = "OWASP is a security organization..."
19+
expected_block = {
20+
"type": "section",
21+
"text": {"type": "mrkdwn", "text": ai_response},
22+
}
23+
24+
mock_process_ai_query.return_value = ai_response
25+
mock_markdown.return_value = expected_block
26+
27+
result = get_blocks(query)
28+
29+
mock_process_ai_query.assert_called_once_with(query.strip())
30+
mock_markdown.assert_called_once_with(ai_response)
31+
assert result == [expected_block]
32+
33+
@patch("apps.slack.common.handlers.ai.process_ai_query")
34+
@patch("apps.slack.common.handlers.ai.get_error_blocks")
35+
def test_get_blocks_with_no_response(self, mock_get_error_blocks, mock_process_ai_query):
36+
"""Test get_blocks when AI returns no response."""
37+
query = "What is OWASP?"
38+
error_blocks = [{"type": "section", "text": {"type": "mrkdwn", "text": "Error message"}}]
39+
40+
mock_process_ai_query.return_value = None
41+
mock_get_error_blocks.return_value = error_blocks
42+
43+
result = get_blocks(query)
44+
45+
mock_process_ai_query.assert_called_once_with(query.strip())
46+
mock_get_error_blocks.assert_called_once()
47+
assert result == error_blocks
48+
49+
@patch("apps.slack.common.handlers.ai.process_ai_query")
50+
@patch("apps.slack.common.handlers.ai.get_error_blocks")
51+
def test_get_blocks_with_empty_response(self, mock_get_error_blocks, mock_process_ai_query):
52+
"""Test get_blocks when AI returns empty response."""
53+
query = "What is OWASP?"
54+
error_blocks = [{"type": "section", "text": {"type": "mrkdwn", "text": "Error message"}}]
55+
56+
mock_process_ai_query.return_value = ""
57+
mock_get_error_blocks.return_value = error_blocks
58+
59+
result = get_blocks(query)
60+
61+
mock_process_ai_query.assert_called_once_with(query.strip())
62+
mock_get_error_blocks.assert_called_once()
63+
assert result == error_blocks
64+
65+
@patch("apps.slack.common.handlers.ai.RagTool")
66+
def test_process_ai_query_success(self, mock_rag_tool):
67+
"""Test successful AI query processing."""
68+
query = "What is OWASP?"
69+
expected_response = "OWASP is a security organization..."
70+
71+
mock_rag_instance = Mock()
72+
mock_rag_instance.query.return_value = expected_response
73+
mock_rag_tool.return_value = mock_rag_instance
74+
75+
result = process_ai_query(query)
76+
77+
mock_rag_tool.assert_called_once_with(
78+
chat_model="gpt-4o",
79+
embedding_model="text-embedding-3-small",
80+
)
81+
mock_rag_instance.query.assert_called_once_with(question=query)
82+
assert result == expected_response
83+
84+
@patch("apps.slack.common.handlers.ai.RagTool")
85+
def test_process_ai_query_failure(self, mock_rag_tool):
86+
"""Test AI query processing failure."""
87+
query = "What is OWASP?"
88+
89+
mock_rag_instance = Mock()
90+
mock_rag_instance.query.side_effect = Exception("AI service error")
91+
mock_rag_tool.return_value = mock_rag_instance
92+
93+
with pytest.raises(Exception, match="AI service error"):
94+
process_ai_query(query)
95+
96+
@patch("apps.slack.common.handlers.ai.RagTool")
97+
def test_process_ai_query_returns_none(self, mock_rag_tool):
98+
"""Test AI query processing when RAG tool returns None."""
99+
query = "What is OWASP?"
100+
101+
mock_rag_instance = Mock()
102+
mock_rag_instance.query.return_value = None
103+
mock_rag_tool.return_value = mock_rag_instance
104+
105+
result = process_ai_query(query)
106+
107+
mock_rag_tool.assert_called_once_with(
108+
chat_model="gpt-4o",
109+
embedding_model="text-embedding-3-small",
110+
)
111+
mock_rag_instance.query.assert_called_once_with(question=query)
112+
assert result is None
113+
114+
@patch("apps.slack.common.handlers.ai.markdown")
115+
def test_get_error_blocks(self, mock_markdown):
116+
"""Test error blocks generation."""
117+
expected_error_message = (
118+
"⚠️*Sorry, I cannot answer your question.*\n"
119+
"Please try again later or contact support if the issue persists."
120+
)
121+
expected_block = {
122+
"type": "section",
123+
"text": {"type": "mrkdwn", "text": expected_error_message},
124+
}
125+
mock_markdown.return_value = expected_block
126+
127+
result = get_error_blocks()
128+
129+
mock_markdown.assert_called_once_with(expected_error_message)
130+
assert result == [expected_block]
131+
132+
def test_get_blocks_strips_whitespace(self):
133+
"""Test that get_blocks properly strips whitespace from query."""
134+
with patch("apps.slack.common.handlers.ai.process_ai_query") as mock_process_ai_query:
135+
mock_process_ai_query.return_value = None
136+
with patch("apps.slack.common.handlers.ai.get_error_blocks") as mock_get_error_blocks:
137+
mock_get_error_blocks.return_value = []
138+
139+
query_with_whitespace = " What is OWASP? "
140+
get_blocks(query_with_whitespace)
141+
142+
mock_process_ai_query.assert_called_once_with("What is OWASP?")

0 commit comments

Comments
 (0)