"""``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) monkeypatch.setenv("KILOSTAR_ENV", "dev") 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