根因:PlatformDbContext 注册时缺少 UseSnakeCaseNamingConvention, 导致 EF 查询用 PascalCase 列名("Id")但实际表是 snake_case("id"), 报 column u.Id does not exist。
96 lines
3.8 KiB
C#
96 lines
3.8 KiB
C#
namespace BuildFlow.Architecture.Tests;
|
||
|
||
using Xunit;
|
||
|
||
/// <summary>
|
||
/// BuildFlow Host 架构约束测试。确保后台只有 BuildFlow Host 一个启动入口,
|
||
/// 业务模块(Domain/Application/Infrastructure 三层)不能退化为独立进程。
|
||
/// HTTP 层已统一并入 BuildFlow.Host,模块不再保留 Api 类库。
|
||
/// </summary>
|
||
public class HostArchitectureTests
|
||
{
|
||
[Fact]
|
||
public void BusinessModulesShouldNotContainStandaloneProgramEntryPoints()
|
||
{
|
||
var repoRoot = FindRepoRoot();
|
||
var moduleRoot = Path.Combine(repoRoot.FullName, "src", "modules");
|
||
|
||
var programFiles = Directory
|
||
.EnumerateFiles(moduleRoot, "Program.cs", SearchOption.AllDirectories)
|
||
.Where(path => !ContainsPathSegment(path, "bin") && !ContainsPathSegment(path, "obj"))
|
||
.Select(path => Path.GetRelativePath(repoRoot.FullName, path).Replace('\\', '/'))
|
||
.Order(StringComparer.Ordinal)
|
||
.ToArray();
|
||
|
||
Assert.True(
|
||
programFiles.Length == 0,
|
||
"业务模块不允许包含独立 Program.cs,后台应只由 src/BuildFlow.Host/Program.cs 统一启动。当前残留:" +
|
||
Environment.NewLine +
|
||
string.Join(Environment.NewLine, programFiles));
|
||
}
|
||
|
||
[Fact]
|
||
public void BusinessModulesShouldNotContainLaunchProfiles()
|
||
{
|
||
var repoRoot = FindRepoRoot();
|
||
var moduleRoot = Path.Combine(repoRoot.FullName, "src", "modules");
|
||
|
||
var launchProfiles = Directory
|
||
.EnumerateFiles(moduleRoot, "launchSettings.json", SearchOption.AllDirectories)
|
||
.Where(path => !ContainsPathSegment(path, "bin") && !ContainsPathSegment(path, "obj"))
|
||
.Select(path => Path.GetRelativePath(repoRoot.FullName, path).Replace('\\', '/'))
|
||
.Order(StringComparer.Ordinal)
|
||
.ToArray();
|
||
|
||
Assert.True(
|
||
launchProfiles.Length == 0,
|
||
"业务模块不允许保留独立启动配置 launchSettings.json。当前残留:" +
|
||
Environment.NewLine +
|
||
string.Join(Environment.NewLine, launchProfiles));
|
||
}
|
||
|
||
[Fact]
|
||
public void BusinessModulesShouldNotHaveStandaloneApiProjects()
|
||
{
|
||
var repoRoot = FindRepoRoot();
|
||
var moduleRoot = Path.Combine(repoRoot.FullName, "src", "modules");
|
||
|
||
var apiProjects = Directory
|
||
.EnumerateFiles(moduleRoot, "*.Api.csproj", SearchOption.AllDirectories)
|
||
.Concat(Directory.EnumerateFiles(moduleRoot, "*Api.csproj", SearchOption.AllDirectories))
|
||
.Where(path => !ContainsPathSegment(path, "bin") && !ContainsPathSegment(path, "obj"))
|
||
.Distinct(StringComparer.Ordinal)
|
||
.Select(path => Path.GetRelativePath(repoRoot.FullName, path).Replace('\\', '/'))
|
||
.Order(StringComparer.Ordinal)
|
||
.ToArray();
|
||
|
||
Assert.True(
|
||
apiProjects.Length == 0,
|
||
"业务模块不允许保留独立 Api 类库项目(HTTP 层已统一并入 BuildFlow.Host)。当前残留:" +
|
||
Environment.NewLine +
|
||
string.Join(Environment.NewLine, apiProjects));
|
||
}
|
||
|
||
private static DirectoryInfo FindRepoRoot()
|
||
{
|
||
var current = new DirectoryInfo(AppContext.BaseDirectory);
|
||
while (current is not null)
|
||
{
|
||
if (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));
|
||
}
|
||
}
|