Add tests for SBOM generation determinism across multiple formats
- 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.
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user