From 6bdb5eccc62d2bca12c5ee18a8182554fb3569ba Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 21:06:03 -0700 Subject: [PATCH 1/4] fix: update say_stream test assertions for slack_sdk v3.42.0 The SDK now includes icon_emoji, icon_url, and username in ChatStream._stream_args. Update assertions to expect these keys. Co-Authored-By: Claude --- tests/slack_bolt/context/test_say_stream.py | 9 +++++++++ tests/slack_bolt_async/context/test_async_say_stream.py | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py index 29d244a65..a490e576e 100644 --- a/tests/slack_bolt/context/test_say_stream.py +++ b/tests/slack_bolt/context/test_say_stream.py @@ -44,6 +44,9 @@ def test_default_params(self): "recipient_team_id": "T111", "recipient_user_id": "U111", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } def test_parameter_overrides(self): @@ -63,6 +66,9 @@ def test_parameter_overrides(self): "recipient_team_id": "T222", "recipient_user_id": "U222", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } def test_buffer_size_overrides(self): @@ -88,4 +94,7 @@ def test_buffer_size_overrides(self): "recipient_team_id": "T222", "recipient_user_id": "U222", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py index 016549bd6..d7c8c264f 100644 --- a/tests/slack_bolt_async/context/test_async_say_stream.py +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -55,6 +55,9 @@ async def test_default_params(self): "recipient_team_id": "T111", "recipient_user_id": "U111", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } @pytest.mark.asyncio @@ -75,6 +78,9 @@ async def test_parameter_overrides(self): "recipient_team_id": "T222", "recipient_user_id": "U222", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } @pytest.mark.asyncio @@ -101,4 +107,7 @@ async def test_buffer_size_overrides(self): "recipient_team_id": "T222", "recipient_user_id": "U222", "task_display_mode": None, + "icon_emoji": None, + "icon_url": None, + "username": None, } From 9734e6f9ce34e3c4d96cf3ad9673ca421cb72431 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 21:06:58 -0700 Subject: [PATCH 2/4] feat: add authorship arguments to SayStream and AsyncSayStream Add icon_emoji, icon_url, and username parameters to say_stream() call, matching the new authorship support in slack_sdk v3.42.0. Co-Authored-By: Claude --- .../context/say_stream/async_say_stream.py | 9 ++++++++ slack_bolt/context/say_stream/say_stream.py | 9 ++++++++ tests/slack_bolt/context/test_say_stream.py | 21 ++++++++++++++++++ .../context/test_async_say_stream.py | 22 +++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/slack_bolt/context/say_stream/async_say_stream.py b/slack_bolt/context/say_stream/async_say_stream.py index af776891b..df9b362e2 100644 --- a/slack_bolt/context/say_stream/async_say_stream.py +++ b/slack_bolt/context/say_stream/async_say_stream.py @@ -34,6 +34,9 @@ async def __call__( recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncChatStream: """Starts a new chat stream with context.""" @@ -51,6 +54,9 @@ async def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return await self.client.chat_stream( @@ -58,5 +64,8 @@ async def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/slack_bolt/context/say_stream/say_stream.py b/slack_bolt/context/say_stream/say_stream.py index b6a5ca797..15bdcc110 100644 --- a/slack_bolt/context/say_stream/say_stream.py +++ b/slack_bolt/context/say_stream/say_stream.py @@ -34,6 +34,9 @@ def __call__( recipient_team_id: Optional[str] = None, recipient_user_id: Optional[str] = None, thread_ts: Optional[str] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> ChatStream: """Starts a new chat stream with context.""" @@ -51,6 +54,9 @@ def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) return self.client.chat_stream( @@ -58,5 +64,8 @@ def __call__( recipient_team_id=recipient_team_id or self.recipient_team_id, recipient_user_id=recipient_user_id or self.recipient_user_id, thread_ts=thread_ts, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py index a490e576e..b1f0060a2 100644 --- a/tests/slack_bolt/context/test_say_stream.py +++ b/tests/slack_bolt/context/test_say_stream.py @@ -98,3 +98,24 @@ def test_buffer_size_overrides(self): "icon_url": None, "username": None, } + + def test_authorship_overrides(self): + say_stream = SayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + + assert stream._stream_args == { + "channel": "C111", + "thread_ts": "111.222", + "recipient_team_id": "T111", + "recipient_user_id": "U111", + "task_display_mode": None, + "icon_emoji": ":maple_leaf:", + "icon_url": None, + "username": "Charlie Brown", + } diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py index d7c8c264f..8104d3964 100644 --- a/tests/slack_bolt_async/context/test_async_say_stream.py +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -111,3 +111,25 @@ async def test_buffer_size_overrides(self): "icon_url": None, "username": None, } + + @pytest.mark.asyncio + async def test_authorship_overrides(self): + say_stream = AsyncSayStream( + client=self.web_client, + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + ) + stream = await say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + + assert stream._stream_args == { + "channel": "C111", + "thread_ts": "111.222", + "recipient_team_id": "T111", + "recipient_user_id": "U111", + "task_display_mode": None, + "icon_emoji": ":maple_leaf:", + "icon_url": None, + "username": "Charlie Brown", + } From 2e6e1f7dce394fdf3fb53ac3c714241197249e14 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Mon, 18 May 2026 21:22:14 -0700 Subject: [PATCH 3/4] feat: add authorship arguments to SetStatus and AsyncSetStatus Add icon_emoji, icon_url, and username parameters to set_status(), matching the new authorship support in slack_sdk v3.42.0. Co-Authored-By: Claude --- slack_bolt/context/set_status/async_set_status.py | 6 ++++++ slack_bolt/context/set_status/set_status.py | 6 ++++++ tests/slack_bolt/context/test_set_status.py | 9 +++++++++ .../slack_bolt_async/context/test_async_set_status.py | 10 ++++++++++ 4 files changed, 31 insertions(+) diff --git a/slack_bolt/context/set_status/async_set_status.py b/slack_bolt/context/set_status/async_set_status.py index e2c451f46..f10cc195c 100644 --- a/slack_bolt/context/set_status/async_set_status.py +++ b/slack_bolt/context/set_status/async_set_status.py @@ -23,6 +23,9 @@ async def __call__( self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> AsyncSlackResponse: return await self.client.assistant_threads_setStatus( @@ -30,5 +33,8 @@ async def __call__( thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/slack_bolt/context/set_status/set_status.py b/slack_bolt/context/set_status/set_status.py index 0ed612e16..055a5cab7 100644 --- a/slack_bolt/context/set_status/set_status.py +++ b/slack_bolt/context/set_status/set_status.py @@ -23,6 +23,9 @@ def __call__( self, status: str, loading_messages: Optional[List[str]] = None, + icon_emoji: Optional[str] = None, + icon_url: Optional[str] = None, + username: Optional[str] = None, **kwargs, ) -> SlackResponse: return self.client.assistant_threads_setStatus( @@ -30,5 +33,8 @@ def __call__( thread_ts=self.thread_ts, status=status, loading_messages=loading_messages, + icon_emoji=icon_emoji, + icon_url=icon_url, + username=username, **kwargs, ) diff --git a/tests/slack_bolt/context/test_set_status.py b/tests/slack_bolt/context/test_set_status.py index fe998df5e..bb5807e96 100644 --- a/tests/slack_bolt/context/test_set_status.py +++ b/tests/slack_bolt/context/test_set_status.py @@ -32,6 +32,15 @@ def test_set_status_loading_messages(self): ) assert response.status_code == 200 + def test_set_status_authorship(self): + set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: SlackResponse = set_status( + status="Thinking...", + icon_emoji=":maple_leaf:", + username="Charlie Brown", + ) + assert response.status_code == 200 + def test_set_status_invalid(self): set_status = SetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") with pytest.raises(TypeError): diff --git a/tests/slack_bolt_async/context/test_async_set_status.py b/tests/slack_bolt_async/context/test_async_set_status.py index e785ff89e..bcf1fcf19 100644 --- a/tests/slack_bolt_async/context/test_async_set_status.py +++ b/tests/slack_bolt_async/context/test_async_set_status.py @@ -40,6 +40,16 @@ async def test_set_status_loading_messages(self): ) assert response.status_code == 200 + @pytest.mark.asyncio + async def test_set_status_authorship(self): + set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") + response: AsyncSlackResponse = await set_status( + status="Thinking...", + icon_emoji=":maple_leaf:", + username="Charlie Brown", + ) + assert response.status_code == 200 + @pytest.mark.asyncio async def test_set_status_invalid(self): set_status = AsyncSetStatus(client=self.web_client, channel_id="C111", thread_ts="123.123") From 0f85e1315e6de3747909e495d027448e0073d937 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 20 May 2026 11:09:57 -0400 Subject: [PATCH 4/4] test: mock chat_stream in say_stream tests to avoid asserting against SDK internals --- tests/slack_bolt/context/test_say_stream.py | 121 +++++++-------- .../context/test_async_say_stream.py | 142 +++++++++--------- 2 files changed, 128 insertions(+), 135 deletions(-) diff --git a/tests/slack_bolt/context/test_say_stream.py b/tests/slack_bolt/context/test_say_stream.py index b1f0060a2..04a52e419 100644 --- a/tests/slack_bolt/context/test_say_stream.py +++ b/tests/slack_bolt/context/test_say_stream.py @@ -1,21 +1,14 @@ import pytest +from unittest.mock import patch, MagicMock + from slack_sdk import WebClient from slack_bolt.context.say_stream.say_stream import SayStream -from tests.mock_web_api_server import cleanup_mock_web_api_server, setup_mock_web_api_server class TestSayStream: - default_chat_stream_buffer_size = WebClient.chat_stream.__kwdefaults__["buffer_size"] - def setup_method(self): - setup_mock_web_api_server(self) - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" - self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url) - - def teardown_method(self): - cleanup_mock_web_api_server(self) + self.web_client = WebClient(token="xoxb-valid") def test_missing_channel_raises(self): say_stream = SayStream(client=self.web_client, channel=None, thread_ts="111.222") @@ -35,19 +28,17 @@ def test_default_params(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream() - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream() + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=None, + icon_url=None, + username=None, + ) def test_parameter_overrides(self): say_stream = SayStream( @@ -57,19 +48,17 @@ def test_parameter_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + mock_chat_stream.assert_called_once_with( + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) def test_buffer_size_overrides(self): say_stream = SayStream( @@ -79,25 +68,24 @@ def test_buffer_size_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream( - buffer_size=100, - channel="C222", - thread_ts="333.444", - recipient_team_id="T222", - recipient_user_id="U222", - ) - - assert stream._buffer_size == 100 - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + mock_chat_stream.assert_called_once_with( + buffer_size=100, + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) def test_authorship_overrides(self): say_stream = SayStream( @@ -107,15 +95,14 @@ def test_authorship_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") - - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - "icon_emoji": ":maple_leaf:", - "icon_url": None, - "username": "Charlie Brown", - } + with patch.object(self.web_client, "chat_stream", return_value=MagicMock()) as mock_chat_stream: + say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=":maple_leaf:", + icon_url=None, + username="Charlie Brown", + ) diff --git a/tests/slack_bolt_async/context/test_async_say_stream.py b/tests/slack_bolt_async/context/test_async_say_stream.py index 8104d3964..7ac084044 100644 --- a/tests/slack_bolt_async/context/test_async_say_stream.py +++ b/tests/slack_bolt_async/context/test_async_say_stream.py @@ -1,28 +1,20 @@ import pytest +from unittest.mock import patch, MagicMock + from slack_sdk.web.async_client import AsyncWebClient from slack_bolt.context.say_stream.async_say_stream import AsyncSayStream -from tests.mock_web_api_server import ( - cleanup_mock_web_api_server, - setup_mock_web_api_server, -) from tests.utils import remove_os_env_temporarily, restore_os_env class TestAsyncSayStream: - default_chat_stream_buffer_size = AsyncWebClient.chat_stream.__kwdefaults__["buffer_size"] - @pytest.fixture(scope="function", autouse=True) def setup_teardown(self): old_os_env = remove_os_env_temporarily() - setup_mock_web_api_server(self) - valid_token = "xoxb-valid" - mock_api_server_base_url = "http://localhost:8888" try: - self.web_client = AsyncWebClient(token=valid_token, base_url=mock_api_server_base_url) - yield # run the test here + self.web_client = AsyncWebClient(token="xoxb-valid") + yield finally: - cleanup_mock_web_api_server(self) restore_os_env(old_os_env) @pytest.mark.asyncio @@ -46,19 +38,22 @@ async def test_default_params(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream() - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream() + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=None, + icon_url=None, + username=None, + ) @pytest.mark.asyncio async def test_parameter_overrides(self): @@ -69,19 +64,22 @@ async def test_parameter_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") - - assert stream._buffer_size == self.default_chat_stream_buffer_size - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream(channel="C222", thread_ts="333.444", recipient_team_id="T222", recipient_user_id="U222") + mock_chat_stream.assert_called_once_with( + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) @pytest.mark.asyncio async def test_buffer_size_overrides(self): @@ -92,25 +90,29 @@ async def test_buffer_size_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream( - buffer_size=100, - channel="C222", - thread_ts="333.444", - recipient_team_id="T222", - recipient_user_id="U222", - ) + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) - assert stream._buffer_size == 100 - assert stream._stream_args == { - "channel": "C222", - "thread_ts": "333.444", - "recipient_team_id": "T222", - "recipient_user_id": "U222", - "task_display_mode": None, - "icon_emoji": None, - "icon_url": None, - "username": None, - } + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream( + buffer_size=100, + channel="C222", + thread_ts="333.444", + recipient_team_id="T222", + recipient_user_id="U222", + ) + mock_chat_stream.assert_called_once_with( + buffer_size=100, + channel="C222", + recipient_team_id="T222", + recipient_user_id="U222", + thread_ts="333.444", + icon_emoji=None, + icon_url=None, + username=None, + ) @pytest.mark.asyncio async def test_authorship_overrides(self): @@ -121,15 +123,19 @@ async def test_authorship_overrides(self): recipient_user_id="U111", thread_ts="111.222", ) - stream = await say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") - - assert stream._stream_args == { - "channel": "C111", - "thread_ts": "111.222", - "recipient_team_id": "T111", - "recipient_user_id": "U111", - "task_display_mode": None, - "icon_emoji": ":maple_leaf:", - "icon_url": None, - "username": "Charlie Brown", - } + mock_chat_stream = MagicMock() + + async def fake_chat_stream(**kwargs): + return mock_chat_stream(**kwargs) + + with patch.object(self.web_client, "chat_stream", side_effect=fake_chat_stream): + await say_stream(icon_emoji=":maple_leaf:", username="Charlie Brown") + mock_chat_stream.assert_called_once_with( + channel="C111", + recipient_team_id="T111", + recipient_user_id="U111", + thread_ts="111.222", + icon_emoji=":maple_leaf:", + icon_url=None, + username="Charlie Brown", + )