Reformat/cleanup
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m10s
All checks were successful
NuGet Package Publish / nuget (push) Successful in 1m10s
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user