mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-22 05:02:57 +00:00
本次提交进行了大规模的系统重构: 1. 拆分租户相关模块到platform平台层,重构租户表名与关联关系 2. 迁移日志、工单、插件等模块到对应层级,统一代码结构 3. 重构批量操作接口路径,从/available/setting改为/status/batch 4. 新增批量删除基础模型,统一处理批量操作逻辑 5. 优化导入导出接口,修正路由方法与描述信息 6. 修复循环引用问题,重构依赖注入与类型导入 7. 更新初始化脚本与路由注册,新增平台管理路由 8. 重构岗位模型,新增岗位编码字段与校验 9. 完善部门删除逻辑,新增子部门删除限制 10. 更新初始化数据与配置文件,适配新架构
212 lines
7.1 KiB
Python
212 lines
7.1 KiB
Python
"""多租户多模块纯单元测试(不依赖数据库连接)。"""
|
|
|
|
import pytest
|
|
|
|
# ==================== 01. 租户 ContextVar ====================
|
|
|
|
|
|
class TestTenantContextVar:
|
|
"""租户上下文变量读写与清理。"""
|
|
|
|
def test_initially_none(self) -> None:
|
|
from app.core.tenant import get_current_tenant_id, get_is_super_admin
|
|
|
|
assert get_current_tenant_id() is None
|
|
assert get_is_super_admin() is False
|
|
|
|
def test_set_and_get(self) -> None:
|
|
from app.core.tenant import (
|
|
clear_current_tenant,
|
|
get_current_tenant_id,
|
|
get_is_super_admin,
|
|
set_current_tenant,
|
|
)
|
|
|
|
set_current_tenant(42, True)
|
|
assert get_current_tenant_id() == 42
|
|
assert get_is_super_admin() is True
|
|
|
|
clear_current_tenant()
|
|
assert get_current_tenant_id() is None
|
|
|
|
|
|
# ==================== 02. ORM 模型定义 ====================
|
|
|
|
|
|
class TestModels:
|
|
"""核心业务模型表名与字段完整性。"""
|
|
|
|
def test_tenant_model(self) -> None:
|
|
from app.api.v1.module_platform.tenant.model import TenantModel
|
|
|
|
assert TenantModel.__tablename__ == "sys_tenant"
|
|
assert hasattr(TenantModel, "name")
|
|
assert hasattr(TenantModel, "code")
|
|
assert hasattr(TenantModel, "status")
|
|
assert hasattr(TenantModel, "end_time")
|
|
|
|
def test_tenant_quota_fields(self) -> None:
|
|
"""测试租户模型中的配额字段(单一大表设计)"""
|
|
from app.api.v1.module_platform.tenant.model import TenantModel
|
|
|
|
assert hasattr(TenantModel, "max_users")
|
|
assert hasattr(TenantModel, "max_roles")
|
|
assert hasattr(TenantModel, "max_storage_mb")
|
|
assert hasattr(TenantModel, "max_depts")
|
|
|
|
def test_tenant_config_model(self) -> None:
|
|
from app.api.v1.module_platform.tenant.model import TenantConfigModel
|
|
|
|
assert TenantConfigModel.__tablename__ == "sys_tenant_config"
|
|
assert hasattr(TenantConfigModel, "config_key")
|
|
|
|
def test_tenant_menu_model(self) -> None:
|
|
from app.api.v1.module_platform.tenant.model import TenantMenuModel
|
|
|
|
assert TenantMenuModel.__tablename__ == "sys_tenant_menu"
|
|
assert hasattr(TenantMenuModel, "menu_id")
|
|
|
|
def test_tenant_user_model(self) -> None:
|
|
from app.api.v1.module_platform.tenant.model import TenantUserModel
|
|
|
|
assert TenantUserModel.__tablename__ == "sys_user_tenant"
|
|
assert hasattr(TenantUserModel, "role")
|
|
assert hasattr(TenantUserModel, "is_default")
|
|
|
|
def test_user_model_has_tenant_id(self) -> None:
|
|
from app.api.v1.module_system.user.model import UserModel
|
|
|
|
assert hasattr(UserModel, "tenant_id")
|
|
assert hasattr(UserModel, "is_superuser")
|
|
|
|
def test_role_model_has_tenant_id(self) -> None:
|
|
from app.api.v1.module_system.role.model import RoleModel
|
|
|
|
assert hasattr(RoleModel, "tenant_id")
|
|
assert hasattr(RoleModel, "menus")
|
|
|
|
def test_menu_model_has_type(self) -> None:
|
|
from app.api.v1.module_system.menu.model import MenuModel
|
|
|
|
assert hasattr(MenuModel, "type")
|
|
assert hasattr(MenuModel, "name")
|
|
assert hasattr(MenuModel, "permission")
|
|
|
|
def test_notice_model(self) -> None:
|
|
from app.api.v1.module_system.notice.model import NoticeModel
|
|
|
|
assert NoticeModel.__tablename__ == "sys_notice"
|
|
assert hasattr(NoticeModel, "notice_title")
|
|
assert hasattr(NoticeModel, "status")
|
|
|
|
def test_plugin_model(self) -> None:
|
|
from app.api.v1.module_platform.plugin.model import PluginModel
|
|
|
|
assert PluginModel.__tablename__ == "sys_plugin"
|
|
assert hasattr(PluginModel, "code")
|
|
|
|
def test_tenant_plugin_model(self) -> None:
|
|
from app.api.v1.module_platform.plugin.model import TenantPluginModel
|
|
|
|
assert TenantPluginModel.__tablename__ == "sys_tenant_plugin"
|
|
|
|
def test_ticket_model(self) -> None:
|
|
from app.api.v1.module_platform.ticket.model import TicketModel
|
|
|
|
assert TicketModel.__tablename__ == "sys_ticket"
|
|
assert hasattr(TicketModel, "ticket_type")
|
|
assert hasattr(TicketModel, "reply")
|
|
|
|
|
|
# ==================== 03. Pydantic Schema ====================
|
|
|
|
|
|
class TestSchemas:
|
|
"""请求/响应Schema字段验证。"""
|
|
|
|
def test_tenant_create_schema(self) -> None:
|
|
from app.api.v1.module_platform.tenant.schema import TenantCreateSchema
|
|
|
|
s = TenantCreateSchema(name="test", code="test001", status="0")
|
|
assert s.name == "test"
|
|
assert s.code == "test001"
|
|
|
|
def test_tenant_update_schema_partial(self) -> None:
|
|
from app.api.v1.module_platform.tenant.schema import TenantUpdateSchema
|
|
|
|
s = TenantUpdateSchema(name="renamed")
|
|
assert s.name == "renamed"
|
|
assert s.code is None # 未传则为 None
|
|
|
|
def test_tenant_quota_schema_validation(self) -> None:
|
|
from app.api.v1.module_platform.tenant.schema import TenantQuotaUpdateSchema
|
|
|
|
s = TenantQuotaUpdateSchema(max_users=100, max_roles=30)
|
|
assert s.max_users == 100
|
|
|
|
# 负数应被拒绝
|
|
with pytest.raises(Exception):
|
|
TenantQuotaUpdateSchema(max_users=-1)
|
|
|
|
def test_ticket_schema(self) -> None:
|
|
from app.api.v1.module_platform.ticket.schema import TicketCreateSchema
|
|
|
|
s = TicketCreateSchema(title="test", ticket_type="suggestion")
|
|
assert s.title == "test"
|
|
assert s.ticket_type == "suggestion"
|
|
|
|
def test_menu_type_enum(self) -> None:
|
|
"""菜单类型: 1=目录 2=菜单 3=按钮 4=链接"""
|
|
from app.api.v1.module_system.menu.model import MenuModel
|
|
|
|
assert hasattr(MenuModel, "type")
|
|
# type 是 int 字段
|
|
|
|
|
|
# ==================== 04. 配置类 ====================
|
|
|
|
|
|
class TestSettings:
|
|
"""配置默认值与环境变量覆盖。"""
|
|
|
|
def test_settings_defaults(self) -> None:
|
|
from app.config.setting import settings
|
|
|
|
assert settings.DATABASE_HOST
|
|
assert settings.DATABASE_PORT > 0
|
|
assert settings.SERVER_PORT > 0
|
|
|
|
def test_redis_config(self) -> None:
|
|
from app.config.setting import settings
|
|
|
|
assert isinstance(settings.REDIS_ENABLE, bool)
|
|
assert settings.REDIS_PORT == 6379
|
|
|
|
def test_database_password_empty_by_default(self) -> None:
|
|
"""密码默认值为空,需通过 .env 或环境变量提供。"""
|
|
from app.config.setting import Settings
|
|
|
|
# 检查字段默认值(不依赖运行时环境变量)
|
|
assert Settings.model_fields["DATABASE_PASSWORD"].default == ""
|
|
|
|
|
|
# ==================== 05. 常量与枚举 ====================
|
|
|
|
|
|
class TestEnums:
|
|
"""业务枚举值完整性。"""
|
|
|
|
def test_status_operate_enum(self) -> None:
|
|
from app.common.enums import BusinessType
|
|
|
|
assert hasattr(BusinessType, "OTHER")
|
|
|
|
def test_menu_type_values(self) -> None:
|
|
"""type: 1=目录 2=菜单 3=按钮 4=链接"""
|
|
from app.api.v1.module_system.menu.model import MenuModel
|
|
|
|
assert MenuModel.__tablename__ == "sys_menu"
|
|
# type 字段是 int 类型
|
|
col = next((c for c in MenuModel.__table__.columns if c.name == "type"), None)
|
|
assert col is not None
|