Reformat/cleanup
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m10s

This commit is contained in:
Joseph Doherty
2026-02-21 07:53:53 -05:00
parent c6f6d9329a
commit 7ebc2cb567
160 changed files with 7258 additions and 7262 deletions

View File

@@ -1,14 +1,12 @@
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using ZB.MOM.WW.CBDDC.Core;
namespace ZB.MOM.WW.CBDDC.Core.Tests;
public class ArchitectureFitnessTests
{
/// <summary>
/// Verifies that the core assembly does not reference outer-layer assemblies.
/// Verifies that the core assembly does not reference outer-layer assemblies.
/// </summary>
[Fact]
public void CoreAssembly_ShouldNotReferenceOuterAssemblies()
@@ -25,71 +23,71 @@ public class ArchitectureFitnessTests
}
/// <summary>
/// Verifies that project references under src form an acyclic graph.
/// Verifies that project references under src form an acyclic graph.
/// </summary>
[Fact]
public void SourceProjectGraph_ShouldBeAcyclic()
{
var repoRoot = FindRepoRoot();
var srcRoot = Path.Combine(repoRoot, "src");
string repoRoot = FindRepoRoot();
string srcRoot = Path.Combine(repoRoot, "src");
var projectFiles = Directory
.EnumerateFiles(srcRoot, "*.csproj", SearchOption.AllDirectories)
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}", StringComparison.Ordinal))
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}",
StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}",
StringComparison.Ordinal))
.ToList();
var nodes = projectFiles.ToDictionary(
p => Path.GetFileNameWithoutExtension(p),
p => new HashSet<string>(StringComparer.Ordinal));
foreach (var projectFile in projectFiles)
foreach (string projectFile in projectFiles)
{
var projectName = Path.GetFileNameWithoutExtension(projectFile);
string projectName = Path.GetFileNameWithoutExtension(projectFile);
var doc = XDocument.Load(projectFile);
var refs = doc.Descendants("ProjectReference")
.Select(x => x.Attribute("Include")?.Value)
.Where(v => !string.IsNullOrWhiteSpace(v))
.Select(v => Path.GetFileNameWithoutExtension(v!.Replace('\\', '/')));
foreach (var reference in refs)
{
foreach (string reference in refs)
if (nodes.ContainsKey(reference))
{
nodes[projectName].Add(reference);
}
}
}
HasCycle(nodes).ShouldBeFalse();
}
/// <summary>
/// Verifies the allowed dependency graph between source projects.
/// Verifies the allowed dependency graph between source projects.
/// </summary>
[Fact]
public void SourceProjectReferences_ShouldMatchAllowedDependencyGraph()
{
var repoRoot = FindRepoRoot();
var srcRoot = Path.Combine(repoRoot, "src");
string repoRoot = FindRepoRoot();
string srcRoot = Path.Combine(repoRoot, "src");
var projectFiles = Directory
.EnumerateFiles(srcRoot, "*.csproj", SearchOption.AllDirectories)
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}", StringComparison.Ordinal))
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}",
StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}",
StringComparison.Ordinal))
.ToList();
var allowedDependencies = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal)
{
["ZB.MOM.WW.CBDDC.Core"] = new HashSet<string>(StringComparer.Ordinal),
["ZB.MOM.WW.CBDDC.Network"] = new HashSet<string>(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Core" },
["ZB.MOM.WW.CBDDC.Persistence"] = new HashSet<string>(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Core" },
["ZB.MOM.WW.CBDDC.Hosting"] = new HashSet<string>(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Network" }
["ZB.MOM.WW.CBDDC.Core"] = new(StringComparer.Ordinal),
["ZB.MOM.WW.CBDDC.Network"] = new(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Core" },
["ZB.MOM.WW.CBDDC.Persistence"] = new(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Core" },
["ZB.MOM.WW.CBDDC.Hosting"] = new(StringComparer.Ordinal) { "ZB.MOM.WW.CBDDC.Network" }
};
foreach (var projectFile in projectFiles)
foreach (string projectFile in projectFiles)
{
var projectName = Path.GetFileNameWithoutExtension(projectFile);
string projectName = Path.GetFileNameWithoutExtension(projectFile);
allowedDependencies.ContainsKey(projectName)
.ShouldBeTrue($"Unexpected source project found: {projectName}");
@@ -105,18 +103,19 @@ public class ArchitectureFitnessTests
var missing = expected.Where(e => !references.Contains(e)).ToList();
extra.ShouldBeEmpty($"Project {projectName} has disallowed references: {string.Join(", ", extra)}");
missing.ShouldBeEmpty($"Project {projectName} is missing required references: {string.Join(", ", missing)}");
missing.ShouldBeEmpty(
$"Project {projectName} is missing required references: {string.Join(", ", missing)}");
}
}
/// <summary>
/// Verifies non-generic ILogger usage is restricted to explicit compatibility shims.
/// Verifies non-generic ILogger usage is restricted to explicit compatibility shims.
/// </summary>
[Fact]
public void SourceCode_ShouldRestrictNonGenericILoggerUsage()
{
var repoRoot = FindRepoRoot();
var srcRoot = Path.Combine(repoRoot, "src");
string repoRoot = FindRepoRoot();
string srcRoot = Path.Combine(repoRoot, "src");
var loggerPattern = new Regex(@"\bILogger\b(?!\s*<|\s*Factory\b)", RegexOptions.Compiled);
var allowedSnippets = new[]
@@ -130,45 +129,39 @@ public class ArchitectureFitnessTests
var violations = new List<string>();
var sourceFiles = Directory.EnumerateFiles(srcRoot, "*.cs", SearchOption.AllDirectories)
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}", StringComparison.Ordinal));
.Where(p => !p.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}",
StringComparison.Ordinal)
&& !p.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}",
StringComparison.Ordinal));
foreach (var file in sourceFiles)
foreach (string file in sourceFiles)
{
var lines = File.ReadAllLines(file);
string[] lines = File.ReadAllLines(file);
for (var i = 0; i < lines.Length; i++)
{
var line = lines[i].Trim();
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//", StringComparison.Ordinal))
{
continue;
}
string line = lines[i].Trim();
if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//", StringComparison.Ordinal)) continue;
if (!loggerPattern.IsMatch(line))
{
continue;
}
if (!loggerPattern.IsMatch(line)) continue;
if (allowedSnippets.Any(line.Contains))
{
continue;
}
if (allowedSnippets.Any(line.Contains)) continue;
var relativePath = Path.GetRelativePath(repoRoot, file).Replace('\\', '/');
string relativePath = Path.GetRelativePath(repoRoot, file).Replace('\\', '/');
violations.Add($"{relativePath}:{i + 1} -> {line}");
}
}
violations.ShouldBeEmpty($"Unexpected non-generic ILogger usage:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}");
violations.ShouldBeEmpty(
$"Unexpected non-generic ILogger usage:{Environment.NewLine}{string.Join(Environment.NewLine, violations)}");
}
/// <summary>
/// Verifies log boundaries push operation context for hosted/background entry points.
/// Verifies log boundaries push operation context for hosted/background entry points.
/// </summary>
[Fact]
public void BoundaryServices_ShouldPushOperationLogContext()
{
var repoRoot = FindRepoRoot();
string repoRoot = FindRepoRoot();
var boundaryFiles = new[]
{
"src/ZB.MOM.WW.CBDDC.Network/CBDDCNodeService.cs",
@@ -180,24 +173,24 @@ public class ArchitectureFitnessTests
"src/ZB.MOM.WW.CBDDC.Hosting/Services/NoOpSyncOrchestrator.cs"
};
foreach (var relativePath in boundaryFiles)
foreach (string relativePath in boundaryFiles)
{
var filePath = Path.Combine(repoRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
string filePath = Path.Combine(repoRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
File.Exists(filePath).ShouldBeTrue($"Missing expected boundary file: {relativePath}");
var contents = File.ReadAllText(filePath);
string contents = File.ReadAllText(filePath);
contents.Contains("LogContext.PushProperty(\"OperationId\"", StringComparison.Ordinal)
.ShouldBeTrue($"Boundary file is missing OperationId log enrichment: {relativePath}");
}
}
/// <summary>
/// Verifies boundary projects include Serilog for LogContext support.
/// Verifies boundary projects include Serilog for LogContext support.
/// </summary>
[Fact]
public void BoundaryProjects_ShouldReferenceSerilog()
{
var repoRoot = FindRepoRoot();
string repoRoot = FindRepoRoot();
var projects = new[]
{
"src/ZB.MOM.WW.CBDDC.Network/ZB.MOM.WW.CBDDC.Network.csproj",
@@ -205,12 +198,12 @@ public class ArchitectureFitnessTests
"samples/ZB.MOM.WW.CBDDC.Sample.Console/ZB.MOM.WW.CBDDC.Sample.Console.csproj"
};
foreach (var relativePath in projects)
foreach (string relativePath in projects)
{
var filePath = Path.Combine(repoRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
string filePath = Path.Combine(repoRoot, relativePath.Replace('/', Path.DirectorySeparatorChar));
File.Exists(filePath).ShouldBeTrue($"Missing project file: {relativePath}");
var contents = File.ReadAllText(filePath);
string contents = File.ReadAllText(filePath);
contents.Contains("<PackageReference Include=\"Serilog\"", StringComparison.Ordinal)
.ShouldBeTrue($"Serilog package reference is required for logging boundary enrichment: {relativePath}");
}
@@ -218,13 +211,10 @@ public class ArchitectureFitnessTests
private static string FindRepoRoot()
{
var dir = AppContext.BaseDirectory;
string dir = AppContext.BaseDirectory;
for (var i = 0; i < 10 && !string.IsNullOrWhiteSpace(dir); i++)
{
if (File.Exists(Path.Combine(dir, "CBDDC.slnx")))
{
return dir;
}
if (File.Exists(Path.Combine(dir, "CBDDC.slnx"))) return dir;
dir = Directory.GetParent(dir)?.FullName ?? string.Empty;
}
@@ -239,24 +229,14 @@ public class ArchitectureFitnessTests
bool Dfs(string node)
{
if (visiting.Contains(node))
{
return true;
}
if (visiting.Contains(node)) return true;
if (!visited.Add(node))
{
return false;
}
if (!visited.Add(node)) return false;
visiting.Add(node);
foreach (var next in graph[node])
{
foreach (string next in graph[node])
if (Dfs(next))
{
return true;
}
}
visiting.Remove(node);
return false;
@@ -264,4 +244,4 @@ public class ArchitectureFitnessTests
return graph.Keys.Any(Dfs);
}
}
}