test(backend): unit/integration tests for automation, otp, neo4j, contract
新增自动化审批状态机/执行器/意图路由/会话管理、OTP 绑定流程、neo4j 客户端、响应契约、置信度门禁、环境门控、Tier1 API 等测试。
This commit is contained in:
@@ -0,0 +1,249 @@
|
||||
# =============================================================================
|
||||
# 企微IT智能服务台 — 审批状态机测试(D7 / P0-6)
|
||||
# =============================================================================
|
||||
# 说明:测试五态流转 + 合法转换校验 + 非法转换拦截。
|
||||
# 状态机路径:
|
||||
# pending → approved → applied → graph_synced
|
||||
# pending → queued → approved → applied → graph_synced
|
||||
# pending → rejected
|
||||
# pending → expired
|
||||
# (改写) → pending
|
||||
# =============================================================================
|
||||
|
||||
import pytest
|
||||
|
||||
from app.schemas.enums import (
|
||||
SuggestionStatusEnum,
|
||||
is_valid_transition,
|
||||
)
|
||||
|
||||
|
||||
class TestApprovalStateMachine:
|
||||
"""审批状态机 — 合法转换测试。"""
|
||||
|
||||
# ── 合法转换 ──
|
||||
|
||||
def test_pending_to_approved_valid(self):
|
||||
"""pending → approved 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.pending, SuggestionStatusEnum.approved
|
||||
) is True
|
||||
|
||||
def test_pending_to_queued_valid(self):
|
||||
"""pending → queued 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.pending, SuggestionStatusEnum.queued
|
||||
) is True
|
||||
|
||||
def test_pending_to_rejected_valid(self):
|
||||
"""pending → rejected 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.pending, SuggestionStatusEnum.rejected
|
||||
) is True
|
||||
|
||||
def test_pending_to_expired_valid(self):
|
||||
"""pending → expired 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.pending, SuggestionStatusEnum.expired
|
||||
) is True
|
||||
|
||||
def test_queued_to_approved_valid(self):
|
||||
"""queued → approved 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.queued, SuggestionStatusEnum.approved
|
||||
) is True
|
||||
|
||||
def test_approved_to_applied_valid(self):
|
||||
"""approved → applied 合法。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.approved, SuggestionStatusEnum.applied
|
||||
) is True
|
||||
|
||||
def test_applied_to_graph_synced_valid(self):
|
||||
"""applied → graph_synced 合法(终态)。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.applied, SuggestionStatusEnum.graph_synced
|
||||
) is True
|
||||
|
||||
# ── 非法转换 ──
|
||||
|
||||
def test_graph_synced_is_terminal(self):
|
||||
"""graph_synced 是终态,不可再转换。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.graph_synced, SuggestionStatusEnum.pending
|
||||
) is False
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.graph_synced, SuggestionStatusEnum.applied
|
||||
) is False
|
||||
|
||||
def test_rejected_is_terminal(self):
|
||||
"""rejected 是终态,不可再转换。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.rejected, SuggestionStatusEnum.pending
|
||||
) is False
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.rejected, SuggestionStatusEnum.approved
|
||||
) is False
|
||||
|
||||
def test_expired_is_terminal(self):
|
||||
"""expired 是终态,不可再转换。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.expired, SuggestionStatusEnum.pending
|
||||
) is False
|
||||
|
||||
def test_pending_direct_to_graph_synced_invalid(self):
|
||||
"""pending 不能直接跳到 graph_synced(必经 approved→applied)。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.pending, SuggestionStatusEnum.graph_synced
|
||||
) is False
|
||||
|
||||
def test_queued_direct_to_applied_invalid(self):
|
||||
"""queued 不能直接跳到 applied(必经 approved)。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.queued, SuggestionStatusEnum.applied
|
||||
) is False
|
||||
|
||||
def test_applied_cannot_go_back_to_pending(self):
|
||||
"""applied 不能回退到 pending。"""
|
||||
assert is_valid_transition(
|
||||
SuggestionStatusEnum.applied, SuggestionStatusEnum.pending
|
||||
) is False
|
||||
|
||||
|
||||
class TestApprovalServiceFlows:
|
||||
"""审批服务层流程测试(需要数据库)。"""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_approve_suggestion_not_found(self, db_session):
|
||||
"""验证审批不存在的建议返回 None。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.approve_suggestion(
|
||||
db_session, "nonexistent-id", "reviewer-001"
|
||||
)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reject_suggestion_not_found(self, db_session):
|
||||
"""验证拒绝不存在的建议返回 None。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.reject_suggestion(
|
||||
db_session, "nonexistent-id", "reviewer-001", "不需要"
|
||||
)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rewrite_suggestion_not_found(self, db_session):
|
||||
"""验证改写不存在的建议返回 None。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.rewrite_suggestion(
|
||||
db_session, "nonexistent-id", "reviewer-001", {"title": "新标题"}
|
||||
)
|
||||
assert result is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rewrite_resets_to_pending(self, db_session):
|
||||
"""验证改写后状态重置为 pending。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
from app.models.knowledge_suggestion import KnowledgeSuggestion
|
||||
|
||||
# 创建测试建议
|
||||
suggestion = KnowledgeSuggestion(
|
||||
suggestion_type="new_faq",
|
||||
status="approved",
|
||||
title="测试标题",
|
||||
content="测试内容",
|
||||
category="网络",
|
||||
tags=[],
|
||||
source_type="conversation",
|
||||
source_data=["conv-001"],
|
||||
reason="测试",
|
||||
confidence=0.8,
|
||||
audience="employee_quick_reply",
|
||||
)
|
||||
db_session.add(suggestion)
|
||||
await db_session.commit()
|
||||
await db_session.refresh(suggestion)
|
||||
|
||||
# 改写
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.rewrite_suggestion(
|
||||
db_session, suggestion.id, "reviewer-001", {"title": "改写后的标题"}
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.status == "pending"
|
||||
assert result.title == "改写后的标题"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_approve_suggestion_full_flow(self, db_session):
|
||||
"""验证完整审批流程:pending→approved→applied。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
from app.models.knowledge_suggestion import KnowledgeSuggestion
|
||||
|
||||
# 创建测试建议
|
||||
suggestion = KnowledgeSuggestion(
|
||||
suggestion_type="new_faq",
|
||||
status="pending",
|
||||
title="VPN连接测试",
|
||||
content="1.检查网络 2.重启客户端",
|
||||
category="网络",
|
||||
tags=["VPN"],
|
||||
source_type="conversation",
|
||||
source_data=["conv-002"],
|
||||
reason="测试流程",
|
||||
confidence=0.86,
|
||||
audience="employee_quick_reply",
|
||||
issue="VPN问题",
|
||||
action="VPN连接修复",
|
||||
relation_type="LEADS_TO",
|
||||
)
|
||||
db_session.add(suggestion)
|
||||
await db_session.commit()
|
||||
await db_session.refresh(suggestion)
|
||||
|
||||
# 审批通过(不传 neo4j_client,只到 applied)
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.approve_suggestion(
|
||||
db_session, suggestion.id, "reviewer-001", neo4j_client=None
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
assert result.status in ("applied", "graph_synced")
|
||||
assert result.reviewer_id == "reviewer-001"
|
||||
assert result.reviewed_at is not None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_suggestion_flow(self, db_session):
|
||||
"""验证入队列流程:pending→queued。"""
|
||||
from app.services.knowledge_iteration_service import KnowledgeIterationService
|
||||
from app.models.knowledge_suggestion import KnowledgeSuggestion
|
||||
|
||||
suggestion = KnowledgeSuggestion(
|
||||
suggestion_type="new_faq",
|
||||
status="pending",
|
||||
title="队列测试",
|
||||
content="测试内容",
|
||||
category="软件",
|
||||
tags=[],
|
||||
source_type="conversation",
|
||||
source_data=["conv-003"],
|
||||
reason="测试入队列",
|
||||
confidence=0.5,
|
||||
audience="employee_quick_reply",
|
||||
)
|
||||
db_session.add(suggestion)
|
||||
await db_session.commit()
|
||||
await db_session.refresh(suggestion)
|
||||
|
||||
service = KnowledgeIterationService()
|
||||
result = await service.queue_suggestion(db_session, suggestion.id)
|
||||
|
||||
assert result is not None
|
||||
assert result.status == "queued"
|
||||
assert result.queued_at is not None
|
||||
Reference in New Issue
Block a user