# ============================================================================= # 阶段5 自动化 - 会话管理服务单元测试 # ============================================================================= # 测试范围:session_manager.py - 会话生命周期、状态机、关单判定 # ============================================================================= import pytest from unittest.mock import AsyncMock, MagicMock, patch from datetime import datetime, timezone from app.services.automation.session_manager import AutoSessionService from app.models.automation import AutoSession, AutoAction class TestAutoSessionService: """会话管理服务测试用例""" @pytest.mark.asyncio async def test_create_session(self, db_session): """测试创建会话""" # Arrange svc = AutoSessionService(db_session) # Act session = await svc.create_session( conversation_id="conv_001", employee_id="emp_001", description="测试会话", mode="real_exec" ) # Assert assert session.id is not None assert session.conversation_id == "conv_001" assert session.employee_id == "emp_001" assert session.status == "created" assert session.mode == "real_exec" assert session.title == "测试会话" @pytest.mark.asyncio async def test_get_session(self, db_session): """测试获取会话""" # Arrange - 先创建会话 svc = AutoSessionService(db_session) created = await svc.create_session( conversation_id="conv_002", employee_id="emp_002", description="查询测试" ) await db_session.flush() # Act session = await svc.get_session(created.id) # Assert assert session is not None assert session.id == created.id assert session.employee_id == "emp_002" @pytest.mark.asyncio async def test_get_session_not_found(self, db_session): """测试获取不存在的会话返回 None""" # Arrange svc = AutoSessionService(db_session) # Act session = await svc.get_session("nonexistent_id") # Assert assert session is None @pytest.mark.asyncio async def test_list_sessions_with_filters(self, db_session): """测试列出会话(支持过滤)""" # Arrange - 创建多个会话 svc = AutoSessionService(db_session) await svc.create_session( employee_id="emp_001", description="会话1" ) await svc.create_session( employee_id="emp_001", description="会话2" ) await svc.create_session( employee_id="emp_002", description="会话3" ) await db_session.flush() # Act - 按 employee_id 过滤 sessions = await svc.list_sessions(employee_id="emp_001") # Assert assert len(sessions) == 2 assert all(s.employee_id == "emp_001" for s in sessions) @pytest.mark.asyncio async def test_list_sessions_by_status(self, db_session): """测试按状态过滤会话""" # Arrange svc = AutoSessionService(db_session) # 创建一个 running 会话 session = await svc.create_session( employee_id="emp_001", description="测试" ) session.status = "running" await db_session.flush() # Act running = await svc.list_sessions(status="running") closed = await svc.list_sessions(status="closed") # Assert assert len(running) >= 1 assert all(s.status == "running" for s in running) assert len(closed) == 0 @pytest.mark.asyncio async def test_takeover(self, db_session): """测试转人工接管""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="测试转人工" ) await db_session.flush() # Act result = await svc.takeover( session_id=session.id, agent_id="agent_001", note="我来接管处理" ) # Assert assert result.status == "handoff" assert result.agent_id == "agent_001" assert result.closed_by == "agent_001" @pytest.mark.asyncio async def test_resolve_feedback_satisfied(self, db_session): """测试反馈处理 - 满意则关单""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="测试反馈" ) await db_session.flush() # Act - 满意 result = await svc.resolve_feedback( session_id=session.id, satisfied=True, note="处理得很好" ) # Assert assert result.status == "closed" assert result.closed_by == session.employee_id @pytest.mark.asyncio async def test_resolve_feedback_unsatisfied(self, db_session): """测试反馈处理 - 不满意则转人工""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="测试反馈" ) await db_session.flush() # Act - 不满意 result = await svc.resolve_feedback( session_id=session.id, satisfied=False, note="没有解决我的问题" ) # Assert assert result.status == "handoff" assert result.closed_by == "employee(reject)" @pytest.mark.asyncio async def test_auto_close_resolved_session(self, db_session): """测试静默关单 - 仅 resolved 态可关""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="测试关单" ) # Case 1: 非 resolved 状态不应关单 session.status = "running" await db_session.flush() await svc.auto_close(session.id) await db_session.refresh(session) assert session.status == "running" # 状态未变 # Case 2: resolved 状态应关单 session.status = "resolved" await db_session.flush() await svc.auto_close(session.id) await db_session.refresh(session) assert session.status == "closed" assert session.closed_by == "system(auto)" @pytest.mark.asyncio async def test_get_session_detail_with_actions(self, db_session): """测试获取会话详情(含动作列表)""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="测试详情" ) await db_session.flush() # 添加动作 action1 = AutoAction( session_id=session.id, action_index=0, action_type="terminal_locate", status="pending", title="定位终端" ) action2 = AutoAction( session_id=session.id, action_index=1, action_type="virus_scan", status="success", title="病毒扫描" ) db_session.add(action1) db_session.add(action2) await db_session.flush() # Act detail = await svc.get_session_detail(session.id) # Assert assert detail is not None assert detail["session"].id == session.id assert len(detail["actions"]) == 2 # 验证动作按 action_index 排序 assert detail["actions"][0].action_type == "terminal_locate" assert detail["actions"][1].action_type == "virus_scan" @pytest.mark.asyncio async def test_get_session_detail_not_found(self, db_session): """测试获取不存在的会话详情返回 None""" # Arrange svc = AutoSessionService(db_session) # Act detail = await svc.get_session_detail("nonexistent_id") # Assert assert detail is None @pytest.mark.asyncio async def test_session_status_flow(self, db_session): """测试会话状态流转""" # Arrange svc = AutoSessionService(db_session) session = await svc.create_session( employee_id="emp_001", description="状态流转测试" ) await db_session.flush() # Assert - 初始状态 assert session.status == "created" # 模拟 start 后的状态变化 session.status = "running" await db_session.flush() await db_session.refresh(session) assert session.status == "running" # 审批暂停 session.status = "paused" await db_session.flush() await db_session.refresh(session) assert session.status == "paused" # 处置成功 session.status = "resolved" await db_session.flush() await db_session.refresh(session) assert session.status == "resolved" # 静默关单 session.status = "closed" await db_session.flush() await db_session.refresh(session) assert session.status == "closed"