214 lines
7.4 KiB
Python
214 lines
7.4 KiB
Python
|
|
# =============================================================================
|
|||
|
|
# 企微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"
|