This commit is contained in:
MuXinCG
2026-02-15 18:08:57 +08:00
parent 919aa15c5e
commit 01018c0d24
4 changed files with 92 additions and 101 deletions
-36
View File
@@ -1,36 +0,0 @@
2026-02-15 13:46:42,249 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 13:46:42,535 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 7200
2026-02-15 13:46:43,622 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 13:46:43,623 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 13:46:43,623 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 13:46:43,623 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 13:46:43,792 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 13:46:43,915 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 13:46:43,916 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 13:57:47,578 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 13:57:47,775 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 6535
2026-02-15 13:57:48,887 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 13:57:48,888 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 13:57:48,888 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 13:57:48,888 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 13:57:49,073 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 13:57:49,294 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 13:57:49,295 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 18:01:03,252 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 18:01:03,471 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 2348
2026-02-15 18:01:03,935 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 18:01:03,935 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 18:01:03,935 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 18:01:03,935 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 18:01:04,118 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 18:01:04,298 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 18:01:04,299 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
2026-02-15 18:03:43,137 [INFO] (client.py:162)_bot_login [botpy] 登录机器人账号中...
2026-02-15 18:03:43,319 [INFO] (robot.py:65)update_access_token [botpy] access_token expires_in 2188
2026-02-15 18:03:43,860 [INFO] (client.py:181)_bot_init [botpy] 程序启动...
2026-02-15 18:03:43,860 [INFO] (connection.py:60)multi_run [botpy] 最大并发连接数: 1, 启动会话数: 1
2026-02-15 18:03:43,861 [INFO] (client.py:242)bot_connect [botpy] 会话启动中...
2026-02-15 18:03:43,861 [INFO] (gateway.py:115)ws_connect [botpy] 启动中...
2026-02-15 18:03:44,033 [INFO] (gateway.py:142)ws_identify [botpy] 鉴权中...
2026-02-15 18:03:44,174 [INFO] (gateway.py:85)on_message [botpy] 机器人「EvoSci-测试中」启动成功!
2026-02-15 18:03:44,174 [INFO] (gateway.py:223)_send_heart [botpy] 心跳维持启动...
Binary file not shown.
+19 -13
View File
@@ -567,7 +567,7 @@ class TestChannelBuildInbound:
sender_id="u1", chat_id="c1", text="hello",
message_id="m1", media_files=["/path/img.jpg"],
)
msg = ch._build_inbound(raw)
msg = ch._raw_to_inbound(raw)
assert msg is not None
assert msg.channel == "stub"
assert msg.sender_id == "u1"
@@ -575,21 +575,27 @@ class TestChannelBuildInbound:
assert msg.media == ["/path/img.jpg"]
def test_drops_disallowed_sender(self):
cfg = _FakeConfig(allowed_senders=["alice"])
ch = StubChannel(cfg)
raw = RawIncoming(sender_id="eve", chat_id="c1", text="hack")
assert ch._build_inbound(raw) is None
async def _test():
cfg = _FakeConfig(allowed_senders=["alice"])
ch = StubChannel(cfg)
raw = RawIncoming(sender_id="eve", chat_id="c1", text="hack")
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 0
_run(_test())
def test_drops_disallowed_channel(self):
cfg = _FakeConfig(allowed_channels=["c1"])
ch = StubChannel(cfg)
raw = RawIncoming(sender_id="u1", chat_id="c2", text="hello")
assert ch._build_inbound(raw) is None
async def _test():
cfg = _FakeConfig(allowed_channels=["c1"])
ch = StubChannel(cfg)
raw = RawIncoming(sender_id="u1", chat_id="c2", text="hello")
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 0
_run(_test())
def test_drops_empty_content_no_media(self):
ch = StubChannel()
raw = RawIncoming(sender_id="u1", chat_id="c1", text="")
assert ch._build_inbound(raw) is None
assert ch._raw_to_inbound(raw) is None
def test_media_only_message_passes(self):
ch = StubChannel()
@@ -597,7 +603,7 @@ class TestChannelBuildInbound:
sender_id="u1", chat_id="c1", text="",
media_files=["/path/file.pdf"],
)
msg = ch._build_inbound(raw)
msg = ch._raw_to_inbound(raw)
assert msg is not None
assert msg.content == "[media only]"
@@ -607,14 +613,14 @@ class TestChannelBuildInbound:
sender_id="u1", chat_id="c1", text="main text",
content_annotations=["[attachment: photo.jpg]"],
)
msg = ch._build_inbound(raw)
msg = ch._raw_to_inbound(raw)
assert "[attachment: photo.jpg]" in msg.content
def test_metadata_preserves_chat_id(self):
ch = StubChannel()
raw = RawIncoming(sender_id="u1", chat_id="c1", text="hi",
metadata={"extra": "data"})
msg = ch._build_inbound(raw)
msg = ch._raw_to_inbound(raw)
assert msg.metadata["chat_id"] == "c1"
assert msg.metadata["extra"] == "data"
+73 -52
View File
@@ -6,6 +6,7 @@ from dataclasses import dataclass
import pytest
from EvoScientist.channels.base import Channel, RawIncoming
from EvoScientist.channels.capabilities import ChannelCapabilities
# ── Minimal concrete channel for testing base-class logic ─────────────
@@ -15,6 +16,7 @@ from EvoScientist.channels.base import Channel, RawIncoming
class _StubConfig:
allowed_senders: set[str] | None = None
require_mention: str = "group"
dm_policy: str = "allowlist"
class _StubChannel(Channel):
@@ -27,6 +29,11 @@ class _StubChannel(Channel):
pass
class _MentionStubChannel(_StubChannel):
"""Stub with mention gating enabled."""
capabilities = ChannelCapabilities(mentions=True)
def _run(coro):
loop = asyncio.new_event_loop()
try:
@@ -86,69 +93,83 @@ class TestShouldProcess:
assert ch._should_process(raw) is True
class TestBuildInboundGating:
"""Tests that _build_inbound integrates _should_process and _strip_mention."""
class TestPipelineGating:
"""Tests that _enqueue_raw pipeline integrates mention gating and _strip_mention."""
def test_group_not_mentioned_returns_none(self):
config = _StubConfig(require_mention="group")
ch = _StubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=True, was_mentioned=False,
)
assert ch._build_inbound(raw) is None
def test_group_not_mentioned_dropped(self):
async def _test():
config = _StubConfig(require_mention="group")
ch = _MentionStubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=True, was_mentioned=False,
)
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 0
_run(_test())
def test_group_mentioned_returns_message(self):
config = _StubConfig(require_mention="group")
ch = _StubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=True, was_mentioned=True,
)
msg = ch._build_inbound(raw)
assert msg is not None
assert msg.content == "hello"
def test_group_mentioned_passes(self):
async def _test():
config = _StubConfig(require_mention="group")
ch = _MentionStubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=True, was_mentioned=True,
)
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 1
msg = await ch._queue.get()
assert msg.content == "hello"
_run(_test())
def test_dm_returns_message_even_when_not_mentioned(self):
config = _StubConfig(require_mention="group")
ch = _StubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=False, was_mentioned=False,
)
msg = ch._build_inbound(raw)
assert msg is not None
def test_dm_passes_even_when_not_mentioned(self):
async def _test():
config = _StubConfig(require_mention="group")
ch = _MentionStubChannel(config)
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="hello",
is_group=False, was_mentioned=False,
)
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 1
_run(_test())
def test_strip_mention_called_for_group(self):
"""When is_group=True, _strip_mention should be applied to text."""
config = _StubConfig(require_mention="group")
ch = _StubChannel(config)
async def _test():
config = _StubConfig(require_mention="group")
ch = _MentionStubChannel(config)
ch._strip_mention = lambda text: text.replace("@bot ", "").strip()
# Rebuild middlewares so MentionGatingMiddleware picks up new strip_fn
ch._inbound_middlewares = ch._build_inbound_middlewares()
# Override _strip_mention to verify it's called
ch._strip_mention = lambda text: text.replace("@bot ", "").strip()
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="@bot hello",
is_group=True, was_mentioned=True,
)
msg = ch._build_inbound(raw)
assert msg is not None
assert msg.content == "hello"
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="@bot hello",
is_group=True, was_mentioned=True,
)
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 1
msg = await ch._queue.get()
assert msg.content == "hello"
_run(_test())
def test_strip_mention_not_called_for_dm(self):
"""When is_group=False, _strip_mention should NOT be applied."""
config = _StubConfig(require_mention="group")
ch = _StubChannel(config)
async def _test():
config = _StubConfig(require_mention="group")
ch = _MentionStubChannel(config)
ch._strip_mention = lambda text: text.replace("@bot ", "").strip()
ch._inbound_middlewares = ch._build_inbound_middlewares()
ch._strip_mention = lambda text: text.replace("@bot ", "").strip()
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="@bot hello",
is_group=False, was_mentioned=True,
)
msg = ch._build_inbound(raw)
assert msg is not None
assert msg.content == "@bot hello"
raw = RawIncoming(
sender_id="u1", chat_id="c1", text="@bot hello",
is_group=False, was_mentioned=True,
)
await ch._enqueue_raw(raw)
assert ch._queue.qsize() == 1
msg = await ch._queue.get()
assert msg.content == "@bot hello"
_run(_test())
# ── Per-channel _strip_mention tests ─────────────────────────────────