@@ -17,33 +17,30 @@ def mock_env():
1717 env_vars = {
1818 "SLACK_BOT_TOKEN_PARAMETER" : "/test/bot-token" ,
1919 "SLACK_SIGNING_SECRET_PARAMETER" : "/test/signing-secret" ,
20- "SLACK_SLASH_COMMAND " : "/ask-eps " ,
20+ "SLACK_DEDUPLICATION_TABLE " : "test-dedup-table " ,
2121 "KNOWLEDGEBASE_ID" : "test-kb-id" ,
2222 "RAG_MODEL_ID" : "test-model-id" ,
2323 "AWS_REGION" : "eu-west-2" ,
2424 "GUARD_RAIL_ID" : "test-guard-id" ,
2525 "GUARD_RAIL_VERSION" : "1" ,
26+ "AWS_LAMBDA_FUNCTION_NAME" : "test-function" ,
2627 }
2728 with patch .dict (os .environ , env_vars ):
2829 yield env_vars
2930
3031
3132@pytest .fixture
32- def mock_ssm_parameters ():
33- """Mock SSM parameters """
33+ def mock_dynamodb_table ():
34+ """Mock DynamoDB table """
3435 with mock_aws ():
35- client = boto3 .client ("ssm" , region_name = "eu-west-2" )
36- client .put_parameter (
37- Name = "/test/bot-token" ,
38- Value = json .dumps ({"token" : TEST_BOT_TOKEN }),
39- Type = "SecureString" ,
36+ dynamodb = boto3 .resource ("dynamodb" , region_name = "eu-west-2" )
37+ table = dynamodb .create_table (
38+ TableName = "test-dedup-table" ,
39+ KeySchema = [{"AttributeName" : "eventId" , "KeyType" : "HASH" }],
40+ AttributeDefinitions = [{"AttributeName" : "eventId" , "AttributeType" : "S" }],
41+ BillingMode = "PAY_PER_REQUEST" ,
4042 )
41- client .put_parameter (
42- Name = "/test/signing-secret" ,
43- Value = json .dumps ({"secret" : TEST_SIGNING_SECRET }),
44- Type = "SecureString" ,
45- )
46- yield
43+ yield table
4744
4845
4946@pytest .fixture
@@ -57,174 +54,210 @@ def lambda_context():
5754
5855@patch ("slack_bolt.App" )
5956@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
60- def test_log_request (mock_get_parameter , mock_app , mock_env ):
57+ @patch ("boto3.resource" )
58+ def test_log_request (mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
6159 """Test middleware function behavior"""
6260 mock_get_parameter .side_effect = [
6361 json .dumps ({"token" : "test-token" }),
6462 json .dumps ({"secret" : "test-secret" }),
6563 ]
64+ mock_boto_resource .return_value .Table .return_value = Mock ()
6665
6766 if "app" in sys .modules :
6867 del sys .modules ["app" ]
6968
70- # Create a simple test function that mimics the middleware behavior
71- def test_log_request (slack_logger , body , next ):
72- # This simulates what the actual middleware does
73- return next ()
74-
75- slack_logger = Mock ()
76- body = {"text" : "test query" }
77- next_func = Mock ()
78- next_func .return_value = "next_result"
79-
80- # Call the test function
81- result = test_log_request (slack_logger , body , next_func )
69+ # Test that the middleware function exists and can be imported
70+ from app import log_request
8271
83- # Verify next was called and result is correct
84- next_func .assert_called_once ()
85- assert result == "next_result"
72+ assert callable (log_request )
8673
8774
8875@patch ("slack_bolt.App" )
8976@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
90- def test_respond_to_slack_within_3_seconds (mock_get_parameter , mock_app , mock_env ):
91- """Test Slack acknowledgment function"""
77+ @patch ("boto3.resource" )
78+ def test_is_duplicate_event (mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
79+ """Test duplicate event detection"""
9280 mock_get_parameter .side_effect = [
9381 json .dumps ({"token" : "test-token" }),
9482 json .dumps ({"secret" : "test-secret" }),
9583 ]
84+ mock_table = Mock ()
85+ mock_boto_resource .return_value .Table .return_value = mock_table
86+ mock_table .get_item .return_value = {"Item" : {"eventId" : "test-event" }}
9687
9788 if "app" in sys .modules :
9889 del sys .modules ["app" ]
9990
100- from app import respond_to_slack_within_3_seconds
91+ from app import is_duplicate_event
92+
93+ result = is_duplicate_event ("test-event" )
94+ assert result is True
95+
96+
97+ @patch ("slack_bolt.App" )
98+ @patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
99+ @patch ("boto3.resource" )
100+ @patch ("time.time" )
101+ def test_mark_event_processed (mock_time , mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
102+ """Test marking event as processed"""
103+ mock_get_parameter .side_effect = [
104+ json .dumps ({"token" : "test-token" }),
105+ json .dumps ({"secret" : "test-secret" }),
106+ ]
107+ mock_table = Mock ()
108+ mock_boto_resource .return_value .Table .return_value = mock_table
109+ mock_time .return_value = 1000
101110
102- body = { "text" : "test query" }
103- ack = Mock ()
111+ if "app" in sys . modules :
112+ del sys . modules [ "app" ]
104113
105- respond_to_slack_within_3_seconds ( body , ack )
114+ from app import mark_event_processed
106115
107- ack .assert_called_once_with ("\n /ask-eps - Processing Request: test query" )
116+ mark_event_processed ("test-event" )
117+ mock_table .put_item .assert_called_once_with (Item = {"eventId" : "test-event" , "ttl" : 4600 , "timestamp" : 1000 })
108118
109119
110120@patch ("slack_bolt.App" )
111121@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
112- def test_respond_to_slack_within_3_seconds_error (mock_get_parameter , mock_app , mock_env ):
113- """Test Slack acknowledgment error handling"""
122+ @patch ("boto3.resource" )
123+ @patch ("boto3.client" )
124+ def test_get_bedrock_knowledgebase_response (
125+ mock_boto_client , mock_boto_resource , mock_get_parameter , mock_app , mock_env
126+ ):
127+ """Test Bedrock knowledge base integration"""
114128 mock_get_parameter .side_effect = [
115129 json .dumps ({"token" : "test-token" }),
116130 json .dumps ({"secret" : "test-secret" }),
117131 ]
132+ mock_boto_resource .return_value .Table .return_value = Mock ()
133+
134+ mock_client = Mock ()
135+ mock_boto_client .return_value = mock_client
136+ mock_client .retrieve_and_generate .return_value = {"output" : {"text" : "bedrock response" }}
118137
119138 if "app" in sys .modules :
120139 del sys .modules ["app" ]
121140
122- from app import respond_to_slack_within_3_seconds
123-
124- body = {} # Missing 'text' key
125- ack = Mock ()
141+ from app import get_bedrock_knowledgebase_response
126142
127- respond_to_slack_within_3_seconds ( body , ack )
143+ result = get_bedrock_knowledgebase_response ( "test query" )
128144
129- ack .assert_called_once_with ("/ask-eps - Sorry an error occurred. Please try again later." )
145+ mock_boto_client .assert_called_once_with (service_name = "bedrock-agent-runtime" , region_name = "eu-west-2" )
146+ mock_client .retrieve_and_generate .assert_called_once ()
147+ assert result ["output" ]["text" ] == "bedrock response"
130148
131149
132150@patch ("slack_bolt.App" )
133151@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
134- def test_process_command_request (mock_get_parameter , mock_app , mock_env ):
135- """Test command processing function"""
152+ @patch ("boto3.resource" )
153+ def test_handler_normal_event (mock_boto_resource , mock_get_parameter , mock_app , mock_env , lambda_context ):
154+ """Test Lambda handler function for normal Slack events"""
136155 mock_get_parameter .side_effect = [
137156 json .dumps ({"token" : "test-token" }),
138157 json .dumps ({"secret" : "test-secret" }),
139158 ]
159+ mock_boto_resource .return_value .Table .return_value = Mock ()
140160
141161 if "app" in sys .modules :
142162 del sys .modules ["app" ]
143163
144- with patch ("app.get_bedrock_knowledgebase_response" ) as mock_bedrock :
145- mock_bedrock .return_value = {"output" : {"text" : "test response" }}
146- from app import process_command_request
164+ with patch ("app.SlackRequestHandler" ) as mock_handler_class :
165+ mock_handler = Mock ()
166+ mock_handler_class .return_value = mock_handler
167+ mock_handler .handle .return_value = {"statusCode" : 200 }
147168
148- body = {"text" : "test query" }
149- respond = Mock ()
169+ from app import handler
150170
151- process_command_request (respond , body )
171+ event = {"body" : "test event" }
172+ result = handler (event , lambda_context )
152173
153- mock_bedrock . assert_called_once_with ("test query" )
154- respond . assert_called_once_with ( " \n /ask-eps - Response: test response \n " )
174+ mock_handler . handle . assert_called_once_with (event , lambda_context )
175+ assert result [ "statusCode" ] == 200
155176
156177
157178@patch ("slack_bolt.App" )
158179@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
159- def test_process_command_request_exception (mock_get_parameter , mock_app , mock_env ):
160- """Test process_command_request exception handling"""
180+ @patch ("boto3.resource" )
181+ def test_handler_async_processing (mock_boto_resource , mock_get_parameter , mock_app , mock_env , lambda_context ):
182+ """Test Lambda handler function for async processing"""
161183 mock_get_parameter .side_effect = [
162184 json .dumps ({"token" : "test-token" }),
163185 json .dumps ({"secret" : "test-secret" }),
164186 ]
187+ mock_boto_resource .return_value .Table .return_value = Mock ()
165188
166189 if "app" in sys .modules :
167190 del sys .modules ["app" ]
168191
169- with patch ("app.get_bedrock_knowledgebase_response" ) as mock_bedrock :
170- mock_bedrock .side_effect = Exception ("Bedrock error" )
171- from app import process_command_request
172-
173- body = {"text" : "test query" }
174- respond = Mock ()
192+ with patch ("app.process_async_slack_event" ) as mock_process :
193+ from app import handler
175194
176- process_command_request (respond , body )
195+ event = {"async_processing" : True , "slack_event" : {"test" : "data" }}
196+ result = handler (event , lambda_context )
177197
178- respond .assert_called_once_with ("/ask-eps - Sorry an error occurred. Please try again later." )
198+ mock_process .assert_called_once_with ({"test" : "data" })
199+ assert result ["statusCode" ] == 200
179200
180201
181202@patch ("slack_bolt.App" )
182203@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
204+ @patch ("boto3.resource" )
183205@patch ("boto3.client" )
184- def test_get_bedrock_knowledgebase_response (mock_boto_client , mock_get_parameter , mock_app , mock_env ):
185- """Test Bedrock knowledge base integration """
206+ def test_trigger_async_processing (mock_boto_client , mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
207+ """Test triggering async processing """
186208 mock_get_parameter .side_effect = [
187209 json .dumps ({"token" : "test-token" }),
188210 json .dumps ({"secret" : "test-secret" }),
189211 ]
190-
191- mock_client = Mock ()
192- mock_boto_client .return_value = mock_client
193- mock_client .retrieve_and_generate .return_value = {"output" : {"text" : "bedrock response" }}
212+ mock_boto_resource .return_value .Table .return_value = Mock ()
213+ mock_lambda_client = Mock ()
214+ mock_boto_client .return_value = mock_lambda_client
194215
195216 if "app" in sys .modules :
196217 del sys .modules ["app" ]
197218
198- from app import get_bedrock_knowledgebase_response
219+ from app import trigger_async_processing
199220
200- result = get_bedrock_knowledgebase_response ("test query" )
221+ event_data = {"test" : "data" }
222+ trigger_async_processing (event_data )
201223
202- mock_boto_client .assert_called_once_with (service_name = "bedrock-agent-runtime" , region_name = "eu-west-2" )
203- mock_client .retrieve_and_generate .assert_called_once ()
204- assert result ["output" ]["text" ] == "bedrock response"
224+ mock_boto_client .assert_called_once_with ("lambda" )
225+ mock_lambda_client .invoke .assert_called_once ()
205226
206227
207228@patch ("slack_bolt.App" )
208229@patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
209- def test_handler (mock_get_parameter , mock_app , mock_env , lambda_context ):
210- """Test Lambda handler function"""
230+ @patch ("boto3.resource" )
231+ def test_handle_app_mention (mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
232+ """Test app mention handler exists and is callable"""
211233 mock_get_parameter .side_effect = [
212234 json .dumps ({"token" : "test-token" }),
213235 json .dumps ({"secret" : "test-secret" }),
214236 ]
237+ mock_boto_resource .return_value .Table .return_value = Mock ()
215238
216239 if "app" in sys .modules :
217240 del sys .modules ["app" ]
218241
219- with patch ("app.SlackRequestHandler" ) as mock_handler_class :
220- mock_handler = Mock ()
221- mock_handler_class .return_value = mock_handler
222- mock_handler .handle .return_value = {"statusCode" : 200 }
242+ from app import handle_app_mention
223243
224- from app import handler
244+ assert callable ( handle_app_mention )
225245
226- event = {"body" : "test event" }
227- result = handler (event , lambda_context )
228246
229- mock_handler .handle .assert_called_once_with (event , lambda_context )
230- assert result ["statusCode" ] == 200
247+ @patch ("slack_bolt.App" )
248+ @patch ("aws_lambda_powertools.utilities.parameters.get_parameter" )
249+ @patch ("boto3.resource" )
250+ def test_handle_direct_message (mock_boto_resource , mock_get_parameter , mock_app , mock_env ):
251+ """Test direct message handler exists and is callable"""
252+ mock_get_parameter .side_effect = [
253+ json .dumps ({"token" : "test-token" }),
254+ json .dumps ({"secret" : "test-secret" }),
255+ ]
256+ mock_boto_resource .return_value .Table .return_value = Mock ()
257+
258+ if "app" in sys .modules :
259+ del sys .modules ["app" ]
260+
261+ from app import handle_direct_message
262+
263+ assert callable (handle_direct_message )
0 commit comments