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
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
# =============================================================================
|
||||
# 企微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"]
|
||||
Reference in New Issue
Block a user