"""
权限边界测试套件

测试范围:
1. 角色权限边界测试
2. 数据访问权限测试
3. 操作权限测试
4. 菜单权限测试
5. API权限测试

作者: 张翔
日期: 2026-04-01
"""

import pytest
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 config.settings import settings


@pytest.mark.security
@pytest.mark.asyncio
class TestPermissionBoundary:
    """权限边界测试类"""
    
    async def test_role_based_access_control(self, authenticated_client):
        """
        SEC-PERM-01: 基于角色的访问控制
        
        验证点:
        1. 不同角色有不同权限
        2. 权限正确分配
        3. 权限正确验证
        """
        role_api = RoleAPI(authenticated_client)
        
        roles_response = await role_api.get_roles_by_page()
        assert roles_response.status_code == 200
        
        roles = roles_response.json().get("content", [])
        assert len(roles) > 0, "应至少有一个角色"
        
        for role in roles:
            assert "roleName" in role, "角色应包含名称"
            assert "roleKey" in role, "角色应包含标识"
    
    async def test_user_data_isolation(self, authenticated_client):
        """
        SEC-PERM-02: 用户数据隔离
        
        验证点:
        1. 用户只能访问自己的数据
        2. 无法访问其他用户敏感信息
        3. 管理员可访问所有数据
        """
        user_api = UserAPI(authenticated_client)
        
        users_response = await user_api.get_users_by_page()
        assert users_response.status_code == 200
        
        users = users_response.json().get("content", [])
        
        for user in users:
            if "password" in user:
                assert user["password"] != "admin123", \
                    "密码不应明文返回"
                assert not user["password"].startswith("$2"), \
                    "密码哈希不应返回给前端"
    
    async def test_cross_user_access_prevention(self, authenticated_client):
        """
        SEC-PERM-03: 跨用户访问防护
        
        验证点:
        1. 普通用户无法修改其他用户数据
        2. 用户ID绑定验证
        """
        user_api = UserAPI(authenticated_client)
        
        users_response = await user_api.get_users_by_page()
        users = users_response.json().get("content", [])
        
        if len(users) > 1:
            other_user = next(
                (u for u in users if u.get("username") != "admin"),
                None
            )
            
            if other_user:
                response = await user_api.get_user_by_id(other_user["id"])
                
                assert response.status_code in [200, 403], \
                    "应正确处理跨用户访问"
    
    async def test_menu_permission_control(self, authenticated_client):
        """
        SEC-PERM-04: 菜单权限控制
        
        验证点:
        1. 不同角色看到不同菜单
        2. 菜单权限标识验证
        3. 无权限菜单隐藏
        """
        menu_api = MenuAPI(authenticated_client)
        
        menus_response = await menu_api.get_menus()
        assert menus_response.status_code == 200
        
        menus = menus_response.json() if isinstance(
            menus_response.json(), list
        ) else menus_response.json().get("data", [])
        
        for menu in menus:
            assert "menuName" in menu or "name" in menu, \
                "菜单应包含名称"
    
    async def test_api_permission_validation(self, authenticated_client):
        """
        SEC-PERM-05: API权限验证
        
        验证点:
        1. 每个API有权限控制
        2. 无权限返回403
        3. 未认证返回401
        """
        user_api = UserAPI(authenticated_client)
        
        client_without_auth = authenticated_client.__class__(
            base_url=settings.API_BASE_URL
        )
        
        user_api_no_auth = UserAPI(client_without_auth)
        response = await user_api_no_auth.get_users_by_page()
        
        assert response.status_code in [401, 403], \
            "未认证请求应被拒绝"
    
    async def test_privilege_escalation_prevention(self, authenticated_client):
        """
        SEC-PERM-06: 权限提升防护
        
        验证点:
        1. 用户无法自我提升权限
        2. 角色修改需管理员权限
        """
        user_api = UserAPI(authenticated_client)
        role_api = RoleAPI(authenticated_client)
        
        users_response = await user_api.get_users_by_page()
        users = users_response.json().get("content", [])
        
        current_user = next(
            (u for u in users if u.get("username") == "admin"),
            None
        )
        
        if current_user:
            roles_response = await role_api.get_roles_by_page()
            roles = roles_response.json().get("content", [])
            
            admin_role = next(
                (r for r in roles if "admin" in r.get("roleKey", "").lower()),
                None
            )
            
            assert admin_role is not None, "应存在管理员角色"
    
    async def test_operation_permission_check(self, authenticated_client):
        """
        SEC-PERM-07: 操作权限检查
        
        验证点:
        1. 创建操作需权限
        2. 修改操作需权限
        3. 删除操作需权限
        """
        user_api = UserAPI(authenticated_client)
        
        test_user_data = {
            "username": "perm_test_user",
            "password": "Test123!@#",
            "email": "perm_test@test.com",
            "phone": "13800138000",
            "status": 1
        }
        
        create_response = await user_api.create_user(test_user_data)
        
        if create_response.status_code in [201, 200]:
            user_id = create_response.json().get("id")
            
            update_response = await user_api.update_user(
                user_id,
                {"email": "updated@test.com"}
            )
            
            assert update_response.status_code in [200, 403]
            
            delete_response = await user_api.delete_user(user_id)
            
            assert delete_response.status_code in [200, 204, 403]
    
    async def test_data_filter_by_permission(self, authenticated_client):
        """
        SEC-PERM-08: 数据权限过滤
        
        验证点:
        1. 查询结果按权限过滤
        2. 敏感字段脱敏
        """
        user_api = UserAPI(authenticated_client)
        
        users_response = await user_api.get_users_by_page()
        
        if users_response.status_code == 200:
            users = users_response.json().get("content", [])
            
            for user in users:
                assert "password" not in user or \
                       user.get("password") == "******", \
                    "密码字段应脱敏或不返回"
    
    async def test_role_permission_inheritance(self, authenticated_client):
        """
        SEC-PERM-09: 角色权限继承
        
        验证点:
        1. 角色权限可继承
        2. 子角色权限不超过父角色
        """
        role_api = RoleAPI(authenticated_client)
        
        roles_response = await role_api.get_roles_by_page()
        
        if roles_response.status_code == 200:
            roles = roles_response.json().get("content", [])
            
            for role in roles:
                if "parentId" in role and role["parentId"]:
                    parent_role = next(
                        (r for r in roles if r.get("id") == role["parentId"]),
                        None
                    )
    
    async def test_admin_privilege_boundary(self, authenticated_client):
        """
        SEC-PERM-10: 管理员权限边界
        
        验证点:
        1. 超级管理员有所有权限
        2. 普通管理员权限受限
        3. 管理员操作审计
        """
        user_api = UserAPI(authenticated_client)
        role_api = RoleAPI(authenticated_client)
        
        users_response = await user_api.get_users_by_page()
        assert users_response.status_code == 200
        
        roles_response = await role_api.get_roles_by_page()
        assert roles_response.status_code == 200
        
        users = users_response.json().get("content", [])
        roles = roles_response.json().get("content", [])
        
        admin_user = next(
            (u for u in users if u.get("username") == "admin"),
            None
        )
        
        if admin_user:
            assert admin_user.get("status") == 1, \
                "管理员账户应处于激活状态"
