185 lines
6.6 KiB
Python
185 lines
6.6 KiB
Python
import pytest
|
|
|
|
from app.services.integrations.feishu_integration_service.mixin_receive_prepare import (
|
|
FeishuReceivePrepareMixin,
|
|
)
|
|
from app.services.integrations.feishu_integration_service.service import FeishuIntegrationService
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_message_receive_ignores_bot_sender() -> None:
|
|
service = FeishuIntegrationService(db=object())
|
|
result = await service._handle_message_receive(
|
|
{
|
|
"message": {"message_id": "m-bot"},
|
|
"sender": {"sender_type": "bot"},
|
|
"chat": {"chat_id": "c1"},
|
|
}
|
|
)
|
|
assert result["code"] == 0
|
|
assert result["msg"] == "Ignore bot/app message"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_message_receive_routes_empty_text_branch(monkeypatch) -> None:
|
|
service = FeishuIntegrationService(db=object())
|
|
called: dict[str, object] = {}
|
|
|
|
def _fake_build_receive_context(*, event, state_trace_id): # noqa: ARG001
|
|
return {
|
|
"ingress_source": "feishu",
|
|
"message": {"message_id": "m-empty", "message_type": "post"},
|
|
"sender": {"sender_name": "测试用户"},
|
|
"message_id": "m-empty",
|
|
"sender_type": "user",
|
|
"raw_message_content": "",
|
|
"message_content": "",
|
|
"chat_id": "chat-empty",
|
|
"mentioned_bot_names": ["PMFlow"],
|
|
"sender_open_id": "ou-empty",
|
|
"perf_t0": None,
|
|
}
|
|
|
|
async def _fake_record_ingress_trace(**kwargs):
|
|
called["trace"] = kwargs
|
|
|
|
async def _fake_reply_for_empty_text_ingress(**kwargs):
|
|
called["empty_reply"] = kwargs
|
|
return True
|
|
|
|
monkeypatch.setattr(service, "_build_receive_context", _fake_build_receive_context)
|
|
monkeypatch.setattr(service, "_record_ingress_trace", _fake_record_ingress_trace)
|
|
monkeypatch.setattr(service, "_reply_for_empty_text_ingress", _fake_reply_for_empty_text_ingress)
|
|
|
|
result = await service._handle_message_receive({"type": "im.message.receive_v1"})
|
|
assert result["code"] == 0
|
|
assert result["msg"] == "No text content"
|
|
assert called["trace"]["message_id"] == "m-empty"
|
|
assert called["empty_reply"]["chat_id"] == "chat-empty"
|
|
|
|
|
|
def test_classify_message_scene_does_not_infer_execute_from_raw_text_only() -> None:
|
|
scene = FeishuIntegrationService._classify_message_scene(
|
|
capture_result={
|
|
"status": "answered",
|
|
"classification": "question",
|
|
"intent": "question",
|
|
},
|
|
message_content="帮我创建项目和信号源",
|
|
)
|
|
|
|
assert scene == "qa"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_handle_message_receive_orchestrates_capture_and_finalize(monkeypatch) -> None:
|
|
service = FeishuIntegrationService(db=object())
|
|
called: dict[str, object] = {}
|
|
|
|
def _fake_build_receive_context(*, event, state_trace_id): # noqa: ARG001
|
|
return {
|
|
"ingress_source": "long_connection",
|
|
"message": {"message_id": "m-1", "message_type": "text"},
|
|
"sender": {"sender_name": "原始用户名"},
|
|
"sender_id": {"open_id": "ou-1"},
|
|
"message_id": "m-1",
|
|
"sender_type": "user",
|
|
"raw_message_content": "hello",
|
|
"message_content": "hello",
|
|
"chat_id": "chat-1",
|
|
"chat_type": "group",
|
|
"captured_at": None,
|
|
"mentioned_bot_names": [],
|
|
"sender_open_id": "ou-1",
|
|
"perf_t0": 1.2,
|
|
"message_attachments": [],
|
|
"occurred_at_iso": None,
|
|
}
|
|
|
|
async def _fake_record_ingress_trace(**kwargs):
|
|
called["trace"] = kwargs
|
|
|
|
async def _fake_resolve_sender_display_name(*, sender, sender_id): # noqa: ARG001
|
|
return "解析后用户名"
|
|
|
|
def _fake_normalize_message_attachments_from_context(*, context): # noqa: ARG001
|
|
return []
|
|
|
|
async def _fake_capture_and_persist_from_ingress(**kwargs):
|
|
called["capture"] = kwargs
|
|
return {
|
|
"status": "captured",
|
|
"classification": "signal",
|
|
"linked_project_ids": [11],
|
|
"auto_tasks": [],
|
|
"ai_trace_id": "trace-1",
|
|
}
|
|
|
|
async def _fake_finalize_receive_result(**kwargs):
|
|
called["finalize"] = kwargs
|
|
return {"code": 0, "msg": "success", "data": {"scene": "execute"}}
|
|
|
|
monkeypatch.setattr(service, "_build_receive_context", _fake_build_receive_context)
|
|
monkeypatch.setattr(service, "_record_ingress_trace", _fake_record_ingress_trace)
|
|
monkeypatch.setattr(service, "_resolve_sender_display_name", _fake_resolve_sender_display_name)
|
|
monkeypatch.setattr(service, "_normalize_message_attachments_from_context", _fake_normalize_message_attachments_from_context)
|
|
monkeypatch.setattr(service, "_capture_and_persist_from_ingress", _fake_capture_and_persist_from_ingress)
|
|
monkeypatch.setattr(service, "_finalize_receive_result", _fake_finalize_receive_result)
|
|
|
|
result = await service._handle_message_receive({"type": "im.message.receive_v1"})
|
|
assert result["code"] == 0
|
|
assert result["data"]["scene"] == "execute"
|
|
assert called["capture"]["chat_id"] == "chat-1"
|
|
assert called["capture"]["sender_name"] == "解析后用户名"
|
|
assert called["finalize"]["chat_type"] == "group"
|
|
|
|
|
|
def test_extract_thread_context_reads_top_level_reply_fields() -> None:
|
|
event = {
|
|
"thread_id": "th-1",
|
|
"root_id": "root-1",
|
|
"parent_id": "reply-1",
|
|
"message": {
|
|
"message_id": "msg-1",
|
|
"content": "{\"text\":\"hello\"}",
|
|
},
|
|
}
|
|
context = FeishuReceivePrepareMixin._extract_thread_context(
|
|
event=event,
|
|
message=event["message"],
|
|
message_payload={"text": "hello", "attachments": [], "message_type": "text"},
|
|
)
|
|
|
|
assert context == {
|
|
"thread_id": "th-1",
|
|
"root_message_id": "root-1",
|
|
"reply_to_message_id": "reply-1",
|
|
}
|
|
|
|
|
|
def test_extract_thread_context_reads_nested_reply_fields() -> None:
|
|
event = {
|
|
"message": {
|
|
"message_id": "msg-1",
|
|
"meta": {
|
|
"conversation": {
|
|
"thread_id": "th-nested",
|
|
"root_message_id": "root-nested",
|
|
"reply_to_message_id": "reply-nested",
|
|
}
|
|
},
|
|
"content": "{\"text\":\"hello\"}",
|
|
}
|
|
}
|
|
context = FeishuReceivePrepareMixin._extract_thread_context(
|
|
event=event,
|
|
message=event["message"],
|
|
message_payload={"text": "hello", "attachments": [], "message_type": "text"},
|
|
)
|
|
|
|
assert context == {
|
|
"thread_id": "th-nested",
|
|
"root_message_id": "root-nested",
|
|
"reply_to_message_id": "reply-nested",
|
|
}
|