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,136 @@
|
||||
using System.Reflection;
|
||||
using NetArchTest.Rules;
|
||||
using Xunit;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace StellaOps.Architecture.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Architecture tests for module dependency rules.
|
||||
/// Enforces proper layering between Core, Infrastructure, and WebService assemblies.
|
||||
/// </summary>
|
||||
[Trait("Category", "Architecture")]
|
||||
public sealed class ModuleDependencyRulesTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Core libraries must not depend on infrastructure (e.g., Postgres storage).
|
||||
/// Core should be pure business logic, infrastructure-agnostic.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CoreLibraries_MustNot_Depend_On_Infrastructure()
|
||||
{
|
||||
var coreAssemblies = GetAssembliesByPattern("Core");
|
||||
|
||||
if (!coreAssemblies.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = Types.InAssemblies(coreAssemblies)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOnAny(
|
||||
"StellaOps.*.Storage.Postgres",
|
||||
"StellaOps.*.Storage.Valkey",
|
||||
"Npgsql",
|
||||
"StackExchange.Redis")
|
||||
.GetResult();
|
||||
|
||||
result.IsSuccessful.Should().BeTrue(
|
||||
$"Core libraries must not depend on infrastructure. " +
|
||||
$"Violations: {string.Join(", ", result.FailingTypeNames ?? Enumerable.Empty<string>())}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WebServices may depend on Core and Storage, but not on other WebServices.
|
||||
/// Each WebService should be independently deployable.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void WebServices_MustNot_Depend_On_Other_WebServices()
|
||||
{
|
||||
var webServiceAssemblies = GetAssembliesByPattern("WebService");
|
||||
|
||||
if (!webServiceAssemblies.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var assembly in webServiceAssemblies)
|
||||
{
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
var otherWebServices = webServiceAssemblies
|
||||
.Where(a => a.GetName().Name != assemblyName)
|
||||
.Select(a => a.GetName().Name!)
|
||||
.ToArray();
|
||||
|
||||
if (!otherWebServices.Any())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var result = Types.InAssembly(assembly)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOnAny(otherWebServices)
|
||||
.GetResult();
|
||||
|
||||
result.IsSuccessful.Should().BeTrue(
|
||||
$"WebService {assemblyName} must not depend on other WebServices. " +
|
||||
$"Violations: {string.Join(", ", result.FailingTypeNames ?? Enumerable.Empty<string>())}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Workers may depend on Core and Storage, but not directly on WebServices.
|
||||
/// Workers are background processes, independent of HTTP layer.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Workers_MustNot_Depend_On_WebServices()
|
||||
{
|
||||
var workerAssemblies = GetAssembliesByPattern("Worker");
|
||||
|
||||
if (!workerAssemblies.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = Types.InAssemblies(workerAssemblies)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOnAny("Microsoft.AspNetCore.Mvc", "StellaOps.*.WebService")
|
||||
.GetResult();
|
||||
|
||||
result.IsSuccessful.Should().BeTrue(
|
||||
$"Worker assemblies must not depend on WebServices. " +
|
||||
$"Violations: {string.Join(", ", result.FailingTypeNames ?? Enumerable.Empty<string>())}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Models/DTOs should not depend on infrastructure or services.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Models_MustNot_Depend_On_Services()
|
||||
{
|
||||
var modelAssemblies = GetAssembliesByPattern("Models");
|
||||
|
||||
if (!modelAssemblies.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var result = Types.InAssemblies(modelAssemblies)
|
||||
.ShouldNot()
|
||||
.HaveDependencyOnAny(
|
||||
"StellaOps.*.Storage.*",
|
||||
"StellaOps.*.WebService",
|
||||
"Microsoft.AspNetCore.*")
|
||||
.GetResult();
|
||||
|
||||
result.IsSuccessful.Should().BeTrue(
|
||||
$"Model assemblies must not depend on services. " +
|
||||
$"Violations: {string.Join(", ", result.FailingTypeNames ?? Enumerable.Empty<string>())}");
|
||||
}
|
||||
|
||||
private static IEnumerable<Assembly> GetAssembliesByPattern(string pattern)
|
||||
{
|
||||
return AppDomain.CurrentDomain.GetAssemblies()
|
||||
.Where(a => a.GetName().Name?.Contains(pattern) == true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user