Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion slack_bolt/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -946,7 +946,7 @@ def reverse_string(ack: Ack, inputs: dict, complete: Complete, fail: Fail):
def __call__(*args, **kwargs):
functions = self._to_listener_functions(kwargs) if kwargs else list(args)
primary_matcher = builtin_matchers.function_executed(callback_id=callback_id, base_logger=self._base_logger)
return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge)
return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, 5)

return __call__

Expand Down Expand Up @@ -1422,6 +1422,7 @@ def _register_listener(
matchers: Optional[Sequence[Callable[..., bool]]],
middleware: Optional[Sequence[Union[Callable, Middleware]]],
auto_acknowledgement: bool = False,
acknowledgement_timeout: int = 3,
) -> Optional[Callable[..., Optional[BoltResponse]]]:
value_to_return = None
if not isinstance(functions, list):
Expand Down Expand Up @@ -1452,6 +1453,7 @@ def _register_listener(
matchers=listener_matchers,
middleware=listener_middleware,
auto_acknowledgement=auto_acknowledgement,
acknowledgement_timeout=acknowledgement_timeout,
base_logger=self._base_logger,
)
)
Expand Down
4 changes: 3 additions & 1 deletion slack_bolt/app/async_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ def __call__(*args, **kwargs):
primary_matcher = builtin_matchers.function_executed(
callback_id=callback_id, base_logger=self._base_logger, asyncio=True
)
return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge)
return self._register_listener(functions, primary_matcher, matchers, middleware, auto_acknowledge, 5)

return __call__

Expand Down Expand Up @@ -1456,6 +1456,7 @@ def _register_listener(
matchers: Optional[Sequence[Callable[..., Awaitable[bool]]]],
middleware: Optional[Sequence[Union[Callable, AsyncMiddleware]]],
auto_acknowledgement: bool = False,
acknowledgement_timeout: int = 3,
) -> Optional[Callable[..., Awaitable[Optional[BoltResponse]]]]:
value_to_return = None
if not isinstance(functions, list):
Expand Down Expand Up @@ -1491,6 +1492,7 @@ def _register_listener(
matchers=listener_matchers,
middleware=listener_middleware,
auto_acknowledgement=auto_acknowledgement,
acknowledgement_timeout=acknowledgement_timeout,
base_logger=self._base_logger,
)
)
Expand Down
4 changes: 4 additions & 0 deletions slack_bolt/listener/async_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class AsyncListener(metaclass=ABCMeta):
ack_function: Callable[..., Awaitable[BoltResponse]]
lazy_functions: Sequence[Callable[..., Awaitable[None]]]
auto_acknowledgement: bool
acknowledgement_timeout: int

async def async_matches(
self,
Expand Down Expand Up @@ -87,6 +88,7 @@ class AsyncCustomListener(AsyncListener):
matchers: Sequence[AsyncListenerMatcher]
middleware: Sequence[AsyncMiddleware]
auto_acknowledgement: bool
acknowledgement_timeout: int
arg_names: MutableSequence[str]
logger: Logger

Expand All @@ -99,6 +101,7 @@ def __init__(
matchers: Sequence[AsyncListenerMatcher],
middleware: Sequence[AsyncMiddleware],
auto_acknowledgement: bool = False,
acknowledgement_timeout: int = 3,
base_logger: Optional[Logger] = None,
):
self.app_name = app_name
Expand All @@ -107,6 +110,7 @@ def __init__(
self.matchers = matchers
self.middleware = middleware
self.auto_acknowledgement = auto_acknowledgement
self.acknowledgement_timeout = acknowledgement_timeout
self.arg_names = get_arg_names_of_callable(ack_function)
self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger)

Expand Down
2 changes: 1 addition & 1 deletion slack_bolt/listener/asyncio_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async def run_ack_function_asynchronously(
self._start_lazy_function(lazy_func, request)

# await for the completion of ack() in the async listener execution
while ack.response is None and time.time() - starting_time <= 3:
while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👾 praise: Love to see this!

await asyncio.sleep(0.01)

if response is None and ack.response is None:
Expand Down
3 changes: 3 additions & 0 deletions slack_bolt/listener/custom_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class CustomListener(Listener):
matchers: Sequence[ListenerMatcher]
middleware: Sequence[Middleware]
auto_acknowledgement: bool
acknowledgement_timeout: int = 3
arg_names: MutableSequence[str]
logger: Logger

Expand All @@ -30,6 +31,7 @@ def __init__(
matchers: Sequence[ListenerMatcher],
middleware: Sequence[Middleware],
auto_acknowledgement: bool = False,
acknowledgement_timeout: int = 3,
base_logger: Optional[Logger] = None,
):
self.app_name = app_name
Expand All @@ -38,6 +40,7 @@ def __init__(
self.matchers = matchers
self.middleware = middleware
self.auto_acknowledgement = auto_acknowledgement
self.acknowledgement_timeout = acknowledgement_timeout
self.arg_names = get_arg_names_of_callable(ack_function)
self.logger = get_bolt_app_logger(app_name, self.ack_function, base_logger)

Expand Down
1 change: 1 addition & 0 deletions slack_bolt/listener/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Listener(metaclass=ABCMeta):
ack_function: Callable[..., BoltResponse]
lazy_functions: Sequence[Callable[..., None]]
auto_acknowledgement: bool
acknowledgement_timeout: int = 3

def matches(
self,
Expand Down
4 changes: 3 additions & 1 deletion slack_bolt/listener/thread_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ def run_ack_function_asynchronously():
self._start_lazy_function(lazy_func, request)

# await for the completion of ack() in the async listener execution
while ack.response is None and time.time() - starting_time <= 3:
print(str(starting_time))
while ack.response is None and time.time() - starting_time <= listener.acknowledgement_timeout:
print("we are sleeping")
time.sleep(0.01)

if response is None and ack.response is None:
Expand Down
35 changes: 34 additions & 1 deletion tests/scenario_tests/test_function.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import time
import pytest
from unittest.mock import Mock

from slack_sdk.signature import SignatureVerifier
from slack_sdk.web import WebClient
Expand Down Expand Up @@ -51,6 +52,10 @@ def build_request_from_body(self, message_body: dict) -> BoltRequest:
timestamp, body = str(int(time.time())), json.dumps(message_body)
return BoltRequest(body=body, headers=self.build_headers(timestamp, body))

def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: Mock):
monkeypatch.setattr(time, "time", time_mock)
monkeypatch.setattr(time, "sleep", sleep_mock)

def test_valid_callback_id_success(self):
app = App(
client=self.web_client,
Expand Down Expand Up @@ -124,20 +129,48 @@ def test_auto_acknowledge_false_with_acknowledging(self):
assert response.status == 200
assert_auth_test_count(self, 1)

def test_auto_acknowledge_false_without_acknowledging(self, caplog):
def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch):
app = App(
client=self.web_client,
signing_secret=self.signing_secret,
)
app.function("reverse", auto_acknowledge=False)(just_no_ack)

request = self.build_request_from_body(function_body)
self.setup_time_mocks(
monkeypatch=monkeypatch,
time_mock=Mock(side_effect=[current_time for current_time in range(100)]),
sleep_mock=Mock(),
)
response = app.dispatch(request)

assert response.status == 404
assert_auth_test_count(self, 1)
assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text

def test_function_handler_timeout(self, monkeypatch):
app = App(
client=self.web_client,
signing_secret=self.signing_secret,
)
app.function("reverse", auto_acknowledge=False)(just_no_ack)
request = self.build_request_from_body(function_body)

sleep_mock = Mock()
self.setup_time_mocks(
monkeypatch=monkeypatch,
time_mock=Mock(side_effect=[current_time for current_time in range(100)]),
sleep_mock=sleep_mock,
)

response = app.dispatch(request)

assert response.status == 404
assert_auth_test_count(self, 1)
assert (
sleep_mock.call_count == 5
), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times"


function_body = {
"token": "verification_token",
Expand Down
37 changes: 35 additions & 2 deletions tests/scenario_tests_async/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time

import pytest
from unittest.mock import AsyncMock, Mock
from slack_sdk.signature import SignatureVerifier
from slack_sdk.web.async_client import AsyncWebClient

Expand Down Expand Up @@ -56,6 +57,10 @@ def build_request_from_body(self, message_body: dict) -> AsyncBoltRequest:
timestamp, body = str(int(time.time())), json.dumps(message_body)
return AsyncBoltRequest(body=body, headers=self.build_headers(timestamp, body))

def setup_time_mocks(self, *, monkeypatch: pytest.MonkeyPatch, time_mock: Mock, sleep_mock: AsyncMock):
monkeypatch.setattr(time, "time", time_mock)
monkeypatch.setattr(asyncio, "sleep", sleep_mock)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just make the tests run faster 🚀

@pytest.mark.asyncio
async def test_mock_server_is_running(self):
resp = await self.web_client.api_test()
Expand Down Expand Up @@ -130,19 +135,47 @@ async def test_auto_acknowledge_false_with_acknowledging(self):
await assert_auth_test_count_async(self, 1)

@pytest.mark.asyncio
async def test_auto_acknowledge_false_without_acknowledging(self, caplog):
async def test_auto_acknowledge_false_without_acknowledging(self, caplog, monkeypatch):
app = AsyncApp(
client=self.web_client,
signing_secret=self.signing_secret,
)
app.function("reverse", auto_acknowledge=False)(just_no_ack)

request = self.build_request_from_body(function_body)
self.setup_time_mocks(
monkeypatch=monkeypatch,
time_mock=Mock(side_effect=[current_time for current_time in range(100)]),
sleep_mock=AsyncMock(),
)
response = await app.async_dispatch(request)
assert response.status == 404
await assert_auth_test_count_async(self, 1)
assert f"WARNING {just_no_ack.__name__} didn't call ack()" in caplog.text

@pytest.mark.asyncio
async def test_function_handler_timeout(self, monkeypatch):
app = AsyncApp(
client=self.web_client,
signing_secret=self.signing_secret,
)
app.function("reverse", auto_acknowledge=False)(just_no_ack)
request = self.build_request_from_body(function_body)

sleep_mock = AsyncMock()
self.setup_time_mocks(
monkeypatch=monkeypatch,
time_mock=Mock(side_effect=[current_time for current_time in range(100)]),
sleep_mock=sleep_mock,
)

response = await app.async_dispatch(request)

assert response.status == 404
await assert_auth_test_count_async(self, 1)
assert (
sleep_mock.call_count == 5
), f"Expected handler to time out after calling time.sleep 5 times, but it was called {sleep_mock.call_count} times"


function_body = {
"token": "verification_token",
Expand Down
Loading