# Copyright 2026 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import unittest from unittest.mock import AsyncMock from unittest.mock import MagicMock from unittest.mock import patch import pytest pytest.importorskip("slack_bolt") from google.adk.integrations.slack import SlackRunner from google.adk.runners import Runner from google.genai import types from slack_bolt.app.async_app import AsyncApp class TestSlackRunner(unittest.IsolatedAsyncioTestCase): def setUp(self): self.mock_runner = MagicMock(spec=Runner) self.mock_slack_app = MagicMock(spec=AsyncApp) self.mock_slack_app.client = MagicMock() self.mock_slack_app.client.chat_update = AsyncMock() self.mock_slack_app.client.chat_delete = AsyncMock() self.slack_runner = SlackRunner(self.mock_runner, self.mock_slack_app) @patch("google.adk.integrations.slack.slack_runner.logger") async def test_handle_message_success(self, mock_logger): # Setup mocks mock_say = AsyncMock() mock_say.return_value = {"ts": "thinking_ts"} event = { "text": "Hello bot", "user": "U12345", "channel": "C67890", "ts": "1234567890.123456", } # Mock runner.run_async to yield a response mock_event = MagicMock() mock_event.content = types.Content( role="model", parts=[types.Part(text="Hi user!")] ) async def mock_run_async(*args, **kwargs): yield mock_event self.mock_runner.run_async.side_effect = mock_run_async # Call the handler await self.slack_runner._handle_message(event, mock_say) # Verify calls self.mock_runner.run_async.assert_called_once() mock_say.assert_called_once_with( text="_Thinking..._", thread_ts="1234567890.123456" ) self.mock_slack_app.client.chat_update.assert_called_once_with( channel="C67890", ts="thinking_ts", text="Hi user!", ) @patch("google.adk.integrations.slack.slack_runner.logger") async def test_handle_message_multi_turn(self, mock_logger): # Setup mocks mock_say = AsyncMock() mock_say.return_value = {"ts": "thinking_ts"} event = { "text": "Tell me two things", "user": "U12345", "channel": "C67890", "ts": "1234567890.123456", } # Mock runner.run_async to yield two responses e1 = MagicMock() e1.content = types.Content( role="model", parts=[types.Part(text="First thing.")] ) e2 = MagicMock() e2.content = types.Content( role="model", parts=[types.Part(text="Second thing.")] ) async def mock_run_async(*args, **kwargs): yield e1 yield e2 self.mock_runner.run_async.side_effect = mock_run_async await self.slack_runner._handle_message(event, mock_say) # First message uses chat_update self.mock_slack_app.client.chat_update.assert_called_once_with( channel="C67890", ts="thinking_ts", text="First thing.", ) # Second message uses say self.assertEqual(mock_say.call_count, 2) mock_say.assert_any_call( text="_Thinking..._", thread_ts="1234567890.123456" ) mock_say.assert_any_call( text="Second thing.", thread_ts="1234567890.123456" ) @patch("google.adk.integrations.slack.slack_runner.logger") async def test_handle_message_error(self, mock_logger): mock_say = AsyncMock() mock_say.return_value = {"ts": "thinking_ts"} event = { "text": "Trigger error", "user": "U12345", "channel": "C67890", "ts": "1234567890.123456", } async def mock_run_async_error(*args, **kwargs): raise Exception("Something went wrong") yield # To make it a generator self.mock_runner.run_async.side_effect = mock_run_async_error await self.slack_runner._handle_message(event, mock_say) mock_say.assert_called_once_with( text="_Thinking..._", thread_ts="1234567890.123456" ) self.mock_slack_app.client.chat_update.assert_called_once() self.assertIn( "Sorry, I encountered an error", self.mock_slack_app.client.chat_update.call_args[1]["text"], ) if __name__ == "__main__": unittest.main()