build-flow.api/tests/BuildFlow.Architecture.Tests/PermissionRegistrationTests.cs
2026-07-24 12:29:06 +08:00

112 lines
4.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Text.RegularExpressions;
using Xunit;
namespace BuildFlow.Architecture.Tests;
/// <summary>
/// 权限码注册约束测试。保证 Host 端点声明的权限码都能进入平台权限表,
/// 避免端点要求了权限但角色管理页无法分配,或登录 token 永远拿不到对应权限码。
/// </summary>
public partial class PermissionRegistrationTests
{
[Fact]
public void EndpointPermissionCodesShouldBeSeeded()
{
var repoRoot = FindRepoRoot();
var endpointCodes = ReadEndpointPermissionCodes(repoRoot).ToArray();
var seededCodes = ReadSeedPermissionCodes(repoRoot).ToHashSet(StringComparer.Ordinal);
var missing = endpointCodes
.Where(code => !seededCodes.Contains(code))
.ToArray();
Assert.True(
missing.Length == 0,
"端点声明的权限码必须同步写入 SeedData.SeedPermissionsAsync,否则角色无法分配这些权限:" +
Environment.NewLine +
string.Join(Environment.NewLine, missing));
}
[Fact]
public void SeededPlatformPermissionCodesShouldBeEnforcedByEndpoints()
{
var repoRoot = FindRepoRoot();
var endpointCodes = ReadEndpointPermissionCodes(repoRoot).ToHashSet(StringComparer.Ordinal);
var seededCodes = ReadSeedPermissionCodes(repoRoot)
// button PermissionCode 允许配置为业务动态权限,不一定对应固定 endpoint。
.Where(code => !code.StartsWith("dynamic:", StringComparison.Ordinal))
.Order(StringComparer.Ordinal)
.ToArray();
var unused = seededCodes
.Where(code => !endpointCodes.Contains(code))
.ToArray();
Assert.True(
unused.Length == 0,
"SeedData 中的平台权限码必须有对应后端端点强校验,否则角色管理页可分配但后端不生效:" +
Environment.NewLine +
string.Join(Environment.NewLine, unused));
}
private static DirectoryInfo FindRepoRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current is not null)
{
if (System.IO.File.Exists(Path.Combine(current.FullName, "BuildFlow.slnx")))
{
return current;
}
current = current.Parent;
}
throw new InvalidOperationException("无法定位 BuildFlow.slnx,权限码注册测试无法确定仓库边界。");
}
private static bool ContainsPathSegment(string path, string segment)
{
return path.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
.Any(part => string.Equals(part, segment, StringComparison.Ordinal));
}
private static IEnumerable<string> ReadEndpointPermissionCodes(DirectoryInfo repoRoot)
{
var endpointRoot = Path.Combine(repoRoot.FullName, "src", "BuildFlow.Host", "Endpoints");
return Directory
.EnumerateFiles(endpointRoot, "*.cs", SearchOption.AllDirectories)
.Where(path => !ContainsPathSegment(path, "bin") && !ContainsPathSegment(path, "obj"))
.SelectMany(path => PermissionCallRegex()
.Matches(System.IO.File.ReadAllText(path))
.Select(match => match.Groups["code"].Value))
.Distinct(StringComparer.Ordinal)
.Order(StringComparer.Ordinal);
}
private static IEnumerable<string> ReadSeedPermissionCodes(DirectoryInfo repoRoot)
{
var seedFile = Path.Combine(
repoRoot.FullName,
"src",
"modules",
"platform",
"Infrastructure",
"Persistence",
"SeedData.cs");
return SeedPermissionRegex()
.Matches(System.IO.File.ReadAllText(seedFile))
.Select(match => match.Groups["code"].Value)
.Distinct(StringComparer.Ordinal)
.Order(StringComparer.Ordinal);
}
[GeneratedRegex("Permissions\\(\"(?<code>[^\"]+)\"\\)")]
private static partial Regex PermissionCallRegex();
[GeneratedRegex("new\\s+SeedPermission\\([^,]+,\\s*\"[^\"]+\",\\s*\"(?<code>[^\"]+)\"")]
private static partial Regex SeedPermissionRegex();
}