feat(system):优化后端
1.新增后端测试 2.增加了后端的加密 3.增加了i18n(国际化)
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
"""``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
|
||||
Reference in New Issue
Block a user