Files
Edu/services/ai/tests/test_permission.py
SpecialX aac26c7c6f feat(ai): v2 新增 GenerateReport RPC + ReportService
第 9 个 RPC GenerateReport(学情报告生成):data-ana 学情数据 → LLM 生成 → 结构化提取

新增 ReportService 业务编排层 + GenerateReportRequest/GeneratedReport 模型

gRPC servicer + HTTP POST /v1/ai/generate/report(权限 ai:report:generate)

proto_gen 重新生成 + 测试覆盖(servicer/service/HTTP/模型/权限 共 26 用例)

402 测试通过,覆盖率 88.5%
2026-07-14 22:57:57 +08:00

86 lines
3.4 KiB
Python

"""权限校验守卫测试."""
import pytest
from src.ai.errors import AIError, ErrorCode
from src.ai.middleware.auth import UserContext
from src.ai.middleware.permission import (
PERMISSION_AI_CHAT,
PERMISSION_AI_LESSON_CONFIRM,
PERMISSION_AI_LESSON_GENERATE,
PERMISSION_AI_QUESTION_GENERATE,
PERMISSION_AI_REPORT_GENERATE,
PermissionGuard,
)
class TestPermissionGuard:
"""PermissionGuard 测试."""
def test_dev_mode_skips_check(self) -> None:
"""dev_mode=true 跳过校验."""
guard = PermissionGuard(dev_mode=True)
ctx = UserContext() # 未认证
# 不抛异常即通过
guard.check(ctx, PERMISSION_AI_CHAT)
def test_unauthenticated_raises(self) -> None:
"""未认证用户抛 AI_UNAUTHORIZED."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext()
with pytest.raises(AIError) as exc_info:
guard.check(ctx, PERMISSION_AI_CHAT)
assert exc_info.value.code == ErrorCode.AI_UNAUTHORIZED
def test_teacher_has_all_permissions(self) -> None:
"""teacher 角色拥有全部权限."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="teacher")
guard.check(ctx, PERMISSION_AI_CHAT)
guard.check(ctx, PERMISSION_AI_QUESTION_GENERATE)
guard.check(ctx, PERMISSION_AI_LESSON_GENERATE)
guard.check(ctx, PERMISSION_AI_LESSON_CONFIRM)
guard.check(ctx, PERMISSION_AI_REPORT_GENERATE)
def test_student_only_chat(self) -> None:
"""student 角色仅有 chat 权限."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="student")
guard.check(ctx, PERMISSION_AI_CHAT)
with pytest.raises(AIError) as exc_info:
guard.check(ctx, PERMISSION_AI_QUESTION_GENERATE)
assert exc_info.value.code == ErrorCode.AI_FORBIDDEN
def test_student_denied_report(self) -> None:
"""student 角色无权生成学情报告."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="student")
with pytest.raises(AIError) as exc_info:
guard.check(ctx, PERMISSION_AI_REPORT_GENERATE)
assert exc_info.value.code == ErrorCode.AI_FORBIDDEN
def test_unknown_role_defaults_student(self) -> None:
"""未知角色降级为 student 权限."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="guest")
guard.check(ctx, PERMISSION_AI_CHAT)
with pytest.raises(AIError):
guard.check(ctx, PERMISSION_AI_LESSON_GENERATE)
def test_empty_role_defaults_student(self) -> None:
"""空角色降级为 student."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="")
guard.check(ctx, PERMISSION_AI_CHAT)
with pytest.raises(AIError):
guard.check(ctx, PERMISSION_AI_QUESTION_GENERATE)
def test_forbidden_includes_details(self) -> None:
"""权限拒绝 details 含 required_permission + role."""
guard = PermissionGuard(dev_mode=False)
ctx = UserContext(user_id="u-1", role="student")
with pytest.raises(AIError) as exc_info:
guard.check(ctx, PERMISSION_AI_LESSON_CONFIRM)
assert exc_info.value.details["required_permission"] == PERMISSION_AI_LESSON_CONFIRM
assert exc_info.value.details["role"] == "student"