using Microsoft.EntityFrameworkCore; using Tiku.Application.Security; using Tiku.Domain.Operations; using Tiku.Domain.Platform; using Tiku.Domain.Tenancy; using Tiku.Infrastructure.Bootstrap; using Tiku.Infrastructure.Persistence; using Tiku.IntegrationTests.Infrastructure; namespace Tiku.IntegrationTests.Bootstrap; public sealed class BuiltinBackofficeCatalogSeederTests { [Fact] public async Task Seed_populates_complete_catalog_and_is_idempotent() { using var database = PostgresTestDatabase.Create(); await using var dbContext = CreateDbContext(database.ConnectionString); var seeder = new BuiltinBackofficeCatalogSeeder(dbContext); await seeder.SeedAsync(); dbContext.ChangeTracker.Clear(); await seeder.SeedAsync(); Assert.Equal(15, await dbContext.SaasFeatures.CountAsync()); Assert.Equal(26, await dbContext.PermissionModules.CountAsync()); Assert.Equal(35, await dbContext.BackendPermissions.CountAsync()); Assert.Equal(21, await dbContext.BackendMenus.CountAsync()); Assert.Equal(15, await dbContext.SaasFeatures.Select(item => item.Code).Distinct().CountAsync()); Assert.Equal(26, await dbContext.PermissionModules.Select(item => item.Code).Distinct().CountAsync()); Assert.Equal(35, await dbContext.BackendPermissions.Select(item => item.Code).Distinct().CountAsync()); Assert.Equal(21, await dbContext.BackendMenus.Select(item => item.Code).Distinct().CountAsync()); } [Fact] public async Task Seed_preserves_existing_values_and_fills_partial_catalog() { using var database = PostgresTestDatabase.Create(); await using var dbContext = CreateDbContext(database.ConnectionString); dbContext.SaasFeatures.Add(new SaasFeature { Code = SaasFeatureCatalog.CoreBackoffice, Name = "Custom feature name", Category = "custom", IsCore = true, Status = SaasFeatureStatus.Active }); dbContext.PermissionModules.Add(new PermissionModule { Code = "tenant_dashboard", Name = "Custom module name", Area = BackendPermissionArea.Tenant }); dbContext.BackendPermissions.Add(new BackendPermission { Code = BackendPermissions.TenantDashboardView, Name = "Custom permission name", Area = BackendPermissionArea.Tenant, PermissionModuleCode = "tenant_dashboard", IsSystem = true }); dbContext.BackendMenus.Add(new BackendMenu { Code = "tenant.dashboard", Title = "Custom menu title", Area = BackendPermissionArea.Tenant, Path = "/custom-dashboard", PermissionCode = BackendPermissions.TenantDashboardView, IsActive = true }); await dbContext.SaveChangesAsync(); dbContext.ChangeTracker.Clear(); await new BuiltinBackofficeCatalogSeeder(dbContext).SeedAsync(); Assert.Equal("Custom feature name", (await dbContext.SaasFeatures.SingleAsync( item => item.Code == SaasFeatureCatalog.CoreBackoffice)).Name); Assert.Equal("Custom module name", (await dbContext.PermissionModules.SingleAsync( item => item.Code == "tenant_dashboard")).Name); Assert.Equal("Custom permission name", (await dbContext.BackendPermissions.SingleAsync( item => item.Code == BackendPermissions.TenantDashboardView)).Name); Assert.Equal("Custom menu title", (await dbContext.BackendMenus.SingleAsync( item => item.Code == "tenant.dashboard")).Title); Assert.Equal(15, await dbContext.SaasFeatures.CountAsync()); Assert.Equal(26, await dbContext.PermissionModules.CountAsync()); Assert.Equal(35, await dbContext.BackendPermissions.CountAsync()); Assert.Equal(21, await dbContext.BackendMenus.CountAsync()); Assert.False(await dbContext.PermissionModules.AnyAsync(module => module.RequiredFeatureCode != null && !dbContext.SaasFeatures.Any(feature => feature.Code == module.RequiredFeatureCode))); Assert.False(await dbContext.BackendPermissions.AnyAsync(permission => !dbContext.PermissionModules.Any(module => module.Code == permission.PermissionModuleCode))); Assert.False(await dbContext.BackendMenus.AnyAsync(menu => menu.PermissionCode != null && !dbContext.BackendPermissions.Any(permission => permission.Code == menu.PermissionCode))); } [Fact] public async Task Seed_backfills_new_permissions_for_existing_system_super_admin() { using var database = PostgresTestDatabase.Create(); await using var dbContext = CreateDbContext(database.ConnectionString); var seeder = new BuiltinBackofficeCatalogSeeder(dbContext); await seeder.SeedAsync(); var role = new PlatformBackendRole { Code = PlatformAdminBootstrapper.SuperAdminRoleCode, Name = "超级管理员", Status = BackendRoleStatus.Active, IsSystem = true }; dbContext.PlatformBackendRoles.Add(role); dbContext.PlatformBackendRolePermissions.Add(new PlatformBackendRolePermission { RoleId = role.Id, PermissionCode = BackendPermissions.PlatformDashboardView }); await dbContext.SaveChangesAsync(); dbContext.ChangeTracker.Clear(); await seeder.SeedAsync(); dbContext.ChangeTracker.Clear(); await seeder.SeedAsync(); var permissionCodes = await dbContext.PlatformBackendRolePermissions.AsNoTracking() .Where(binding => binding.RoleId == role.Id) .Select(binding => binding.PermissionCode) .ToArrayAsync(); Assert.Equal(BackendPermissions.Platform.Count, permissionCodes.Length); Assert.Contains(BackendPermissions.PlatformQuestionBankManage, permissionCodes); Assert.Equal(BackendPermissions.Platform.Count, permissionCodes.Distinct(StringComparer.Ordinal).Count()); } private static TikuDbContext CreateDbContext(string connectionString) { var options = new DbContextOptionsBuilder() .UseNpgsql(connectionString, npgsql => npgsql.MigrationsAssembly(typeof(TikuDbContext).Assembly.FullName)) .Options; return new TikuDbContext(options); } }