test(backend): update orchestrator service tests

- test_orchestrator_service.py: update orchestrator service tests
This commit is contained in:
caoxiaozhu
2026-05-13 13:08:22 +00:00
parent 70cff69b7f
commit a6526c5159

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
from collections.abc import Generator from collections.abc import Generator
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime, timedelta
from decimal import Decimal
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from sqlalchemy import create_engine, func, select from sqlalchemy import create_engine, func, select
@@ -70,6 +71,106 @@ def test_orchestrator_routes_user_query_to_user_agent() -> None:
assert run_detail["tool_calls"][0]["tool_type"] == "database" assert run_detail["tool_calls"][0]["tool_type"] == "database"
def test_orchestrator_scopes_my_expense_query_to_current_user() -> None:
client, session_factory = build_client()
user_id = "zhaoliu@example.com"
with session_factory() as db:
employee = Employee(
employee_no="E9001",
name="赵六",
email=user_id,
)
db.add(employee)
db.flush()
db.add_all(
[
ExpenseClaim(
claim_no="EXP-TEST-001",
employee_id=employee.id,
employee_name="赵六",
department_name="测试部",
project_code="PRJ-TEST-01",
expense_type="travel",
reason="上海客户拜访",
location="上海",
amount=Decimal("120.00"),
currency="CNY",
invoice_count=1,
occurred_at=datetime(2026, 5, 10, 9, 0, tzinfo=UTC),
submitted_at=datetime(2026, 5, 10, 18, 0, tzinfo=UTC),
status="submitted",
approval_stage="finance_review",
risk_flags_json=[],
),
ExpenseClaim(
claim_no="EXP-TEST-002",
employee_name=user_id,
department_name="测试部",
project_code="PRJ-TEST-02",
expense_type="meal",
reason="客户午餐",
location="杭州",
amount=Decimal("300.00"),
currency="CNY",
invoice_count=1,
occurred_at=datetime(2026, 5, 11, 12, 0, tzinfo=UTC),
submitted_at=datetime(2026, 5, 11, 13, 0, tzinfo=UTC),
status="draft",
approval_stage=None,
risk_flags_json=[],
),
ExpenseClaim(
claim_no="EXP-TEST-003",
employee_name="张三",
department_name="财务部",
project_code="PRJ-OTHER-01",
expense_type="hotel",
reason="外地出差住宿",
location="深圳",
amount=Decimal("999.00"),
currency="CNY",
invoice_count=1,
occurred_at=datetime(2026, 5, 12, 8, 30, tzinfo=UTC),
submitted_at=datetime(2026, 5, 12, 9, 30, tzinfo=UTC),
status="approved",
approval_stage="completed",
risk_flags_json=[],
),
]
)
db.commit()
response = client.post(
"/api/v1/orchestrator/run",
json={
"source": "user_message",
"user_id": user_id,
"message": "请查询我的报销单",
"context_json": {
"role_codes": ["employee"],
"name": "赵六",
},
},
)
assert response.status_code == 200
payload = response.json()
assert payload["selected_agent"] == "user_agent"
assert payload["status"] == "succeeded"
assert "查到你的报销单共 2 笔" in payload["result"]["answer"]
assert "EXP-TEST-001" in payload["result"]["answer"]
assert "EXP-TEST-002" in payload["result"]["answer"]
assert "EXP-TEST-003" not in payload["result"]["answer"]
run_detail = client.get(f"/api/v1/agent-runs/{payload['run_id']}").json()
tool_response = run_detail["tool_calls"][0]["response_json"]
assert tool_response["record_count"] == 2
assert tool_response["total_amount"] == 420.0
assert tool_response["scoped_to_current_user"] is True
assert tool_response["scope_label"] == "你的报销单"
def test_orchestrator_routes_schedule_to_hermes() -> None: def test_orchestrator_routes_schedule_to_hermes() -> None:
client, session_factory = build_client() client, session_factory = build_client()
@@ -640,6 +741,109 @@ def test_orchestrator_can_delete_all_user_conversations() -> None:
assert other_count == 1 assert other_count == 1
def test_orchestrator_can_delete_current_user_single_conversation() -> None:
client, session_factory = build_client()
first_response = client.post(
"/api/v1/orchestrator/run",
json={
"source": "user_message",
"user_id": "single_delete_user",
"message": "查一下本周报销金额",
"context_json": {"role_codes": ["finance"]},
},
)
second_response = client.post(
"/api/v1/orchestrator/run",
json={
"source": "user_message",
"user_id": "single_delete_user",
"message": "帮我生成差旅报销草稿",
"context_json": {"role_codes": ["finance"]},
},
)
assert first_response.status_code == 200
assert second_response.status_code == 200
first_conversation_id = first_response.json()["conversation_id"]
second_conversation_id = second_response.json()["conversation_id"]
delete_response = client.delete(
f"/api/v1/orchestrator/conversations/{first_conversation_id}",
params={"user_id": "single_delete_user"},
)
assert delete_response.status_code == 200
assert delete_response.json()["deleted_count"] == 1
with session_factory() as db:
remaining_ids = list(
db.scalars(
select(AgentConversation.conversation_id)
.where(AgentConversation.user_id == "single_delete_user")
.order_by(AgentConversation.created_at.asc())
).all()
)
assert first_conversation_id not in remaining_ids
assert second_conversation_id in remaining_ids
def test_orchestrator_can_delete_user_conversations_by_session_type() -> None:
client, session_factory = build_client()
expense_response = client.post(
"/api/v1/orchestrator/run",
json={
"source": "user_message",
"user_id": "typed_delete_user",
"message": "帮我生成差旅报销草稿",
"context_json": {
"role_codes": ["finance"],
"session_type": "expense",
},
},
)
knowledge_response = client.post(
"/api/v1/orchestrator/run",
json={
"source": "user_message",
"user_id": "typed_delete_user",
"message": "发票抬头不一致还能报销吗",
"context_json": {
"role_codes": ["finance"],
"session_type": "knowledge",
},
},
)
assert expense_response.status_code == 200
assert knowledge_response.status_code == 200
delete_response = client.delete(
"/api/v1/orchestrator/conversations",
params={
"user_id": "typed_delete_user",
"session_type": "knowledge",
},
)
assert delete_response.status_code == 200
assert delete_response.json()["deleted_count"] == 1
with session_factory() as db:
remaining = list(
db.scalars(
select(AgentConversation)
.where(AgentConversation.user_id == "typed_delete_user")
.order_by(AgentConversation.created_at.asc())
).all()
)
assert len(remaining) == 1
remaining_session_type = str((remaining[0].state_json or {}).get("session_type") or "").strip() or "expense"
assert remaining_session_type == "expense"
def test_orchestrator_tool_failure_is_logged_and_degraded() -> None: def test_orchestrator_tool_failure_is_logged_and_degraded() -> None:
client, _ = build_client() client, _ = build_client()