7 documents covering stack, integrations, architecture, structure, conventions, testing, and concerns.
8.5 KiB
Testing Patterns
Analysis Date: 2026-04-01
Test Framework
Runner: pytest 8.x
Config: pyproject.toml [tool.pytest.ini_options]
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
pythonpath = ["."]
Async support: pytest-asyncio with asyncio_mode = "auto" — all async def test functions run automatically without decorators.
Coverage: pytest-cov (available but no minimum threshold configured)
Run commands:
pytest tests/ -v # all tests
pytest tests/core/ -v # core layer only
pytest tests/adapter/telegram/ -v # telegram adapter only
pytest tests/adapter/matrix/ -v # matrix adapter only
pytest tests/ --cov=. --cov-report=term # with coverage report
Test Directory Structure
tests/
├── __init__.py
├── core/
│ ├── test_auth.py — AuthManager unit tests
│ ├── test_chat.py — ChatManager unit tests
│ ├── test_dispatcher.py — EventDispatcher routing tests
│ ├── test_integration.py — full flow smoke tests (dispatcher + managers + mock)
│ ├── test_protocol.py — dataclass defaults and construction
│ ├── test_settings.py — SettingsManager unit tests
│ ├── test_store.py — InMemoryStore + SQLiteStore tests
│ └── test_voice_slot.py — handle_message() handler unit tests
├── adapter/
│ ├── __init__.py
│ ├── test_forum_db.py — Telegram SQLite DB helpers (untracked, new)
│ └── matrix/
│ ├── __init__.py
│ ├── test_converter.py — matrix-nio event → IncomingEvent converter
│ ├── test_dispatcher.py — full Matrix bot integration (build_runtime)
│ ├── test_reactions.py — reaction text builders and emoji mapping
│ └── test_store.py — Matrix store helper functions
└── platform/
└── test_mock.py — MockPlatformClient behavior
Tests mirror the source tree. New tests for adapter/telegram/ go in tests/adapter/telegram/ (directory exists in .worktrees/telegram branch, not yet merged to main).
conftest.py
conftest.py at project root (/Users/a/MAI/sem2/lambda/surfaces-bot/conftest.py) handles a sys.path conflict: the project has a local platform/ (now sdk/) package that shadows Python's stdlib platform module. It inserts the project root at sys.path[0] and removes the cached stdlib platform module.
No shared fixtures are defined in conftest.py. All fixtures are local to test files.
Test Structure
Fixture pattern — local to each test file:
@pytest.fixture
def mgr():
return AuthManager(MockPlatformClient(), InMemoryStore())
@pytest.fixture
def store() -> InMemoryStore:
return InMemoryStore()
Async tests require no decorator (asyncio_mode = "auto"):
async def test_not_authenticated_initially(mgr):
assert await mgr.is_authenticated("u1") is False
Sync tests are used for pure-function tests (protocol dataclass construction, reaction text builders):
def test_incoming_message_defaults():
msg = IncomingMessage(user_id="u1", platform="telegram", chat_id="C1", text="hi")
assert msg.attachments == []
Integration fixture pattern — builds full runtime in-process:
@pytest.fixture
def dispatcher():
platform = MockPlatformClient()
store = InMemoryStore()
d = EventDispatcher(
platform=platform,
chat_mgr=ChatManager(platform, store),
auth_mgr=AuthManager(platform, store),
settings_mgr=SettingsManager(platform, store),
)
register_all(d)
return d
Mocking Strategy
Primary mock: MockPlatformClient from sdk/mock.py
All tests use MockPlatformClient() directly — it is the real mock for the SDK layer. No unittest.mock patching of MockPlatformClient is needed.
unittest.mock.AsyncMock is used only when testing integration with external clients (matrix-nio AsyncClient):
from unittest.mock import AsyncMock
client = SimpleNamespace(
room_create=AsyncMock(return_value=SimpleNamespace(room_id="!r2:example"))
)
types.SimpleNamespace is used to fabricate matrix-nio event objects without importing the full nio library:
def text_event(body: str, sender: str = "@a:m.org", event_id: str = "$e1"):
return SimpleNamespace(
sender=sender, body=body, event_id=event_id, msgtype="m.text", replyto_event_id=None
)
This is the pattern for all matrix converter tests — define factory functions at module level that return SimpleNamespace objects.
tmp_path pytest fixture is used for SQLiteStore tests to get a throwaway database file:
async def test_sqlite_set_and_get(tmp_path):
store = SQLiteStore(str(tmp_path / "test.db"))
monkeypatch.setenv is used in tests/adapter/test_forum_db.py to inject DB_PATH env var and reload the module with a fresh database:
@pytest.fixture(autouse=True)
def fresh_db(tmp_path, monkeypatch):
db_file = str(tmp_path / "test.db")
monkeypatch.setenv("DB_PATH", db_file)
import importlib
import adapter.telegram.db as db_mod
importlib.reload(db_mod)
db_mod.init_db()
return db_mod
What NOT to mock:
InMemoryStore— use it directly; it's a real in-memory implementationMockPlatformClient— use it directly; patching it defeats the purpose- Core manager classes (
AuthManager,ChatManager,SettingsManager) — always instantiate real ones
Test Data Patterns
User IDs: short strings like "u1", "u2", "tg_123", "@alice:m.org"
Chat IDs: "C1", "C2", "C3" — matches the workspace slot naming
Platform strings: literal "telegram" or "matrix"
Room IDs: "!r:m.org", "!dm:example.org" — valid Matrix room ID format
No shared factories or fixtures files. Test data is constructed inline or via simple factory functions local to the test module.
What Is Tested
| Area | Status |
|---|---|
core/protocol.py — dataclass defaults |
Covered (test_protocol.py) |
core/store.py — InMemoryStore + SQLiteStore |
Covered (test_store.py) |
core/auth.py — AuthManager |
Covered (test_auth.py) |
core/chat.py — ChatManager |
Covered (test_chat.py) |
core/settings.py — SettingsManager |
Covered (test_settings.py) |
core/handler.py — EventDispatcher routing |
Covered (test_dispatcher.py) |
core/handlers/message.py — handle_message |
Covered (test_voice_slot.py) |
| Full dispatcher + all core handlers integration | Covered (test_integration.py) |
sdk/mock.py — MockPlatformClient |
Covered (test_mock.py) |
adapter/matrix/converter.py — event parsing |
Covered (test_converter.py) |
adapter/matrix/store.py — store helpers |
Covered (test_store.py) |
adapter/matrix/reactions.py — text builders |
Covered (test_reactions.py) |
adapter/matrix/bot.py — MatrixBot + build_runtime |
Covered (test_dispatcher.py) |
adapter/telegram/db.py — SQLite helpers |
Covered (test_forum_db.py, untracked) |
Coverage Gaps
Telegram adapter handlers — adapter/telegram/handlers/ (auth.py, chat.py, confirm.py, forum.py, settings.py) have no tests in main. Tests exist only in .worktrees/telegram branch (not yet merged).
Telegram converter — adapter/telegram/converter.py has no tests in main.
core/handlers/callback.py and core/handlers/settings.py — tested indirectly through integration tests but lack dedicated unit tests.
adapter/matrix/room_router.py — resolve_chat_id has no direct unit tests; exercised only through MatrixBot.on_room_message integration path.
adapter/matrix/handlers/ — individual handler files (auth.py, chat.py, confirm.py, settings.py) are tested only via test_dispatcher.py integration; no isolated unit tests.
sdk/mock.py streaming — stream_message is not tested; only send_message is covered.
Error paths — ChatManager.rename raises ValueError when chat not found; no test exercises this path. Same for ChatManager.archive.
Naming Conventions
- Test functions:
test_<behavior_under_test>— descriptive, no abbreviations - Fixture names match the object they create:
mgr,store,dispatcher,deps - Factory functions in converter tests:
text_event(),file_event(),image_event(),reaction_event()
Testing analysis: 2026-04-01