Files
wecom_it_smart_desk/backend/tests/test_auth_unified.py
T
Simon bea288e414 feat: 2026-07-11 全量更新 - 代办集成+会议室预定+知识迭代修复+UI统一+Bug修复
== 已部署上线 (9项) ==
- 代办事项真实数据源集成 (企微审批API 8bug修复链)
- H5/坐席端 Logo样式统一+绿色背景
- 视频引导页修复 (localStorage key v2)
- 坐席端 v9 Vue版本修复 (ElMessage._context)
- 截图按钮 v10 修复 (getDisplayMedia user gesture)
- 扫码样式恢复+H5扫码登录跳转修复
- H5截图快捷键提示

== 代码完成待部署 (3项) ==
- 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查)
- 会议室预定-小鱼易联终端 (40文件, 40/40测试通过)
- IT资产升级审批推送 (asset_service.py)

== 需求文档 (2项) ==
- 坐席端AI辅助消息框-PRD (4项新功能确认)
- 坐席端布局优化建议 v2.0 (7天计划)

== 新增文档 ==
- 日报-2026-07-11.md
- 知识迭代Bug修复报告-20260711.md
- 会议室预定-部署指南.md
- CHANGELOG.md 更新

== 测试 ==
- test_todo_integration.py: 40/40
- test_meetingroom.py: 40/40
- test_bugfix_ki_suggestions.py: 21/21
2026-07-11 23:13:10 +08:00

233 lines
8.2 KiB
Python

# =============================================================================
# 企微IT智能服务台 — 统一认证 API 测试
# =============================================================================
# 测试覆盖:
# 1. GET /auth/qrcode — 获取扫码登录二维码
# 2. GET /auth/scan/status — 轮询扫码状态
# 3. POST /auth/verify — 验证 Token
# 4. POST /auth/logout — 登出
# 5. GET /auth/me — 获取当前用户信息
# 6. POST /auth/switch-role — 切换角色
#
# 说明:这些端点复用现有的 auth_qrcode 服务,因此重点测试统一认证 API 层的封装
# =============================================================================
import json
import secrets
from datetime import datetime
import pytest
from tests.conftest import MockRedis
# --------------------------------------------------------------------------
# 工具: 创建测试 Token 并写入 Redis
# --------------------------------------------------------------------------
async def _create_test_token(
mock_redis: MockRedis,
employee_id: str = "test-user-001",
name: str = "测试用户",
roles: list = None,
login_source: str = "h5",
) -> str:
"""在 mock_redis 里手动写一个用户 token,返回 token 字符串。
与 TokenService.create_token 一致: 写 user:token:{token}
"""
if roles is None:
roles = ["user"]
token = secrets.token_urlsafe(32)
token_data = {
"employee_id": employee_id,
"name": name,
"department": "测试部",
"avatar": "",
"roles": roles,
"current_role": roles[0] if roles else "user",
"login_source": login_source,
"created_at": datetime.now().isoformat(),
"last_active": datetime.now().isoformat(),
}
await mock_redis.setex(
f"user:token:{token}",
8 * 60 * 60,
json.dumps(token_data, ensure_ascii=False),
)
return token
# --------------------------------------------------------------------------
# 1. GET /auth/qrcode — 获取扫码登录二维码
# --------------------------------------------------------------------------
class TestQrcodeEndpoint:
"""测试统一认证的二维码获取端点。"""
@pytest.mark.asyncio
async def test_get_qrcode_returns_ticket_and_url(self, client, mock_redis):
"""验证获取二维码返回 ticket + qrcode_url + qrcode_png_base64 + expires_in。"""
response = await client.get("/auth/qrcode")
assert response.status_code == 200
body = response.json()
assert body["code"] == 0
assert "data" in body
assert body["data"] is not None
data = body["data"]
assert "ticket" in data
assert len(data["ticket"]) >= 16
assert "qrcode_url" in data
assert "qrcode_png_base64" in data
assert "expires_in" in data
assert "expires_at" in data
# 有效期应该是 120 秒 (从 QrcodeService 来的)
assert data["expires_in"] == 120
# --------------------------------------------------------------------------
# 2. POST /auth/verify — 验证 Token
# --------------------------------------------------------------------------
class TestVerifyTokenEndpoint:
"""测试统一认证的 Token 验证端点。"""
@pytest.mark.asyncio
async def test_verify_invalid_token_returns_false(self, client, mock_redis):
"""验证无效 Token 返回 valid=False。"""
response = await client.post(
"/auth/verify",
json={"token": "invalid-token-xyz"},
)
assert response.status_code == 200
body = response.json()
assert body["code"] == 0
data = body["data"]
assert data["valid"] is False
assert data["employee_id"] is None
assert data["name"] is None
@pytest.mark.asyncio
async def test_verify_blacklisted_token_returns_false(self, client, mock_redis):
"""验证已加入黑名单的 Token 返回 valid=False。"""
# 创建 token
token = await _create_test_token(
mock_redis,
employee_id="test-user-001",
name="测试用户",
)
# 将 token 加入黑名单
import hashlib
token_hash = hashlib.sha256(token.encode()).hexdigest()
await mock_redis.setex(f"token:blacklist:{token_hash}", 8 * 60 * 60, "1")
# 验证黑名单中的 token
response = await client.post(
"/auth/verify",
json={"token": token},
)
assert response.status_code == 200
body = response.json()
assert body["code"] == 0
data = body["data"]
assert data["valid"] is False
# --------------------------------------------------------------------------
# 3. POST /auth/logout — 登出
# --------------------------------------------------------------------------
class TestLogoutEndpoint:
"""测试统一认证的登出端点。"""
@pytest.mark.asyncio
async def test_logout_without_auth_returns_error(self, client, mock_redis):
"""验证未登录登出返回错误。"""
response = await client.post("/auth/logout")
# 未鉴权应该返回 401 或 403
assert response.status_code in (401, 403)
# --------------------------------------------------------------------------
# 4. GET /auth/me — 获取当前用户信息
# --------------------------------------------------------------------------
class TestCurrentUserEndpoint:
"""测试统一认证的获取当前用户信息端点。"""
@pytest.mark.asyncio
async def test_get_current_user_returns_info(self, client, mock_redis):
"""验证获取当前用户信息成功。"""
# 创建有效 token
token = await _create_test_token(
mock_redis,
employee_id="test-user-001",
name="测试用户",
roles=["agent"],
login_source="agent",
)
# 获取当前用户
response = await client.get(
"/auth/me",
headers={"Authorization": f"Bearer {token}"},
)
assert response.status_code == 200
body = response.json()
assert body["code"] == 0
data = body["data"]
assert data["employee_id"] == "test-user-001"
assert data["name"] == "测试用户"
assert data["roles"] == ["agent"]
assert data["current_role"] == "agent"
assert data["login_source"] == "agent"
@pytest.mark.asyncio
async def test_get_current_user_without_auth_returns_error(self, client, mock_redis):
"""验证未登录获取用户信息返回错误。"""
response = await client.get("/auth/me")
# 未鉴权应该返回 401 或 403
assert response.status_code in (401, 403)
# --------------------------------------------------------------------------
# 5. POST /auth/switch-role — 切换角色
# --------------------------------------------------------------------------
class TestSwitchRoleEndpoint:
"""测试统一认证的角色切换端点。"""
@pytest.mark.asyncio
async def test_switch_role_without_auth_returns_error(self, client, mock_redis):
"""验证未登录切换角色返回错误。"""
response = await client.post(
"/auth/switch-role",
json={"role": "admin"},
)
# 未鉴权应该返回 401 或 403
assert response.status_code in (401, 403)
# --------------------------------------------------------------------------
# 6. 废弃 API 测试
# --------------------------------------------------------------------------
class TestDeprecatedAPIs:
"""测试废弃的登录 API 是否正确禁用或返回提示。"""
@pytest.mark.asyncio
async def test_agents_login_still_works(self, client, mock_redis, db_session):
"""验证坐席登录端点仍然可用。"""
# agents/login 是坐席登录,仍应可用
response = await client.post(
"/agents/login",
json={"user_id": "test-agent-001", "name": "测试坐席"},
)
# 应该返回 200
assert response.status_code == 200
body = response.json()
assert body["code"] == 0
assert "token" in body["data"]