diff --git a/packages/slackBotFunction/app/core/config.py b/packages/slackBotFunction/app/core/config.py index 6e980c55b..f6819d416 100644 --- a/packages/slackBotFunction/app/core/config.py +++ b/packages/slackBotFunction/app/core/config.py @@ -119,7 +119,7 @@ class Constants: constants = Constants( - FEEDBACK_PREFIX="feedback:", + FEEDBACK_PREFIX=r"^[\W\s_]{0,10}(feedback)[\W\s_]{0,10}", CONTEXT_TYPE_DM="DM", CONTEXT_TYPE_THREAD="thread", CHANNEL_TYPE_IM="im", diff --git a/packages/slackBotFunction/app/slack/slack_events.py b/packages/slackBotFunction/app/slack/slack_events.py index 726968cc6..042839175 100644 --- a/packages/slackBotFunction/app/slack/slack_events.py +++ b/packages/slackBotFunction/app/slack/slack_events.py @@ -7,6 +7,7 @@ import time import traceback import json +import html from datetime import datetime from concurrent.futures import ThreadPoolExecutor from typing import Any, Dict @@ -413,7 +414,9 @@ def process_async_slack_event(event: Dict[str, Any], event_id: str, client: WebC logger.error(f"Can not find pull request details: {e}", extra={"error": traceback.format_exc()}) post_error_message(channel=channel_id, thread_ts=thread_ts, client=client) return - if message_text.lower().startswith(constants.FEEDBACK_PREFIX): + # Unescape HTML entities to handle cases where Slack formats the message with entities (e.g., < instead of <) + feedback_formatted_text = html.unescape(message_text) + if re.match(constants.FEEDBACK_PREFIX, feedback_formatted_text, re.IGNORECASE | re.DOTALL | re.MULTILINE): process_feedback_event( message_text=message_text, conversation_key=conversation_key, diff --git a/packages/slackBotFunction/tests/test_feedback_regex.py b/packages/slackBotFunction/tests/test_feedback_regex.py new file mode 100644 index 000000000..b65417125 --- /dev/null +++ b/packages/slackBotFunction/tests/test_feedback_regex.py @@ -0,0 +1,163 @@ +""" +Unit tests for the feedback regex pattern. +Tests validate the FEEDBACK_PREFIX regex against various input formats. +""" + +import html +import re +import pytest +from app.core.config import constants + + +class TestFeedbackRegex: + """Test suite for the FEEDBACK_PREFIX regex pattern""" + + def isMatch(self, pattern, text): + """Helper method to check if the pattern matches the text""" + formatted_text = html.unescape(text) + return re.match(pattern, formatted_text, re.IGNORECASE | re.DOTALL | re.MULTILINE) is not None + + @pytest.fixture + def feedback_pattern(self): + """Fixture providing the feedback regex pattern""" + return constants.FEEDBACK_PREFIX + + def test_simple_feedback_with_colon(self, feedback_pattern): + """Test simple 'feedback:' format with text after colon""" + text = "feedback: um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_with_hyphen(self, feedback_pattern): + """Test 'feedback -' format with text after hyphen""" + text = "feedback - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_no_space_colon(self, feedback_pattern): + """Test 'feedback:' with no space after colon""" + text = "feedback:um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_multiple_colons(self, feedback_pattern): + """Test 'feedback::' with double colon""" + text = "feedback::um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_newline_before_text(self, feedback_pattern): + """Test 'feedback' with newline before text""" + text = "feedback \n- um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_parentheses(self, feedback_pattern): + """Test '(FeedBack)' format with parentheses""" + text = "(FeedBack) um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_parentheses_encoded(self, feedback_pattern): + """Test '(FeedBack)' format with encoded parentheses""" + text = "(FeedBack) um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_brackets(self, feedback_pattern): + """Test '[feedback]:' format with brackets""" + text = "[feedback]: um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_brackets_encoded(self, feedback_pattern): + """Test '[feedback]:' format with encoded brackets""" + text = "[feedback]: um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_angle_brackets(self, feedback_pattern): + """Test ' -' format with angle brackets""" + text = " - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_angle_brackets_encoded(self, feedback_pattern): + """Test '<feedback> -' format with angle brackets""" + text = "<feedback> - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_bold(self, feedback_pattern): + """Test '**feedback** -' format with bold text""" + text = "**feedback** - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_bold_encoded(self, feedback_pattern): + """Test '**feedback** -' format with encoded bold text""" + text = "**feedback** - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_underscore(self, feedback_pattern): + """Test '_feedback_' format with underscores""" + text = "_feedback_ - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_underscore_encoded(self, feedback_pattern): + """Test '_feedback_' format with encoded underscores""" + text = "_feedback_ - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_italic(self, feedback_pattern): + """Test '*feedback*' format with italics""" + text = "*feedback* - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_italic_encoded(self, feedback_pattern): + """Test '*feedback*' format with encoded italics""" + text = "*feedback* - um dolor sit amet, consectetur adipiscing elit" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_case_insensitive_uppercase(self, feedback_pattern): + """Test case insensitivity with uppercase FEEDBACK""" + text = "FEEDBACK: test feedback message" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_case_insensitive_mixed(self, feedback_pattern): + """Test case insensitivity with mixed case FeedBack""" + text = "FeedBack: test feedback message" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_with_leading_spaces(self, feedback_pattern): + """Test feedback with leading spaces before the keyword""" + text = " feedback: test message" + assert self.isMatch(feedback_pattern, text) + + def test_not_feedback_wrong_keyword(self, feedback_pattern): + """Test that non-feedback keywords don't match""" + text = "question: what is the answer" + assert not self.isMatch(feedback_pattern, text) + + def test_not_feedback_partial_match(self, feedback_pattern): + """Test that partial feedback keyword doesn't match at start""" + text = "my feedback on this" + assert not self.isMatch(feedback_pattern, text) + + def test_feedback_empty_text_after(self, feedback_pattern): + """Test feedback with no text after the delimiter""" + text = "feedback:" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_only_whitespace_after(self, feedback_pattern): + """Test feedback with only whitespace after""" + text = "feedback: " + assert self.isMatch(feedback_pattern, text) + + def test_feedback_with_emoji(self, feedback_pattern): + """Test feedback with emoji delimiter""" + text = "feedback: 👍 test" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_tab_delimiter(self, feedback_pattern): + """Test feedback with tab as delimiter""" + text = "feedback\ttest message" + assert self.isMatch(feedback_pattern, text) + + def test_feedback_with_many_leading_spaces(self, feedback_pattern): + """Test feedback with maximum allowed leading spaces (up to 10)""" + text = " feedback: test message" # 10 spaces + assert self.isMatch(feedback_pattern, text) + + def test_feedback_with_many_trailing_spaces(self, feedback_pattern): + """Test feedback with spaces after the keyword""" + text = "feedback : test message" + assert self.isMatch(feedback_pattern, text)