216 lines
8.1 KiB
C#
216 lines
8.1 KiB
C#
using FsCheck;
|
|
using FsCheck.Xunit;
|
|
using MiAssessment.Admin.Business.Attributes;
|
|
using MiAssessment.Admin.Business.Controllers;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.AspNetCore.Mvc.Routing;
|
|
using System.Reflection;
|
|
using Xunit;
|
|
|
|
namespace MiAssessment.Tests.Services;
|
|
|
|
/// <summary>
|
|
/// 权限验证属性测试
|
|
/// Property 18: Authentication Enforcement - 所有业务控制器方法都需要认证
|
|
/// Property 19: Permission Enforcement - 所有业务控制器方法都有权限标记
|
|
/// </summary>
|
|
public class PermissionPropertyTests
|
|
{
|
|
private static readonly Type[] BusinessControllerTypes = new[]
|
|
{
|
|
typeof(ConfigController),
|
|
typeof(UserController),
|
|
typeof(VipController),
|
|
typeof(GoodsController),
|
|
typeof(PrizesController),
|
|
typeof(GoodsTypesController),
|
|
typeof(OrderController),
|
|
typeof(FinanceController),
|
|
typeof(DashboardController)
|
|
};
|
|
|
|
/// <summary>
|
|
/// Property 18: 所有业务控制器都继承自 BusinessControllerBase
|
|
/// 验证所有业务控制器都有统一的基类,确保认证机制一致
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property18_AllBusinessControllers_InheritFromBusinessControllerBase()
|
|
{
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
Assert.True(
|
|
typeof(BusinessControllerBase).IsAssignableFrom(controllerType),
|
|
$"{controllerType.Name} should inherit from BusinessControllerBase"
|
|
);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 18: 所有业务控制器都有 Route 特性
|
|
/// 验证所有业务控制器都有正确的路由配置
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property18_AllBusinessControllers_HaveRouteAttribute()
|
|
{
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
var routeAttr = controllerType.GetCustomAttribute<RouteAttribute>();
|
|
Assert.NotNull(routeAttr);
|
|
Assert.StartsWith("api/admin/business", routeAttr.Template);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 19: 所有公开的 Action 方法都有 BusinessPermission 特性
|
|
/// 验证权限控制的完整性
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property19_AllPublicActions_HaveBusinessPermissionAttribute()
|
|
{
|
|
var exceptions = new List<string>
|
|
{
|
|
"ConfigController.GetConfigKeys" // 获取配置键列表不需要权限
|
|
};
|
|
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
var actionMethods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
|
.Where(m => m.GetCustomAttributes().Any(a => a is HttpMethodAttribute))
|
|
.ToList();
|
|
|
|
foreach (var method in actionMethods)
|
|
{
|
|
var fullName = $"{controllerType.Name}.{method.Name}";
|
|
if (exceptions.Contains(fullName))
|
|
continue;
|
|
|
|
var permissionAttr = method.GetCustomAttribute<BusinessPermissionAttribute>();
|
|
Assert.NotNull(permissionAttr);
|
|
Assert.False(string.IsNullOrEmpty(permissionAttr.PermissionCode),
|
|
$"{fullName} should have a non-empty permission code");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 19: 权限编码格式正确 (module:action)
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property19_PermissionCodes_HaveCorrectFormat()
|
|
{
|
|
var allPermissionCodes = new HashSet<string>();
|
|
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
var actionMethods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
|
.Where(m => m.GetCustomAttributes().Any(a => a is HttpMethodAttribute));
|
|
|
|
foreach (var method in actionMethods)
|
|
{
|
|
var permissionAttr = method.GetCustomAttribute<BusinessPermissionAttribute>();
|
|
if (permissionAttr != null)
|
|
{
|
|
allPermissionCodes.Add(permissionAttr.PermissionCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (var code in allPermissionCodes)
|
|
{
|
|
// 权限编码格式: module:action
|
|
Assert.Matches(@"^[a-z]+:[a-z_]+$", code);
|
|
|
|
var parts = code.Split(':');
|
|
Assert.Equal(2, parts.Length);
|
|
Assert.False(string.IsNullOrEmpty(parts[0]), $"Module part of {code} should not be empty");
|
|
Assert.False(string.IsNullOrEmpty(parts[1]), $"Action part of {code} should not be empty");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 19: 验证所有预期的权限编码都已使用
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property19_AllExpectedPermissionCodes_AreUsed()
|
|
{
|
|
var expectedPermissions = new HashSet<string>
|
|
{
|
|
// 配置模块
|
|
"config:view", "config:edit",
|
|
// 用户模块
|
|
"user:list", "user:view", "user:money", "user:status", "user:test", "user:clear", "user:gift",
|
|
// VIP模块
|
|
"vip:list", "vip:edit",
|
|
// 商品模块
|
|
"goods:list", "goods:view", "goods:add", "goods:edit", "goods:delete", "goods:status",
|
|
// 订单模块
|
|
"order:list", "order:view", "order:ship", "order:export",
|
|
// 财务模块
|
|
"finance:view",
|
|
// 仪表盘模块
|
|
"dashboard:view", "dashboard:edit"
|
|
};
|
|
|
|
var actualPermissions = new HashSet<string>();
|
|
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
var actionMethods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
|
.Where(m => m.GetCustomAttributes().Any(a => a is HttpMethodAttribute));
|
|
|
|
foreach (var method in actionMethods)
|
|
{
|
|
var permissionAttr = method.GetCustomAttribute<BusinessPermissionAttribute>();
|
|
if (permissionAttr != null)
|
|
{
|
|
actualPermissions.Add(permissionAttr.PermissionCode);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 验证所有预期的权限都被使用
|
|
foreach (var expected in expectedPermissions)
|
|
{
|
|
Assert.Contains(expected, actualPermissions);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 19: 同一模块的权限应该有一致的前缀
|
|
/// </summary>
|
|
[Fact]
|
|
public void Property19_PermissionCodes_HaveConsistentModulePrefix()
|
|
{
|
|
var controllerPermissions = new Dictionary<string, HashSet<string>>
|
|
{
|
|
{ "ConfigController", new HashSet<string> { "config" } },
|
|
{ "UserController", new HashSet<string> { "user" } },
|
|
{ "VipController", new HashSet<string> { "vip" } },
|
|
{ "GoodsController", new HashSet<string> { "goods" } },
|
|
{ "PrizesController", new HashSet<string> { "goods" } }, // 奖品属于商品模块
|
|
{ "GoodsTypesController", new HashSet<string> { "goods" } }, // 商品类型属于商品模块
|
|
{ "OrderController", new HashSet<string> { "order" } },
|
|
{ "FinanceController", new HashSet<string> { "finance" } },
|
|
{ "DashboardController", new HashSet<string> { "dashboard" } }
|
|
};
|
|
|
|
foreach (var controllerType in BusinessControllerTypes)
|
|
{
|
|
var expectedModules = controllerPermissions[controllerType.Name];
|
|
|
|
var actionMethods = controllerType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
|
|
.Where(m => m.GetCustomAttributes().Any(a => a is HttpMethodAttribute));
|
|
|
|
foreach (var method in actionMethods)
|
|
{
|
|
var permissionAttr = method.GetCustomAttribute<BusinessPermissionAttribute>();
|
|
if (permissionAttr != null)
|
|
{
|
|
var module = permissionAttr.PermissionCode.Split(':')[0];
|
|
Assert.Contains(module, expectedModules);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|