Skip to content

Commit

Permalink
Add back tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sedders123 committed Feb 9, 2025
1 parent 645c295 commit dc3440f
Show file tree
Hide file tree
Showing 8 changed files with 547 additions and 9 deletions.
12 changes: 6 additions & 6 deletions phial/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from slack_sdk.web import WebClient

from phial.commands import help_command
from phial.errors import ArgumentTypeValidationError
from phial.errors import ArgumentTypeValidationError, ArgumentValidationError
from phial.globals import _command_ctx_stack
from phial.scheduler import Schedule, ScheduledJob, Scheduler
from phial.utils import parse_slack_event, validate_kwargs
Expand Down Expand Up @@ -496,13 +496,13 @@ def _send_response(self, response: PhialResponse, original_channel: str) -> None
if isinstance(response, Attachment):
self.upload_attachment(response)

def _handle_message(self, client: SocketModeClient, req: SocketModeRequest) -> None:
def _handle_request(self, client: SocketModeClient, req: SocketModeRequest) -> None:
try:
self._handle_message_internal(client, req)
self._handle_request_internal(client, req)
except Exception as e:
self.logger.error(e)

def _handle_message_internal(
def _handle_request_internal(
self,
client: SocketModeClient,
req: SocketModeRequest,
Expand Down Expand Up @@ -547,7 +547,7 @@ def _handle_message_internal(
response = command.func(**kwargs)
self._send_response(response, message.channel)
return
except ArgumentTypeValidationError as e:
except (ArgumentValidationError, ArgumentTypeValidationError) as e:
self._send_response(str(e), message.channel)
return
finally:
Expand All @@ -570,7 +570,7 @@ def _start(self) -> None: # pragma: no cover
When called will start the bot listening to messages from Slack
"""
self.slack_client.socket_mode_request_listeners.append(self._handle_message) # type: ignore
self.slack_client.socket_mode_request_listeners.append(self._handle_request) # type: ignore
self.slack_client.connect()

self.logger.info("Phial connected and running!")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,5 @@ mypy-init-return = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["E402"]
"**/{tests,docs}/*" = ["D104", "S101"]
"**/{tests,docs}/*" = ["D104", "S101", "ANN401", "ANN002", "ANN003", "ARG001", "SLF001"]
"examples/*" = ["ANN401", "ARG001", "D103", "D401", "INP001"]
243 changes: 243 additions & 0 deletions tests/bot/test_handle_request.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
"""Test handle_request."""

from typing import Any, cast

from slack_sdk.socket_mode import SocketModeClient
from slack_sdk.socket_mode.request import SocketModeRequest

from phial import Message, Phial


class MockClient:
"""Mock client for testing."""

def send_socket_mode_response(self, *args: Any, **kwargs: Any) -> None:
"""Mock send_socket_mode_response."""


def build_request(
text: str,
channel: str,
user: str,
timestamp: str,
team: str | None,
) -> SocketModeRequest:
"""Build a request for testing."""
return SocketModeRequest(
type="events_api",
envelope_id="envelope_id",
payload={
"event": {
"type": "message",
"channel": channel,
"user": user,
"text": text,
"ts": timestamp,
"team": team,
},
},
)


def test_handle_request_handles_none_correctly() -> None:
"""Test handle_request handle None correctly."""

def command() -> None:
raise Exception("Should not be called")

def middleware(message: Message) -> None:
raise Exception("Should not be called")

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
bot._handle_request(client, None) # type: ignore


def test_request_passed_to_middleware() -> None:
"""Test handle_request passes to middleware."""

def command() -> None:
raise Exception("Should not be called")

middleware_calls = [0]

def middleware(message: Message) -> None:
middleware_calls[0] += 1

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
request = build_request("text", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert middleware_calls[0] == 1


def test_requests_ignored_if_not_events_api_type() -> None:
"""Test handle_request ignores requests that are not events_api type."""

def command() -> None:
raise Exception("Should not be called")

middleware_calls = [0]

def middleware(message: Message) -> None:
middleware_calls[0] += 1

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
request = build_request("text", "channel", "user", "timestamp", "team")
request.type = "not_events_api"
bot._handle_request(client, request)
assert middleware_calls[0] == 0


def test_request_ignored_if_no_prefix() -> None:
"""Test message is ignored if it has no prefix."""
middleware_calls = [0]
command_calls = [0]

def command() -> None:
command_calls[0] += 1

def middleware(message: Message) -> Message:
middleware_calls[0] += 1
return message

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
request = build_request("text", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert middleware_calls[0] == 1
assert command_calls[0] == 0


def test_request_calls_command_correctly() -> None:
"""Test message invokes a command correctly."""
middleware_calls = [0]
command_calls = [0]

def command() -> None:
command_calls[0] += 1

def middleware(message: Message) -> Message:
middleware_calls[0] += 1
return message

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
request = build_request("!test", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert middleware_calls[0] == 1
assert command_calls[0] == 1


def test_request_calls_command_correctly_when_no_prefix() -> None:
"""Test message invokes a command correctly with no prefix."""
middleware_calls = [0]
command_calls = [0]

def command() -> None:
command_calls[0] += 1

def middleware(message: Message) -> Message:
middleware_calls[0] += 1
return message

bot = Phial("app-token", "bot-token", config={"prefix": ""})
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
request = build_request("test", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert middleware_calls[0] == 1
assert command_calls[0] == 1


def test_request_falls_back_correctly() -> None:
"""Test message hits fallback command correctly."""
middleware_calls = [0]
command_calls = [0]
fallback_calls = [0]

def command() -> None:
command_calls[0] += 1

def middleware(message: Message) -> Message:
middleware_calls[0] += 1
return message

def fallback(message: Message) -> None:
fallback_calls[0] += 1

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
bot.add_middleware(middleware)
bot.add_fallback_command(fallback)
request = build_request("!test-fallback", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert middleware_calls[0] == 1
assert command_calls[0] == 0
assert fallback_calls[0] == 1


def test_argument_validation_works_correctly() -> None:
"""Test argument validation works correctly."""
command_calls = [0]

def command(name: str) -> None:
command_calls[0] += 1

def mock_send_response(response: str, channel: str) -> None:
assert response == "Parameter name not provided to command"

bot = Phial("app-token", "bot-token")
bot._send_response = mock_send_response # type: ignore
client = cast(SocketModeClient, MockClient())
bot.add_command("test", command)
request = build_request("!test", "channel", "user", "timestamp", "team")
bot._handle_request_internal(client, request)
assert command_calls[0] == 0


def test_argument_type_validation_works_correctly() -> None:
"""Test argument type validation works correctly."""
command_calls = [0]

def command(age: int) -> None:
command_calls[0] += 1

def mock_send_response(response: str, channel: str) -> None:
assert response == "foo could not be converted to int"

bot = Phial("app-token", "bot-token")
bot._send_response = mock_send_response # type: ignore
client = cast(SocketModeClient, MockClient())
bot.add_command("test <age>", command)
request = build_request("!test foo", "channel", "user", "timestamp", "team")
bot._handle_request_internal(client, request)
assert command_calls[0] == 0


def test_type_validation_works_correctly() -> None:
"""Test type validation works correctly."""
command_calls = [0]

def command(age: int) -> None:
command_calls[0] += 1

bot = Phial("app-token", "bot-token")
client = cast(SocketModeClient, MockClient())
bot.add_command("test <age>", command)
request = build_request("!test string", "channel", "user", "timestamp", "team")
bot._handle_request(client, request)
assert command_calls[0] == 0
94 changes: 94 additions & 0 deletions tests/bot/test_send_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Test send_message."""

from typing import Any

import pytest
import slack_sdk

from phial import Phial, Response
from tests.helpers import wildpatch


def test_send_message() -> None:
"""Test send_message works correctly."""

def mock_api_call(*_: Any, **kwargs: Any) -> None:
assert kwargs["channel"] == "channel"
assert kwargs["text"] == "message"
assert kwargs["attachments"] == "null"
assert kwargs["thread_ts"] is None

wildpatch(slack_sdk.WebClient, "chat_postMessage", mock_api_call)

response = Response("channel", text="message")
bot = Phial("app-token", "bot-token")

bot.send_message(response)


def test_send_full_message() -> None:
"""Test send message works correctly when all properties populated."""

def mock_api_call(*_: Any, **kwargs: Any) -> None:
assert kwargs["channel"] == "channel"
assert kwargs["text"] == "message"
assert kwargs["thread_ts"] == "orig_time"
assert type(kwargs["attachments"]) is str # Serialised to JSON

assert "reaction" not in kwargs

wildpatch(slack_sdk.WebClient, "chat_postMessage", mock_api_call)

response = Response(
"channel",
text="message",
original_ts="orig_time",
reaction="reaction",
ephemeral=False,
attachments=[{"foo": "bar"}],
)
bot = Phial("app-token", "bot-token")

bot.send_message(response)


def test_send_ephemeral_message() -> None:
"""Test sending ephemeral messages works."""

def mock_api_call(*_: Any, **kwargs: Any) -> None:
assert kwargs["channel"] == "channel"
assert kwargs["text"] == "message"
assert kwargs["user"] == "user"
assert type(kwargs["attachments"]) is str # Serialised to JSON

assert "reaction" not in kwargs

wildpatch(slack_sdk.WebClient, "chat_postEphemeral", mock_api_call)

response = Response(
"channel",
text="message",
original_ts="orig_time",
reaction="reaction",
ephemeral=True,
user="user",
attachments=[{"foo": "bar"}],
)
bot = Phial("app-token", "bot-token")

bot.send_message(response)


def test_send_ephemeral_message_throws_with_no_user() -> None:
"""Test sending ephemeral throws when no user is specified."""
response = Response(
"channel",
text="message",
original_ts="orig_time",
reaction="reaction",
ephemeral=True,
attachments=[{"foo": "bar"}],
)
bot = Phial("app-token", "bot-token")
with pytest.raises(ValueError):
bot.send_message(response)
Loading

0 comments on commit dc3440f

Please sign in to comment.