- Created `StellaOps.TestKit.Tests` project for unit tests related to determinism. - Implemented `DeterminismManifestTests` to validate deterministic output for canonical bytes and strings, file read/write operations, and error handling for invalid schema versions. - Added `SbomDeterminismTests` to ensure identical inputs produce consistent SBOMs across SPDX 3.0.1 and CycloneDX 1.6/1.7 formats, including parallel execution tests. - Updated project references in `StellaOps.Integration.Determinism` to include the new determinism testing library.
159 lines
5.8 KiB
C#
159 lines
5.8 KiB
C#
using System.Reflection;
|
|
using NetArchTest.Rules;
|
|
using Xunit;
|
|
using FluentAssertions;
|
|
|
|
namespace StellaOps.Architecture.Tests;
|
|
|
|
/// <summary>
|
|
/// Architecture tests for naming convention rules.
|
|
/// Enforces consistent naming across the codebase.
|
|
/// </summary>
|
|
[Trait("Category", "Architecture")]
|
|
public sealed class NamingConventionRulesTests
|
|
{
|
|
/// <summary>
|
|
/// Test projects must end with .Tests.
|
|
/// </summary>
|
|
[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)");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Plugin assemblies must follow naming pattern.
|
|
/// </summary>
|
|
[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.<Module>.Plugin.* or StellaOps.<Module>.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.<Module>.Plugin[s].*");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connector assemblies must follow naming pattern.
|
|
/// </summary>
|
|
[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.<Module>.Connector.* or StellaOps.<Module>.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.<Module>.Connector[.*]");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Storage assemblies must follow naming pattern.
|
|
/// </summary>
|
|
[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.<Module>.Storage or StellaOps.<Module>.Storage.<Provider>
|
|
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.<Module>.Storage[.<Provider>]");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Interface types should start with 'I'.
|
|
/// </summary>
|
|
[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<string>())}");
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|