Files
wecom_it_smart_desk/backend/tests/test_automation_approval.py
T
Simon 5e53146a9a test(backend): unit/integration tests for automation, otp, neo4j, contract
新增自动化审批状态机/执行器/意图路由/会话管理、OTP 绑定流程、neo4j 客户端、响应契约、置信度门禁、环境门控、Tier1 API 等测试。
2026-07-09 11:49:50 +08:00

209 lines
6.4 KiB
Python

# =============================================================================
# 阶段5 自动化 - 审批单服务单元测试
# =============================================================================
# 测试范围:approval.py - 审批单创建/流转/审计
# =============================================================================
import pytest
from unittest.mock import AsyncMock, MagicMock, patch
from datetime import datetime, timezone
from app.services.automation.approval import ApprovalService
from app.models.automation import ApprovalTicket
class TestApprovalService:
"""审批单服务测试用例"""
@pytest.mark.asyncio
async def test_ensure_ticket_creates_new_ticket(self, db_session):
"""测试 ensure_ticket 创建新审批单"""
# Arrange
approval_svc = ApprovalService(db_session)
# 创建 mock action
action = MagicMock()
action.id = "action_001"
action.session_id = "session_001"
# Act
ticket = await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="测试审批单"
)
# Assert
assert ticket is not None
assert ticket.action_id == "action_001"
assert ticket.session_id == "session_001"
assert ticket.channel == "agent"
assert ticket.status == "pending"
assert ticket.reason == "测试审批单"
@pytest.mark.asyncio
async def test_ensure_ticket_idempotent(self, db_session):
"""测试 ensure_ticket 幂等性 - 相同动作只创建一张待决审批单"""
# Arrange
approval_svc = ApprovalService(db_session)
action = MagicMock()
action.id = "action_002"
action.session_id = "session_002"
# Act - 第一次调用
ticket1 = await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="第一次创建"
)
# Act - 第二次调用(幂等)
ticket2 = await approval_svc.ensure_ticket(
action=action,
channel="h5",
reason="第二次创建(应返回已存在的)"
)
# Assert - 两次返回同一张审批单
assert ticket1.id == ticket2.id
assert ticket1.status == "pending"
# 保持原有信息不变
assert ticket1.reason == "第一次创建"
@pytest.mark.asyncio
async def test_decide_approve(self, db_session):
"""测试 decide 审批通过"""
# Arrange
approval_svc = ApprovalService(db_session)
# 先创建审批单
action = MagicMock()
action.id = "action_003"
action.session_id = "session_003"
ticket = await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="测试审批"
)
# Act - 审批通过
result = await approval_svc.decide(
ticket_id=ticket.id,
decision="approve",
note="同意执行",
approver_id="agent_001"
)
# Assert
assert result.status == "approved"
assert result.decision_note == "同意执行"
assert result.approver_id == "agent_001"
assert result.decided_at is not None
@pytest.mark.asyncio
async def test_decide_reject(self, db_session):
"""测试 decide 审批驳回"""
# Arrange
approval_svc = ApprovalService(db_session)
action = MagicMock()
action.id = "action_004"
action.session_id = "session_004"
ticket = await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="测试审批"
)
# Act - 审批驳回
result = await approval_svc.decide(
ticket_id=ticket.id,
decision="reject",
note="风险太高,驳回",
approver_id="agent_002"
)
# Assert
assert result.status == "rejected"
assert result.decision_note == "风险太高,驳回"
assert result.approver_id == "agent_002"
@pytest.mark.asyncio
async def test_decide_nonexistent_ticket(self, db_session):
"""测试 decide 对不存在的审批单抛出异常"""
# Arrange
approval_svc = ApprovalService(db_session)
# Act & Assert
with pytest.raises(ValueError, match="审批单不存在"):
await approval_svc.decide(
ticket_id="nonexistent_id",
decision="approve",
note="test",
approver_id="agent_001"
)
@pytest.mark.asyncio
async def test_decide_already_decided(self, db_session):
"""测试 decide 对已决审批单不重复处理"""
# Arrange
approval_svc = ApprovalService(db_session)
action = MagicMock()
action.id = "action_005"
action.session_id = "session_005"
ticket = await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="测试"
)
# 第一次审批通过
await approval_svc.decide(
ticket_id=ticket.id,
decision="approve",
note="通过",
approver_id="agent_001"
)
# 第二次尝试审批(应该直接返回,不处理)
result = await approval_svc.decide(
ticket_id=ticket.id,
decision="reject",
note="试图驳回",
approver_id="agent_002"
)
# Assert - 状态应保持为 approved,不受第二次影响
assert result.status == "approved"
assert result.approver_id == "agent_001" # 保持原审批人
@pytest.mark.asyncio
async def test_get_pending_for_action(self, db_session):
"""测试 get_pending_for_action 查询待决审批单"""
# Arrange
approval_svc = ApprovalService(db_session)
action = MagicMock()
action.id = "action_006"
action.session_id = "session_006"
# 创建审批单
await approval_svc.ensure_ticket(
action=action,
channel="agent",
reason="待决审批单"
)
# Act
pending = await approval_svc.get_pending_for_action(action.id)
# Assert
assert pending is not None
assert pending.action_id == action.id
assert pending.status == "pending"