- 新增 app/api、app/service 数据分析 Agent 全套服务与接口 - schemas.py 重构为 schemas 包(analyst schema) - 新增 SQL 防注入、guardrail、缓存、字典、LLM 等服务 - 新增 tests 测试套件与 scripts/dev、scripts/setup 脚本 - 补充需求规格、架构说明书、开发清单、表设计等文档
74 lines
3.1 KiB
Python
74 lines
3.1 KiB
Python
"""集成测试:真实 MySQL + 真实 DeepSeek,覆盖 §7 验收场景核心路径。"""
|
|
import unittest
|
|
|
|
from app.service.analyst_agent import AnalystAgent
|
|
from app.service.analytics_repo import AnalyticsRepo
|
|
from app.service.llm import DeepSeekLLM
|
|
from app.utils.auth import AuthContext
|
|
|
|
|
|
class TestIntegration(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.repo = AnalyticsRepo()
|
|
cls.agent = AnalystAgent(llm=DeepSeekLLM(), repo=cls.repo)
|
|
r = cls.repo.execute_readonly(
|
|
"SELECT staff_id FROM core_staff WHERE staff_type='advisor' AND is_active=1 LIMIT 1"
|
|
)
|
|
cls.advisor_id = r["rows"][0][0]
|
|
cls.scope = cls.repo.resolve_advisor_scope(cls.advisor_id)
|
|
all_cust = [
|
|
row[0]
|
|
for row in cls.repo.execute_readonly(
|
|
"SELECT customer_id FROM core_customer WHERE is_active=1 LIMIT 100"
|
|
)["rows"]
|
|
]
|
|
cls.out_customer = next(c for c in all_cust if c not in cls.scope)
|
|
|
|
def _ctx(self, roles, subject):
|
|
return AuthContext(subject_id=subject, token_type="staff", roles=roles, staff_type=roles[0])
|
|
|
|
def test_analyst_holding_by_product(self):
|
|
resp = self.agent.run("按产品类型统计总持仓规模", self._ctx(["analyst"], "STAFF-20001"))
|
|
self.assertIn(resp.status, ("success", "degrade"))
|
|
self.assertTrue(resp.sql)
|
|
|
|
def test_analyst_holding_pnl_sort(self):
|
|
resp = self.agent.run("把客户持仓按盈亏排序", self._ctx(["analyst"], "STAFF-20001"))
|
|
self.assertIn(resp.status, ("success", "degrade"))
|
|
self.assertTrue(resp.sql)
|
|
|
|
def test_advisor_own_scope(self):
|
|
resp = self.agent.run("我名下客户有多少高风险", self._ctx(["advisor"], self.advisor_id))
|
|
self.assertIn(resp.status, ("success", "degrade"))
|
|
|
|
def test_advisor_out_of_scope_denied(self):
|
|
resp = self.agent.run(f"查 {self.out_customer} 的持仓", self._ctx(["advisor"], self.advisor_id))
|
|
self.assertEqual(resp.status, "deny")
|
|
self.assertEqual(resp.error_code, "AUTH_403_NOT_ASSIGNED")
|
|
|
|
def test_risk_officer_pending_alerts(self):
|
|
resp = self.agent.run("当前待处理预警有多少", self._ctx(["risk_officer"], "STAFF-30001"))
|
|
self.assertIn(resp.status, ("success", "degrade"))
|
|
|
|
def test_ops_aggregate(self):
|
|
resp = self.agent.run("近30天申购金额总额", self._ctx(["ops"], "STAFF-50001"))
|
|
self.assertIn(resp.status, ("success", "degrade"))
|
|
|
|
def test_cache_hit(self):
|
|
q = "客户总数是多少"
|
|
r1 = self.agent.run(q, self._ctx(["analyst"], "STAFF-20001"))
|
|
r2 = self.agent.run(q, self._ctx(["analyst"], "STAFF-20001"))
|
|
self.assertTrue(r2.meta.cache_hit)
|
|
|
|
def test_trace_audit_recorded(self):
|
|
resp = self.agent.run("客户总数是多少", self._ctx(["analyst"], "STAFF-20001"))
|
|
rows = self.repo.execute_readonly(
|
|
f"SELECT trace_id FROM jinrong_agent.analytics_query_log WHERE trace_id='{resp.trace_id}'"
|
|
)["rows"]
|
|
self.assertGreaterEqual(len(rows), 1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|