using System.Reflection;
using NetArchTest.Rules;
using Xunit;
using FluentAssertions;
namespace StellaOps.Architecture.Tests;
///
/// Architecture tests for naming convention rules.
/// Enforces consistent naming across the codebase.
///
[Trait("Category", "Architecture")]
public sealed class NamingConventionRulesTests
{
///
/// Test projects must end with .Tests.
///
[Fact]
public void TestProjects_MustEndWith_Tests()
{
var testAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name?.StartsWith("StellaOps") == true)
.Where(a => ContainsTestTypes(a));
foreach (var assembly in testAssemblies)
{
var name = assembly.GetName().Name;
// If it has test types, it should end with .Tests
if (!name!.EndsWith(".Tests"))
{
// Check if it's in a known test location pattern
var isValidTestAssembly = name.Contains("Test") ||
name.EndsWith(".Tests") ||
name.Contains("Testing");
isValidTestAssembly.Should().BeTrue(
$"Assembly {name} contains tests but doesn't follow naming convention (.Tests suffix)");
}
}
}
///
/// Plugin assemblies must follow naming pattern.
///
[Fact]
public void Plugins_MustFollow_NamingPattern()
{
var pluginAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name?.Contains("Plugin") == true &&
a.GetName().Name?.StartsWith("StellaOps") == true);
foreach (var assembly in pluginAssemblies)
{
var name = assembly.GetName().Name!;
// Valid patterns: StellaOps..Plugin.* or StellaOps..Plugins.*
var isValidPluginName =
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Plugin\.\w+$") ||
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Plugins\.\w+$") ||
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Plugin$");
isValidPluginName.Should().BeTrue(
$"Plugin assembly {name} doesn't follow naming pattern StellaOps..Plugin[s].*");
}
}
///
/// Connector assemblies must follow naming pattern.
///
[Fact]
public void Connectors_MustFollow_NamingPattern()
{
var connectorAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name?.Contains("Connector") == true &&
a.GetName().Name?.StartsWith("StellaOps") == true);
foreach (var assembly in connectorAssemblies)
{
var name = assembly.GetName().Name!;
// Valid patterns: StellaOps..Connector.* or StellaOps..Connector
var isValidConnectorName =
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Connector\.\w+$") ||
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Connector$") ||
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Connector\.Common$");
isValidConnectorName.Should().BeTrue(
$"Connector assembly {name} doesn't follow naming pattern StellaOps..Connector[.*]");
}
}
///
/// Storage assemblies must follow naming pattern.
///
[Fact]
public void Storage_MustFollow_NamingPattern()
{
var storageAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name?.Contains("Storage") == true &&
a.GetName().Name?.StartsWith("StellaOps") == true);
foreach (var assembly in storageAssemblies)
{
var name = assembly.GetName().Name!;
// Valid patterns: StellaOps..Storage or StellaOps..Storage.
var isValidStorageName =
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Storage$") ||
System.Text.RegularExpressions.Regex.IsMatch(name, @"^StellaOps\.\w+\.Storage\.\w+$");
isValidStorageName.Should().BeTrue(
$"Storage assembly {name} doesn't follow naming pattern StellaOps..Storage[.]");
}
}
///
/// Interface types should start with 'I'.
///
[Fact]
public void Interfaces_MustStartWith_I()
{
var stellaOpsAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name?.StartsWith("StellaOps") == true &&
!a.GetName().Name?.Contains("Test") == true);
if (!stellaOpsAssemblies.Any())
{
return;
}
var result = Types.InAssemblies(stellaOpsAssemblies)
.That()
.AreInterfaces()
.Should()
.HaveNameStartingWith("I")
.GetResult();
result.IsSuccessful.Should().BeTrue(
$"Interface types must start with 'I'. " +
$"Violations: {string.Join(", ", result.FailingTypeNames ?? Enumerable.Empty())}");
}
private static bool ContainsTestTypes(Assembly assembly)
{
try
{
return assembly.GetTypes()
.Any(t => t.GetMethods()
.Any(m => m.GetCustomAttributes(typeof(FactAttribute), false).Any() ||
m.GetCustomAttributes(typeof(TheoryAttribute), false).Any()));
}
catch
{
return false;
}
}
}