99520c69d7
1.新增后端测试 2.增加了后端的加密 3.增加了i18n(国际化)
169 lines
5.5 KiB
Python
169 lines
5.5 KiB
Python
"""``api/platform/onebot.py`` 中纯函数与事件分派的覆盖。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from kilostar.api.platform import onebot as onebot_mod
|
|
from kilostar.core.individual.regulatory_node.template import MessageResponse
|
|
|
|
|
|
# ─── _verify_token ──────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_verify_token_skipped_when_env_missing(monkeypatch):
|
|
monkeypatch.delenv("ONEBOT_ACCESS_TOKEN", raising=False)
|
|
onebot_mod._verify_token(None)
|
|
onebot_mod._verify_token("anything")
|
|
|
|
|
|
def test_verify_token_accepts_bearer_prefix(monkeypatch):
|
|
monkeypatch.setenv("ONEBOT_ACCESS_TOKEN", "expected")
|
|
onebot_mod._verify_token("Bearer expected")
|
|
|
|
|
|
def test_verify_token_accepts_token_prefix(monkeypatch):
|
|
monkeypatch.setenv("ONEBOT_ACCESS_TOKEN", "expected")
|
|
onebot_mod._verify_token("Token expected")
|
|
|
|
|
|
def test_verify_token_accepts_raw_token(monkeypatch):
|
|
monkeypatch.setenv("ONEBOT_ACCESS_TOKEN", "expected")
|
|
onebot_mod._verify_token("expected")
|
|
|
|
|
|
def test_verify_token_rejects_missing(monkeypatch):
|
|
monkeypatch.setenv("ONEBOT_ACCESS_TOKEN", "expected")
|
|
with pytest.raises(HTTPException) as exc:
|
|
onebot_mod._verify_token(None)
|
|
assert exc.value.status_code == 401
|
|
|
|
|
|
def test_verify_token_rejects_wrong_token(monkeypatch):
|
|
monkeypatch.setenv("ONEBOT_ACCESS_TOKEN", "expected")
|
|
with pytest.raises(HTTPException) as exc:
|
|
onebot_mod._verify_token("Bearer wrong")
|
|
assert exc.value.status_code == 401
|
|
|
|
|
|
# ─── _extract_plain_text ────────────────────────────────────────────────────
|
|
|
|
|
|
def test_extract_plain_text_handles_string():
|
|
assert onebot_mod._extract_plain_text("hello") == "hello"
|
|
|
|
|
|
def test_extract_plain_text_handles_segment_array():
|
|
seg = [
|
|
{"type": "text", "data": {"text": "hello "}},
|
|
{"type": "image", "data": {"file": "1.png"}},
|
|
{"type": "text", "data": {"text": "world"}},
|
|
]
|
|
assert onebot_mod._extract_plain_text(seg) == "hello world"
|
|
|
|
|
|
def test_extract_plain_text_handles_unknown_type():
|
|
assert onebot_mod._extract_plain_text(None) == ""
|
|
assert onebot_mod._extract_plain_text({"foo": "bar"}) == ""
|
|
|
|
|
|
# ─── _dispatch_event ────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def regulatory_actor(fake_actors):
|
|
"""注入一个 regulatory_node Mock,默认返回固定的 MessageResponse。"""
|
|
inner = MagicMock()
|
|
inner.working.remote = AsyncMock(
|
|
return_value=MessageResponse(
|
|
platform="onebot", platform_id="private:1234", reply_message="pong"
|
|
)
|
|
)
|
|
fake_actors.register("regulatory_node", inner)
|
|
return inner
|
|
|
|
|
|
async def test_dispatch_event_ignores_non_message(regulatory_actor):
|
|
res = await onebot_mod._dispatch_event(
|
|
{"post_type": "meta_event", "meta_event_type": "heartbeat"}
|
|
)
|
|
assert res is None
|
|
regulatory_actor.working.remote.assert_not_called()
|
|
|
|
|
|
async def test_dispatch_event_ignores_empty_text(regulatory_actor):
|
|
res = await onebot_mod._dispatch_event(
|
|
{
|
|
"post_type": "message",
|
|
"message_type": "private",
|
|
"user_id": 1234,
|
|
"message": " ",
|
|
}
|
|
)
|
|
assert res is None
|
|
regulatory_actor.working.remote.assert_not_called()
|
|
|
|
|
|
async def test_dispatch_event_private_message_returns_quick_reply(regulatory_actor):
|
|
payload = {
|
|
"post_type": "message",
|
|
"message_type": "private",
|
|
"user_id": 1234,
|
|
"sender": {"nickname": "alice"},
|
|
"message": "ping",
|
|
}
|
|
res = await onebot_mod._dispatch_event(payload)
|
|
assert res is not None
|
|
assert res["reply"] == "pong"
|
|
assert "at_sender" not in res
|
|
assert res["_target"]["message_type"] == "private"
|
|
assert res["_target"]["user_id"] == 1234
|
|
|
|
|
|
async def test_dispatch_event_group_message_includes_at_sender(regulatory_actor):
|
|
payload = {
|
|
"post_type": "message",
|
|
"message_type": "group",
|
|
"user_id": 1234,
|
|
"group_id": 5678,
|
|
"sender": {"card": "alice", "nickname": "fallback"},
|
|
"message": [{"type": "text", "data": {"text": "ping"}}],
|
|
}
|
|
res = await onebot_mod._dispatch_event(payload)
|
|
assert res["reply"] == "pong"
|
|
assert res["at_sender"] is False
|
|
assert res["_target"]["group_id"] == 5678
|
|
|
|
|
|
async def test_dispatch_event_swallows_actor_error(regulatory_actor):
|
|
regulatory_actor.working.remote = AsyncMock(side_effect=RuntimeError("ray fail"))
|
|
payload = {
|
|
"post_type": "message",
|
|
"message_type": "private",
|
|
"user_id": 1,
|
|
"message": "hi",
|
|
}
|
|
res = await onebot_mod._dispatch_event(payload)
|
|
assert res is None
|
|
|
|
|
|
async def test_dispatch_event_returns_none_when_reply_empty(fake_actors):
|
|
inner = MagicMock()
|
|
inner.working.remote = AsyncMock(
|
|
return_value=MessageResponse(
|
|
platform="onebot", platform_id="private:1", reply_message=""
|
|
)
|
|
)
|
|
fake_actors.register("regulatory_node", inner)
|
|
payload = {
|
|
"post_type": "message",
|
|
"message_type": "private",
|
|
"user_id": 1,
|
|
"message": "hi",
|
|
}
|
|
res = await onebot_mod._dispatch_event(payload)
|
|
assert res is None
|