using System.Reflection; using System.Xml.Linq; namespace FinalFactory.Rendezvous.Tests.Architecture; public sealed class DependencyBoundaryTests { [Fact] public void ContractsAreTransportAndEngineIndependent() { string[] references = GetReferences("FinalFactory.Rendezvous.Contracts"); Assert.DoesNotContain(references, IsGodotAssembly); Assert.DoesNotContain(references, IsServerAssembly); Assert.DoesNotContain(references, IsLiteNetLibAssembly); } [Fact] public void ClientIsGodotAndServerIndependent() { string[] references = GetReferences("FinalFactory.Rendezvous.Client"); Assert.DoesNotContain(references, IsGodotAssembly); Assert.DoesNotContain(references, IsServerAssembly); } [Fact] public void TestClientUsesOnlyPublicRendezvousDependencies() { string[] references = GetReferences("FinalFactory.Rendezvous.TestClient"); Assert.DoesNotContain(references, IsGodotAssembly); Assert.DoesNotContain(references, IsServerAssembly); } [Fact] public void DeclaredDependencyGraphMatchesTheArchitecture() { AssertDeclaredDependencies( "src/FinalFactory.Rendezvous.Contracts/FinalFactory.Rendezvous.Contracts.csproj", [], ["System.Text.Json"]); AssertDeclaredDependencies( "src/FinalFactory.Rendezvous.Client/FinalFactory.Rendezvous.Client.csproj", ["FinalFactory.Rendezvous.Contracts"], ["LiteNetLib"]); AssertDeclaredDependencies( "src/FinalFactory.Rendezvous.Server/FinalFactory.Rendezvous.Server.csproj", ["FinalFactory.Rendezvous.Contracts"], [ "LiteNetLib", "Microsoft.AspNetCore.OpenApi", "Microsoft.Extensions.ApiDescription.Server", ]); AssertDeclaredDependencies( "src/FinalFactory.Rendezvous.TestClient/FinalFactory.Rendezvous.TestClient.csproj", ["FinalFactory.Rendezvous.Client", "FinalFactory.Rendezvous.Contracts"], ["LiteNetLib"]); } private static void AssertDeclaredDependencies( string projectPath, string[] expectedProjects, string[] expectedPackages) { XDocument project = XDocument.Load(Path.Combine(FindRepositoryRoot(), projectPath)); string[] projects = project .Descendants("ProjectReference") .Select(static element => Path.GetFileNameWithoutExtension(element.Attribute("Include")?.Value) ?? string.Empty) .Order(StringComparer.Ordinal) .ToArray(); string[] packages = project .Descendants("PackageReference") .Select(static element => element.Attribute("Include")?.Value ?? string.Empty) .Order(StringComparer.Ordinal) .ToArray(); Assert.Equal(expectedProjects.Order(StringComparer.Ordinal), projects); Assert.Equal(expectedPackages.Order(StringComparer.Ordinal), packages); } private static string FindRepositoryRoot() { DirectoryInfo? directory = new(AppContext.BaseDirectory); while (directory is not null) { if (File.Exists(Path.Combine(directory.FullName, "Rendezvous.slnx"))) { return directory.FullName; } directory = directory.Parent; } throw new DirectoryNotFoundException("Could not locate the Rendezvous repository root."); } private static string[] GetReferences(string assemblyName) => Assembly .Load(assemblyName) .GetReferencedAssemblies() .Select(static reference => reference.Name ?? string.Empty) .ToArray(); private static bool IsGodotAssembly(string assemblyName) => assemblyName.StartsWith("Godot", StringComparison.OrdinalIgnoreCase); private static bool IsLiteNetLibAssembly(string assemblyName) => string.Equals(assemblyName, "LiteNetLib", StringComparison.Ordinal); private static bool IsServerAssembly(string assemblyName) => string.Equals( assemblyName, "FinalFactory.Rendezvous.Server", StringComparison.Ordinal); }