Files
ColaFlow/colaflow-api/tests/ColaFlow.ArchitectureTests/ModuleBoundaryTests.cs
Yaojia Wang 014d62bcc2 Project Init
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 23:55:18 +01:00

176 lines
5.4 KiB
C#

using NetArchTest.Rules;
using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate;
using ColaFlow.Shared.Kernel.Common;
namespace ColaFlow.ArchitectureTests;
/// <summary>
/// Architecture tests to enforce module boundaries and dependencies.
/// These tests ensure the modular monolith architecture is maintained.
/// </summary>
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<string>())}");
}
[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<string>())}");
}
[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<string>())}");
}
[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<string>())}");
}
[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<string>())}");
}
[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<string>())}");
}
}