refactor(test): 重构测试套件结构并优化测试配置
feat(test-suite): 新增测试套件模块,包含API测试客户端和测试配置 fix(api): 修复数据库实体和仓库的删除操作返回值 style(api): 统一数据库表名和字段命名 perf(api): 添加缓存注解提升配置查询性能 test(api): 添加H2测试数据库配置支持 chore: 清理旧的测试文件和脚本
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
UAT验收测试套件
|
||||
|
||||
本模块包含用户验收测试(User Acceptance Testing)相关测试用例
|
||||
|
||||
测试类型:
|
||||
1. 业务场景验收测试 - 验证核心业务流程的完整性
|
||||
2. 用户体验验收测试 - 验证界面友好性和操作便捷性
|
||||
3. 安全验收测试 - 验证权限隔离和敏感数据保护
|
||||
4. 性能验收测试 - 验证系统性能指标
|
||||
"""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,536 @@
|
||||
"""
|
||||
UAT业务场景验收测试
|
||||
|
||||
测试范围:
|
||||
1. 新员工入职流程
|
||||
2. 员工离职流程
|
||||
3. 权限变更流程
|
||||
4. 组织架构调整流程
|
||||
5. 系统配置变更流程
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
import uuid
|
||||
import asyncio
|
||||
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.config_api import ConfigAPI
|
||||
from api.audit_api import AuditAPI
|
||||
from config.settings import settings
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.business_scenario
|
||||
@pytest.mark.asyncio
|
||||
class TestBusinessScenarioUAT:
|
||||
"""业务场景验收测试类"""
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
"""已认证的HTTP客户端"""
|
||||
async with AuthAPI.create_client() as client:
|
||||
auth_api = AuthAPI(client)
|
||||
await auth_api.login(settings.TEST_USERNAME, settings.TEST_PASSWORD)
|
||||
yield client
|
||||
|
||||
@pytest.fixture
|
||||
async def test_data_manager(self):
|
||||
"""测试数据管理器"""
|
||||
from utils.test_data_manager import TestDataManager
|
||||
manager = TestDataManager()
|
||||
yield manager
|
||||
await manager.cleanup_all()
|
||||
|
||||
async def test_bs_new_employee_onboarding(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-01: 新员工入职流程
|
||||
|
||||
业务场景:
|
||||
1. HR创建新员工账户
|
||||
2. 分配基础角色(普通员工)
|
||||
3. 员工首次登录并修改密码
|
||||
4. 员工完善个人信息
|
||||
5. 验证权限范围
|
||||
|
||||
验收标准:
|
||||
- 账户创建成功
|
||||
- 角色分配正确
|
||||
- 首次登录强制修改密码
|
||||
- 个人信息可更新
|
||||
- 权限范围符合预期
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
auth_api = AuthAPI(authenticated_client)
|
||||
|
||||
unique_id = f"onboard_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
employee_role_data = {
|
||||
"roleName": f"新员工角色_{unique_id}",
|
||||
"roleKey": f"employee_role_{unique_id}",
|
||||
"roleSort": 10,
|
||||
"status": 1,
|
||||
"remark": "业务场景测试-新员工角色"
|
||||
}
|
||||
role_response = await role_api.create_role(employee_role_data)
|
||||
assert role_response.status_code == 201, "创建员工角色失败"
|
||||
role_id = role_response.json()["id"]
|
||||
test_data_manager.add_role(role_id)
|
||||
|
||||
employee_data = {
|
||||
"username": f"new_employee_{unique_id}",
|
||||
"password": "Welcome123!@#",
|
||||
"email": f"new_employee_{unique_id}@company.com",
|
||||
"phone": f"138001380{unique_id[-4:]}",
|
||||
"roleId": role_id,
|
||||
"status": 1,
|
||||
"remark": "业务场景测试-新入职员工"
|
||||
}
|
||||
user_response = await user_api.create_user(employee_data)
|
||||
assert user_response.status_code == 201, "创建员工账户失败"
|
||||
user_id = user_response.json()["id"]
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
login_response = await auth_api.login(
|
||||
f"new_employee_{unique_id}",
|
||||
"Welcome123!@#"
|
||||
)
|
||||
assert login_response.status_code == 200, "员工登录失败"
|
||||
login_data = login_response.json()
|
||||
assert "token" in login_data, "登录应返回token"
|
||||
|
||||
profile_data = {
|
||||
"nickName": f"张三_{unique_id}",
|
||||
"phone": f"139001390{unique_id[-4:]}",
|
||||
"remark": "业务场景测试-完善个人信息"
|
||||
}
|
||||
profile_response = await user_api.update_user_profile(profile_data)
|
||||
assert profile_response.status_code == 200, "更新个人信息失败"
|
||||
|
||||
permissions_response = await role_api.get_role_permissions(role_id)
|
||||
assert permissions_response.status_code == 200, "获取权限失败"
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
await role_api.delete_role(role_id)
|
||||
test_data_manager._roles.remove(role_id)
|
||||
|
||||
async def test_bs_employee_resignation(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-02: 员工离职流程
|
||||
|
||||
业务场景:
|
||||
1. 员工提交离职申请
|
||||
2. 管理员禁用员工账户
|
||||
3. 回收员工权限
|
||||
4. 归档员工数据
|
||||
5. 验证账户无法登录
|
||||
|
||||
验收标准:
|
||||
- 账户状态变更为禁用
|
||||
- 权限已回收
|
||||
- 数据已归档
|
||||
- 无法登录系统
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
auth_api = AuthAPI(authenticated_client)
|
||||
|
||||
unique_id = f"resign_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
role_data = {
|
||||
"roleName": f"离职测试角色_{unique_id}",
|
||||
"roleKey": f"resign_role_{unique_id}",
|
||||
"roleSort": 10,
|
||||
"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)
|
||||
|
||||
user_data = {
|
||||
"username": f"resign_employee_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"resign_{unique_id}@company.com",
|
||||
"roleId": role_id,
|
||||
"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)
|
||||
|
||||
disable_data = {"status": 0}
|
||||
disable_response = await user_api.update_user(user_id, disable_data)
|
||||
assert disable_response.status_code == 200, "禁用账户失败"
|
||||
|
||||
user_detail = await user_api.get_user_by_id(user_id)
|
||||
assert user_detail.status_code == 200
|
||||
user_info = user_detail.json()
|
||||
assert user_info["status"] == 0, "账户状态应为禁用"
|
||||
|
||||
login_response = await auth_api.login(
|
||||
f"resign_employee_{unique_id}",
|
||||
"Test123!@#"
|
||||
)
|
||||
assert login_response.status_code != 200, "已禁用账户不应能登录"
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
await role_api.delete_role(role_id)
|
||||
test_data_manager._roles.remove(role_id)
|
||||
|
||||
async def test_bs_permission_change(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-03: 权限变更流程
|
||||
|
||||
业务场景:
|
||||
1. 员工晋升为经理
|
||||
2. 更新角色权限
|
||||
3. 验证新权限即时生效
|
||||
4. 验证旧权限已撤销
|
||||
|
||||
验收标准:
|
||||
- 角色变更成功
|
||||
- 新权限立即生效
|
||||
- 旧权限已撤销
|
||||
- 审计日志记录完整
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
menu_api = MenuAPI(authenticated_client)
|
||||
|
||||
unique_id = f"perm_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
employee_role_data = {
|
||||
"roleName": f"员工角色_{unique_id}",
|
||||
"roleKey": f"emp_role_{unique_id}",
|
||||
"roleSort": 10,
|
||||
"status": 1
|
||||
}
|
||||
emp_role_response = await role_api.create_role(employee_role_data)
|
||||
assert emp_role_response.status_code == 201
|
||||
emp_role_id = emp_role_response.json()["id"]
|
||||
test_data_manager.add_role(emp_role_id)
|
||||
|
||||
manager_role_data = {
|
||||
"roleName": f"经理角色_{unique_id}",
|
||||
"roleKey": f"mgr_role_{unique_id}",
|
||||
"roleSort": 5,
|
||||
"status": 1
|
||||
}
|
||||
mgr_role_response = await role_api.create_role(manager_role_data)
|
||||
assert mgr_role_response.status_code == 201
|
||||
mgr_role_id = mgr_role_response.json()["id"]
|
||||
test_data_manager.add_role(mgr_role_id)
|
||||
|
||||
user_data = {
|
||||
"username": f"perm_user_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"perm_{unique_id}@company.com",
|
||||
"roleId": emp_role_id,
|
||||
"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)
|
||||
|
||||
user_before = await user_api.get_user_by_id(user_id)
|
||||
assert user_before.json()["roleId"] == emp_role_id
|
||||
|
||||
update_data = {"roleId": mgr_role_id}
|
||||
update_response = await user_api.update_user(user_id, update_data)
|
||||
assert update_response.status_code == 200, "角色变更失败"
|
||||
|
||||
user_after = await user_api.get_user_by_id(user_id)
|
||||
assert user_after.json()["roleId"] == mgr_role_id, "角色变更未生效"
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
await role_api.delete_role(mgr_role_id)
|
||||
test_data_manager._roles.remove(mgr_role_id)
|
||||
await role_api.delete_role(emp_role_id)
|
||||
test_data_manager._roles.remove(emp_role_id)
|
||||
|
||||
async def test_bs_organization_restructure(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-04: 组织架构调整流程
|
||||
|
||||
业务场景:
|
||||
1. 创建新部门
|
||||
2. 批量调整员工部门
|
||||
3. 调整部门权限
|
||||
4. 验证组织架构变更
|
||||
|
||||
验收标准:
|
||||
- 部门创建成功
|
||||
- 员工部门调整成功
|
||||
- 权限调整成功
|
||||
- 组织架构更新正确
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
unique_id = f"org_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
dept_role_data = {
|
||||
"roleName": f"新部门角色_{unique_id}",
|
||||
"roleKey": f"new_dept_role_{unique_id}",
|
||||
"roleSort": 8,
|
||||
"status": 1,
|
||||
"remark": "业务场景测试-新部门"
|
||||
}
|
||||
role_response = await role_api.create_role(dept_role_data)
|
||||
assert role_response.status_code == 201
|
||||
role_id = role_response.json()["id"]
|
||||
test_data_manager.add_role(role_id)
|
||||
|
||||
users_to_create = []
|
||||
for i in range(3):
|
||||
user_data = {
|
||||
"username": f"org_user_{i}_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"org_user_{i}_{unique_id}@company.com",
|
||||
"roleId": role_id,
|
||||
"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)
|
||||
users_to_create.append(user_id)
|
||||
|
||||
assert len(users_to_create) == 3, "应创建3个用户"
|
||||
|
||||
for user_id in users_to_create:
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
|
||||
await role_api.delete_role(role_id)
|
||||
test_data_manager._roles.remove(role_id)
|
||||
|
||||
async def test_bs_system_config_change(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-05: 系统配置变更流程
|
||||
|
||||
业务场景:
|
||||
1. 修改系统配置
|
||||
2. 验证配置生效
|
||||
3. 配置回滚
|
||||
4. 验证回滚生效
|
||||
|
||||
验收标准:
|
||||
- 配置修改成功
|
||||
- 新配置立即生效
|
||||
- 回滚操作成功
|
||||
- 回滚后配置正确
|
||||
"""
|
||||
config_api = ConfigAPI(authenticated_client)
|
||||
|
||||
unique_id = f"config_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
config_data = {
|
||||
"configKey": f"test_config_{unique_id}",
|
||||
"configName": f"测试配置_{unique_id}",
|
||||
"configType": "Y",
|
||||
"configValue": "initial_value",
|
||||
"remark": "业务场景测试-初始配置"
|
||||
}
|
||||
create_response = await config_api.create_config(config_data)
|
||||
assert create_response.status_code == 201
|
||||
config_id = create_response.json()["id"]
|
||||
test_data_manager.add_config(config_id)
|
||||
|
||||
get_response = await config_api.get_config_by_key(f"test_config_{unique_id}")
|
||||
assert get_response.status_code == 200
|
||||
assert get_response.json()["configValue"] == "initial_value"
|
||||
|
||||
update_data = {
|
||||
"configValue": "updated_value",
|
||||
"remark": "业务场景测试-更新配置"
|
||||
}
|
||||
update_response = await config_api.update_config(config_id, update_data)
|
||||
assert update_response.status_code == 200, "配置更新失败"
|
||||
|
||||
verify_response = await config_api.get_config_by_key(f"test_config_{unique_id}")
|
||||
assert verify_response.json()["configValue"] == "updated_value", "配置更新未生效"
|
||||
|
||||
rollback_data = {
|
||||
"configValue": "initial_value",
|
||||
"remark": "业务场景测试-配置回滚"
|
||||
}
|
||||
rollback_response = await config_api.update_config(config_id, rollback_data)
|
||||
assert rollback_response.status_code == 200, "配置回滚失败"
|
||||
|
||||
final_response = await config_api.get_config_by_key(f"test_config_{unique_id}")
|
||||
assert final_response.json()["configValue"] == "initial_value", "配置回滚未生效"
|
||||
|
||||
await config_api.delete_config(config_id)
|
||||
test_data_manager._configs.remove(config_id)
|
||||
|
||||
async def test_bs_audit_trail_verification(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-06: 审计日志验证流程
|
||||
|
||||
业务场景:
|
||||
1. 执行关键操作
|
||||
2. 查询审计日志
|
||||
3. 验证日志完整性
|
||||
4. 导出审计日志
|
||||
|
||||
验收标准:
|
||||
- 操作被记录
|
||||
- 日志信息完整
|
||||
- 日志可查询
|
||||
- 日志可导出
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
audit_api = AuditAPI(authenticated_client)
|
||||
|
||||
unique_id = f"audit_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
user_data = {
|
||||
"username": f"audit_user_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"audit_{unique_id}@company.com",
|
||||
"status": 1
|
||||
}
|
||||
create_response = await user_api.create_user(user_data)
|
||||
assert create_response.status_code == 201
|
||||
user_id = create_response.json()["id"]
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
audit_response = await audit_api.get_audit_logs(
|
||||
page=0,
|
||||
size=10,
|
||||
module="用户管理",
|
||||
operation="创建用户"
|
||||
)
|
||||
assert audit_response.status_code == 200, "查询审计日志失败"
|
||||
|
||||
audit_logs = audit_response.json()
|
||||
assert len(audit_logs) > 0, "应存在审计日志"
|
||||
|
||||
latest_log = audit_logs[0]
|
||||
assert "operation" in latest_log, "日志应包含操作类型"
|
||||
assert "operator" in latest_log, "日志应包含操作人"
|
||||
assert "timestamp" in latest_log, "日志应包含时间戳"
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.business_scenario
|
||||
@pytest.mark.regression
|
||||
class TestBusinessScenarioRegression:
|
||||
"""业务场景回归测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AuthAPI.create_client() as client:
|
||||
auth_api = AuthAPI(client)
|
||||
await auth_api.login(settings.TEST_USERNAME, settings.TEST_PASSWORD)
|
||||
yield client
|
||||
|
||||
@pytest.fixture
|
||||
async def test_data_manager(self):
|
||||
from utils.test_data_manager import TestDataManager
|
||||
manager = TestDataManager()
|
||||
yield manager
|
||||
await manager.cleanup_all()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bs_concurrent_user_operations(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-REG-01: 并发用户操作测试
|
||||
|
||||
验证点:
|
||||
- 多用户并发创建
|
||||
- 数据一致性
|
||||
- 无死锁
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
unique_id = f"concurrent_{int(time.time() * 1000)}"
|
||||
|
||||
async def create_user(index: int):
|
||||
user_data = {
|
||||
"username": f"concurrent_user_{index}_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"concurrent_{index}_{unique_id}@company.com",
|
||||
"status": 1
|
||||
}
|
||||
response = await user_api.create_user(user_data)
|
||||
return response
|
||||
|
||||
tasks = [create_user(i) for i in range(5)]
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
success_count = sum(1 for r in results if not isinstance(r, Exception) and r.status_code == 201)
|
||||
assert success_count >= 3, f"至少应有3个用户创建成功,实际: {success_count}"
|
||||
|
||||
for result in results:
|
||||
if not isinstance(result, Exception) and result.status_code == 201:
|
||||
user_id = result.json()["id"]
|
||||
await user_api.delete_user(user_id)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bs_data_consistency(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
BS-REG-02: 数据一致性测试
|
||||
|
||||
验证点:
|
||||
- 创建后立即查询
|
||||
- 数据一致性
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
unique_id = f"consistency_{int(time.time() * 1000)}"
|
||||
|
||||
user_data = {
|
||||
"username": f"consistency_user_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"consistency_{unique_id}@company.com",
|
||||
"status": 1
|
||||
}
|
||||
create_response = await user_api.create_user(user_data)
|
||||
assert create_response.status_code == 201
|
||||
user_id = create_response.json()["id"]
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
get_response = await user_api.get_user_by_id(user_id)
|
||||
assert get_response.status_code == 200
|
||||
|
||||
created_user = create_response.json()
|
||||
fetched_user = get_response.json()
|
||||
|
||||
assert created_user["username"] == fetched_user["username"], "用户名应一致"
|
||||
assert created_user["email"] == fetched_user["email"], "邮箱应一致"
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
test_data_manager._users.remove(user_id)
|
||||
@@ -0,0 +1,483 @@
|
||||
"""
|
||||
UAT测试套件 - 用户验收测试场景
|
||||
|
||||
测试范围:
|
||||
1. 用户注册登录验收场景
|
||||
2. 用户管理业务验收场景
|
||||
3. 角色权限配置验收场景
|
||||
4. 系统配置管理验收场景
|
||||
5. 审计日志查询验收场景
|
||||
|
||||
作者: 张翔
|
||||
日期: 2026-04-01
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
import uuid
|
||||
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.config_api import ConfigAPI
|
||||
from api.audit_api import AuditAPI
|
||||
from config.settings import settings
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.asyncio
|
||||
class TestUATUserScenarios:
|
||||
"""UAT用户场景测试类"""
|
||||
|
||||
async def test_uat_new_user_registration_and_login(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-USER-01: 新用户注册登录验收场景
|
||||
|
||||
业务场景:
|
||||
作为新用户,我希望能够注册账号并登录系统
|
||||
|
||||
验收标准:
|
||||
1. 用户能够成功注册
|
||||
2. 注册后能够立即登录
|
||||
3. 登录后能看到正确的用户信息
|
||||
4. 用户信息显示完整准确
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
auth_api = AuthAPI(authenticated_client)
|
||||
|
||||
unique_id = f"uat_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
user_data = {
|
||||
"username": f"newuser_{unique_id}",
|
||||
"password": "SecurePass123!@#",
|
||||
"email": f"newuser_{unique_id}@company.com",
|
||||
"phone": "13900139000",
|
||||
"nickname": "新员工张三",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(user_data)
|
||||
assert create_response.status_code in [201, 200], \
|
||||
"❌ 用户注册失败"
|
||||
user_id = create_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
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, \
|
||||
"❌ 未获取到登录令牌"
|
||||
|
||||
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"], \
|
||||
"❌ 邮箱不匹配"
|
||||
assert user_info["nickname"] == user_data["nickname"], \
|
||||
"❌ 昵称不匹配"
|
||||
|
||||
print("✅ UAT-USER-01: 新用户注册登录验收通过")
|
||||
|
||||
async def test_uat_user_profile_management(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-USER-02: 用户信息管理验收场景
|
||||
|
||||
业务场景:
|
||||
作为已登录用户,我希望能够修改我的个人信息
|
||||
|
||||
验收标准:
|
||||
1. 用户能够修改昵称
|
||||
2. 用户能够修改邮箱
|
||||
3. 用户能够修改手机号
|
||||
4. 修改后信息立即生效
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
unique_id = f"uat_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
user_data = {
|
||||
"username": f"profileuser_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"profile_{unique_id}@test.com",
|
||||
"phone": "13800138000",
|
||||
"nickname": "原始昵称",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(user_data)
|
||||
user_id = create_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
update_data = {
|
||||
"nickname": "更新后的昵称",
|
||||
"email": f"updated_{unique_id}@test.com",
|
||||
"phone": "13900139000"
|
||||
}
|
||||
|
||||
update_response = await user_api.update_user(user_id, update_data)
|
||||
assert update_response.status_code == 200, \
|
||||
"❌ 更新用户信息失败"
|
||||
|
||||
verify_response = await user_api.get_user_by_id(user_id)
|
||||
updated_user = verify_response.json()
|
||||
|
||||
assert updated_user["nickname"] == update_data["nickname"], \
|
||||
"❌ 昵称未更新"
|
||||
assert updated_user["email"] == update_data["email"], \
|
||||
"❌ 邮箱未更新"
|
||||
assert updated_user["phone"] == update_data["phone"], \
|
||||
"❌ 手机号未更新"
|
||||
|
||||
print("✅ UAT-USER-02: 用户信息管理验收通过")
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.asyncio
|
||||
class TestUATRolePermissionScenarios:
|
||||
"""UAT角色权限场景测试类"""
|
||||
|
||||
async def test_uat_role_creation_and_permission_assignment(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-ROLE-01: 角色创建与权限分配验收场景
|
||||
|
||||
业务场景:
|
||||
作为系统管理员,我希望能够创建新角色并分配相应权限
|
||||
|
||||
验收标准:
|
||||
1. 能够创建新角色
|
||||
2. 能够为角色分配菜单权限
|
||||
3. 分配给用户后权限立即生效
|
||||
4. 用户只能访问被授权的功能
|
||||
"""
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
user_api = UserAPI(authenticated_client)
|
||||
menu_api = MenuAPI(authenticated_client)
|
||||
|
||||
unique_id = f"uat_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
role_data = {
|
||||
"roleName": f"部门经理_{unique_id}",
|
||||
"roleKey": f"dept_manager_{unique_id}",
|
||||
"roleSort": 10,
|
||||
"status": 1,
|
||||
"remark": "部门经理角色,具有用户管理权限"
|
||||
}
|
||||
|
||||
create_response = await role_api.create_role(role_data)
|
||||
assert create_response.status_code in [201, 200], \
|
||||
"❌ 创建角色失败"
|
||||
role_id = create_response.json().get("id")
|
||||
test_data_manager.add_role(role_id)
|
||||
|
||||
menus_response = await menu_api.get_menus()
|
||||
menus = menus_response.json() if isinstance(
|
||||
menus_response.json(), list
|
||||
) else menus_response.json().get("data", [])
|
||||
|
||||
if menus:
|
||||
menu_ids = [m["id"] for m in menus[:3]]
|
||||
|
||||
perm_response = await role_api.assign_permissions(
|
||||
role_id,
|
||||
{"menuIds": menu_ids}
|
||||
)
|
||||
assert perm_response.status_code == 200, \
|
||||
"❌ 分配菜单权限失败"
|
||||
|
||||
user_data = {
|
||||
"username": f"roleuser_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"roleuser_{unique_id}@test.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1,
|
||||
"roleId": role_id
|
||||
}
|
||||
|
||||
user_response = await user_api.create_user(user_data)
|
||||
user_id = user_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
user_info = await user_api.get_user_by_id(user_id)
|
||||
assert user_info.status_code == 200, \
|
||||
"❌ 用户角色分配失败"
|
||||
|
||||
print("✅ UAT-ROLE-01: 角色创建与权限分配验收通过")
|
||||
|
||||
async def test_uat_permission_inheritance(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-ROLE-02: 权限继承验证场景
|
||||
|
||||
业务场景:
|
||||
作为系统管理员,我希望子角色能够继承父角色的权限
|
||||
|
||||
验收标准:
|
||||
1. 子角色继承父角色权限
|
||||
2. 子角色可以扩展额外权限
|
||||
3. 子角色权限不超过父角色
|
||||
"""
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
roles = roles_response.json().get("content", [])
|
||||
|
||||
assert len(roles) > 0, \
|
||||
"❌ 系统中应至少有一个角色"
|
||||
|
||||
admin_role = next(
|
||||
(r for r in roles if "admin" in r.get("roleKey", "").lower()),
|
||||
None
|
||||
)
|
||||
|
||||
if admin_role:
|
||||
assert admin_role.get("status") == 1, \
|
||||
"❌ 管理员角色应处于激活状态"
|
||||
|
||||
print("✅ UAT-ROLE-02: 权限继承验证通过")
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.asyncio
|
||||
class TestUATSystemManagementScenarios:
|
||||
"""UAT系统管理场景测试类"""
|
||||
|
||||
async def test_uat_system_configuration_management(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-SYS-01: 系统配置管理验收场景
|
||||
|
||||
业务场景:
|
||||
作为系统管理员,我希望能够管理系统配置参数
|
||||
|
||||
验收标准:
|
||||
1. 能够创建新配置项
|
||||
2. 能够修改配置值
|
||||
3. 配置修改立即生效
|
||||
4. 能够删除不需要的配置
|
||||
"""
|
||||
config_api = ConfigAPI(authenticated_client)
|
||||
|
||||
unique_id = f"uat_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
config_data = {
|
||||
"configKey": f"system.setting.{unique_id}",
|
||||
"configValue": "initial_value",
|
||||
"configName": f"测试配置_{unique_id}",
|
||||
"remark": "UAT测试配置项"
|
||||
}
|
||||
|
||||
try:
|
||||
create_response = await config_api.create_config(config_data)
|
||||
|
||||
if create_response.status_code in [201, 200]:
|
||||
config_id = create_response.json().get("id")
|
||||
|
||||
update_data = {
|
||||
"configValue": "updated_value"
|
||||
}
|
||||
update_response = await config_api.update_config(
|
||||
config_id,
|
||||
update_data
|
||||
)
|
||||
assert update_response.status_code == 200, \
|
||||
"❌ 更新配置失败"
|
||||
|
||||
get_response = await config_api.get_config_by_key(
|
||||
config_data["configKey"]
|
||||
)
|
||||
assert get_response.status_code == 200, \
|
||||
"❌ 查询配置失败"
|
||||
|
||||
delete_response = await config_api.delete_config(config_id)
|
||||
assert delete_response.status_code in [200, 204], \
|
||||
"❌ 删除配置失败"
|
||||
|
||||
print("✅ UAT-SYS-01: 系统配置管理验收通过")
|
||||
else:
|
||||
pytest.skip("系统配置功能不可用")
|
||||
except Exception as e:
|
||||
pytest.skip(f"系统配置测试跳过: {str(e)}")
|
||||
|
||||
async def test_uat_audit_log_query(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-SYS-02: 审计日志查询验收场景
|
||||
|
||||
业务场景:
|
||||
作为系统管理员,我希望能够查询系统操作日志
|
||||
|
||||
验收标准:
|
||||
1. 能够查询操作日志
|
||||
2. 能够按时间范围筛选
|
||||
3. 能够按用户筛选
|
||||
4. 日志信息完整准确
|
||||
"""
|
||||
audit_api = AuditAPI(authenticated_client)
|
||||
user_api = UserAPI(authenticated_client)
|
||||
|
||||
unique_id = f"uat_{int(time.time() * 1000)}"
|
||||
|
||||
user_data = {
|
||||
"username": f"audituser_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"audit_{unique_id}@test.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(user_data)
|
||||
|
||||
if create_response.status_code in [201, 200]:
|
||||
user_id = create_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
await user_api.delete_user(user_id)
|
||||
|
||||
operation_logs = await audit_api.get_operation_logs(
|
||||
page=0,
|
||||
size=10
|
||||
)
|
||||
assert operation_logs.status_code == 200, \
|
||||
"❌ 查询操作日志失败"
|
||||
|
||||
logs_data = operation_logs.json()
|
||||
assert "content" in logs_data or "data" in logs_data, \
|
||||
"❌ 日志数据格式不正确"
|
||||
|
||||
print("✅ UAT-SYS-02: 审计日志查询验收通过")
|
||||
else:
|
||||
pytest.skip("审计日志功能不可用")
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.asyncio
|
||||
class TestUATBusinessWorkflows:
|
||||
"""UAT业务流程测试类"""
|
||||
|
||||
async def test_uat_complete_user_onboarding_workflow(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-WF-01: 完整用户入职流程
|
||||
|
||||
业务场景:
|
||||
模拟真实的企业员工入职流程
|
||||
|
||||
流程步骤:
|
||||
1. HR创建新员工账号
|
||||
2. 分配相应角色
|
||||
3. 员工首次登录
|
||||
4. 员工修改个人信息
|
||||
5. 验证权限正确
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
auth_api = AuthAPI(authenticated_client)
|
||||
|
||||
unique_id = f"onboard_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
roles_response = await role_api.get_roles_by_page(size=1)
|
||||
roles = roles_response.json().get("content", [])
|
||||
role_id = roles[0]["id"] if roles else None
|
||||
|
||||
employee_data = {
|
||||
"username": f"employee_{unique_id}",
|
||||
"password": "Welcome123!@#",
|
||||
"email": f"employee_{unique_id}@company.com",
|
||||
"phone": "13900139000",
|
||||
"nickname": "新员工李四",
|
||||
"status": 1,
|
||||
"roleId": role_id
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(employee_data)
|
||||
assert create_response.status_code in [201, 200], \
|
||||
"❌ HR创建员工账号失败"
|
||||
user_id = create_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
login_response = await auth_api.login(
|
||||
employee_data["username"],
|
||||
employee_data["password"]
|
||||
)
|
||||
assert login_response.status_code == 200, \
|
||||
"❌ 员工首次登录失败"
|
||||
|
||||
update_data = {
|
||||
"nickname": "李四(已认证)",
|
||||
"phone": "13900139001"
|
||||
}
|
||||
update_response = await user_api.update_user(user_id, update_data)
|
||||
assert update_response.status_code == 200, \
|
||||
"❌ 员工修改个人信息失败"
|
||||
|
||||
print("✅ UAT-WF-01: 完整用户入职流程验收通过")
|
||||
|
||||
async def test_uat_role_permission_change_workflow(
|
||||
self, authenticated_client, test_data_manager
|
||||
):
|
||||
"""
|
||||
UAT-WF-02: 角色权限变更流程
|
||||
|
||||
业务场景:
|
||||
模拟员工晋升后权限调整流程
|
||||
|
||||
流程步骤:
|
||||
1. 创建普通员工账号
|
||||
2. 验证初始权限
|
||||
3. 员工晋升,调整角色
|
||||
4. 验证新权限生效
|
||||
"""
|
||||
user_api = UserAPI(authenticated_client)
|
||||
role_api = RoleAPI(authenticated_client)
|
||||
|
||||
unique_id = f"promo_{int(time.time() * 1000)}_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
roles_response = await role_api.get_roles_by_page()
|
||||
roles = roles_response.json().get("content", [])
|
||||
|
||||
if len(roles) >= 2:
|
||||
initial_role = roles[0]
|
||||
promoted_role = roles[1]
|
||||
|
||||
user_data = {
|
||||
"username": f"promoted_{unique_id}",
|
||||
"password": "Test123!@#",
|
||||
"email": f"promoted_{unique_id}@test.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1,
|
||||
"roleId": initial_role["id"]
|
||||
}
|
||||
|
||||
create_response = await user_api.create_user(user_data)
|
||||
user_id = create_response.json().get("id")
|
||||
test_data_manager.add_user(user_id)
|
||||
|
||||
assign_response = await user_api.assign_roles(
|
||||
user_id,
|
||||
[promoted_role["id"]]
|
||||
)
|
||||
assert assign_response.status_code == 200, \
|
||||
"❌ 调整角色失败"
|
||||
|
||||
print("✅ UAT-WF-02: 角色权限变更流程验收通过")
|
||||
else:
|
||||
pytest.skip("需要至少2个角色才能测试权限变更")
|
||||
@@ -0,0 +1,421 @@
|
||||
"""
|
||||
UAT用户体验验收测试
|
||||
|
||||
测试范围:
|
||||
1. 界面友好性验证
|
||||
2. 操作便捷性验证
|
||||
3. 错误提示友好性验证
|
||||
4. 响应时间验收
|
||||
5. 可访问性验证
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
import asyncio
|
||||
from typing import Dict, Any
|
||||
from playwright.async_api import async_playwright, Page, expect
|
||||
from api.auth_api import AuthAPI
|
||||
from api.user_api import UserAPI
|
||||
from api.role_api import RoleAPI
|
||||
from config.settings import settings
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.user_experience
|
||||
@pytest.mark.asyncio
|
||||
class TestUserExperienceUAT:
|
||||
"""用户体验验收测试类"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
"""浏览器fixture"""
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
"""浏览器上下文fixture"""
|
||||
context = await browser.new_context(
|
||||
viewport={"width": 1920, "height": 1080},
|
||||
locale="zh-CN"
|
||||
)
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
"""页面fixture"""
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_page(self, page):
|
||||
"""已认证的页面fixture"""
|
||||
await page.goto(f"{settings.FRONTEND_URL}/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await page.wait_for_url("**/")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
yield page
|
||||
|
||||
async def test_ue_interface_friendly_login(self, page):
|
||||
"""
|
||||
UE-01: 登录界面友好性验证
|
||||
|
||||
验证点:
|
||||
- 登录页面布局合理
|
||||
- 输入框提示清晰
|
||||
- 按钮位置合理
|
||||
- 错误提示友好
|
||||
"""
|
||||
await page.goto(f"{settings.FRONTEND_URL}/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
username_input = await page.query_selector('input[placeholder="请输入用户名"]')
|
||||
assert username_input is not None, "用户名输入框应存在"
|
||||
|
||||
password_input = await page.query_selector('input[placeholder="请输入密码"]')
|
||||
assert password_input is not None, "密码输入框应存在"
|
||||
|
||||
submit_button = await page.query_selector('button[type="submit"]')
|
||||
assert submit_button is not None, "登录按钮应存在"
|
||||
|
||||
await page.fill('input[placeholder="请输入用户名"]', "wrong_user")
|
||||
await page.fill('input[placeholder="请输入密码"]', "wrong_pass")
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
error_message = await page.query_selector('.el-message--error')
|
||||
assert error_message is not None, "应显示错误提示"
|
||||
|
||||
error_text = await error_message.text_content()
|
||||
assert len(error_text) > 0, "错误提示应有内容"
|
||||
|
||||
async def test_ue_interface_friendly_dashboard(self, authenticated_page):
|
||||
"""
|
||||
UE-02: 仪表盘界面友好性验证
|
||||
|
||||
验证点:
|
||||
- 页面布局清晰
|
||||
- 导航菜单易用
|
||||
- 数据展示直观
|
||||
- 响应式设计
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
sidebar = await page.query_selector('.sidebar-container')
|
||||
assert sidebar is not None, "侧边栏应存在"
|
||||
|
||||
navbar = await page.query_selector('.navbar')
|
||||
assert navbar is not None, "导航栏应存在"
|
||||
|
||||
main_content = await page.query_selector('.app-main')
|
||||
assert main_content is not None, "主内容区应存在"
|
||||
|
||||
await page.set_viewport_size({"width": 768, "height": 1024})
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
mobile_menu = await page.query_selector('.mobile-menu')
|
||||
assert mobile_menu is not None or sidebar is not None, "移动端应显示菜单按钮或侧边栏"
|
||||
|
||||
async def test_ue_operation_convenience_user_management(self, authenticated_page):
|
||||
"""
|
||||
UE-03: 用户管理操作便捷性验证
|
||||
|
||||
验证点:
|
||||
- 列表加载快速
|
||||
- 搜索功能便捷
|
||||
- 操作按钮明显
|
||||
- 表单填写简单
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
start_time = time.time()
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
load_time = time.time() - start_time
|
||||
|
||||
assert load_time < 3.0, f"用户管理页面加载时间应小于3秒,实际: {load_time:.2f}秒"
|
||||
|
||||
search_input = await page.query_selector('input[placeholder*="搜索"]')
|
||||
if search_input:
|
||||
await search_input.fill("admin")
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
table_rows = await page.query_selector_all('.el-table__row')
|
||||
assert len(table_rows) > 0, "搜索应返回结果"
|
||||
|
||||
create_button = await page.query_selector('button:has-text("新增")')
|
||||
assert create_button is not None, "新增按钮应存在且明显"
|
||||
|
||||
await create_button.click()
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
dialog = await page.query_selector('.el-dialog')
|
||||
assert dialog is not None, "新增用户对话框应弹出"
|
||||
|
||||
form_items = await dialog.query_selector_all('.el-form-item')
|
||||
assert len(form_items) > 0, "表单应包含必填项"
|
||||
|
||||
async def test_ue_operation_convenience_role_management(self, authenticated_page):
|
||||
"""
|
||||
UE-04: 角色管理操作便捷性验证
|
||||
|
||||
验证点:
|
||||
- 角色列表清晰
|
||||
- 权限树易操作
|
||||
- 批量操作支持
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
await page.click('text=角色管理')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
role_table = await page.query_selector('.el-table')
|
||||
assert role_table is not None, "角色表格应存在"
|
||||
|
||||
edit_button = await page.query_selector('button:has-text("编辑")')
|
||||
if edit_button:
|
||||
await edit_button.click()
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
permission_tree = await page.query_selector('.el-tree')
|
||||
assert permission_tree is not None, "权限树应存在"
|
||||
|
||||
tree_checkboxes = await permission_tree.query_selector_all('.el-checkbox')
|
||||
assert len(tree_checkboxes) > 0, "权限树应包含可选项"
|
||||
|
||||
async def test_ue_error_message_friendly(self, page):
|
||||
"""
|
||||
UE-05: 错误提示友好性验证
|
||||
|
||||
验证点:
|
||||
- 错误信息清晰
|
||||
- 错误位置明确
|
||||
- 解决建议提供
|
||||
"""
|
||||
await page.goto(f"{settings.FRONTEND_URL}/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
await page.click('button[type="submit"]')
|
||||
await asyncio.sleep(1)
|
||||
|
||||
validation_errors = await page.query_selector_all('.el-form-item__error')
|
||||
assert len(validation_errors) > 0, "应显示表单验证错误"
|
||||
|
||||
for error in validation_errors:
|
||||
error_text = await error.text_content()
|
||||
assert len(error_text) > 0, "错误信息应有内容"
|
||||
assert "请" in error_text or "不能为空" in error_text, "错误信息应友好"
|
||||
|
||||
async def test_ue_response_time_acceptance(self, authenticated_page):
|
||||
"""
|
||||
UE-06: 响应时间验收
|
||||
|
||||
验证点:
|
||||
- 页面加载时间 < 3秒
|
||||
- API响应时间 < 1秒
|
||||
- 列表查询时间 < 2秒
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
pages_to_test = [
|
||||
("用户管理", "用户管理页面"),
|
||||
("角色管理", "角色管理页面"),
|
||||
("菜单管理", "菜单管理页面")
|
||||
]
|
||||
|
||||
for menu_text, page_name in pages_to_test:
|
||||
start_time = time.time()
|
||||
await page.click(f'text={menu_text}')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
load_time = time.time() - start_time
|
||||
|
||||
assert load_time < 3.0, f"{page_name}加载时间应小于3秒,实际: {load_time:.2f}秒"
|
||||
|
||||
async def test_ue_accessibility_verification(self, authenticated_page):
|
||||
"""
|
||||
UE-07: 可访问性验证
|
||||
|
||||
验证点:
|
||||
- 键盘导航支持
|
||||
- ARIA标签存在
|
||||
- 对比度合理
|
||||
- 字体大小合适
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
await page.keyboard.press('Tab')
|
||||
await asyncio.sleep(0.2)
|
||||
|
||||
focused_element = await page.query_selector(':focus')
|
||||
assert focused_element is not None, "应支持键盘导航"
|
||||
|
||||
buttons = await page.query_selector_all('button')
|
||||
for button in buttons[:5]:
|
||||
aria_label = await button.get_attribute('aria-label')
|
||||
text_content = await button.text_content()
|
||||
assert aria_label or text_content, "按钮应有ARIA标签或文本内容"
|
||||
|
||||
body = await page.query_selector('body')
|
||||
font_size = await body.evaluate('el => window.getComputedStyle(el).fontSize')
|
||||
font_size_value = float(font_size.replace('px', ''))
|
||||
assert font_size_value >= 14, f"字体大小应不小于14px,实际: {font_size_value}px"
|
||||
|
||||
async def test_ue_form_validation_feedback(self, authenticated_page):
|
||||
"""
|
||||
UE-08: 表单验证反馈验证
|
||||
|
||||
验证点:
|
||||
- 实时验证反馈
|
||||
- 验证规则清晰
|
||||
- 错误位置标记
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
create_button = await page.query_selector('button:has-text("新增")')
|
||||
await create_button.click()
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
username_input = await page.query_selector('input[placeholder*="用户名"]')
|
||||
if username_input:
|
||||
await username_input.fill("a")
|
||||
await username_input.blur()
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
error_message = await page.query_selector('.el-form-item__error')
|
||||
if error_message:
|
||||
error_text = await error_message.text_content()
|
||||
assert len(error_text) > 0, "应显示验证错误信息"
|
||||
|
||||
async def test_ue_loading_state_feedback(self, authenticated_page):
|
||||
"""
|
||||
UE-09: 加载状态反馈验证
|
||||
|
||||
验证点:
|
||||
- 加载动画显示
|
||||
- 加载提示清晰
|
||||
- 禁用重复提交
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
loading_overlay = await page.query_selector('.el-loading-mask')
|
||||
|
||||
create_button = await page.query_selector('button:has-text("新增")')
|
||||
if create_button:
|
||||
is_disabled = await create_button.is_disabled()
|
||||
assert not is_disabled, "按钮应可点击"
|
||||
|
||||
async def test_ue_confirmation_dialog(self, authenticated_page):
|
||||
"""
|
||||
UE-10: 确认对话框验证
|
||||
|
||||
验证点:
|
||||
- 危险操作有确认
|
||||
- 确认信息清晰
|
||||
- 取消操作支持
|
||||
"""
|
||||
page = authenticated_page
|
||||
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
delete_buttons = await page.query_selector_all('button:has-text("删除")')
|
||||
|
||||
if len(delete_buttons) > 0:
|
||||
await delete_buttons[0].click()
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
confirm_dialog = await page.query_selector('.el-message-box')
|
||||
assert confirm_dialog is not None, "删除操作应弹出确认对话框"
|
||||
|
||||
cancel_button = await confirm_dialog.query_selector('button:has-text("取消")')
|
||||
assert cancel_button is not None, "确认对话框应有取消按钮"
|
||||
|
||||
await cancel_button.click()
|
||||
await asyncio.sleep(0.5)
|
||||
|
||||
dialog_closed = await page.query_selector('.el-message-box')
|
||||
assert dialog_closed is None, "点击取消应关闭对话框"
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.user_experience
|
||||
@pytest.mark.regression
|
||||
class TestUserExperienceRegression:
|
||||
"""用户体验回归测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context(viewport={"width": 1920, "height": 1080})
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ue_browser_compatibility(self, page):
|
||||
"""
|
||||
UE-REG-01: 浏览器兼容性验证
|
||||
|
||||
验证点:
|
||||
- Chrome浏览器兼容
|
||||
- 主要功能正常
|
||||
"""
|
||||
await page.goto(f"{settings.FRONTEND_URL}/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
login_form = await page.query_selector('.login-form')
|
||||
assert login_form is not None, "登录表单应正常显示"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_ue_responsive_design(self, page):
|
||||
"""
|
||||
UE-REG-02: 响应式设计验证
|
||||
|
||||
验证点:
|
||||
- 桌面端显示正常
|
||||
- 平板端显示正常
|
||||
- 移动端显示正常
|
||||
"""
|
||||
viewports = [
|
||||
{"width": 1920, "height": 1080, "name": "桌面端"},
|
||||
{"width": 768, "height": 1024, "name": "平板端"},
|
||||
{"width": 375, "height": 667, "name": "移动端"}
|
||||
]
|
||||
|
||||
for viewport in viewports:
|
||||
await page.set_viewport_size({"width": viewport["width"], "height": viewport["height"]})
|
||||
await page.goto(f"{settings.FRONTEND_URL}/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
login_form = await page.query_selector('.login-form')
|
||||
assert login_form is not None, f"{viewport['name']}登录表单应正常显示"
|
||||
@@ -0,0 +1,751 @@
|
||||
"""
|
||||
用户生命周期UAT测试 - 模拟真实业务场景
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import time
|
||||
from playwright.async_api import async_playwright, Page
|
||||
from httpx import AsyncClient
|
||||
|
||||
from config.settings import settings
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.user_lifecycle
|
||||
class TestUserLifecycleUAT:
|
||||
"""用户生命周期UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
"""浏览器fixture"""
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
"""浏览器上下文fixture"""
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
"""页面fixture"""
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
"""已认证的HTTP客户端"""
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_user_registration_and_login(self, page, authenticated_client):
|
||||
"""UAT: 用户注册和登录流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"uat_user_{timestamp}"
|
||||
email = f"uat_{timestamp}@example.com"
|
||||
password = "Test123!@#"
|
||||
|
||||
# 步骤1: 通过API创建用户
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": password,
|
||||
"email": email,
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201, f"创建用户失败: {response.text}"
|
||||
user_id = response.json()["id"]
|
||||
|
||||
try:
|
||||
# 步骤2: 通过前端登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', password)
|
||||
await page.click('button[type="submit"]')
|
||||
|
||||
await page.wait_for_url("**/")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 步骤3: 验证登录成功
|
||||
title = await page.title()
|
||||
assert "首页" in title or "Dashboard" in title, "登录后未跳转到首页"
|
||||
|
||||
# 步骤4: 验证用户信息显示
|
||||
user_info = await page.query_selector('.user-info')
|
||||
assert user_info is not None, "用户信息元素未找到"
|
||||
|
||||
finally:
|
||||
# 清理
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_user_profile_update(self, page, authenticated_client):
|
||||
"""UAT: 用户资料更新流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"uat_profile_{timestamp}"
|
||||
email = f"uat_profile_{timestamp}@example.com"
|
||||
|
||||
# 步骤1: 创建测试用户
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": email,
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201
|
||||
user_id = response.json()["id"]
|
||||
|
||||
try:
|
||||
# 步骤2: 登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', "Test123!@#")
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 步骤3: 访问用户资料页面
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
# 步骤4: 编辑用户
|
||||
await page.click('text=编辑')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 步骤5: 更新资料
|
||||
await page.fill('input[placeholder=""]', '测试用户昵称')
|
||||
await page.fill('input[placeholder=""]', '13900139000')
|
||||
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 步骤6: 验证更新
|
||||
page_content = await page.content()
|
||||
assert "测试用户昵称" in page_content
|
||||
|
||||
finally:
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_user_status_change(self, page, authenticated_client):
|
||||
"""UAT: 用户状态变更流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"uat_status_{timestamp}"
|
||||
|
||||
# 创建用户
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"uat_status_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201
|
||||
user_id = response.json()["id"]
|
||||
|
||||
try:
|
||||
# 登录并禁用用户
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', "Test123!@#")
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 尝试禁用用户
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
# 禁用用户
|
||||
await page.click('button:has-text("禁用")')
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证状态变更
|
||||
page_content = await page.content()
|
||||
assert "禁用" in page_content
|
||||
|
||||
finally:
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_user_delete(self, page, authenticated_client):
|
||||
"""UAT: 用户删除流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"uat_delete_{timestamp}"
|
||||
|
||||
# 创建用户
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"uat_delete_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201
|
||||
user_id = response.json()["id"]
|
||||
|
||||
# 登录并删除用户
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', "Test123!@#")
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
await page.click('button:has-text("删除")')
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证删除
|
||||
page_content = await page.content()
|
||||
assert username not in page_content
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.role_workflow
|
||||
class TestRoleWorkflowUAT:
|
||||
"""角色工作流UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_role_assignment(self, page, authenticated_client):
|
||||
"""UAT: 角色分配流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
username = f"uat_role_user_{timestamp}"
|
||||
role_name = f"UAT_Role_{timestamp}"
|
||||
|
||||
# 创建角色
|
||||
role_response = await authenticated_client.post(
|
||||
"/api/roles",
|
||||
json={
|
||||
"roleName": role_name,
|
||||
"roleKey": f"uat_role_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert role_response.status_code == 201
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
try:
|
||||
# 创建用户
|
||||
user_response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"uat_role_user_{timestamp}@example.com",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert user_response.status_code == 201
|
||||
user_id = user_response.json()["id"]
|
||||
|
||||
# 登录并分配角色
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', "Test123!@#")
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
# 分配角色
|
||||
await page.click('text=分配角色')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 选择角色
|
||||
await page.click(f'text={role_name}')
|
||||
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证角色分配
|
||||
user_info = await authenticated_client.get(f"/api/users/{user_id}")
|
||||
assert user_info.status_code == 200
|
||||
user_data = user_info.json()
|
||||
assert user_data["roleId"] == role_id
|
||||
|
||||
finally:
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
await authenticated_client.delete(f"/api/roles/{role_id}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_role_permission_management(self, page, authenticated_client):
|
||||
"""UAT: 角色权限管理流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
role_name = f"UAT_Permission_Role_{timestamp}"
|
||||
|
||||
# 创建角色
|
||||
role_response = await authenticated_client.post(
|
||||
"/api/roles",
|
||||
json={
|
||||
"roleName": role_name,
|
||||
"roleKey": f"uat_perm_role_{timestamp}",
|
||||
"roleSort": 1,
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert role_response.status_code == 201
|
||||
role_id = role_response.json()["id"]
|
||||
|
||||
try:
|
||||
# 登录并访问角色管理
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=角色管理')
|
||||
await page.wait_for_url("**/roles")
|
||||
|
||||
# 编辑角色权限
|
||||
await page.click('text=编辑')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 更新角色信息
|
||||
await page.fill('input[placeholder=""]', f"{role_name}_updated")
|
||||
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证更新
|
||||
roles_response = await authenticated_client.get("/api/roles")
|
||||
roles = roles_response.json()
|
||||
role_exists = any(r['roleName'] == f"{role_name}_updated" for r in roles)
|
||||
assert role_exists
|
||||
|
||||
finally:
|
||||
await authenticated_client.delete(f"/api/roles/{role_id}")
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.config_workflow
|
||||
class TestConfigWorkflowUAT:
|
||||
"""配置工作流UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_system_config_update(self, page, authenticated_client):
|
||||
"""UAT: 系统配置更新流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 登录并访问系统配置
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=系统配置')
|
||||
await page.wait_for_url("**/config")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 编辑配置
|
||||
await page.click('text=编辑')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 更新配置
|
||||
await page.fill('input[placeholder=""]', f"Test_Config_{timestamp}")
|
||||
await page.fill('textarea[placeholder=""]', '测试配置内容')
|
||||
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证更新
|
||||
config_response = await authenticated_client.get("/api/config")
|
||||
assert config_response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.data_dict_workflow
|
||||
class TestDataDictWorkflowUAT:
|
||||
"""数据字典工作流UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_dict_type_management(self, page, authenticated_client):
|
||||
"""UAT: 字典类型管理流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
dict_type = f"UAT_DICT_{timestamp}"
|
||||
|
||||
# 登录并访问字典管理
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=字典管理')
|
||||
await page.wait_for_url("**/dicts")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 创建字典类型
|
||||
await page.click('text=新增字典')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
await page.fill('input[placeholder=""]', dict_type)
|
||||
await page.fill('input[placeholder=""]', f"uat_dict_{timestamp}")
|
||||
await page.fill('textarea[placeholder=""]', '测试字典类型')
|
||||
|
||||
await page.click('button:has-text("确定")')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证创建
|
||||
dicts_response = await authenticated_client.get("/api/dict/types")
|
||||
assert dicts_response.status_code == 200
|
||||
dicts = dicts_response.json()
|
||||
dict_exists = any(d['type'] == dict_type for d in dicts)
|
||||
assert dict_exists
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_dict_data_management(self, page, authenticated_client):
|
||||
"""UAT: 字典数据管理流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 登录并访问字典管理
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=字典管理')
|
||||
await page.wait_for_url("**/dicts")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 查看字典数据
|
||||
await page.click('text=查看')
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证字典数据列表
|
||||
page_content = await page.content()
|
||||
assert "字典数据" in page_content or "code" in page_content.lower()
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.audit_workflow
|
||||
class TestAuditWorkflowUAT:
|
||||
"""审计工作流UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_operation_log_audit(self, page, authenticated_client):
|
||||
"""UAT: 操作日志审计流程"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 登录并访问操作日志
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=操作日志')
|
||||
await page.wait_for_url("**/operation-logs")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证操作日志列表
|
||||
await page.wait_for_selector('.el-card', timeout=10000)
|
||||
|
||||
# 通过API验证
|
||||
logs_response = await authenticated_client.get("/api/audit/operation-logs")
|
||||
assert logs_response.status_code == 200
|
||||
logs = logs_response.json()
|
||||
assert len(logs) > 0, "操作日志为空"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_login_log_audit(self, page, authenticated_client):
|
||||
"""UAT: 登录日志审计流程"""
|
||||
# 登录并访问登录日志
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', settings.TEST_USERNAME)
|
||||
await page.fill('input[placeholder="请输入密码"]', settings.TEST_PASSWORD)
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
await page.click('text=登录日志')
|
||||
await page.wait_for_url("**/login-logs")
|
||||
await page.wait_for_load_state("networkidle")
|
||||
|
||||
# 验证登录日志列表
|
||||
await page.wait_for_selector('.el-card', timeout=10000)
|
||||
|
||||
# 通过API验证
|
||||
login_logs_response = await authenticated_client.get("/api/audit/login-logs")
|
||||
assert login_logs_response.status_code == 200
|
||||
login_logs = login_logs_response.json()
|
||||
assert len(login_logs) > 0, "登录日志为空"
|
||||
|
||||
|
||||
@pytest.mark.uat
|
||||
@pytest.mark.comprehensive_workflow
|
||||
class TestComprehensiveWorkflowUAT:
|
||||
"""综合业务流程UAT测试"""
|
||||
|
||||
@pytest.fixture
|
||||
async def browser(self):
|
||||
async with async_playwright() as p:
|
||||
browser = await p.chromium.launch(headless=True)
|
||||
yield browser
|
||||
await browser.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def context(self, browser):
|
||||
context = await browser.new_context()
|
||||
yield context
|
||||
await context.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def page(self, context):
|
||||
page = await context.new_page()
|
||||
page.set_default_timeout(30000)
|
||||
yield page
|
||||
await page.close()
|
||||
|
||||
@pytest.fixture
|
||||
async def authenticated_client(self):
|
||||
async with AsyncClient(base_url=settings.API_BASE_URL) as client:
|
||||
response = await client.post(
|
||||
"/api/auth/login",
|
||||
json={
|
||||
"username": settings.TEST_USERNAME,
|
||||
"password": settings.TEST_PASSWORD
|
||||
}
|
||||
)
|
||||
assert response.status_code == 200
|
||||
token = response.json().get("token")
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
yield client
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_uat_complete_business_workflow(self, page, authenticated_client):
|
||||
"""UAT: 完整业务流程测试"""
|
||||
timestamp = int(time.time() * 1000)
|
||||
|
||||
# 步骤1: 用户注册
|
||||
username = f"uat_complete_{timestamp}"
|
||||
response = await authenticated_client.post(
|
||||
"/api/users",
|
||||
json={
|
||||
"username": username,
|
||||
"password": "Test123!@#",
|
||||
"email": f"uat_complete_{timestamp}@example.com",
|
||||
"phone": "13800138000",
|
||||
"status": 1
|
||||
}
|
||||
)
|
||||
assert response.status_code == 201
|
||||
user_id = response.json()["id"]
|
||||
|
||||
try:
|
||||
# 步骤2: 用户登录
|
||||
await page.goto("http://localhost:3002/login")
|
||||
await page.fill('input[placeholder="请输入用户名"]', username)
|
||||
await page.fill('input[placeholder="请输入密码"]', "Test123!@#")
|
||||
await page.click('button[type="submit"]')
|
||||
await page.wait_for_url("**/")
|
||||
|
||||
# 步骤3: 浏览用户管理
|
||||
await page.click('text=用户管理')
|
||||
await page.wait_for_url("**/users")
|
||||
|
||||
# 步骤4: 浏览角色管理
|
||||
await page.click('text=角色管理')
|
||||
await page.wait_for_url("**/roles")
|
||||
|
||||
# 步骤5: 浏览系统配置
|
||||
await page.click('text=系统配置')
|
||||
await page.wait_for_url("**/config")
|
||||
|
||||
# 步骤6: 浏览字典管理
|
||||
await page.click('text=字典管理')
|
||||
await page.wait_for_url("**/dicts")
|
||||
|
||||
# 步骤7: 浏览通知管理
|
||||
await page.click('text=通知管理')
|
||||
await page.wait_for_url("**/notices")
|
||||
|
||||
# 步骤8: 浏览文件管理
|
||||
await page.click('text=文件管理')
|
||||
await page.wait_for_url("**/files")
|
||||
|
||||
# 步骤9: 浏览审计日志
|
||||
await page.click('text=操作日志')
|
||||
await page.wait_for_url("**/operation-logs")
|
||||
|
||||
# 步骤10: 登出
|
||||
await page.click('text=退出登录')
|
||||
await page.wait_for_url("**/login")
|
||||
|
||||
finally:
|
||||
await authenticated_client.delete(f"/api/users/{user_id}")
|
||||
Reference in New Issue
Block a user