using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.DataProtection; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Tiku.Application.Security; using Tiku.Domain.Identity; using Tiku.Infrastructure.Bootstrap; using Tiku.Infrastructure.Auth; using Tiku.Infrastructure.Persistence; namespace Tiku.UnitTests.Bootstrap; public sealed class PlatformAdminBootstrapperTests { private const string TemporaryPassword = "Temporary9Password"; [Fact] public async Task Bootstrap_creates_forced_enrollment_super_admin_and_audit() { await using var provider = CreateProvider(); await using var scope = provider.CreateAsyncScope(); var bootstrapper = ActivatorUtilities.CreateInstance(scope.ServiceProvider); var result = await bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions( "admin@example.com", TemporaryPassword, "Initial Administrator")); var context = scope.ServiceProvider.GetRequiredService(); var user = await context.Users.SingleAsync(item => item.Id == result.UserId); Assert.True(user.ForcePasswordChange); Assert.True(user.EmailConfirmed); Assert.Equal(UserStatus.Active, user.Status); var role = await context.PlatformBackendRoles.SingleAsync(item => item.Id == result.RoleId); Assert.Equal(PlatformAdminBootstrapper.SuperAdminRoleCode, role.Code); Assert.True(role.IsSystem); Assert.Equal(BackendPermissions.Platform.Count, await context.PlatformBackendRolePermissions.CountAsync()); Assert.True(await context.PlatformBackendUserRoles.AnyAsync(item => item.UserId == user.Id && item.RoleId == role.Id)); Assert.True(await context.AuditLogs.AnyAsync(item => item.ActorUserId == user.Id && item.Action == "platform.bootstrap_admin.created")); var userManager = scope.ServiceProvider.GetRequiredService>(); Assert.True(await userManager.CheckPasswordAsync(user, TemporaryPassword)); } [Fact] public async Task Bootstrap_rejects_a_second_platform_administrator_without_mutating_data() { await using var provider = CreateProvider(); await using var scope = provider.CreateAsyncScope(); var bootstrapper = ActivatorUtilities.CreateInstance(scope.ServiceProvider); await bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("first@example.com", TemporaryPassword)); var exception = await Assert.ThrowsAsync(() => bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("second@example.com", TemporaryPassword))); Assert.Equal("platform_admin_already_exists", exception.Code); var context = scope.ServiceProvider.GetRequiredService(); Assert.Single(context.PlatformBackendUserRoles); Assert.Single(context.PlatformBackendRoles); Assert.Single(context.AuditLogs); Assert.Single(context.Users); } [Fact] public async Task Bootstrap_rejects_an_existing_user_email() { await using var provider = CreateProvider(); await using var scope = provider.CreateAsyncScope(); var userManager = scope.ServiceProvider.GetRequiredService>(); var createResult = await userManager.CreateAsync(new User { Email = "existing@example.com", UserName = "existing@example.com" }, TemporaryPassword); Assert.True(createResult.Succeeded); var bootstrapper = ActivatorUtilities.CreateInstance(scope.ServiceProvider); var exception = await Assert.ThrowsAsync(() => bootstrapper.BootstrapAsync(new PlatformAdminBootstrapOptions("existing@example.com", TemporaryPassword))); Assert.Equal("bootstrap_user_already_exists", exception.Code); } [Fact] public async Task Development_async_seed_creates_one_forced_enrollment_administrator() { await using var provider = CreateProvider(); await using var scope = provider.CreateAsyncScope(); var context = scope.ServiceProvider.GetRequiredService(); var created = await DevelopmentPlatformAdminSeeder.SeedAsync(context); var createdAgain = await DevelopmentPlatformAdminSeeder.SeedAsync(context); Assert.True(created); Assert.False(createdAgain); var user = await context.Users.SingleAsync(); Assert.Equal(DevelopmentPlatformAdminSeeder.Email, user.Email); Assert.True(user.ForcePasswordChange); Assert.Equal(BackendPermissions.Platform.Count, await context.PlatformBackendRolePermissions.CountAsync()); Assert.Single(context.PlatformBackendUserRoles); Assert.Single(context.AuditLogs); var quote = await context.PlatformBillingQuotes.SingleAsync(); Assert.Equal(quote.OriginalAmountCents - quote.DiscountAmountCents, quote.TotalAmountCents); var order = await context.PlatformBillingOrders.SingleAsync(); Assert.Equal(order.OriginalAmountCents - order.DiscountAmountCents, order.TotalAmountCents); } [Fact] public async Task Development_sync_seed_is_idempotent() { await using var provider = CreateProvider(); await using var scope = provider.CreateAsyncScope(); var context = scope.ServiceProvider.GetRequiredService(); var created = DevelopmentPlatformAdminSeeder.Seed(context); var createdAgain = DevelopmentPlatformAdminSeeder.Seed(context); Assert.True(created); Assert.False(createdAgain); Assert.Single(context.Users); Assert.Single(context.PlatformBackendUserRoles); } private static ServiceProvider CreateProvider() { var services = new ServiceCollection(); services.AddLogging(); services.AddDbContext(options => options.UseInMemoryDatabase(Guid.NewGuid().ToString())); services.AddIdentityCore(options => { options.Password.RequiredLength = 8; options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireUppercase = false; options.Password.RequireNonAlphanumeric = false; }) .AddEntityFrameworkStores() .AddDefaultTokenProviders() .AddPasswordValidator>(); services.AddDataProtection().UseEphemeralDataProtectionProvider(); return services.BuildServiceProvider(); } }