dingdanquanliucheng/backend/tests/test_system.py

205 lines
7.3 KiB
Python
Raw Normal View History

2026-06-14 16:20:04 +08:00
"""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