213 lines
6.9 KiB
Python
213 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from app.services.ai_pmo.ai_pmo_intent import CanonicalIntent
|
|
from app.services.ai_pmo.ai_pmo_typed_executor_discovery.mixin_discover import (
|
|
DiscoveryFlowMixin,
|
|
)
|
|
from app.services.ai_pmo.ai_pmo_typed_executor_execute.mixin_execute_signal_source import (
|
|
ExecuteSignalSourceMixin,
|
|
)
|
|
from app.services.signal.signal_source_orchestration import SignalSourceOrchestrationService
|
|
|
|
|
|
def test_signal_source_onboard_idempotency_key_prefers_message_key() -> None:
|
|
key_one = ExecuteSignalSourceMixin._build_signal_source_onboard_idempotency_key(
|
|
source_system="feishu",
|
|
source_message_id="msg-123",
|
|
)
|
|
key_two = ExecuteSignalSourceMixin._build_signal_source_onboard_idempotency_key(
|
|
source_system="feishu",
|
|
source_message_id="msg-123",
|
|
)
|
|
bare_key = ExecuteSignalSourceMixin._build_signal_source_onboard_idempotency_key(
|
|
source_system="",
|
|
source_message_id="msg-123",
|
|
)
|
|
|
|
assert key_one == "feishu:msg-123"
|
|
assert key_two == "feishu:msg-123"
|
|
assert bare_key == "msg-123"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_signal_source_onboard_replays_existing_ledger_without_reexecution() -> None:
|
|
class _FakeLedger:
|
|
def __init__(self) -> None:
|
|
self.status = "applied"
|
|
self.result_payload = {
|
|
"status": "succeeded",
|
|
"signal_id": 88,
|
|
"project_id": 11,
|
|
"signal_source_workflow": {
|
|
"status": "completed",
|
|
"run_status": "completed",
|
|
},
|
|
"signal_source_orchestration": {
|
|
"signal_source": {
|
|
"registry_id": 501,
|
|
"source_name": "Webhook A",
|
|
}
|
|
},
|
|
"auto_tasks": [],
|
|
}
|
|
|
|
class _FakeWorkflow:
|
|
def __init__(self) -> None:
|
|
self.called = False
|
|
|
|
async def run(self, **kwargs):
|
|
self.called = True
|
|
raise AssertionError("signal_source_workflow.run should not be called on replay")
|
|
|
|
class _FakeDB:
|
|
def __init__(self) -> None:
|
|
self.committed = False
|
|
|
|
async def commit(self):
|
|
self.committed = True
|
|
|
|
class _FakeExecutor(ExecuteSignalSourceMixin):
|
|
def __init__(self) -> None:
|
|
self.db = _FakeDB()
|
|
self.tenant_id = 1
|
|
self.signal_source_workflow = _FakeWorkflow()
|
|
self.loaded_key = None
|
|
|
|
async def _load_signal_source_onboard_ledger(self, *, idempotency_key):
|
|
self.loaded_key = idempotency_key
|
|
return _FakeLedger()
|
|
|
|
async def _claim_signal_source_onboard_ledger(self, **kwargs):
|
|
raise AssertionError("ledger claim should not be reached when replaying an applied result")
|
|
|
|
def _build_signal_source_semantic_payload(self, **kwargs):
|
|
raise AssertionError("semantic payload should not be rebuilt on replay")
|
|
|
|
async def _create_signal(self, **kwargs):
|
|
raise AssertionError("signal creation should not be reached on replay")
|
|
|
|
@staticmethod
|
|
def _normalize_title_candidate(value):
|
|
return str(value or "").strip()
|
|
|
|
@staticmethod
|
|
def _is_acceptable_title(text):
|
|
return bool(text)
|
|
|
|
@staticmethod
|
|
def _extract_auto_tasks(control_plane_payload):
|
|
return []
|
|
|
|
kernel = _FakeExecutor()
|
|
result = await kernel._execute_signal_source_onboard_intent(
|
|
intent=type("Intent", (), {"project_id": 11, "signal_summary": "摘要", "signal_title": "标题"})(),
|
|
message_text="请接入这个信号源",
|
|
operator_user_id=9,
|
|
source_system="feishu",
|
|
source_message_id="msg-abc",
|
|
channel_ref="oc-1",
|
|
)
|
|
|
|
assert result["status"] == "succeeded"
|
|
assert result["signal_id"] == 88
|
|
assert kernel.loaded_key == "feishu:msg-abc"
|
|
assert kernel.db.committed is False
|
|
assert kernel.signal_source_workflow.called is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_project_signal_discovery_uses_project_id_without_name_error() -> None:
|
|
class _FakeDiscovery(DiscoveryFlowMixin):
|
|
tenant_id = 1
|
|
|
|
@staticmethod
|
|
def _extract_requested_signal_count(*, message_text, metadata):
|
|
_ = (message_text, metadata)
|
|
return 1
|
|
|
|
async def _analyze_signal_batch_with_high_tier_model(self, **kwargs):
|
|
_ = kwargs
|
|
return {
|
|
"items": [
|
|
{
|
|
"title": "候选信号",
|
|
"summary": "摘要",
|
|
"content": "内容",
|
|
"signal_type": "新需求",
|
|
"priority": "medium",
|
|
}
|
|
]
|
|
}
|
|
|
|
async def _collect_project_signal_discovery_context(self, **kwargs):
|
|
_ = kwargs
|
|
return {
|
|
"retrieval_items": [{"content": "evidence"}],
|
|
"retrieval_metrics": {},
|
|
"entity_matches": [],
|
|
"tool_trace": [],
|
|
"source_registries": [],
|
|
"truth_nodes": [],
|
|
"experiments": [],
|
|
"recent_signals": [],
|
|
"capability_bundle": {},
|
|
}
|
|
|
|
async def _refine_signal_title_with_ai(self, **kwargs):
|
|
_ = kwargs
|
|
return "信号A"
|
|
|
|
@staticmethod
|
|
def _build_candidate_dedupe_key(*, project_id, candidate):
|
|
return f"{project_id}:{candidate.get('title')}"
|
|
|
|
@staticmethod
|
|
def _build_signal_title_refine_error_message(exc):
|
|
return str(exc)
|
|
|
|
kernel = _FakeDiscovery()
|
|
result = await kernel.discover_project_signal_candidates(
|
|
intent=CanonicalIntent(
|
|
name="project_signal.discover",
|
|
confidence=0.9,
|
|
project_id=2,
|
|
signal_title=None,
|
|
signal_summary=None,
|
|
metadata={},
|
|
),
|
|
message_text="给项目 2 先出一条候选信号",
|
|
source_system="feishu",
|
|
source_message_id="msg-1",
|
|
channel_ref="oc-chat-1",
|
|
)
|
|
|
|
assert result["status"] == "pending_confirmation"
|
|
assert result["project_id"] == 2
|
|
assert result["candidate_signals"][0]["title"] == "信号A"
|
|
|
|
|
|
def test_signal_source_kind_uses_explicit_repo_locator_without_hint_table() -> None:
|
|
assert (
|
|
SignalSourceOrchestrationService._normalize_source_kind(
|
|
None,
|
|
text="请把这个来源接进来",
|
|
requested_via="",
|
|
source_payload={"repo": "acme/reco-engine"},
|
|
)
|
|
== "github"
|
|
)
|
|
|
|
|
|
def test_signal_source_kind_uses_channel_config_without_hint_table() -> None:
|
|
assert (
|
|
SignalSourceOrchestrationService._normalize_source_kind(
|
|
None,
|
|
text="先挂到当前群",
|
|
requested_via="",
|
|
source_payload={"config": {"channel_id": "oc_abc123"}},
|
|
)
|
|
== "feishu"
|
|
)
|