using NetArchTest.Rules; using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate; using ColaFlow.Shared.Kernel.Common; namespace ColaFlow.ArchitectureTests; /// /// Architecture tests to enforce module boundaries and dependencies. /// These tests ensure the modular monolith architecture is maintained. /// public class ModuleBoundaryTests { private const string PM_DOMAIN = "ColaFlow.Modules.ProjectManagement.Domain"; private const string PM_APPLICATION = "ColaFlow.Modules.ProjectManagement.Application"; private const string PM_INFRASTRUCTURE = "ColaFlow.Modules.ProjectManagement.Infrastructure"; private const string SHARED_KERNEL = "ColaFlow.Shared.Kernel"; [Fact] public void Domain_Should_Not_Depend_On_Application() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .ResideInNamespace(PM_DOMAIN) .ShouldNot() .HaveDependencyOn(PM_APPLICATION) .GetResult(); // Assert Assert.True(result.IsSuccessful, $"Domain layer should not depend on Application layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } [Fact] public void Domain_Should_Not_Depend_On_Infrastructure() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .ResideInNamespace(PM_DOMAIN) .ShouldNot() .HaveDependencyOn(PM_INFRASTRUCTURE) .GetResult(); // Assert Assert.True(result.IsSuccessful, $"Domain layer should not depend on Infrastructure layer. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } [Fact] public void Domain_Can_Only_Depend_On_Shared_Kernel() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .ResideInNamespace(PM_DOMAIN) .And() .HaveDependencyOnAny("ColaFlow") .Should() .HaveDependencyOn(SHARED_KERNEL) .Or() .ResideInNamespace(PM_DOMAIN) .GetResult(); // Assert Assert.True(result.IsSuccessful, $"Domain layer should only depend on Shared.Kernel. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } [Fact] public void Application_Should_Not_Depend_On_Infrastructure() { // Arrange // Note: Application assembly might not have types yet, so we'll skip this test for now // This test will be enabled when Application layer is implemented Assert.True(true, "Skipped - Application layer not yet implemented"); } [Fact] public void Project_Should_Be_AggregateRoot() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .HaveName("Project") .Should() .Inherit(typeof(AggregateRoot)) .GetResult(); // Assert Assert.True(result.IsSuccessful, $"Project should inherit from AggregateRoot. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } [Fact] public void Entities_Should_Inherit_From_Entity() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .ResideInNamespace($"{PM_DOMAIN}.Aggregates") .And() .AreClasses() .And() .AreNotAbstract() .Should() .Inherit(typeof(Entity)) .GetResult(); // Assert Assert.True(result.IsSuccessful, $"All entity classes should inherit from Entity. Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } [Fact] public void Domain_Events_Should_Be_Records() { // Arrange var assembly = typeof(Project).Assembly; // Act var types = Types.InAssembly(assembly) .That() .ResideInNamespace($"{PM_DOMAIN}.Events") .And() .DoNotHaveName("DomainEvent") // Exclude base class .GetTypes(); // Assert - Check if types are records (records are sealed and inherit from a specific base) foreach (var type in types) { Assert.True(type.IsClass && type.IsSealed, $"Event {type.Name} should be a record (sealed class)"); } } [Fact] public void ValueObjects_Should_Be_Immutable() { // Arrange var assembly = typeof(Project).Assembly; // Act var result = Types.InAssembly(assembly) .That() .ResideInNamespace($"{PM_DOMAIN}.ValueObjects") .And() .AreClasses() .And() .AreNotAbstract() .Should() .BeSealed() .GetResult(); // Assert Assert.True(result.IsSuccessful, $"All value objects should be sealed (immutable). Violations: {string.Join(", ", result.FailingTypeNames ?? Array.Empty())}"); } }