using FsCheck;
using FsCheck.Xunit;
using NSubstitute;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using XiangYi.Application.Services;
using XiangYi.Application.Options;
using XiangYi.Core.Entities.Admin;
using XiangYi.Core.Interfaces;
using XiangYi.Infrastructure.Cache;
// Alias for Options.Create to avoid namespace conflict
using OptionsFactory = Microsoft.Extensions.Options.Options;
namespace XiangYi.Application.Tests.Services;
///
/// AdminPermission属性测试
///
public class AdminPermissionPropertyTests
{
///
/// **Feature: backend-api, Property 21: 管理员权限验证**
/// **Validates: Requirements 13.4**
///
/// *For any* 管理员访问无权限的接口, 应返回403状态码
///
/// 此测试验证:当管理员没有所需权限时,GetAdminPermissionsAsync返回的权限列表不包含该权限
///
[Property(MaxTest = 100)]
public Property AdminWithoutPermission_ShouldNotHaveRequiredPermission()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
adminIdArb =>
{
var adminId = (long)adminIdArb.Get;
var requiredPermission = "user:delete"; // 需要的权限
// Arrange - 创建一个没有任何角色的管理员
var mockAdminUserRepository = Substitute.For>();
var mockAdminUserRoleRepository = Substitute.For>();
var mockAdminRoleRepository = Substitute.For>();
var mockAdminRolePermissionRepository = Substitute.For>();
var mockAdminPermissionRepository = Substitute.For>();
var mockAdminRoleMenuRepository = Substitute.For>();
var mockAdminMenuRepository = Substitute.For>();
var mockCacheService = Substitute.For();
var mockLogger = Substitute.For>();
var jwtOptions = OptionsFactory.Create(new JwtOptions
{
Secret = "test-secret-key-that-is-long-enough-for-256-bits",
Issuer = "test-issuer",
Audience = "test-audience",
ExpireMinutes = 60
});
// 管理员没有任何角色
mockAdminUserRoleRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List()));
var service = new AdminAuthService(
mockAdminUserRepository,
mockAdminUserRoleRepository,
mockAdminRoleRepository,
mockAdminRolePermissionRepository,
mockAdminPermissionRepository,
mockAdminRoleMenuRepository,
mockAdminMenuRepository,
mockCacheService,
jwtOptions,
mockLogger);
// Act
var permissions = service.GetAdminPermissionsAsync(adminId).Result;
// Assert - 没有角色的管理员应该没有任何权限
var hasNoPermissions = permissions.Count == 0;
var doesNotHaveRequiredPermission = !permissions.Contains(requiredPermission);
return hasNoPermissions && doesNotHaveRequiredPermission;
});
}
///
/// **Feature: backend-api, Property 21: 管理员权限验证**
/// **Validates: Requirements 13.4**
///
/// 验证:当管理员有特定角色但该角色没有所需权限时,权限列表不包含该权限
///
[Property(MaxTest = 100)]
public Property AdminWithRoleButNoPermission_ShouldNotHaveRequiredPermission()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
adminIdArb =>
{
var adminId = (long)adminIdArb.Get;
var roleId = adminId + 100;
var requiredPermission = "user:delete"; // 需要的权限
var grantedPermission = "user:view"; // 实际拥有的权限
// Arrange
var mockAdminUserRepository = Substitute.For>();
var mockAdminUserRoleRepository = Substitute.For>();
var mockAdminRoleRepository = Substitute.For>();
var mockAdminRolePermissionRepository = Substitute.For>();
var mockAdminPermissionRepository = Substitute.For>();
var mockAdminRoleMenuRepository = Substitute.For>();
var mockAdminMenuRepository = Substitute.For>();
var mockCacheService = Substitute.For();
var mockLogger = Substitute.For>();
var jwtOptions = OptionsFactory.Create(new JwtOptions
{
Secret = "test-secret-key-that-is-long-enough-for-256-bits",
Issuer = "test-issuer",
Audience = "test-audience",
ExpireMinutes = 60
});
// 管理员有一个角色
mockAdminUserRoleRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminUserRole { UserId = adminId, RoleId = roleId }
}));
// 角色有一个权限(但不是所需的权限)
var permissionId = roleId + 1000;
mockAdminRolePermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminRolePermission { RoleId = roleId, PermissionId = permissionId }
}));
// 权限详情
mockAdminPermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminPermission { Id = permissionId, PermissionCode = grantedPermission }
}));
var service = new AdminAuthService(
mockAdminUserRepository,
mockAdminUserRoleRepository,
mockAdminRoleRepository,
mockAdminRolePermissionRepository,
mockAdminPermissionRepository,
mockAdminRoleMenuRepository,
mockAdminMenuRepository,
mockCacheService,
jwtOptions,
mockLogger);
// Act
var permissions = service.GetAdminPermissionsAsync(adminId).Result;
// Assert
// 1. 应该有权限(因为有角色)
var hasPermissions = permissions.Count > 0;
// 2. 应该有被授予的权限
var hasGrantedPermission = permissions.Contains(grantedPermission);
// 3. 不应该有未授予的权限
var doesNotHaveRequiredPermission = !permissions.Contains(requiredPermission);
return hasPermissions && hasGrantedPermission && doesNotHaveRequiredPermission;
});
}
///
/// **Feature: backend-api, Property 21: 管理员权限验证**
/// **Validates: Requirements 13.4**
///
/// 验证:当管理员有所需权限时,权限列表应包含该权限
///
[Property(MaxTest = 100)]
public Property AdminWithPermission_ShouldHaveRequiredPermission()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
adminIdArb =>
{
var adminId = (long)adminIdArb.Get;
var roleId = adminId + 100;
var requiredPermission = "user:delete"; // 需要的权限
// Arrange
var mockAdminUserRepository = Substitute.For>();
var mockAdminUserRoleRepository = Substitute.For>();
var mockAdminRoleRepository = Substitute.For>();
var mockAdminRolePermissionRepository = Substitute.For>();
var mockAdminPermissionRepository = Substitute.For>();
var mockAdminRoleMenuRepository = Substitute.For>();
var mockAdminMenuRepository = Substitute.For>();
var mockCacheService = Substitute.For();
var mockLogger = Substitute.For>();
var jwtOptions = OptionsFactory.Create(new JwtOptions
{
Secret = "test-secret-key-that-is-long-enough-for-256-bits",
Issuer = "test-issuer",
Audience = "test-audience",
ExpireMinutes = 60
});
// 管理员有一个角色
mockAdminUserRoleRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminUserRole { UserId = adminId, RoleId = roleId }
}));
// 角色有所需的权限
var permissionId = roleId + 1000;
mockAdminRolePermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminRolePermission { RoleId = roleId, PermissionId = permissionId }
}));
// 权限详情
mockAdminPermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminPermission { Id = permissionId, PermissionCode = requiredPermission }
}));
var service = new AdminAuthService(
mockAdminUserRepository,
mockAdminUserRoleRepository,
mockAdminRoleRepository,
mockAdminRolePermissionRepository,
mockAdminPermissionRepository,
mockAdminRoleMenuRepository,
mockAdminMenuRepository,
mockCacheService,
jwtOptions,
mockLogger);
// Act
var permissions = service.GetAdminPermissionsAsync(adminId).Result;
// Assert - 应该有所需的权限
return permissions.Contains(requiredPermission);
});
}
///
/// **Feature: backend-api, Property 21: 管理员权限验证**
/// **Validates: Requirements 13.4**
///
/// 验证:超级管理员(拥有"*"权限)应该可以访问所有资源
///
[Property(MaxTest = 100)]
public Property SuperAdmin_ShouldHaveWildcardPermission()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
adminIdArb =>
{
var adminId = (long)adminIdArb.Get;
var roleId = adminId + 100;
var wildcardPermission = "*"; // 超级管理员权限
// Arrange
var mockAdminUserRepository = Substitute.For>();
var mockAdminUserRoleRepository = Substitute.For>();
var mockAdminRoleRepository = Substitute.For>();
var mockAdminRolePermissionRepository = Substitute.For>();
var mockAdminPermissionRepository = Substitute.For>();
var mockAdminRoleMenuRepository = Substitute.For>();
var mockAdminMenuRepository = Substitute.For>();
var mockCacheService = Substitute.For();
var mockLogger = Substitute.For>();
var jwtOptions = OptionsFactory.Create(new JwtOptions
{
Secret = "test-secret-key-that-is-long-enough-for-256-bits",
Issuer = "test-issuer",
Audience = "test-audience",
ExpireMinutes = 60
});
// 管理员有超级管理员角色
mockAdminUserRoleRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminUserRole { UserId = adminId, RoleId = roleId }
}));
// 角色有通配符权限
var permissionId = roleId + 1000;
mockAdminRolePermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminRolePermission { RoleId = roleId, PermissionId = permissionId }
}));
// 权限详情 - 通配符权限
mockAdminPermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminPermission { Id = permissionId, PermissionCode = wildcardPermission }
}));
var service = new AdminAuthService(
mockAdminUserRepository,
mockAdminUserRoleRepository,
mockAdminRoleRepository,
mockAdminRolePermissionRepository,
mockAdminPermissionRepository,
mockAdminRoleMenuRepository,
mockAdminMenuRepository,
mockCacheService,
jwtOptions,
mockLogger);
// Act
var permissions = service.GetAdminPermissionsAsync(adminId).Result;
// Assert - 超级管理员应该有通配符权限
return permissions.Contains(wildcardPermission);
});
}
///
/// **Feature: backend-api, Property 21: 管理员权限验证**
/// **Validates: Requirements 13.4**
///
/// 验证:管理员拥有多个角色时,应该合并所有角色的权限
///
[Property(MaxTest = 100)]
public Property AdminWithMultipleRoles_ShouldHaveCombinedPermissions()
{
return Prop.ForAll(
Arb.Default.PositiveInt(),
adminIdArb =>
{
var adminId = (long)adminIdArb.Get;
var roleId1 = adminId + 100;
var roleId2 = adminId + 200;
var permission1 = "user:view";
var permission2 = "user:edit";
// Arrange
var mockAdminUserRepository = Substitute.For>();
var mockAdminUserRoleRepository = Substitute.For>();
var mockAdminRoleRepository = Substitute.For>();
var mockAdminRolePermissionRepository = Substitute.For>();
var mockAdminPermissionRepository = Substitute.For>();
var mockAdminRoleMenuRepository = Substitute.For>();
var mockAdminMenuRepository = Substitute.For>();
var mockCacheService = Substitute.For();
var mockLogger = Substitute.For>();
var jwtOptions = OptionsFactory.Create(new JwtOptions
{
Secret = "test-secret-key-that-is-long-enough-for-256-bits",
Issuer = "test-issuer",
Audience = "test-audience",
ExpireMinutes = 60
});
// 管理员有两个角色
mockAdminUserRoleRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminUserRole { UserId = adminId, RoleId = roleId1 },
new AdminUserRole { UserId = adminId, RoleId = roleId2 }
}));
// 两个角色各有一个权限
var permissionId1 = roleId1 + 1000;
var permissionId2 = roleId2 + 1000;
mockAdminRolePermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminRolePermission { RoleId = roleId1, PermissionId = permissionId1 },
new AdminRolePermission { RoleId = roleId2, PermissionId = permissionId2 }
}));
// 权限详情
mockAdminPermissionRepository.GetListAsync(Arg.Any>>())
.Returns(Task.FromResult(new List
{
new AdminPermission { Id = permissionId1, PermissionCode = permission1 },
new AdminPermission { Id = permissionId2, PermissionCode = permission2 }
}));
var service = new AdminAuthService(
mockAdminUserRepository,
mockAdminUserRoleRepository,
mockAdminRoleRepository,
mockAdminRolePermissionRepository,
mockAdminPermissionRepository,
mockAdminRoleMenuRepository,
mockAdminMenuRepository,
mockCacheService,
jwtOptions,
mockLogger);
// Act
var permissions = service.GetAdminPermissionsAsync(adminId).Result;
// Assert - 应该同时拥有两个角色的权限
var hasPermission1 = permissions.Contains(permission1);
var hasPermission2 = permissions.Contains(permission2);
return hasPermission1 && hasPermission2;
});
}
}