Files

236 lines
7.3 KiB
Python

"""内部公文权限控制测试。"""
import pytest
from starlette.responses import JSONResponse
from fastapi_modules.fastapi_leaudit.controllers.govdocController import GovdocController
from fastapi_modules.fastapi_leaudit.domian.vo.rbacVo import RbacRouteVO
from fastapi_modules.fastapi_leaudit.services.impl.rbacServiceImpl import RbacServiceImpl
class _DenyPermissionService:
"""拒绝所有权限的测试权限服务。"""
async def CheckPermission(self, user_id: int, permission_key: str) -> bool:
"""检查权限。"""
return False
class _AllowPermissionService:
"""允许所有权限的测试权限服务。"""
async def CheckPermission(self, user_id: int, permission_key: str) -> bool:
"""检查权限。"""
return True
class _FakeGovdocService:
"""记录调用的测试公文服务。"""
def __init__(self) -> None:
self.list_called = False
self.upload_called = False
async def ListDocuments(self, **kwargs):
"""记录列表调用。"""
self.list_called = True
return {"items": [], "total": 0, "page": kwargs["page"], "pageSize": kwargs["pageSize"]}
async def UploadDocument(self, **kwargs):
"""记录上传调用。"""
self.upload_called = True
return {"documentId": 1}
def _find_endpoint(controller: GovdocController, path: str, method: str):
"""根据路径和方法查找路由 endpoint。"""
full_path = f"{controller.router.prefix}{path}"
for route in controller.router.routes:
if getattr(route, "path", "") == full_path and method in getattr(route, "methods", set()):
return route.endpoint
raise AssertionError(f"未找到路由 {method} {full_path}")
@pytest.mark.asyncio
async def test_govdoc_list_requires_document_read_permission():
"""公文列表无查看权限时返回 403,且不调用业务服务。"""
controller = GovdocController()
service = _FakeGovdocService()
controller.GovdocService = service
controller.PermissionService = _DenyPermissionService()
endpoint = _find_endpoint(controller, "/documents", "GET")
response = await endpoint(
page=1,
pageSize=20,
keyword=None,
fileExt=None,
region=None,
tenant_code=None,
entry_module_id=None,
type_ids=None,
document_type_id=None,
status=None,
resultStatus=None,
createdBy=None,
dateFrom=None,
dateTo=None,
payload={"user_id": 7},
)
assert isinstance(response, JSONResponse)
assert response.status_code == 403
assert service.list_called is False
@pytest.mark.asyncio
async def test_govdoc_upload_requires_document_create_permission():
"""公文上传无创建权限时返回 403,且不调用业务服务。"""
controller = GovdocController()
service = _FakeGovdocService()
controller.GovdocService = service
controller.PermissionService = _DenyPermissionService()
endpoint = _find_endpoint(controller, "/documents", "POST")
response = await endpoint(file=object(), payload={"user_id": 7})
assert isinstance(response, JSONResponse)
assert response.status_code == 403
assert service.upload_called is False
@pytest.mark.asyncio
async def test_govdoc_list_calls_service_when_permission_granted():
"""公文列表有查看权限时正常调用业务服务。"""
controller = GovdocController()
service = _FakeGovdocService()
controller.GovdocService = service
controller.PermissionService = _AllowPermissionService()
endpoint = _find_endpoint(controller, "/documents", "GET")
response = await endpoint(
page=1,
pageSize=20,
keyword=None,
fileExt=None,
region=None,
tenant_code=None,
entry_module_id=None,
type_ids=None,
document_type_id=None,
status=None,
resultStatus=None,
createdBy=None,
dateFrom=None,
dateTo=None,
payload={"user_id": 7},
)
assert response.data["total"] == 0
assert service.list_called is True
def test_govdoc_root_route_marks_frontend_route_set_ready():
"""只有内部公文模块路由时也视为新版路由,避免兼容菜单补出列表和上传。"""
service = RbacServiceImpl()
routes = [
RbacRouteVO(
id=1,
route_path="/govdoc",
route_name="govdoc",
component="govdoc",
parent_id=None,
route_title="内部公文处理",
)
]
assert service._isFrontendRouteSetReady(routes) is True
def test_govdoc_parent_route_does_not_expose_ungranted_child_routes():
"""只有内部公文父路由和接口权限时,不应补出未勾选的列表/上传子路由。"""
service = RbacServiceImpl()
routes = [
RbacRouteVO(
id=1,
route_path="/govdoc",
route_name="govdoc",
component="govdoc",
parent_id=None,
route_title="内部公文处理",
children=[
RbacRouteVO(
id=2,
route_path="/govdoc/audits",
route_name="govdoc-audits",
component="govdoc.audits",
parent_id=1,
route_title="公文列表",
),
RbacRouteVO(
id=3,
route_path="/govdoc/upload",
route_name="govdoc-upload",
component="govdoc.upload",
parent_id=1,
route_title="公文上传",
),
],
)
]
filtered = service._filterRoutesByRouteAndPermissionScope(
routes,
{"/govdoc"},
{"govdoc:document:read", "govdoc:document:create"},
)
paths = service._collectRoutePaths(filtered)
assert "/govdoc" in paths
assert "/govdoc/audits" not in paths
assert "/govdoc/upload" not in paths
def test_legacy_govdoc_audit_route_does_not_grant_current_govdoc_child_route():
"""旧 /govdoc-audit 残留授权不应继续放行当前 /govdoc 子路由。"""
service = RbacServiceImpl()
routes = [
RbacRouteVO(
id=1,
route_path="/govdoc",
route_name="govdoc",
component="govdoc",
parent_id=None,
route_title="内部公文处理",
),
RbacRouteVO(
id=2,
route_path="/govdoc-audit/audits",
route_name="legacy-govdoc-audits",
component="govdoc-audit.audits",
parent_id=None,
route_title="旧公文列表",
),
RbacRouteVO(
id=3,
route_path="/govdoc-audit/upload",
route_name="legacy-govdoc-upload",
component="govdoc-audit.upload",
parent_id=None,
route_title="旧公文上传",
),
]
filtered = service._filterRoutesByRouteAndPermissionScope(
routes,
service._collectCurrentFrontendRoutePaths(routes),
{"govdoc:document:read", "govdoc:document:create"},
)
paths = service._collectRoutePaths(filtered)
assert "/govdoc" in paths
assert "/govdoc-audit/audits" not in paths
assert "/govdoc-audit/upload" not in paths
assert "/govdoc/audits" not in paths
assert "/govdoc/upload" not in paths