mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-21 17:12:32 +00:00
test(signal): consolidate channel-capture setup into one factory
Two test classes (TestHandleDataMessageDM, TestHandleDataMessageGroup) plus three TestCommandHandling tests each repeated the same handful of lines: build a channel, mock _handle_message to record kwargs, replace _start_typing with a no-op, paper over the assignment with type: ignore. Hoist the pattern into _make_channel_with_capture and call it from all five sites. Drops 30+ lines of duplication and 7 type: ignore comments. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
626f262121
commit
a786e3d225
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import AsyncMock, MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -62,6 +62,24 @@ class _FakeHTTPClient:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _make_channel_with_capture(**overrides) -> tuple[SignalChannel, list[dict]]:
|
||||||
|
"""Build a SignalChannel with _handle_message captured into a list and a
|
||||||
|
no-op _start_typing, used by every receive-flow test class.
|
||||||
|
"""
|
||||||
|
ch = _make_channel(**overrides)
|
||||||
|
handled: list[dict] = []
|
||||||
|
|
||||||
|
async def capture(**kwargs):
|
||||||
|
handled.append(kwargs)
|
||||||
|
|
||||||
|
async def noop_typing(chat_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
ch._handle_message = capture # type: ignore[method-assign]
|
||||||
|
ch._start_typing = noop_typing # type: ignore[method-assign]
|
||||||
|
return ch, handled
|
||||||
|
|
||||||
|
|
||||||
def _make_channel(
|
def _make_channel(
|
||||||
*,
|
*,
|
||||||
phone_number: str = "+10000000000",
|
phone_number: str = "+10000000000",
|
||||||
@ -590,19 +608,9 @@ class TestAttachmentsDir:
|
|||||||
|
|
||||||
class TestHandleDataMessageDM:
|
class TestHandleDataMessageDM:
|
||||||
def _make_dm_channel(self, policy="open", allow_from=None) -> tuple[SignalChannel, list]:
|
def _make_dm_channel(self, policy="open", allow_from=None) -> tuple[SignalChannel, list]:
|
||||||
ch = _make_channel(dm_enabled=True, dm_policy=policy, dm_allow_from=allow_from or [])
|
return _make_channel_with_capture(
|
||||||
handled: list[dict] = []
|
dm_enabled=True, dm_policy=policy, dm_allow_from=allow_from or []
|
||||||
|
)
|
||||||
async def capture(**kwargs):
|
|
||||||
handled.append(kwargs)
|
|
||||||
|
|
||||||
ch._handle_message = capture # type: ignore[method-assign]
|
|
||||||
|
|
||||||
async def noop_typing(chat_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
ch._start_typing = noop_typing # type: ignore[method-assign]
|
|
||||||
return ch, handled
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_dm_open_policy_accepted(self):
|
async def test_dm_open_policy_accepted(self):
|
||||||
@ -777,24 +785,12 @@ class TestHandleDataMessageGroup:
|
|||||||
allow_from=None,
|
allow_from=None,
|
||||||
require_mention=True,
|
require_mention=True,
|
||||||
) -> tuple[SignalChannel, list]:
|
) -> tuple[SignalChannel, list]:
|
||||||
ch = _make_channel(
|
return _make_channel_with_capture(
|
||||||
group_enabled=True,
|
group_enabled=True,
|
||||||
group_policy=policy,
|
group_policy=policy,
|
||||||
group_allow_from=allow_from or [],
|
group_allow_from=allow_from or [],
|
||||||
require_mention=require_mention,
|
require_mention=require_mention,
|
||||||
)
|
)
|
||||||
handled: list[dict] = []
|
|
||||||
|
|
||||||
async def capture(**kwargs):
|
|
||||||
handled.append(kwargs)
|
|
||||||
|
|
||||||
ch._handle_message = capture # type: ignore[method-assign]
|
|
||||||
|
|
||||||
async def noop_typing(chat_id):
|
|
||||||
pass
|
|
||||||
|
|
||||||
ch._start_typing = noop_typing # type: ignore[method-assign]
|
|
||||||
return ch, handled
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_group_disabled_rejected(self):
|
async def test_group_disabled_rejected(self):
|
||||||
@ -1007,55 +1003,31 @@ class TestCommandHandling:
|
|||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_dm_command_forwarded_to_bus(self):
|
async def test_dm_command_forwarded_to_bus(self):
|
||||||
"""Slash commands in DMs are forwarded to the bus for AgentLoop to handle."""
|
"""Slash commands in DMs are forwarded to the bus for AgentLoop to handle."""
|
||||||
ch = _make_channel(dm_enabled=True, dm_policy="open")
|
ch, forwarded = _make_channel_with_capture(dm_enabled=True, dm_policy="open")
|
||||||
forwarded: list[dict] = []
|
|
||||||
|
|
||||||
async def capture(**kw):
|
|
||||||
forwarded.append(kw)
|
|
||||||
|
|
||||||
ch._handle_message = capture # type: ignore[method-assign]
|
|
||||||
ch._start_typing = AsyncMock()
|
|
||||||
|
|
||||||
params = _dm_envelope(source_number="+19995550001", message="/reset")
|
params = _dm_envelope(source_number="+19995550001", message="/reset")
|
||||||
await ch._handle_receive_notification(params)
|
await ch._handle_receive_notification(params)
|
||||||
|
|
||||||
assert len(forwarded) == 1
|
assert len(forwarded) == 1
|
||||||
assert forwarded[0]["content"].strip() == "/reset"
|
assert forwarded[0]["content"].strip() == "/reset"
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_group_command_bypasses_mention_requirement(self):
|
async def test_group_command_bypasses_mention_requirement(self):
|
||||||
"""Slash commands in groups bypass the mention requirement and reach the bus."""
|
"""Slash commands in groups bypass the mention requirement and reach the bus."""
|
||||||
ch = _make_channel(
|
ch, forwarded = _make_channel_with_capture(
|
||||||
group_enabled=True, group_policy="open", require_mention=True
|
group_enabled=True, group_policy="open", require_mention=True
|
||||||
)
|
)
|
||||||
forwarded: list[dict] = []
|
params = _group_envelope(
|
||||||
|
source_number="+19995550001", group_id="grp==", message="/reset"
|
||||||
async def capture(**kw):
|
)
|
||||||
forwarded.append(kw)
|
|
||||||
|
|
||||||
ch._handle_message = capture # type: ignore[method-assign]
|
|
||||||
ch._start_typing = AsyncMock()
|
|
||||||
|
|
||||||
params = _group_envelope(source_number="+19995550001", group_id="grp==", message="/reset")
|
|
||||||
await ch._handle_receive_notification(params)
|
await ch._handle_receive_notification(params)
|
||||||
|
|
||||||
assert len(forwarded) == 1
|
assert len(forwarded) == 1
|
||||||
assert "/reset" in forwarded[0]["content"]
|
assert "/reset" in forwarded[0]["content"]
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_command_denied_for_disallowed_dm_sender(self):
|
async def test_command_denied_for_disallowed_dm_sender(self):
|
||||||
"""Commands from senders not on the DM allowlist are dropped."""
|
"""Commands from senders not on the DM allowlist are dropped."""
|
||||||
ch = _make_channel(dm_enabled=False)
|
ch, forwarded = _make_channel_with_capture(dm_enabled=False)
|
||||||
forwarded: list[dict] = []
|
|
||||||
|
|
||||||
async def capture(**kw):
|
|
||||||
forwarded.append(kw)
|
|
||||||
|
|
||||||
ch._handle_message = capture # type: ignore[method-assign]
|
|
||||||
|
|
||||||
params = _dm_envelope(source_number="+19995550001", message="/reset")
|
params = _dm_envelope(source_number="+19995550001", message="/reset")
|
||||||
await ch._handle_receive_notification(params)
|
await ch._handle_receive_notification(params)
|
||||||
|
|
||||||
assert forwarded == []
|
assert forwarded == []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user