using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Xml.Linq; namespace MxGateway.Worker.Tests.ProjectStructure; public sealed class WorkerProjectReferenceTests { /// Verifies that the worker project targets .NET Framework 4.8 with x86 platform. [Fact] public void WorkerProject_TargetsNet48AndX86() { XDocument project = LoadProject("MxGateway.Worker"); Assert.Equal("net48", ElementValue(project, "TargetFramework")); Assert.Equal("x86", ElementValue(project, "PlatformTarget")); Assert.Equal("true", ElementValue(project, "Prefer32Bit")); } /// Verifies that the worker test project targets .NET Framework 4.8 with x86 platform. [Fact] public void WorkerTestProject_TargetsNet48AndX86() { XDocument project = LoadProject("MxGateway.Worker.Tests"); Assert.Equal("net48", ElementValue(project, "TargetFramework")); Assert.Equal("x86", ElementValue(project, "PlatformTarget")); } /// /// Verifies that the MXAccess COM interop is referenced only by the /// worker project and its test project — never by the gateway server /// or the contracts project. The gateway must never load MXAccess COM /// directly (see gateway.md); the worker test project /// legitimately references the interop so it can exercise the /// COM-facing worker code (e.g. WnWrapAlarmConsumer). /// [Fact] public void MxAccessInteropReference_ExistsOnlyInWorkerAndWorkerTestProjects() { DirectoryInfo repositoryRoot = FindRepositoryRoot(); string[] projectFiles = Directory.GetFiles(repositoryRoot.FullName, "*.csproj", SearchOption.AllDirectories) .Where(path => path.IndexOf($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0) .Where(path => path.IndexOf($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.OrdinalIgnoreCase) < 0) .ToArray(); IReadOnlyList projectsWithMxAccessReference = projectFiles .Where(ProjectReferencesMxAccess) .Select(path => Path.GetFileNameWithoutExtension(path)) .OrderBy(name => name, StringComparer.Ordinal) .ToArray(); Assert.Equal( ["MxGateway.Worker", "MxGateway.Worker.Tests"], projectsWithMxAccessReference); } private static bool ProjectReferencesMxAccess(string projectPath) { XDocument project = XDocument.Load(projectPath); return project .Descendants() .Where(element => element.Name.LocalName is "Reference" or "COMReference" or "COMFileReference" or "PackageReference") .Select(element => (string?)element.Attribute("Include") ?? string.Empty) .Concat(project.Descendants().Where(element => element.Name.LocalName == "HintPath").Select(element => element.Value)) .Any(reference => reference.IndexOf("MxAccess", StringComparison.OrdinalIgnoreCase) >= 0 || reference.IndexOf("ArchestrA.MXAccess", StringComparison.OrdinalIgnoreCase) >= 0 || reference.IndexOf("LMXProxy", StringComparison.OrdinalIgnoreCase) >= 0); } private static XDocument LoadProject(string projectName) { DirectoryInfo repositoryRoot = FindRepositoryRoot(); string projectPath = Path.Combine(repositoryRoot.FullName, projectName, $"{projectName}.csproj"); return XDocument.Load(projectPath); } private static string ElementValue(XDocument project, string elementName) { return project .Descendants() .Single(element => element.Name.LocalName == elementName) .Value; } private static DirectoryInfo FindRepositoryRoot() { DirectoryInfo? current = new(AppContext.BaseDirectory); while (current is not null) { if (File.Exists(Path.Combine(current.FullName, "MxGateway.sln"))) { return current; } current = current.Parent; } throw new DirectoryNotFoundException("Could not locate src/MxGateway.sln from the test output directory."); } }