Files

214 lines
7.4 KiB
Python
Raw Permalink Normal View History

# =============================================================================
# 企微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 中存储了 tokenkey 格式: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"