Added unit tests for route API and route handling infrastructure.
CI / Formatting (push) Successful in 4s
CI / Linting (push) Successful in 5s
CI / Tests (Python 3.12) (push) Successful in 19s
CI / Tests (Python 3.13) (push) Successful in 19s
CI / Tests (Python 3.14) (push) Successful in 26s
CI / Type Checking (push) Failing after 32s
CI / Spelling (push) Successful in 29s

This commit is contained in:
2026-03-26 17:18:00 -04:00
parent 20272fedfd
commit 4ff35fdf6b
4 changed files with 994 additions and 66 deletions
+28 -44
View File
@@ -29,7 +29,7 @@ from typing import TYPE_CHECKING, Any
import pytest
from owlbot.api.context import EventContext, ModuleContext, PropagationState
from owlbot.api.context import EventContext, PropagationState
from owlbot.api.event_types import (
ChatEvent,
EventType,
@@ -43,8 +43,10 @@ from owlbot.registries.events import (
HandlerEntry,
ModuleEvents,
)
from tests.conftest import make_module_context
if TYPE_CHECKING:
from owlbot.api.context import ModuleContext
from owlbot.api.storage import ModuleStorage
@@ -84,24 +86,6 @@ def _make_chat_event(
)
def _make_module_context(
storage: ModuleStorage | None, module_name: str = "mod_a"
) -> ModuleContext:
"""Build a stub ModuleContext backed by real or None storage."""
return ModuleContext(
module_name=module_name,
config=None, # type: ignore[arg-type]
owncast_client=None, # type: ignore[arg-type]
storage=storage, # type: ignore[arg-type]
commands=None, # type: ignore[arg-type]
events=None, # type: ignore[arg-type]
routes=None, # type: ignore[arg-type]
http=None, # type: ignore[arg-type]
templates=None, # type: ignore[arg-type]
admin_client=None,
)
def _unused_get_module_context(name: str) -> ModuleContext:
"""Stub for EventDispatcher tests that never invoke get_module_context."""
raise AssertionError("get_module_context should not be called")
@@ -446,7 +430,7 @@ class TestEventDispatcherDispatch:
async def handler(ctx: EventContext[Any]) -> None:
received.append(ctx.event)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -475,7 +459,7 @@ class TestEventDispatcherDispatch:
async def low_handler(ctx: EventContext[Any]) -> None:
call_order.append("low")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -501,7 +485,7 @@ class TestEventDispatcherDispatch:
async def second(ctx: EventContext[Any]) -> None:
call_order.append("second")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -524,7 +508,7 @@ class TestEventDispatcherDispatch:
async def command_dispatch(event: ChatEvent) -> None:
pass
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
dispatcher = EventDispatcher(
command_dispatch=command_dispatch,
get_module_context=lambda name: module_ctx,
@@ -552,7 +536,7 @@ class TestEventDispatcherDispatch:
async def command_dispatch(event: ChatEvent) -> None:
pass
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
dispatcher = EventDispatcher(
command_dispatch=command_dispatch,
get_module_context=lambda name: module_ctx,
@@ -576,7 +560,7 @@ class TestEventDispatcherDispatch:
async def handler_b(ctx: EventContext[Any]) -> None:
call_order.append("b")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -602,7 +586,7 @@ class TestEventDispatcherDispatch:
async def handler_b(ctx: EventContext[Any]) -> None:
pass
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -633,7 +617,7 @@ class TestEventDispatcherDispatch:
async def follower(ctx: EventContext[Any]) -> None:
call_order.append("follower")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -666,7 +650,7 @@ class TestEventDispatcherStorage:
"INSERT INTO test_tbl (val) VALUES (?)", ("committed",)
)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -696,7 +680,7 @@ class TestEventDispatcherStorage:
)
raise RuntimeError("boom")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -728,7 +712,7 @@ class TestEventDispatcherStorage:
async def second_handler(ctx: EventContext[Any]) -> None:
call_order.append("second")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -751,7 +735,7 @@ class TestEventDispatcherStorage:
async def slow_handler(ctx: EventContext[Any]) -> None:
await asyncio.sleep(10)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -781,7 +765,7 @@ class TestEventDispatcherStorage:
)
await asyncio.sleep(10)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -811,7 +795,7 @@ class TestEventDispatcherStorage:
)
await asyncio.sleep(10)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
async def command_dispatch(event: ChatEvent) -> None:
pass
@@ -866,7 +850,7 @@ class TestCommandDispatchPhase:
async def command_dispatch(event: ChatEvent) -> None:
command_calls.append(event)
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
dispatcher = EventDispatcher(
command_dispatch=command_dispatch,
get_module_context=lambda name: module_ctx,
@@ -890,7 +874,7 @@ class TestCommandDispatchPhase:
async def stopper(ctx: EventContext[Any]) -> None:
ctx.stop_propagation("spam")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
dispatcher = EventDispatcher(
command_dispatch=command_dispatch,
get_module_context=lambda name: module_ctx,
@@ -912,7 +896,7 @@ class TestCommandDispatchPhase:
async def command_dispatch(event: ChatEvent) -> None:
raise RuntimeError("fail")
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
dispatcher = EventDispatcher(
command_dispatch=command_dispatch,
get_module_context=lambda name: module_ctx,
@@ -1080,7 +1064,7 @@ class TestEventContext:
self, storage: ModuleStorage, prop: str, use_is: bool
) -> None:
"""EventContext properties proxy to the underlying ModuleContext."""
module_ctx = _make_module_context(storage, "mod_a")
module_ctx = make_module_context(storage, "mod_a")
event = _make_chat_event()
ctx: EventContext[ChatEvent] = EventContext(event=event, module=module_ctx)
ctx_val = getattr(ctx, prop)
@@ -1092,14 +1076,14 @@ class TestEventContext:
def test_event_attribute(self) -> None:
"""ctx.event is the event object passed at construction."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
event = _make_chat_event()
ctx: EventContext[ChatEvent] = EventContext(event=event, module=module_ctx)
assert ctx.event is event
def test_propagation_stopped_default(self) -> None:
"""propagation_stopped is False by default."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
ctx: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx
)
@@ -1107,7 +1091,7 @@ class TestEventContext:
def test_stop_propagation(self) -> None:
"""After stop_propagation(), propagation_stopped is True."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
ctx: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx
)
@@ -1116,7 +1100,7 @@ class TestEventContext:
def test_stop_propagation_stores_reason(self) -> None:
"""stop_propagation() stores the reason string."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
ctx: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx
)
@@ -1125,7 +1109,7 @@ class TestEventContext:
def test_first_reason_wins(self) -> None:
"""Only the first non-empty reason is stored."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
ctx: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx
)
@@ -1135,7 +1119,7 @@ class TestEventContext:
def test_none_reason_leaves_empty(self) -> None:
"""stop_propagation(None) leaves reason as empty string."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
ctx: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx
)
@@ -1144,7 +1128,7 @@ class TestEventContext:
def test_shared_propagation_across_contexts(self) -> None:
"""Two contexts sharing a PropagationState see each other's mutations."""
module_ctx = _make_module_context(None, "mod_a")
module_ctx = make_module_context(None, "mod_a")
propagation = PropagationState()
ctx_a: EventContext[ChatEvent] = EventContext(
event=_make_chat_event(), module=module_ctx, _propagation=propagation