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; }); } }