refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage

This commit is contained in:
张翔
2026-04-17 18:35:50 +08:00
parent 666189b676
commit deb961c427
916 changed files with 108360 additions and 38328 deletions
+11
View File
@@ -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']}登录表单应正常显示"
+751
View File
@@ -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}")