- hexi core: message handling, rate limiting, cooldown, plugin manager - Custom plugins: BF stats, daily check-in, quotes, persona cards, etc. - Community plugins vendored under hexi/plugins with local fixes - Web admin frontends (learning-chat, persona-admin), unified hexi/web - Tests for rate_limit/cooldown/memes/persona; poetry.lock Co-Authored-By: Claude <noreply@anthropic.com>
105 lines
3.1 KiB
Python
105 lines
3.1 KiB
Python
"""表情裸词操作补丁(hexi/plugins/memes_ops)的回归测试。
|
|
|
|
验证 `对称 右` 这类“表情 + 裸词操作”能被新 matcher 正确解析为选项,
|
|
同时 `-右`/`--right` 旧形式仍然可用。
|
|
|
|
若 nonebot_plugin_memes 缺失或补丁因版本不兼容而失效,本模块整体跳过。
|
|
"""
|
|
|
|
import nonebot
|
|
import pytest
|
|
|
|
try:
|
|
nonebot.init()
|
|
from nonebot import get_loaded_plugins, load_plugin
|
|
|
|
load_plugin("hexi.plugins.memes_ops")
|
|
_memes_loaded = True
|
|
except Exception:
|
|
_memes_loaded = False
|
|
|
|
if not _memes_loaded:
|
|
pytest.skip(
|
|
"无法加载 nonebot_plugin_memes / memes_ops,跳过裸词操作补丁测试",
|
|
allow_module_level=True,
|
|
)
|
|
|
|
_patchers = [
|
|
matcher
|
|
for plugin in get_loaded_plugins()
|
|
for matcher in plugin.matcher
|
|
if matcher.priority == 11 and matcher.module_name == "hexi.plugins.memes_ops"
|
|
]
|
|
|
|
if not _patchers:
|
|
pytest.skip(
|
|
"memes_ops 补丁 matcher 未注册(版本不兼容?),跳过裸词操作补丁测试",
|
|
allow_module_level=True,
|
|
)
|
|
|
|
_cmd_start = list(nonebot.get_driver().config.command_start)[0]
|
|
|
|
|
|
def _parse(msg: str):
|
|
"""在所有新 matcher 中解析消息,返回 (选项, 文字参数) 命中集合。"""
|
|
hits = []
|
|
for matcher in _patchers:
|
|
try:
|
|
result = matcher.command().parse(msg)
|
|
except Exception:
|
|
continue
|
|
if result.matched:
|
|
options = {
|
|
key: value.value
|
|
for key, value in result.options.items()
|
|
if value.value not in (None, False)
|
|
}
|
|
params = [
|
|
seg.text for seg in result.query("meme_params", ()) if seg.text
|
|
]
|
|
hits.append((options, params))
|
|
return hits
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("msg", "expected_options"),
|
|
[
|
|
(f"{_cmd_start}对称 右", {"right"}),
|
|
(f"{_cmd_start}对称 左", {"left"}),
|
|
(f"{_cmd_start}对称 上", {"top"}),
|
|
(f"{_cmd_start}对称 下", {"bottom"}),
|
|
(f"{_cmd_start}鬼畜 右", {"right"}),
|
|
(f"{_cmd_start}循环 下", {"bottom"}),
|
|
(f"{_cmd_start}摸 圆", {"circle"}),
|
|
(f"{_cmd_start}小丑 爷", {"person"}),
|
|
(f"{_cmd_start}小丑面具 前", {"front"}),
|
|
(f"{_cmd_start}ba说 左", {"left"}),
|
|
],
|
|
)
|
|
def test_bare_operation_maps_to_option(msg, expected_options):
|
|
hits = _parse(msg)
|
|
assert hits, f"{msg!r} 未被任何补丁 matcher 命中"
|
|
# 新 matcher 优先(priority 11),取第一个命中
|
|
options, params = hits[0]
|
|
assert set(options) == expected_options
|
|
assert params == []
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"msg",
|
|
[f"{_cmd_start}对称 -右", f"{_cmd_start}对称 --right", f"{_cmd_start}鬼畜 --bottom"],
|
|
)
|
|
def test_dash_syntax_still_works(msg):
|
|
options, params = _parse(msg)[0]
|
|
assert options, f"{msg!r} 应解析出选项"
|
|
assert params == []
|
|
|
|
|
|
def test_non_alias_word_stays_text():
|
|
msg = f"{_cmd_start}对称 任意文本"
|
|
hits = _parse(msg)
|
|
assert hits
|
|
options, params = hits[0]
|
|
assert options == {}
|
|
assert params == ["任意文本"]
|