# ============================================================================= # 企微IT智能服务台 — 坐席认证与管理测试 # ============================================================================= # 测试覆盖: # 1. 坐席登录(新坐席注册 + 已有坐席重新登录) # 2. Token 存 Redis(验证 TTL 和格式) # 3. 获取当前坐席信息(有效 Token / 无效 Token / 过期 Token) # 4. 更新坐席状态(online/busy/offline) # 5. 无效状态值校验 # 6. 获取坐席列表 # 7. 缺少 Authorization 头返回未授权 # ============================================================================= import pytest import pytest_asyncio from unittest.mock import patch from app.models.agent import Agent from tests.conftest import create_test_agent, MockRedis class TestAgentLogin: """测试坐席登录。""" @pytest.mark.asyncio async def test_login_new_agent(self, client, db_session, mock_redis): """验证新坐席首次登录自动注册。""" response = await client.post( "/agents/login", json={"user_id": "new_agent_001", "name": "新坐席"}, ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["data"]["user_id"] == "new_agent_001" assert data["data"]["name"] == "新坐席" assert data["data"]["status"] == "online" assert "token" in data["data"] @pytest.mark.asyncio async def test_login_existing_agent(self, client, db_session, mock_redis): """验证已有坐席重新登录更新信息。""" # 先创建坐席 agent = create_test_agent(user_id="exist_agent", name="旧名字") db_session.add(agent) await db_session.flush() response = await client.post( "/agents/login", json={"user_id": "exist_agent", "name": "新名字"}, ) assert response.status_code == 200 data = response.json() assert data["data"]["name"] == "新名字" assert data["data"]["status"] == "online" @pytest.mark.asyncio async def test_login_returns_token(self, client, db_session, mock_redis): """验证登录返回 Token 存入 Redis。""" response = await client.post( "/agents/login", json={"user_id": "token_test_agent", "name": "Token测试"}, ) data = response.json() assert "token" in data["data"] # 验证 Redis 中存储了 token(key 格式:agent:token:{token}) token = data["data"]["token"] redis_key = f"agent:token:{token}" stored_value = await mock_redis.get(redis_key) assert stored_value is not None class TestAgentAuthentication: """测试坐席认证。""" @pytest.mark.asyncio async def test_get_agent_me_with_valid_token(self, client, db_session, mock_redis): """验证有效 Token 获取坐席信息。""" # 先登录获取 token login_resp = await client.post( "/agents/login", json={"user_id": "me_test_agent", "name": "我测试"}, ) token = login_resp.json()["data"]["token"] # 用 token 获取坐席信息 response = await client.get( "/agents/me", headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert data["data"]["user_id"] == "me_test_agent" @pytest.mark.asyncio async def test_get_agent_me_without_token(self, client, db_session, mock_redis): """验证缺少 Token 返回未授权。""" response = await client.get("/agents/me") data = response.json() assert data["code"] == 1002 # ERR_UNAUTHORIZED @pytest.mark.asyncio async def test_get_agent_me_with_invalid_token(self, client, db_session, mock_redis): """验证无效 Token 返回未授权。""" response = await client.get( "/agents/me", headers={"Authorization": "Bearer invalid_token_12345"}, ) data = response.json() assert data["code"] == 1002 class TestAgentStatusUpdate: """测试坐席状态更新。""" @pytest.mark.asyncio async def test_update_status_to_busy(self, client, db_session, mock_redis): """验证更新坐席状态为忙碌。""" # 先登录 login_resp = await client.post( "/agents/login", json={"user_id": "status_test_agent", "name": "状态测试"}, ) token = login_resp.json()["data"]["token"] # 更新状态 response = await client.put( "/agents/me/status", json={"status": "busy"}, headers={"Authorization": f"Bearer {token}"}, ) assert response.status_code == 200 data = response.json() assert data["data"]["status"] == "busy" @pytest.mark.asyncio async def test_update_status_to_offline(self, client, db_session, mock_redis): """验证更新坐席状态为离线。""" login_resp = await client.post( "/agents/login", json={"user_id": "offline_test_agent", "name": "离线测试"}, ) token = login_resp.json()["data"]["token"] response = await client.put( "/agents/me/status", json={"status": "offline"}, headers={"Authorization": f"Bearer {token}"}, ) data = response.json() assert data["data"]["status"] == "offline" @pytest.mark.asyncio async def test_update_status_invalid_value(self, client, db_session, mock_redis): """验证无效状态值返回校验错误。""" login_resp = await client.post( "/agents/login", json={"user_id": "invalid_status_agent", "name": "无效状态测试"}, ) token = login_resp.json()["data"]["token"] response = await client.put( "/agents/me/status", json={"status": "invalid_status"}, headers={"Authorization": f"Bearer {token}"}, ) # Pydantic 校验应返回 422 assert response.status_code == 422 class TestAgentList: """测试坐席列表。""" @pytest.mark.asyncio async def test_list_agents(self, client, db_session, mock_redis): """验证获取坐席列表。""" agent1 = create_test_agent(user_id="list_agent_1", name="坐席一") agent2 = create_test_agent(user_id="list_agent_2", name="坐席二") db_session.add_all([agent1, agent2]) await db_session.flush() response = await client.get("/agents") assert response.status_code == 200 data = response.json() assert data["code"] == 0 assert len(data["data"]["items"]) >= 2 @pytest.mark.asyncio async def test_list_agents_by_status(self, client, db_session, mock_redis): """验证按状态过滤坐席列表。""" online_agent = create_test_agent(user_id="online_filter_agent", name="在线坐席", status="online") offline_agent = create_test_agent(user_id="offline_filter_agent", name="离线坐席", status="offline") db_session.add_all([online_agent, offline_agent]) await db_session.flush() response = await client.get("/agents?status=online") data = response.json() assert data["code"] == 0 for item in data["data"]["items"]: assert item["status"] == "online"