latest updates
This commit is contained in:
200
tests/test_base_adapter.py
Normal file
200
tests/test_base_adapter.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""
|
||||
Tests for the Base Adapter and IncomingMessage.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from adapters.base import BaseAdapter, IncomingMessage
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Concrete adapter for testing (implements all abstract methods)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class MockAdapter(BaseAdapter):
|
||||
"""A minimal concrete adapter for testing BaseAdapter."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.sent_messages: list[dict] = []
|
||||
self._started = False
|
||||
|
||||
@property
|
||||
def source_name(self) -> str:
|
||||
return "mock"
|
||||
|
||||
def start(self) -> None:
|
||||
self._started = True
|
||||
|
||||
def start_async(self) -> None:
|
||||
self._started = True
|
||||
|
||||
def stop(self) -> None:
|
||||
self._started = False
|
||||
|
||||
def send_message(
|
||||
self,
|
||||
channel_id: str,
|
||||
text: str,
|
||||
thread_id: str | None = None,
|
||||
) -> None:
|
||||
self.sent_messages.append({
|
||||
"channel_id": channel_id,
|
||||
"text": text,
|
||||
"thread_id": thread_id,
|
||||
})
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def adapter():
|
||||
return MockAdapter()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_message():
|
||||
return IncomingMessage(
|
||||
text="Hello world",
|
||||
user_id="U123",
|
||||
user_name="testuser",
|
||||
channel_id="C456",
|
||||
channel_name="general",
|
||||
conversation_id="conv-789",
|
||||
source="mock",
|
||||
is_dm=False,
|
||||
raw_event={"thread_id": "T100"},
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: IncomingMessage
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestIncomingMessage:
|
||||
def test_create_message(self, sample_message):
|
||||
assert sample_message.text == "Hello world"
|
||||
assert sample_message.user_id == "U123"
|
||||
assert sample_message.user_name == "testuser"
|
||||
assert sample_message.channel_id == "C456"
|
||||
assert sample_message.source == "mock"
|
||||
assert sample_message.is_dm is False
|
||||
|
||||
def test_timestamp_default(self):
|
||||
msg = IncomingMessage(
|
||||
text="test",
|
||||
user_id="U1",
|
||||
user_name="user",
|
||||
channel_id="C1",
|
||||
channel_name="ch",
|
||||
conversation_id="conv",
|
||||
source="test",
|
||||
is_dm=True,
|
||||
)
|
||||
assert msg.timestamp is not None
|
||||
assert msg.timestamp.tzinfo is not None # has timezone
|
||||
|
||||
def test_raw_event_default(self):
|
||||
msg = IncomingMessage(
|
||||
text="test",
|
||||
user_id="U1",
|
||||
user_name="user",
|
||||
channel_id="C1",
|
||||
channel_name="ch",
|
||||
conversation_id="conv",
|
||||
source="test",
|
||||
is_dm=False,
|
||||
)
|
||||
assert msg.raw_event == {}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tests: BaseAdapter
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class TestBaseAdapter:
|
||||
def test_register_handler(self, adapter):
|
||||
handler = lambda msg: "response"
|
||||
adapter.on_message(handler)
|
||||
assert len(adapter._message_handlers) == 1
|
||||
|
||||
def test_on_message_as_decorator(self, adapter):
|
||||
@adapter.on_message
|
||||
def my_handler(msg):
|
||||
return "decorated response"
|
||||
|
||||
assert len(adapter._message_handlers) == 1
|
||||
assert my_handler("test") == "decorated response"
|
||||
|
||||
def test_dispatch_calls_handler(self, adapter, sample_message):
|
||||
responses = []
|
||||
|
||||
@adapter.on_message
|
||||
def handler(msg):
|
||||
responses.append(msg.text)
|
||||
return f"reply to: {msg.text}"
|
||||
|
||||
adapter._dispatch(sample_message)
|
||||
assert responses == ["Hello world"]
|
||||
|
||||
def test_dispatch_sends_response(self, adapter, sample_message):
|
||||
@adapter.on_message
|
||||
def handler(msg):
|
||||
return "Auto reply"
|
||||
|
||||
adapter._dispatch(sample_message)
|
||||
assert len(adapter.sent_messages) == 1
|
||||
assert adapter.sent_messages[0]["text"] == "Auto reply"
|
||||
assert adapter.sent_messages[0]["channel_id"] == "C456"
|
||||
|
||||
def test_dispatch_no_response(self, adapter, sample_message):
|
||||
@adapter.on_message
|
||||
def handler(msg):
|
||||
return None # explicit no response
|
||||
|
||||
adapter._dispatch(sample_message)
|
||||
assert len(adapter.sent_messages) == 0
|
||||
|
||||
def test_dispatch_handler_error(self, adapter, sample_message):
|
||||
@adapter.on_message
|
||||
def bad_handler(msg):
|
||||
raise ValueError("Something broke")
|
||||
|
||||
# Should not raise — dispatch catches errors
|
||||
adapter._dispatch(sample_message)
|
||||
# Should send error message
|
||||
assert len(adapter.sent_messages) == 1
|
||||
assert "Something went wrong" in adapter.sent_messages[0]["text"]
|
||||
|
||||
def test_multiple_handlers(self, adapter, sample_message):
|
||||
calls = []
|
||||
|
||||
@adapter.on_message
|
||||
def handler1(msg):
|
||||
calls.append("h1")
|
||||
return None
|
||||
|
||||
@adapter.on_message
|
||||
def handler2(msg):
|
||||
calls.append("h2")
|
||||
return "from h2"
|
||||
|
||||
adapter._dispatch(sample_message)
|
||||
assert calls == ["h1", "h2"]
|
||||
assert len(adapter.sent_messages) == 1 # only h2 returned a response
|
||||
|
||||
def test_source_name(self, adapter):
|
||||
assert adapter.source_name == "mock"
|
||||
|
||||
def test_start_stop(self, adapter):
|
||||
adapter.start()
|
||||
assert adapter._started is True
|
||||
adapter.stop()
|
||||
assert adapter._started is False
|
||||
Reference in New Issue
Block a user