205 lines
7.3 KiB
Python
205 lines
7.3 KiB
Python
"""SYS-001 ~ SYS-016: 系统用户、角色、菜单管理测试。
|
||
|
||
覆盖功能点:
|
||
- 用户 CRUD(新增、停用、重置密码)
|
||
- 角色 CRUD(新增、编码重复)
|
||
- 菜单管理
|
||
- 角色授权
|
||
- 权限越权测试
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import pytest
|
||
|
||
|
||
@pytest.mark.system
|
||
@pytest.mark.p0
|
||
class TestUserManagement:
|
||
"""SYS-001 ~ SYS-005: 用户管理测试。"""
|
||
|
||
def test_create_user_success(self, client, admin_headers, seed_roles):
|
||
"""SYS-001: 新增用户成功。"""
|
||
resp = client.post("/api/system/users", headers=admin_headers, json={
|
||
"username": "newuser01",
|
||
"password": "password123",
|
||
"real_name": "新用户",
|
||
"mobile": "13800009001",
|
||
"role_id": 2,
|
||
})
|
||
assert resp.status_code == 200
|
||
data = resp.json()
|
||
assert data["code"] == 0
|
||
|
||
def test_duplicate_username(self, client, admin_headers, seed_users):
|
||
"""SYS-002: 用户账号重复应返回 40006。"""
|
||
resp = client.post("/api/system/users", headers=admin_headers, json={
|
||
"username": "admin01",
|
||
"password": "password123",
|
||
"real_name": "重复用户",
|
||
"role_id": 1,
|
||
})
|
||
assert resp.status_code in (400, 409)
|
||
|
||
def test_disable_user(self, client, admin_headers, seed_users, db_session):
|
||
"""SYS-004: 停用用户成功。
|
||
|
||
实际接口: POST /api/system/users/{user_id}/status
|
||
请求体: {"status": 0}
|
||
"""
|
||
from backend.app.models.system import User
|
||
user = db_session.query(User).filter(User.username == "sales01").first()
|
||
|
||
resp = client.post(f"/api/system/users/{user.id}/status", headers=admin_headers, json={
|
||
"status": 0,
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_reset_password(self, client, admin_headers, seed_users, db_session):
|
||
"""SYS-005: 重置密码成功。
|
||
|
||
实际接口: POST /api/system/users/{user_id}/reset-password
|
||
请求体: {"new_password": "xxx"}
|
||
"""
|
||
from backend.app.models.system import User
|
||
user = db_session.query(User).filter(User.username == "sales01").first()
|
||
|
||
resp = client.post(f"/api/system/users/{user.id}/reset-password", headers=admin_headers, json={
|
||
"new_password": "newpassword123",
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
def test_user_list(self, client, admin_headers, seed_users):
|
||
"""SYS-015: 用户列表查询。"""
|
||
resp = client.get("/api/system/users", headers=admin_headers)
|
||
assert resp.status_code == 200
|
||
data = resp.json()
|
||
assert "list" in data["data"]
|
||
|
||
|
||
@pytest.mark.system
|
||
@pytest.mark.p0
|
||
class TestRoleManagement:
|
||
"""SYS-006 ~ SYS-008: 角色管理测试。"""
|
||
|
||
def test_create_role_success(self, client, admin_headers):
|
||
"""SYS-006: 新增角色成功。"""
|
||
resp = client.post("/api/system/roles", headers=admin_headers, json={
|
||
"role_name": "测试角色",
|
||
"role_code": "test_role",
|
||
"status": 1,
|
||
})
|
||
assert resp.status_code == 200
|
||
data = resp.json()
|
||
assert data["code"] == 0
|
||
|
||
def test_duplicate_role_code(self, client, admin_headers, seed_roles):
|
||
"""SYS-007: 角色编码重复应返回 40006。"""
|
||
resp = client.post("/api/system/roles", headers=admin_headers, json={
|
||
"role_name": "重复角色",
|
||
"role_code": "admin",
|
||
"status": 1,
|
||
})
|
||
assert resp.status_code in (400, 409)
|
||
|
||
def test_role_list(self, client, admin_headers, seed_roles):
|
||
"""角色列表查询。
|
||
|
||
注意: seed_roles fixture 创建了 5 个角色,
|
||
但 seed_data 也会创建角色(通过 seed_menus 依赖 seed_users),
|
||
实际数量取决于 fixture 顺序。
|
||
"""
|
||
resp = client.get("/api/system/roles", headers=admin_headers)
|
||
assert resp.status_code == 200
|
||
data = resp.json()
|
||
# 至少有 seed_roles 创建的 5 个角色
|
||
roles = data["data"] if isinstance(data["data"], list) else data["data"].get("list", [])
|
||
assert len(roles) >= 5
|
||
|
||
|
||
@pytest.mark.system
|
||
@pytest.mark.p1
|
||
class TestMenuManagement:
|
||
"""SYS-009 ~ SYS-011: 菜单管理测试。"""
|
||
|
||
def test_create_menu_success(self, client, admin_headers):
|
||
"""SYS-009: 新增菜单成功。"""
|
||
resp = client.post("/api/system/menus", headers=admin_headers, json={
|
||
"menu_name": "测试菜单",
|
||
"menu_path": "/test",
|
||
"menu_type": "page",
|
||
"permission_code": "test:menu:view",
|
||
"sort_no": 99,
|
||
})
|
||
assert resp.status_code == 200
|
||
data = resp.json()
|
||
assert data["code"] == 0
|
||
|
||
def test_duplicate_permission_code(self, client, admin_headers, seed_menus):
|
||
"""SYS-010: 菜单权限编码重复应返回 40006。"""
|
||
resp = client.post("/api/system/menus", headers=admin_headers, json={
|
||
"menu_name": "重复菜单",
|
||
"menu_path": "/test2",
|
||
"menu_type": "page",
|
||
"permission_code": "order:list",
|
||
"sort_no": 100,
|
||
})
|
||
assert resp.status_code in (400, 409)
|
||
|
||
def test_menu_tree(self, client, admin_headers, seed_menus):
|
||
"""菜单树查询。"""
|
||
resp = client.get("/api/system/menus", headers=admin_headers)
|
||
assert resp.status_code == 200
|
||
|
||
|
||
@pytest.mark.system
|
||
@pytest.mark.p0
|
||
class TestRoleAuthorization:
|
||
"""SYS-012: 角色授权测试。"""
|
||
|
||
def test_save_role_menus(self, client, admin_headers, seed_roles, seed_menus):
|
||
"""SYS-012: 角色授权保存成功。
|
||
|
||
实际接口: PUT /api/system/roles/{role_id}/menus
|
||
"""
|
||
role_id = 2 # 业务员角色
|
||
|
||
resp = client.put(f"/api/system/roles/{role_id}/menus", headers=admin_headers, json={
|
||
"menu_ids": [1, 2, 4],
|
||
})
|
||
assert resp.status_code == 200
|
||
|
||
|
||
@pytest.mark.system
|
||
@pytest.mark.p0
|
||
class TestPermissionBoundary:
|
||
"""SYS-013: 权限越权测试。"""
|
||
|
||
def test_salesman_cannot_access_system(self, client, salesman_headers):
|
||
"""SYS-013: 业务员不能访问系统管理接口。"""
|
||
resp = client.get("/api/system/users", headers=salesman_headers)
|
||
assert resp.status_code == 403
|
||
|
||
def test_driver_cannot_access_system(self, client, driver_headers):
|
||
"""司机不能访问系统管理接口。"""
|
||
resp = client.get("/api/system/users", headers=driver_headers)
|
||
assert resp.status_code == 403
|
||
|
||
def test_manager_cannot_manage_users(self, client, manager_headers):
|
||
"""管理层不能管理用户。"""
|
||
resp = client.post("/api/system/users", headers=manager_headers, json={
|
||
"username": "test",
|
||
"password": "test",
|
||
"real_name": "test",
|
||
"role_id": 1,
|
||
})
|
||
assert resp.status_code == 403
|
||
|
||
def test_admin_full_access(self, client, admin_headers):
|
||
"""管理员可以访问所有系统管理接口。"""
|
||
resp = client.get("/api/system/users", headers=admin_headers)
|
||
assert resp.status_code == 200
|
||
resp = client.get("/api/system/roles", headers=admin_headers)
|
||
assert resp.status_code == 200
|
||
resp = client.get("/api/system/menus", headers=admin_headers)
|
||
assert resp.status_code == 200
|