build-flow.api/tests/BuildFlow.Platform.Tests/Auth/BootstrapAdminSeederTests.cs
2026-07-23 17:55:24 +08:00

105 lines
3.3 KiB
C#

using BuildFlow.Platform.Domain.Entities.Role;
using BuildFlow.Platform.Infrastructure.Configuration;
using BuildFlow.Platform.Infrastructure.Persistence;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace BuildFlow.Platform.Tests.Auth;
/// <summary>
/// 首次部署管理员初始化安全边界测试。
/// </summary>
public class BootstrapAdminSeederTests
{
private static BootstrapAdminOptions ValidOptions(string password = "BootstrapAdmin1") => new()
{
Enabled = true,
Username = "bootstrap-admin",
Email = "bootstrap-admin@test.local",
Password = password,
};
private static async Task SeedAdminRoleAsync(PlatformDbContext db)
{
db.Roles.Add(PlatformTestDb.WithAudit(new RoleEntity
{
Id = BootstrapAdminSeeder.AdminRoleId,
Name = "Admin",
Description = "管理员",
}));
await db.SaveChangesAsync();
}
[Fact]
public async Task DisabledShouldNotCreateAUser()
{
await using var db = PlatformTestDb.CreateDbContext();
var created = await BootstrapAdminSeeder.SeedAsync(
db,
new BootstrapAdminOptions());
created.Should().BeFalse();
db.Users.Should().BeEmpty();
}
[Fact]
public async Task EnabledWithoutPasswordShouldFailClearly()
{
await using var db = PlatformTestDb.CreateDbContext();
var options = ValidOptions("");
var act = () => BootstrapAdminSeeder.SeedAsync(db, options);
await act.Should().ThrowAsync<InvalidOperationException>()
.WithMessage("*密码不能为空*");
}
[Fact]
public async Task WeakPasswordShouldFailClearly()
{
await using var db = PlatformTestDb.CreateDbContext();
var act = () => BootstrapAdminSeeder.SeedAsync(db, ValidOptions("weakpass"));
await act.Should().ThrowAsync<InvalidOperationException>()
.WithMessage("*大写字母*");
}
[Fact]
public async Task FirstRunShouldCreateAnAdminWithTheConfiguredPassword()
{
await using var db = PlatformTestDb.CreateDbContext();
await SeedAdminRoleAsync(db);
var options = ValidOptions();
var created = await BootstrapAdminSeeder.SeedAsync(db, options);
created.Should().BeTrue();
var user = await db.Users.SingleAsync();
user.Username.Should().Be(options.Username);
BCrypt.Net.BCrypt.Verify(options.Password, user.PasswordHash).Should().BeTrue();
(await db.UserRoles.CountAsync(x =>
x.UserId == user.Id && x.RoleId == BootstrapAdminSeeder.AdminRoleId))
.Should().Be(1);
}
[Fact]
public async Task RepeatedRunShouldNotResetTheExistingAdminPassword()
{
await using var db = PlatformTestDb.CreateDbContext();
await SeedAdminRoleAsync(db);
await BootstrapAdminSeeder.SeedAsync(db, ValidOptions("BootstrapAdmin1"));
var originalHash = await db.Users.Select(x => x.PasswordHash).SingleAsync();
var created = await BootstrapAdminSeeder.SeedAsync(
db,
ValidOptions("DifferentAdmin2"));
created.Should().BeFalse();
(await db.Users.Select(x => x.PasswordHash).SingleAsync()).Should().Be(originalHash);
BCrypt.Net.BCrypt.Verify("BootstrapAdmin1", originalHash).Should().BeTrue();
}
}