472 lines
17 KiB
Python
472 lines
17 KiB
Python
"""
|
|
E2E关键业务流程测试套件
|
|
|
|
测试范围:
|
|
1. 用户管理完整生命周期流程
|
|
2. 角色权限管理流程
|
|
3. 菜单权限配置流程
|
|
4. 文件上传下载流程
|
|
5. 审计日志记录流程
|
|
|
|
作者: 张翔
|
|
日期: 2026-04-01
|
|
"""
|
|
|
|
import pytest
|
|
import asyncio
|
|
import time
|
|
import uuid
|
|
from typing import Dict, Any
|
|
from api.auth_api import AuthAPI
|
|
from api.user_api import UserAPI
|
|
from api.role_api import RoleAPI
|
|
from api.menu_api import MenuAPI
|
|
from api.file_api import FileAPI
|
|
from api.audit_api import AuditAPI
|
|
from config.settings import settings
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.critical
|
|
@pytest.mark.asyncio
|
|
class TestE2ECriticalWorkflows:
|
|
"""E2E关键业务流程测试类"""
|
|
|
|
async def test_e2e_user_lifecycle_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-01: 用户管理完整生命周期流程
|
|
|
|
测试场景:
|
|
1. 创建新用户
|
|
2. 分配角色
|
|
3. 用户登录验证
|
|
4. 权限验证
|
|
5. 用户信息更新
|
|
6. 用户禁用
|
|
7. 用户删除
|
|
"""
|
|
user_api = UserAPI(authenticated_client)
|
|
role_api = RoleAPI(authenticated_client)
|
|
auth_api = AuthAPI(authenticated_client)
|
|
|
|
unique_id = f"e2e_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
# 步骤1: 创建测试角色
|
|
role_data = {
|
|
"roleName": f"E2E_Test_Role_{unique_id}",
|
|
"roleKey": f"e2e_test_role_{unique_id}",
|
|
"roleSort": 1,
|
|
"status": 1,
|
|
"remark": "E2E测试角色"
|
|
}
|
|
role_response = await role_api.create_role(role_data)
|
|
assert role_response.status_code == 201, "创建角色失败"
|
|
role_id = role_response.json()["id"]
|
|
test_data_manager.add_role(role_id)
|
|
|
|
# 步骤2: 创建新用户
|
|
user_data = {
|
|
"username": f"e2e_user_{unique_id}",
|
|
"password": "Test123!@#",
|
|
"email": f"e2e_user_{unique_id}@test.com",
|
|
"nickname": "E2E测试用户",
|
|
"phone": "13800138000",
|
|
"status": 1,
|
|
"roleId": role_id
|
|
}
|
|
user_response = await user_api.create_user(user_data)
|
|
assert user_response.status_code == 201, "创建用户失败"
|
|
user_id = user_response.json()["id"]
|
|
test_data_manager.add_user(user_id)
|
|
|
|
# 步骤3: 用户登录验证
|
|
login_response = await auth_api.login(user_data["username"], user_data["password"])
|
|
assert login_response.status_code == 200, "用户登录失败"
|
|
token = login_response.json().get("token")
|
|
assert token is not None, "未获取到登录Token"
|
|
|
|
# 步骤4: 验证用户信息
|
|
user_info_response = await user_api.get_user_by_id(user_id)
|
|
assert user_info_response.status_code == 200, "获取用户信息失败"
|
|
user_info = user_info_response.json()
|
|
assert user_info["username"] == user_data["username"], "用户名不匹配"
|
|
assert user_info["email"] == user_data["email"], "邮箱不匹配"
|
|
|
|
# 步骤5: 更新用户信息(使用后端支持的字段)
|
|
update_data = {
|
|
"email": f"updated_{unique_id}@example.com",
|
|
"status": 1
|
|
}
|
|
update_response = await user_api.update_user(user_id, update_data)
|
|
assert update_response.status_code == 200, "更新用户信息失败"
|
|
|
|
# 步骤6: 验证更新结果
|
|
updated_user_response = await user_api.get_user_by_id(user_id)
|
|
updated_user = updated_user_response.json()
|
|
assert updated_user["email"] == update_data["email"], "邮箱更新失败"
|
|
|
|
# 步骤7: 禁用用户
|
|
disable_response = await user_api.update_user(user_id, {"status": 0})
|
|
assert disable_response.status_code == 200, "禁用用户失败"
|
|
|
|
# 步骤8: 验证用户已被禁用
|
|
disabled_user_response = await user_api.get_user_by_id(user_id)
|
|
disabled_user = disabled_user_response.json()
|
|
assert disabled_user["status"] == 0, "用户状态未更新为禁用"
|
|
|
|
# 步骤9: 删除用户
|
|
delete_response = await user_api.delete_user(user_id)
|
|
assert delete_response.status_code in [200, 204], "删除用户失败"
|
|
|
|
# 步骤10: 验证用户已被删除
|
|
verify_delete_response = await user_api.get_user_by_id(user_id)
|
|
assert verify_delete_response.status_code == 404, "用户未正确删除"
|
|
|
|
async def test_e2e_role_permission_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-02: 角色权限管理流程
|
|
|
|
测试场景:
|
|
1. 创建角色
|
|
2. 分配菜单权限
|
|
3. 创建用户并分配角色
|
|
4. 验证用户权限
|
|
5. 修改角色权限
|
|
6. 验证权限即时生效
|
|
7. 删除角色
|
|
"""
|
|
role_api = RoleAPI(authenticated_client)
|
|
user_api = UserAPI(authenticated_client)
|
|
menu_api = MenuAPI(authenticated_client)
|
|
|
|
unique_id = f"{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
# 步骤1: 创建角色
|
|
role_data = {
|
|
"roleName": f"E2E_Role_{unique_id}",
|
|
"roleKey": f"e2e_role_{unique_id}",
|
|
"roleSort": 1,
|
|
"status": 1
|
|
}
|
|
role_response = await role_api.create_role(role_data)
|
|
assert role_response.status_code == 201, "创建角色失败"
|
|
role_id = role_response.json()["id"]
|
|
test_data_manager.add_role(role_id)
|
|
|
|
# 步骤2: 获取菜单列表
|
|
menus_response = await menu_api.get_menu_list()
|
|
assert menus_response.status_code == 200, "获取菜单列表失败"
|
|
menus = menus_response.json()
|
|
assert len(menus) > 0, "菜单列表为空"
|
|
|
|
# 步骤3: 分配菜单权限给角色
|
|
menu_ids = [menu["id"] for menu in menus[:3]] # 选择前3个菜单
|
|
assign_response = await role_api.assign_menus(role_id, menu_ids)
|
|
assert assign_response.status_code == 200, "分配菜单权限失败"
|
|
|
|
# 步骤4: 创建用户并分配角色
|
|
user_data = {
|
|
"username": f"e2e_perm_user_{unique_id}",
|
|
"password": "Test123!@#",
|
|
"email": f"e2e_perm_user_{unique_id}@test.com",
|
|
"phone": "13800138001",
|
|
"nickname": "E2E权限测试用户",
|
|
"status": 1,
|
|
"roleId": role_id
|
|
}
|
|
user_response = await user_api.create_user(user_data)
|
|
assert user_response.status_code == 201, "创建用户失败"
|
|
user_id = user_response.json()["id"]
|
|
test_data_manager.add_user(user_id)
|
|
|
|
# 步骤5: 验证用户权限
|
|
user_info_response = await user_api.get_user_by_id(user_id)
|
|
user_info = user_info_response.json()
|
|
assert "roles" in user_info, "用户信息中缺少角色信息"
|
|
|
|
# 步骤6: 修改角色权限(移除部分菜单)
|
|
updated_menu_ids = menu_ids[:2] # 只保留前2个菜单
|
|
update_perm_response = await role_api.assign_menus(role_id, updated_menu_ids)
|
|
assert update_perm_response.status_code == 200, "更新角色权限失败"
|
|
|
|
# 步骤7: 验证权限已更新
|
|
permissions_response = await role_api.get_role_permissions(role_id)
|
|
assert permissions_response.status_code == 200, "获取角色权限失败"
|
|
permissions = permissions_response.json()
|
|
assert len(permissions) == 2, "权限数量不正确"
|
|
|
|
# 步骤8: 删除角色
|
|
delete_response = await role_api.delete_role(role_id)
|
|
assert delete_response.status_code in [200, 204], "删除角色失败"
|
|
|
|
async def test_e2e_file_management_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-03: 文件上传下载流程
|
|
|
|
测试场景:
|
|
1. 上传文件
|
|
2. 验证文件信息
|
|
3. 下载文件
|
|
4. 删除文件
|
|
"""
|
|
file_api = FileAPI(authenticated_client)
|
|
|
|
# 步骤1: 上传文件
|
|
test_file_content = b"E2E test file content for upload"
|
|
test_filename = f"e2e_test_{int(time.time() * 1000)}.txt"
|
|
|
|
upload_response = await file_api.upload_file(
|
|
file_content=test_file_content,
|
|
filename=test_filename
|
|
)
|
|
assert upload_response.status_code == 201, "文件上传失败"
|
|
file_id = upload_response.json()["id"]
|
|
test_data_manager.add_file(file_id)
|
|
|
|
# 步骤2: 验证文件信息
|
|
file_info_response = await file_api.get_file_info(file_id)
|
|
assert file_info_response.status_code == 200, "获取文件信息失败"
|
|
file_info = file_info_response.json()
|
|
assert file_info["fileName"] == test_filename, "文件名不匹配"
|
|
|
|
# 步骤3: 下载文件
|
|
download_response = await file_api.download_file(file_id)
|
|
assert download_response.status_code == 200, "文件下载失败"
|
|
downloaded_content = download_response.content
|
|
assert downloaded_content == test_file_content, "文件内容不匹配"
|
|
|
|
# 步骤4: 删除文件
|
|
delete_response = await file_api.delete_file(file_id)
|
|
assert delete_response.status_code in [200, 204], "文件删除失败"
|
|
|
|
# 步骤5: 验证文件已删除
|
|
verify_delete_response = await file_api.get_file_info(file_id)
|
|
assert verify_delete_response.status_code == 404, "文件未正确删除"
|
|
|
|
async def test_e2e_audit_log_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-04: 审计日志记录流程
|
|
|
|
测试场景:
|
|
1. 执行用户操作
|
|
2. 验证操作日志记录
|
|
3. 查询操作日志
|
|
4. 验证日志详情
|
|
"""
|
|
user_api = UserAPI(authenticated_client)
|
|
audit_api = AuditAPI(authenticated_client)
|
|
|
|
unique_id = f"e2e_audit_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
# 步骤1: 执行用户创建操作
|
|
user_data = {
|
|
"username": f"e2e_audit_user_{unique_id}",
|
|
"password": "Test123!@#",
|
|
"email": f"e2e_audit_user_{unique_id}@test.com",
|
|
"phone": "13800138000",
|
|
"nickname": "E2E审计测试用户",
|
|
"status": 1
|
|
}
|
|
user_response = await user_api.create_user(user_data)
|
|
assert user_response.status_code == 201, "创建用户失败"
|
|
user_id = user_response.json()["id"]
|
|
test_data_manager.add_user(user_id)
|
|
|
|
# 步骤2: 等待日志记录
|
|
await asyncio.sleep(1)
|
|
|
|
# 步骤3: 查询操作日志
|
|
log_response = await audit_api.get_operation_logs(
|
|
page=0,
|
|
size=10
|
|
)
|
|
assert log_response.status_code == 200, "查询操作日志失败"
|
|
logs = log_response.json()["content"]
|
|
assert len(logs) > 0, "未找到操作日志"
|
|
|
|
# 步骤4: 验证日志详情
|
|
latest_log = logs[0]
|
|
assert "username" in latest_log, "日志中缺少用户名"
|
|
assert "operation" in latest_log, "日志中缺少操作类型"
|
|
assert "createdAt" in latest_log, "日志中缺少创建时间"
|
|
|
|
# 步骤5: 清理测试数据
|
|
await user_api.delete_user(user_id)
|
|
|
|
async def test_e2e_menu_management_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-05: 菜单管理流程
|
|
|
|
测试场景:
|
|
1. 创建菜单
|
|
2. 更新菜单
|
|
3. 验证菜单树结构
|
|
4. 删除菜单
|
|
"""
|
|
menu_api = MenuAPI(authenticated_client)
|
|
|
|
unique_id = f"e2e_menu_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
# 步骤1: 创建父菜单
|
|
parent_menu_data = {
|
|
"menuName": f"E2E父菜单_{unique_id}",
|
|
"parentId": 0,
|
|
"orderNum": 1,
|
|
"menuType": "M",
|
|
"status": 1,
|
|
"perms": f"e2e:parent:{unique_id}",
|
|
"component": "Layout"
|
|
}
|
|
parent_response = await menu_api.create_menu(parent_menu_data)
|
|
assert parent_response.status_code == 201, "创建父菜单失败"
|
|
parent_id = parent_response.json()["id"]
|
|
test_data_manager.add_menu(parent_id)
|
|
|
|
# 步骤2: 创建子菜单
|
|
child_menu_data = {
|
|
"menuName": f"E2E子菜单_{unique_id}",
|
|
"parentId": parent_id,
|
|
"orderNum": 1,
|
|
"menuType": "C",
|
|
"status": 1,
|
|
"perms": f"e2e:child:{unique_id}",
|
|
"component": "views/e2e-test/index"
|
|
}
|
|
child_response = await menu_api.create_menu(child_menu_data)
|
|
assert child_response.status_code == 201, "创建子菜单失败"
|
|
child_id = child_response.json()["id"]
|
|
test_data_manager.add_menu(child_id)
|
|
|
|
# 步骤3: 验证菜单树结构
|
|
tree_response = await menu_api.get_menu_tree()
|
|
assert tree_response.status_code == 200, "获取菜单树失败"
|
|
menu_tree = tree_response.json()
|
|
|
|
# 查找父菜单
|
|
parent_menu = None
|
|
for menu in menu_tree:
|
|
if menu["id"] == parent_id:
|
|
parent_menu = menu
|
|
break
|
|
|
|
assert parent_menu is not None, "未找到父菜单"
|
|
assert "children" in parent_menu, "父菜单缺少子菜单列表"
|
|
|
|
# 验证子菜单
|
|
child_found = False
|
|
for child in parent_menu["children"]:
|
|
if child["id"] == child_id:
|
|
child_found = True
|
|
break
|
|
assert child_found, "未找到子菜单"
|
|
|
|
# 步骤4: 更新菜单
|
|
update_data = {
|
|
"menuName": f"E2E子菜单-已更新_{unique_id}"
|
|
}
|
|
update_response = await menu_api.update_menu(child_id, update_data)
|
|
assert update_response.status_code == 200, "更新菜单失败"
|
|
|
|
# 步骤5: 验证更新结果
|
|
updated_menu_response = await menu_api.get_menu_by_id(child_id)
|
|
updated_menu = updated_menu_response.json()
|
|
assert updated_menu["menuName"] == update_data["menuName"], "菜单名称更新失败"
|
|
|
|
# 步骤6: 删除菜单(先删除子菜单,再删除父菜单)
|
|
delete_child_response = await menu_api.delete_menu(child_id)
|
|
assert delete_child_response.status_code in [200, 204], "删除子菜单失败"
|
|
|
|
delete_parent_response = await menu_api.delete_menu(parent_id)
|
|
assert delete_parent_response.status_code in [200, 204], "删除父菜单失败"
|
|
|
|
|
|
@pytest.mark.e2e
|
|
@pytest.mark.integration
|
|
@pytest.mark.asyncio
|
|
class TestE2EIntegrationScenarios:
|
|
"""E2E集成场景测试类"""
|
|
|
|
async def test_e2e_cross_module_workflow(
|
|
self, authenticated_client, test_data_manager
|
|
):
|
|
"""
|
|
E2E-06: 跨模块集成测试
|
|
|
|
测试场景:
|
|
1. 创建角色并分配权限
|
|
2. 创建用户并分配角色
|
|
3. 用户执行操作
|
|
4. 验证审计日志
|
|
5. 验证权限控制
|
|
"""
|
|
role_api = RoleAPI(authenticated_client)
|
|
user_api = UserAPI(authenticated_client)
|
|
menu_api = MenuAPI(authenticated_client)
|
|
audit_api = AuditAPI(authenticated_client)
|
|
|
|
unique_id = f"{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
|
|
|
# 步骤1: 创建角色
|
|
role_data = {
|
|
"roleName": f"E2E集成测试角色_{unique_id}",
|
|
"roleKey": f"e2e_integration_role_{unique_id}",
|
|
"roleSort": 1,
|
|
"status": 1
|
|
}
|
|
role_response = await role_api.create_role(role_data)
|
|
assert role_response.status_code == 201
|
|
role_id = role_response.json()["id"]
|
|
test_data_manager.add_role(role_id)
|
|
|
|
# 步骤2: 创建用户
|
|
user_data = {
|
|
"username": f"e2e_integration_user_{unique_id}",
|
|
"password": "Test123!@#",
|
|
"email": f"e2e_integration_{unique_id}@test.com",
|
|
"phone": "13800138000",
|
|
"nickname": "E2E集成测试用户",
|
|
"status": 1,
|
|
"roleId": role_id
|
|
}
|
|
user_response = await user_api.create_user(user_data)
|
|
assert user_response.status_code == 201
|
|
user_id = user_response.json()["id"]
|
|
test_data_manager.add_user(user_id)
|
|
|
|
# 步骤3: 等待审计日志记录
|
|
await asyncio.sleep(1)
|
|
|
|
# 步骤4: 验证审计日志
|
|
log_response = await audit_api.get_operation_logs(
|
|
page=0,
|
|
size=10,
|
|
username=user_data["username"]
|
|
)
|
|
assert log_response.status_code == 200
|
|
logs = log_response.json()["content"]
|
|
|
|
# 注意: 如果后端审计日志功能未完整实现,此断言可能失败
|
|
# 建议后端团队完善审计日志记录功能
|
|
if len(logs) == 0:
|
|
import warnings
|
|
warnings.warn(
|
|
"审计日志功能未完整实现,建议后端团队完善审计日志记录功能",
|
|
UserWarning
|
|
)
|
|
else:
|
|
assert len(logs) > 0, "未找到相关审计日志"
|
|
|
|
# 步骤5: 清理数据
|
|
await user_api.delete_user(user_id)
|
|
await role_api.delete_role(role_id)
|